IpoWindow, Action curve update.
[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 #include "MEM_guardedalloc.h"
41
42 #include "PIL_time.h"
43
44 #include "BLI_blenlib.h"
45 #include "BLI_arithb.h"
46
47 #include "DNA_action_types.h"
48 #include "DNA_armature_types.h"
49 #include "DNA_curve_types.h"
50 #include "DNA_ipo_types.h"
51 #include "DNA_object_types.h"
52 #include "DNA_scene_types.h"
53 #include "DNA_screen_types.h"
54 #include "DNA_userdef_types.h"
55 #include "DNA_constraint_types.h"
56 #include "DNA_key_types.h"
57 #include "DNA_mesh_types.h"
58 #include "DNA_lattice_types.h"
59
60 #include "BKE_action.h"
61 #include "BKE_armature.h"
62 #include "BKE_constraint.h"
63 #include "BKE_curve.h"
64 #include "BKE_depsgraph.h"
65 #include "BKE_global.h"
66 #include "BKE_ipo.h"
67 #include "BKE_key.h"
68 #include "BKE_library.h"
69 #include "BKE_main.h"
70 #include "BKE_utildefines.h"
71
72 #include "BIF_butspace.h"
73 #include "BIF_editaction.h"
74 #include "BIF_editview.h"
75 #include "BIF_editarmature.h"
76 #include "BIF_gl.h"
77 #include "BIF_interface.h"
78 #include "BIF_mywindow.h"
79 #include "BIF_poseobject.h"
80 #include "BIF_screen.h"
81 #include "BIF_space.h"
82 #include "BIF_toolbox.h"
83
84 #include "BSE_edit.h"
85 #include "BSE_drawipo.h"
86 #include "BSE_headerbuttons.h"
87 #include "BSE_editipo.h"
88 #include "BSE_trans_types.h"
89
90 #include "BDR_editobject.h"
91
92 #include "mydevice.h"
93 #include "blendef.h"
94 #include "nla.h"
95
96 extern int count_action_levels (bAction *act);
97
98 #define BEZSELECTED(bezt)   (((bezt)->f1 & 1) || ((bezt)->f2 & 1) || ((bezt)->f3 & 1))
99
100 /* Local Function prototypes, are forward needed */
101 static void insertactionkey(bAction *act, bActionChannel *achan, bPoseChannel *chan, int adrcode, short makecurve, float time);
102 static void hilight_channel (bAction *act, bActionChannel *chan, short hilight);
103 static void set_action_key_time (bAction *act, bPoseChannel *chan, int adrcode, short makecurve, float time);
104
105 static void up_sel_action(void);
106 static void down_sel_action(void);
107 static void top_sel_action(void);
108 static void bottom_sel_action(void);
109
110 /* Implementation */
111
112 short showsliders = 0;
113 short ACTWIDTH = NAMEWIDTH;
114
115 static void select_poseelement_by_name (char *name, int select)
116 {
117         /* Synchs selection of channels with selection of object elements in posemode */
118
119         Object *ob= OBACT;
120
121         if (!ob)
122                 return;
123
124         switch (ob->type){
125         case OB_ARMATURE:
126                 select_bone_by_name ((bArmature*)ob->data, name, select);
127                 break;
128         default:
129                 break;
130         }
131 }
132
133 bAction* bake_action_with_client (bAction *act, Object *armob, float tolerance)
134 {
135         bArmature               *arm;
136         bAction                 *result=NULL;
137         bActionChannel *achan;
138         bAction                 *temp;
139         bPoseChannel    *pchan;
140         float                   actlen;
141         int                             oldframe;
142         int                             curframe;
143         char                    newname[64];
144
145         if (!act)
146                 return NULL;
147
148         arm = get_armature(armob);
149
150         if (G.obedit){
151                 error ("Actions can't be baked in Edit Mode");
152                 return NULL;
153         }
154
155         if (!arm || armob->pose==NULL){
156                 error ("Select an armature before baking");
157                 return NULL;
158         }
159         
160         /* Get a new action */
161         result = add_empty_action();
162
163         /* Assign the new action a unique name */
164         sprintf (newname, "%s.BAKED", act->id.name+2);
165         rename_id(&result->id, newname);
166
167         actlen = calc_action_end(act);
168
169         oldframe = G.scene->r.cfra;
170
171         temp = armob->action;
172         armob->action = act;
173         
174         for (curframe=1; curframe<ceil(actlen+1); curframe++){
175
176                 /* Apply the old action */
177                 
178                 G.scene->r.cfra = curframe;
179
180                 /* Apply the object ipo */
181                 extract_pose_from_action(armob->pose, act, curframe);
182
183                 where_is_pose(armob);
184                 
185                 /* For each channel: set quats and locs if channel is a bone */
186                 for (pchan=armob->pose->chanbase.first; pchan; pchan=pchan->next){
187
188                         /* Apply to keys */
189                         set_action_key_time (result, pchan, AC_QUAT_X, 1, curframe);
190                         set_action_key_time (result, pchan, AC_QUAT_Y, 1, curframe);
191                         set_action_key_time (result, pchan, AC_QUAT_Z, 1, curframe);
192                         set_action_key_time (result, pchan, AC_QUAT_W, 1, curframe);
193                         set_action_key_time (result, pchan, AC_LOC_X, 1, curframe);
194                         set_action_key_time (result, pchan, AC_LOC_Y, 1, curframe);
195                         set_action_key_time (result, pchan, AC_LOC_Z, 1, curframe);
196                         set_action_key_time (result, pchan, AC_SIZE_X, 1, curframe);
197                         set_action_key_time (result, pchan, AC_SIZE_Y, 1, curframe);
198                         set_action_key_time (result, pchan, AC_SIZE_Z, 1, curframe);
199                 }
200         }
201
202
203         /* Make another pass to ensure all keyframes are set to linear interpolation mode */
204         for (achan = result->chanbase.first; achan; achan=achan->next){
205                 IpoCurve* icu;
206                 for (icu = achan->ipo->curve.first; icu; icu=icu->next){
207                         icu->ipo= IPO_LIN;
208                 }
209         }
210
211         notice ("Made a new action named \"%s\"", newname);
212         G.scene->r.cfra = oldframe;
213         armob->action = temp;
214                 
215         /* restore */
216         extract_pose_from_action(armob->pose, act, G.scene->r.cfra);
217         where_is_pose(armob);
218         
219         allqueue(REDRAWACTION, 1);
220         
221         return result;
222 }
223
224 /* apparently within active object context */
225 void select_actionchannel_by_name (bAction *act, char *name, int select)
226 {
227         bActionChannel *chan;
228
229         if (!act)
230                 return;
231
232         for (chan = act->chanbase.first; chan; chan=chan->next){
233                 if (!strcmp (chan->name, name)){
234                         act->achan = chan;
235                         if (select){
236                                 chan->flag |= ACHAN_SELECTED;
237                                 hilight_channel (act, chan, 1);
238                                 select_poseelement_by_name(chan->name, 1);
239                         }
240                         else{
241                                 chan->flag &= ~ACHAN_SELECTED;
242                                 hilight_channel (act, chan, 0);
243                                 select_poseelement_by_name(chan->name, 0);
244                         }
245                         return;
246                 }
247         }
248 }
249
250 void remake_action_ipos(bAction *act)
251 {
252         bActionChannel *chan;
253         bConstraintChannel *conchan;
254         IpoCurve                *icu;
255
256         for (chan= act->chanbase.first; chan; chan=chan->next){
257                 if (chan->ipo){
258                         for (icu = chan->ipo->curve.first; icu; icu=icu->next){
259                                 sort_time_ipocurve(icu);
260                                 testhandles_ipocurve(icu);
261                         }
262                 }
263                 for (conchan=chan->constraintChannels.first; conchan; conchan=conchan->next){
264                         if (conchan->ipo){
265                                 for (icu = conchan->ipo->curve.first; icu; icu=icu->next){
266                                         sort_time_ipocurve(icu);
267                                         testhandles_ipocurve(icu);
268                                 }
269                         }
270                 }
271         }
272 }
273
274 static void remake_meshaction_ipos(Ipo *ipo)
275 {
276         /* this puts the bezier triples in proper
277          * order and makes sure the bezier handles
278          * aren't too strange.
279          */
280         IpoCurve *icu;
281
282         for (icu = ipo->curve.first; icu; icu=icu->next){
283                 sort_time_ipocurve(icu);
284                 testhandles_ipocurve(icu);
285         }
286 }
287
288 static void meshkey_do_redraw(Key *key)
289 {
290         remake_meshaction_ipos(key->ipo);
291
292         DAG_object_flush_update(G.scene, OBACT, OB_RECALC_DATA);
293         
294         allspace(REMAKEIPO, 0);
295         allqueue(REDRAWACTION, 0);
296         allqueue(REDRAWIPO, 0);
297         allqueue(REDRAWNLA, 0);
298
299 }
300
301 void duplicate_meshchannel_keys(Key *key)
302 {
303         duplicate_ipo_keys(key->ipo);
304         transform_meshchannel_keys ('g', key);
305 }
306
307
308 void duplicate_actionchannel_keys(void)
309 {
310         bAction *act;
311         bActionChannel *chan;
312         bConstraintChannel *conchan;
313
314         act=G.saction->action;
315         if (!act)
316                 return;
317
318         /* Find selected items */
319         for (chan = act->chanbase.first; chan; chan=chan->next){
320                 duplicate_ipo_keys(chan->ipo);
321                 for (conchan=chan->constraintChannels.first; conchan; conchan=conchan->next)
322                         duplicate_ipo_keys(conchan->ipo);
323         }
324
325         transform_actionchannel_keys ('g');
326 }
327
328 static bActionChannel *get_nearest_actionchannel_key (float *index, short *sel, bConstraintChannel **rchan){
329         bAction *act;
330         bActionChannel *chan;
331         IpoCurve *icu;
332         bActionChannel *firstchan=NULL;
333         bConstraintChannel *conchan, *firstconchan=NULL;
334         int     foundsel=0;
335         float firstvert=-1, foundx=-1;
336                 int i;
337         short mval[2];
338         float ymin, ymax;
339         rctf    rectf;
340         *index=0;
341
342         *rchan=NULL;
343         act=G.saction->action; /* We presume that we are only called during a valid action */
344         
345         getmouseco_areawin (mval);
346
347         mval[0]-=7;
348         areamouseco_to_ipoco(G.v2d, mval, &rectf.xmin, &rectf.ymin);
349         mval[0]+=14;
350         areamouseco_to_ipoco(G.v2d, mval, &rectf.xmax, &rectf.ymax);
351
352         ymax = count_action_levels(act) * (CHANNELHEIGHT + CHANNELSKIP);
353         ymax += CHANNELHEIGHT/2;
354         
355         *sel=0;
356
357         for (chan=act->chanbase.first; chan; chan=chan->next){
358
359                 /* Check action channel */
360                 ymin= ymax-(CHANNELHEIGHT+CHANNELSKIP);
361                 if (!((ymax < rectf.ymin) || (ymin > rectf.ymax))){
362                         for (icu=chan->ipo->curve.first; icu; icu=icu->next){
363                                 for (i=0; i<icu->totvert; i++){
364                                         if (icu->bezt[i].vec[1][0] > rectf.xmin && icu->bezt[i].vec[1][0] <= rectf.xmax ){
365                                                 if (!firstchan){
366                                                         firstchan=chan;
367                                                         firstvert=icu->bezt[i].vec[1][0];
368                                                         *sel = icu->bezt[i].f2 & 1;     
369                                                 }
370                                                 
371                                                 if (icu->bezt[i].f2 & 1){ 
372                                                         if (!foundsel){
373                                                                 foundsel=1;
374                                                                 foundx = icu->bezt[i].vec[1][0];
375                                                         }
376                                                 }
377                                                 else if (foundsel && icu->bezt[i].vec[1][0] != foundx){
378                                                         *index=icu->bezt[i].vec[1][0];
379                                                         *sel = 0;
380                                                         return chan;
381                                                 }
382                                         }
383                                 }
384                         }
385                 }
386                 ymax=ymin;
387                 
388                 /* Check constraint channels */
389                 for (conchan=chan->constraintChannels.first; conchan; conchan=conchan->next){
390                         ymin=ymax-(CHANNELHEIGHT+CHANNELSKIP);
391                         if (!((ymax < rectf.ymin) || (ymin > rectf.ymax))){
392                                 for (icu=conchan->ipo->curve.first; icu; icu=icu->next){
393                                         for (i=0; i<icu->totvert; i++){
394                                                 if (icu->bezt[i].vec[1][0] > rectf.xmin && icu->bezt[i].vec[1][0] <= rectf.xmax ){
395                                                         if (!firstchan){
396                                                                 firstchan=chan;
397                                                                 firstconchan=conchan;
398                                                                 firstvert=icu->bezt[i].vec[1][0];
399                                                                 *sel = icu->bezt[i].f2 & 1;     
400                                                         }
401                                                         
402                                                         if (icu->bezt[i].f2 & 1){ 
403                                                                 if (!foundsel){
404                                                                         foundsel=1;
405                                                                         foundx = icu->bezt[i].vec[1][0];
406                                                                 }
407                                                         }
408                                                         else if (foundsel && icu->bezt[i].vec[1][0] != foundx){
409                                                                 *index=icu->bezt[i].vec[1][0];
410                                                                 *sel = 0;
411                                                                 *rchan = conchan;
412                                                                 return chan;
413                                                         }
414                                                 }
415                                         }
416                                 }
417                         }
418                         ymax=ymin;
419                 }
420         }       
421         
422         *rchan = firstconchan;
423         *index=firstvert;
424         return firstchan;
425 }
426
427 static IpoCurve *get_nearest_meshchannel_key (float *index, short *sel)
428 {
429         /* This function tries to find the RVK key that is
430          * closest to the user's mouse click
431          */
432     Key      *key;
433     IpoCurve *icu; 
434     IpoCurve *firsticu=NULL;
435     int      foundsel=0;
436     float    firstvert=-1, foundx=-1;
437         int      i;
438     short    mval[2];
439     float    ymin, ymax, ybase;
440     rctf     rectf;
441
442     *index=0;
443
444     key = get_action_mesh_key();
445         
446     /* lets get the mouse position and process it so 
447      * we can start testing selections
448      */
449     getmouseco_areawin (mval);
450     mval[0]-=7;
451     areamouseco_to_ipoco(G.v2d, mval, &rectf.xmin, &rectf.ymin);
452     mval[0]+=14;
453     areamouseco_to_ipoco(G.v2d, mval, &rectf.xmax, &rectf.ymax);
454
455     ybase = key->totkey * (CHANNELHEIGHT + CHANNELSKIP);
456         ybase += CHANNELHEIGHT/2;
457     *sel=0;
458
459     /* lets loop through the IpoCurves trying to find the closest
460      * bezier
461      */
462         if (!key->ipo) return NULL;
463     for (icu = key->ipo->curve.first; icu ; icu = icu->next) {
464         /* lets not deal with the "speed" Ipo
465          */
466         if (!icu->adrcode) continue;
467
468         ymax = ybase    - (CHANNELHEIGHT+CHANNELSKIP)*(icu->adrcode-1);
469         ymin=ymax-(CHANNELHEIGHT+CHANNELSKIP);
470
471         /* Does this curve coorespond to the right
472          * strip?
473          */
474         if (!((ymax < rectf.ymin) || (ymin > rectf.ymax))){
475                         
476             /* loop through the beziers in the curve
477              */
478             for (i=0; i<icu->totvert; i++){
479
480                 /* Is this bezier in the right area?
481                  */
482                 if (icu->bezt[i].vec[1][0] > rectf.xmin && 
483                     icu->bezt[i].vec[1][0] <= rectf.xmax ){
484
485                     /* if no other curves have been picked ...
486                      */
487                     if (!firsticu){
488                         /* mark this curve/bezier as the first
489                          * selected
490                          */
491                         firsticu=icu;
492                         firstvert=icu->bezt[i].vec[1][0];
493
494                         /* sel = (is the bezier is already selected) ? 1 : 0;
495                          */
496                         *sel = icu->bezt[i].f2 & 1;     
497                     }
498
499                     /* if the bezier is selected ...
500                      */
501                     if (icu->bezt[i].f2 & 1){ 
502                         /* if we haven't found a selected one yet ...
503                          */
504                         if (!foundsel){
505                             /* record the found x value
506                              */
507                             foundsel=1;
508                             foundx = icu->bezt[i].vec[1][0];
509                         }
510                     }
511
512                     /* if the bezier is unselected and not at the x
513                      * position of a previous found selected bezier ...
514                      */
515                     else if (foundsel && icu->bezt[i].vec[1][0] != foundx){
516                         /* lets return this found curve/bezier
517                          */
518                         *index=icu->bezt[i].vec[1][0];
519                         *sel = 0;
520                         return icu;
521                     }
522                 }
523             }
524         }
525         }
526         
527     /* return what we've found
528      */
529     *index=firstvert;
530     return firsticu;
531 }
532
533 /* apparently within active object context */
534 static void mouse_action(int selectmode)
535 {
536         bAction *act;
537         short sel;
538         float   selx;
539         bActionChannel *chan;
540         bConstraintChannel *conchan;
541         short   mval[2];
542
543         act=G.saction->action;
544         if (!act)
545                 return;
546
547         getmouseco_areawin (mval);
548
549         chan=get_nearest_actionchannel_key(&selx, &sel, &conchan);
550
551         if (chan){
552                 if (selectmode == SELECT_REPLACE) {
553                         if (sel == 0)
554                                 selectmode = SELECT_ADD;
555                         else
556                                 selectmode = SELECT_SUBTRACT;
557                         deselect_actionchannel_keys(act, 0);
558                         deselect_actionchannels(act, 0);
559                         act->achan = chan;
560                         chan->flag |= ACHAN_SELECTED;
561                         hilight_channel (act, chan, 1);
562                         select_poseelement_by_name(chan->name, 1);
563                 }
564                 
565                 if (conchan)
566                         select_ipo_key(conchan->ipo, selx, selectmode);
567                 else
568                         select_ipo_key(chan->ipo, selx, selectmode);
569
570                 BIF_undo_push("Select Action");
571                 allqueue(REDRAWIPO, 0);
572                 allqueue(REDRAWVIEW3D, 0);
573                 allqueue(REDRAWACTION, 0);
574                 allqueue(REDRAWNLA, 0);
575
576         }
577 }
578
579 static void mouse_mesh_action(int selectmode, Key *key)
580 {
581         /* Handle a right mouse click selection in an
582          * action window displaying RVK data
583          */
584
585     IpoCurve *icu;
586     short  sel;
587     float  selx;
588     short  mval[2];
589
590     /* going to assume that the only reason 
591      * we got here is because it has been 
592      * determined that we are a mesh with
593      * the right properties (i.e., have key
594      * data, etc)
595      */
596
597         /* get the click location, and the cooresponding
598          * ipo curve and selection time value
599          */
600     getmouseco_areawin (mval);
601     icu = get_nearest_meshchannel_key(&selx, &sel);
602
603     if (icu){
604         if (selectmode == SELECT_REPLACE) {
605                         /* if we had planned to replace the
606                          * selection, then we will first deselect
607                          * all of the keys, and if the clicked on
608                          * key had been unselected, we will select 
609                          * it, otherwise, we are done.
610                          */
611             deselect_meshchannel_keys(key, 0);
612
613             if (sel == 0)
614                 selectmode = SELECT_ADD;
615             else
616                                 /* the key is selected so we should
617                                  * deselect -- but everything is now deselected
618                                  * so we are done.
619                                  */
620                                 return;
621         }
622                 
623                 /* select the key using the given mode
624                  * and redraw as mush stuff as needed.
625                  */
626                 select_icu_key(icu, selx, selectmode);
627
628                 BIF_undo_push("Select Action key");
629         allqueue(REDRAWIPO, 0);
630         allqueue(REDRAWVIEW3D, 0);
631         allqueue(REDRAWACTION, 0);
632         allqueue(REDRAWNLA, 0);
633
634     }
635 }
636
637 void borderselect_action(void)
638
639         rcti rect;
640         rctf rectf;
641         int val, selectmode;            
642         short   mval[2];
643         bActionChannel *chan;
644         bConstraintChannel *conchan;
645         bAction *act;
646         float   ymin, ymax;
647
648         act=G.saction->action;
649
650
651         if (!act)
652                 return;
653
654         if ( (val = get_border(&rect, 3)) ){
655     if (val == LEFTMOUSE)
656       selectmode = SELECT_ADD;
657     else
658       selectmode = SELECT_SUBTRACT;
659
660                 mval[0]= rect.xmin;
661                 mval[1]= rect.ymin+2;
662                 areamouseco_to_ipoco(G.v2d, mval, &rectf.xmin, &rectf.ymin);
663                 mval[0]= rect.xmax;
664                 mval[1]= rect.ymax-2;
665                 areamouseco_to_ipoco(G.v2d, mval, &rectf.xmax, &rectf.ymax);
666                 
667                 ymax= count_action_levels(act) * (CHANNELHEIGHT+CHANNELSKIP);
668                 ymax += CHANNELHEIGHT/2;
669                 
670                 for (chan=act->chanbase.first; chan; chan=chan->next){
671                         
672                         /* Check action */
673                         ymin=ymax-(CHANNELHEIGHT+CHANNELSKIP);
674                         if (!((ymax < rectf.ymin) || (ymin > rectf.ymax)))
675           borderselect_ipo_key(chan->ipo, rectf.xmin, rectf.xmax,
676                                selectmode);
677
678                         ymax=ymin;
679
680                         /* Check constraints */
681                         for (conchan=chan->constraintChannels.first; conchan; conchan=conchan->next){
682                                 ymin=ymax-(CHANNELHEIGHT+CHANNELSKIP);
683                                 if (!((ymax < rectf.ymin) || (ymin > rectf.ymax)))
684                                         borderselect_ipo_key(conchan->ipo, rectf.xmin, rectf.xmax,
685                                selectmode);
686                                 
687                                 ymax=ymin;
688                         }
689                 }       
690                 BIF_undo_push("Border Select Action");
691                 allqueue(REDRAWNLA, 0);
692                 allqueue(REDRAWACTION, 0);
693                 allqueue(REDRAWIPO, 0);
694         }
695 }
696
697 void borderselect_mesh(Key *key)
698
699         rcti     rect;
700         int      val, adrcodemax, adrcodemin;
701         short    mval[2];
702         float    xmin, xmax;
703         int      (*select_function)(BezTriple *);
704         IpoCurve *icu;
705
706         if ( (val = get_border(&rect, 3)) ){
707                 /* set the selection function based on what
708                  * mouse button had been used in the border
709                  * select
710                  */
711                 if (val == LEFTMOUSE)
712                         select_function = select_bezier_add;
713                 else
714                         select_function = select_bezier_subtract;
715
716                 /* get the minimum and maximum adrcode numbers
717                  * for the IpoCurves (this is the number that
718                  * relates an IpoCurve to the keyblock it
719                  * controls).
720                  */
721                 mval[0]= rect.xmin;
722                 mval[1]= rect.ymin+2;
723                 adrcodemax = get_nearest_key_num(key, mval, &xmin);
724                 adrcodemax = (adrcodemax >= key->totkey) ? key->totkey : adrcodemax;
725
726                 mval[0]= rect.xmax;
727                 mval[1]= rect.ymax-2;
728                 adrcodemin = get_nearest_key_num(key, mval, &xmax);
729                 adrcodemin = (adrcodemin < 1) ? 1 : adrcodemin;
730
731                 /* Lets loop throug the IpoCurves and do borderselect
732                  * on the curves with adrcodes in our selected range.
733                  */
734                 for (icu = key->ipo->curve.first; icu ; icu = icu->next) {
735                         /* lets not deal with the "speed" Ipo
736                          */
737                         if (!icu->adrcode) continue;
738                         if ( (icu->adrcode >= adrcodemin) && 
739                                  (icu->adrcode <= adrcodemax) ) {
740                                 borderselect_icu_key(icu, xmin, xmax, select_function);
741                         }
742                 }
743
744                 /* redraw stuff */
745                 BIF_undo_push("Border select Action Key");
746                 allqueue(REDRAWNLA, 0);
747                 allqueue(REDRAWACTION, 0);
748                 allqueue(REDRAWIPO, 0);
749         }
750 }
751
752 /* used by ipo, outliner, buttons to find the active channel */
753 bActionChannel* get_hilighted_action_channel(bAction* action)
754 {
755         bActionChannel *chan;
756
757         if (!action)
758                 return NULL;
759
760         for (chan=action->chanbase.first; chan; chan=chan->next){
761                 if (chan->flag & ACHAN_SELECTED && chan->flag & ACHAN_HILIGHTED)
762                         return chan;
763         }
764
765         return NULL;
766
767 }
768
769 /* sets action->achan to active channel, also adds if needed */
770 void verify_active_action_channel(Object *ob)
771 {
772         if(ob) {
773                 bPoseChannel *pchan;
774                 bActionChannel *achan;
775                 
776                 if(ob->action==NULL) return;
777                 
778                 pchan= get_active_posechannel(ob);
779                 if(pchan) {
780                         /* See if this action channel exists already */ 
781                         for (achan=ob->action->chanbase.first; achan; achan=achan->next){
782                                 if (!strcmp (pchan->name, achan->name))
783                                         break;
784                         }
785                         
786                         if (!achan){
787                                 achan = MEM_callocN (sizeof(bActionChannel), "actionChannel");
788                                 strcpy (achan->name, pchan->name);
789                                 BLI_addtail (&ob->action->chanbase, achan);
790                         }
791                         
792                         ob->action->achan= achan;
793                         ob->action->pchan= pchan;
794                         
795                         for (achan=ob->action->chanbase.first; achan; achan=achan->next)
796                                 achan->flag &= ~(ACHAN_SELECTED|ACHAN_HILIGHTED);
797                         
798                         ob->action->achan->flag |= ACHAN_SELECTED|ACHAN_HILIGHTED;
799
800                 }
801         }               
802 }
803
804 void set_exprap_action(int mode)
805 {
806         if(G.saction->action && G.saction->action->id.lib) return;
807
808         error ("Not yet implemented!");
809 }
810
811 void set_action_key (struct bAction *act, struct bPoseChannel *chan, int adrcode, short makecurve)
812 {
813         set_action_key_time (act, chan, adrcode, makecurve, frame_to_float(CFRA));
814 }
815
816 static void set_action_key_time (bAction *act, bPoseChannel *chan, int adrcode, short makecurve, float time)
817 {
818         bActionChannel  *achan;
819         char    ipstr[256];
820
821         if (!act)
822                 return;
823
824         if (!chan)
825                 return;
826         
827         /* See if this action channel exists already */ 
828         for (achan=act->chanbase.first; achan; achan=achan->next){
829                 if (!strcmp (chan->name, achan->name))
830                         break;
831         }
832
833         if (!achan){
834                 if (!makecurve)
835                         return;
836                 achan = MEM_callocN (sizeof(bActionChannel), "actionChannel");
837                 strcpy (achan->name, chan->name);
838                 BLI_addtail (&act->chanbase, achan);
839         }
840
841         /* Ensure the channel appears selected in the action window */
842         /* ton: added flag hilighted, for display in ipowin. dunno what the difference is between select/hilite */
843         achan->flag |= ACHAN_SELECTED|ACHAN_HILIGHTED;
844
845         /* Ensure this action channel has a valid Ipo */
846         if (!achan->ipo){
847                 sprintf (ipstr, "%s.%s", act->id.name+2, chan->name);
848                 ipstr[23]=0;
849                 achan->ipo=     add_ipo(ipstr, ID_AC);  
850         }
851
852         insertactionkey(act, achan, chan, adrcode, makecurve, time);
853
854 }
855
856 static void insertactionkey(bAction *act, bActionChannel *achan, bPoseChannel *chan, int adrcode, short makecurve, float cfra)
857 {
858         IpoCurve *icu;
859         void *poin;
860         float curval;
861         int type;
862         ID *id;
863         
864         if (!act){
865                 return;
866         }
867         if (act->id.lib){
868                 error ("Can't pose library actions");
869                 return;
870         }
871         act->achan=achan;
872         act->pchan=chan;
873
874         id=(ID*) act;
875
876         /* First see if this curve exists */
877         if (!makecurve){
878                 if (!achan->ipo)
879                         return;
880
881                 for (icu = achan->ipo->curve.first; icu; icu=icu->next){
882                         if (icu->adrcode == adrcode)
883                                 break;
884                 }
885                 if (!icu)
886                         return;
887         }
888
889         
890         icu = get_ipocurve (id, GS(id->name), adrcode, achan->ipo);
891
892         if(icu) {
893                 poin= get_ipo_poin(id, icu, &type);
894                 if(poin) {
895                         curval= read_ipo_poin(poin, type);
896         //              cfra= frame_to_float(CFRA);
897                         insert_vert_ipo(icu, cfra, curval);
898                 }
899         }
900         
901 }
902
903 bAction *add_empty_action(void)
904 {
905         bAction *act;
906
907         act= alloc_libblock(&G.main->action, ID_AC, "Action");
908         act->id.flag |= LIB_FAKEUSER;
909         act->id.us++;
910         return act;
911 }
912
913 void transform_actionchannel_keys(char mode)
914 {
915         bAction *act;
916         TransVert *tv;
917         Object *ob= OBACT;
918         bConstraintChannel *conchan;
919         bActionChannel  *chan;
920         float   deltax, startx;
921         float   cenf[2];
922         float   sval[2], cval[2], lastcval[2];
923         float   fac=0.0F;
924         int             loop=1;
925         int             tvtot=0;
926         int             invert=0, firsttime=1;
927         int             i;
928         short   cancel=0;
929         short   mvals[2], mvalc[2], cent[2];
930         char    str[256];
931
932         act=G.saction->action;
933
934         /* Ensure that partial selections result in beztriple selections */
935         for (chan=act->chanbase.first; chan; chan=chan->next){
936                 tvtot+=fullselect_ipo_keys(chan->ipo);
937
938                 for (conchan=chan->constraintChannels.first; conchan; conchan=conchan->next)
939                         tvtot+=fullselect_ipo_keys(conchan->ipo);
940         }
941         
942         /* If nothing is selected, bail out */
943         if (!tvtot)
944                 return;
945         
946         
947         /* Build the transvert structure */
948         tv = MEM_callocN (sizeof(TransVert) * tvtot, "transVert");
949         tvtot=0;
950         for (chan=act->chanbase.first; chan; chan=chan->next){
951                 /* Add the actionchannel */
952                 tvtot = add_trans_ipo_keys(chan->ipo, tv, tvtot);
953                 for (conchan=chan->constraintChannels.first; conchan; conchan=conchan->next)
954                         tvtot = add_trans_ipo_keys(conchan->ipo, tv, tvtot);
955         }
956
957         /* Do the event loop */
958         cent[0] = curarea->winx + (G.saction->v2d.hor.xmax)/2;
959         cent[1] = curarea->winy + (G.saction->v2d.hor.ymax)/2;
960         areamouseco_to_ipoco(G.v2d, cent, &cenf[0], &cenf[1]);
961
962         getmouseco_areawin (mvals);
963         areamouseco_to_ipoco(G.v2d, mvals, &sval[0], &sval[1]);
964
965         startx=sval[0];
966         while (loop) {
967                 /*              Get the input */
968                 /*              If we're cancelling, reset transformations */
969                 /*                      Else calc new transformation */
970                 /*              Perform the transformations */
971                 while (qtest()) {
972                         short val;
973                         unsigned short event= extern_qread(&val);
974
975                         if (val) {
976                                 switch (event) {
977                                 case LEFTMOUSE:
978                                 case SPACEKEY:
979                                 case RETKEY:
980                                         loop=0;
981                                         break;
982                                 case XKEY:
983                                         break;
984                                 case ESCKEY:
985                                 case RIGHTMOUSE:
986                                         cancel=1;
987                                         loop=0;
988                                         break;
989                                 default:
990                                         arrows_move_cursor(event);
991                                         break;
992                                 };
993                         }
994                 }
995
996                 if (cancel) {
997                         for (i=0; i<tvtot; i++) {
998                                 tv[i].loc[0]=tv[i].oldloc[0];
999                                 tv[i].loc[1]=tv[i].oldloc[1];
1000                         }
1001                 } else {
1002                         getmouseco_areawin (mvalc);
1003                         areamouseco_to_ipoco(G.v2d, mvalc, &cval[0], &cval[1]);
1004
1005                         if (!firsttime && lastcval[0]==cval[0] && lastcval[1]==cval[1]) {
1006                                 PIL_sleep_ms(1);
1007                         } else {
1008                                 for (i=0; i<tvtot; i++){
1009                                         tv[i].loc[0]=tv[i].oldloc[0];
1010
1011                                         switch (mode){
1012                                         case 'g':
1013                                                 deltax = cval[0]-sval[0];
1014                                                 fac= deltax;
1015                                                 
1016                                                 apply_keyb_grid(&fac, 0.0, 1.0, 0.1, U.flag & USER_AUTOGRABGRID);
1017
1018                                                 tv[i].loc[0]+=fac;
1019                                                 break;
1020                                         case 's':
1021                                                 startx=mvals[0]-(ACTWIDTH/2+(curarea->winrct.xmax-curarea->winrct.xmin)/2);
1022                                                 deltax=mvalc[0]-(ACTWIDTH/2+(curarea->winrct.xmax-curarea->winrct.xmin)/2);
1023                                                 fac= fabs(deltax/startx);
1024                                                 
1025                                                 apply_keyb_grid(&fac, 0.0, 0.2, 0.1, U.flag & USER_AUTOSIZEGRID);
1026                 
1027                                                 if (invert){
1028                                                         if (i % 03 == 0){
1029                                                                 memcpy (tv[i].loc, tv[i].oldloc, sizeof(tv[i+2].oldloc));
1030                                                         }
1031                                                         if (i % 03 == 2){
1032                                                                 memcpy (tv[i].loc, tv[i].oldloc, sizeof(tv[i-2].oldloc));
1033                                                         }
1034         
1035                                                         fac*=-1;
1036                                                 }
1037                                                 startx= (G.scene->r.cfra);
1038                                         
1039                                                 tv[i].loc[0]-= startx;
1040                                                 tv[i].loc[0]*=fac;
1041                                                 tv[i].loc[0]+= startx;
1042                 
1043                                                 break;
1044                                         }
1045                                 }
1046                         }
1047         
1048                         if (mode=='s'){
1049                                 sprintf(str, "sizeX: %.3f", fac);
1050                                 headerprint(str);
1051                         }
1052                         else if (mode=='g'){
1053                                 sprintf(str, "deltaX: %.3f", fac);
1054                                 headerprint(str);
1055                         }
1056         
1057                         if (G.saction->lock){
1058                                 if(ob && ob->pose) {
1059                                         ob->pose->ctime= -123456.0f;
1060                                         DAG_object_flush_update(G.scene, ob, OB_RECALC_DATA);
1061                                 }
1062                                 force_draw_plus(SPACE_VIEW3D, 0);
1063                         }
1064                         else {
1065                                 force_draw(0);
1066                         }
1067                 }
1068                 
1069                 lastcval[0]= cval[0];
1070                 lastcval[1]= cval[1];
1071                 firsttime= 0;
1072         }
1073         
1074         /*              Update the curve */
1075         /*              Depending on the lock status, draw necessary views */
1076
1077         if(ob && ob->pose) {
1078                 ob->pose->ctime= -123456.0f;
1079                 DAG_object_flush_update(G.scene, OBACT, OB_RECALC_DATA);
1080         }
1081         remake_action_ipos(act);
1082
1083         if(cancel==0) BIF_undo_push("Transform Action");
1084         allqueue (REDRAWVIEW3D, 0);
1085         allqueue (REDRAWACTION, 0);
1086         allqueue(REDRAWNLA, 0);
1087         allqueue (REDRAWIPO, 0);
1088         MEM_freeN (tv);
1089 }
1090
1091 void transform_meshchannel_keys(char mode, Key *key)
1092 {
1093         /* this is the function that determines what happens
1094          * to those little blocky rvk key things you have selected 
1095          * after you press a 'g' or an 's'. I'd love to say that
1096          * I have an intimate knowledge of all of what this function
1097          * is doing, but instead I'm just going to pretend.
1098          */
1099     TransVert *tv;
1100     int /*sel=0,*/  i;
1101     short       mvals[2], mvalc[2], cent[2];
1102     float       sval[2], cval[2], lastcval[2];
1103     short       cancel=0;
1104     float       fac=0.0F;
1105     int         loop=1;
1106     int         tvtot=0;
1107     float       deltax, startx;
1108     float       cenf[2];
1109     int         invert=0, firsttime=1;
1110     char        str[256];
1111
1112         /* count all of the selected beziers, and
1113          * set all 3 control handles to selected
1114          */
1115     tvtot=fullselect_ipo_keys(key->ipo);
1116     
1117     /* If nothing is selected, bail out 
1118          */
1119     if (!tvtot)
1120         return;
1121         
1122         
1123     /* Build the transvert structure 
1124          */
1125     tv = MEM_callocN (sizeof(TransVert) * tvtot, "transVert");
1126     tvtot=0;
1127
1128     tvtot = add_trans_ipo_keys(key->ipo, tv, tvtot);
1129
1130     /* Do the event loop 
1131          */
1132     cent[0] = curarea->winx + (G.saction->v2d.hor.xmax)/2;
1133     cent[1] = curarea->winy + (G.saction->v2d.hor.ymax)/2;
1134     areamouseco_to_ipoco(G.v2d, cent, &cenf[0], &cenf[1]);
1135
1136     getmouseco_areawin (mvals);
1137     areamouseco_to_ipoco(G.v2d, mvals, &sval[0], &sval[1]);
1138
1139     startx=sval[0];
1140     while (loop) {
1141         /* Get the input
1142                  * If we're cancelling, reset transformations
1143                  * Else calc new transformation
1144                  * Perform the transformations 
1145                  */
1146         while (qtest()) {
1147             short val;
1148             unsigned short event= extern_qread(&val);
1149
1150             if (val) {
1151                 switch (event) {
1152                 case LEFTMOUSE:
1153                 case SPACEKEY:
1154                 case RETKEY:
1155                     loop=0;
1156                     break;
1157                 case XKEY:
1158                     break;
1159                 case ESCKEY:
1160                 case RIGHTMOUSE:
1161                     cancel=1;
1162                     loop=0;
1163                     break;
1164                 default:
1165                     arrows_move_cursor(event);
1166                     break;
1167                 };
1168             }
1169         }
1170         
1171         if (cancel) {
1172             for (i=0; i<tvtot; i++) {
1173                 tv[i].loc[0]=tv[i].oldloc[0];
1174                 tv[i].loc[1]=tv[i].oldloc[1];
1175             }
1176         } 
1177                 else {
1178             getmouseco_areawin (mvalc);
1179             areamouseco_to_ipoco(G.v2d, mvalc, &cval[0], &cval[1]);
1180                         
1181             if (!firsttime && lastcval[0]==cval[0] && lastcval[1]==cval[1]) {
1182                 PIL_sleep_ms(1);
1183             } else {
1184                 for (i=0; i<tvtot; i++){
1185                     tv[i].loc[0]=tv[i].oldloc[0];
1186
1187                     switch (mode){
1188                     case 'g':
1189                         deltax = cval[0]-sval[0];
1190                         fac= deltax;
1191                                                 
1192                         apply_keyb_grid(&fac, 0.0, 1.0, 0.1, 
1193                                         U.flag & USER_AUTOGRABGRID);
1194
1195                         tv[i].loc[0]+=fac;
1196                         break;
1197                     case 's':
1198                         startx=mvals[0]-(ACTWIDTH/2+(curarea->winrct.xmax
1199                                                      -curarea->winrct.xmin)/2);
1200                         deltax=mvalc[0]-(ACTWIDTH/2+(curarea->winrct.xmax
1201                                                      -curarea->winrct.xmin)/2);
1202                         fac= fabs(deltax/startx);
1203                                                 
1204                         apply_keyb_grid(&fac, 0.0, 0.2, 0.1, 
1205                                         U.flag & USER_AUTOSIZEGRID);
1206                 
1207                         if (invert){
1208                             if (i % 03 == 0){
1209                                 memcpy (tv[i].loc, tv[i].oldloc, 
1210                                         sizeof(tv[i+2].oldloc));
1211                             }
1212                             if (i % 03 == 2){
1213                                 memcpy (tv[i].loc, tv[i].oldloc, 
1214                                         sizeof(tv[i-2].oldloc));
1215                             }
1216                                                         
1217                             fac*=-1;
1218                         }
1219                         startx= (G.scene->r.cfra);
1220                         
1221                         tv[i].loc[0]-= startx;
1222                         tv[i].loc[0]*=fac;
1223                         tv[i].loc[0]+= startx;
1224                 
1225                         break;
1226                     }
1227                 }
1228             }
1229                         /* Display a message showing the magnitude of
1230                          * the grab/scale we are performing
1231                          */
1232             if (mode=='s'){
1233                 sprintf(str, "sizeX: %.3f", fac);
1234                 headerprint(str);
1235             }
1236             else if (mode=='g'){
1237                 sprintf(str, "deltaX: %.3f", fac);
1238                 headerprint(str);
1239             }
1240         
1241             if (G.saction->lock){
1242                                 /* doubt any of this code ever gets
1243                                  * executed, but it might in the
1244                                  * future
1245                                  */
1246                                  
1247                                 DAG_object_flush_update(G.scene, OBACT, OB_RECALC_DATA);
1248                 allqueue (REDRAWVIEW3D, 0);
1249                 allqueue (REDRAWACTION, 0);
1250                 allqueue (REDRAWIPO, 0);
1251                 allqueue(REDRAWNLA, 0);
1252                 force_draw_all(0);
1253             }
1254             else {
1255                 addqueue (curarea->win, REDRAWALL, 0);
1256                 force_draw(0);
1257             }
1258         }
1259                 
1260         lastcval[0]= cval[0];
1261         lastcval[1]= cval[1];
1262         firsttime= 0;
1263     }
1264         
1265         /* fix up the Ipocurves and redraw stuff
1266          */
1267     meshkey_do_redraw(key);
1268         BIF_undo_push("Transform Action Keys");
1269
1270     MEM_freeN (tv);
1271
1272         /* did you understand all of that? I pretty much understand
1273          * what it does, but the specifics seem a little weird and crufty.
1274          */
1275 }
1276
1277
1278 void deselect_actionchannel_keys (bAction *act, int test)
1279 {
1280         bActionChannel  *chan;
1281         bConstraintChannel *conchan;
1282         int             sel=1;;
1283
1284         if (!act)
1285                 return;
1286
1287         /* Determine if this is selection or deselection */
1288         
1289         if (test){
1290                 for (chan=act->chanbase.first; chan; chan=chan->next){
1291                         /* Test the channel ipos */
1292                         if (is_ipo_key_selected(chan->ipo)){
1293                                 sel = 0;
1294                                 break;
1295                         }
1296
1297                         /* Test the constraint ipos */
1298                         for (conchan=chan->constraintChannels.first; conchan; conchan=conchan->next){
1299                                 if (is_ipo_key_selected(conchan->ipo)){
1300                                         sel = 0;
1301                                         break;
1302                                 }
1303                         }
1304
1305                         if (sel == 0)
1306                                 break;
1307                 }
1308         }
1309         else
1310                 sel=0;
1311         
1312         /* Set the flags */
1313         for (chan=act->chanbase.first; chan; chan=chan->next){
1314                 set_ipo_key_selection(chan->ipo, sel);
1315                 for (conchan=chan->constraintChannels.first; conchan; conchan=conchan->next)
1316                         set_ipo_key_selection(conchan->ipo, sel);
1317         }
1318 }
1319
1320 void deselect_meshchannel_keys (Key *key, int test)
1321 {
1322         /* should deselect the rvk keys
1323          */
1324     int         sel=1;
1325
1326     /* Determine if this is selection or deselection */
1327     if (test){
1328         if (is_ipo_key_selected(key->ipo)){
1329             sel = 0;
1330         }
1331     }
1332     else {
1333         sel=0;
1334     }
1335         
1336     /* Set the flags */
1337     set_ipo_key_selection(key->ipo, sel);
1338 }
1339
1340 /* apparently within active object context */
1341 void deselect_actionchannels (bAction *act, int test)
1342 {
1343         bActionChannel *chan;
1344         bConstraintChannel *conchan;
1345         int                     sel=1;  
1346
1347         if (!act)
1348                 return;
1349
1350         /* See if we should be selecting or deselecting */
1351         if (test){
1352                 for (chan=act->chanbase.first; chan; chan=chan->next){
1353                         if (!sel)
1354                                 break;
1355
1356                         if (chan->flag & ACHAN_SELECTED){
1357                                 sel=0;
1358                                 break;
1359                         }
1360                         if (sel){
1361                                 for (conchan=chan->constraintChannels.first; conchan; conchan=conchan->next){
1362                                         if (conchan->flag & CONSTRAINT_CHANNEL_SELECT){
1363                                                 sel=0;
1364                                                 break;
1365                                         }
1366                                 }
1367                         }
1368                 }
1369         }
1370         else
1371                 sel=0;
1372
1373         /* Now set the flags */
1374         for (chan=act->chanbase.first; chan; chan=chan->next){
1375                 select_poseelement_by_name(chan->name, sel);
1376
1377                 if (sel)
1378                         chan->flag |= ACHAN_SELECTED;
1379                 else
1380                         chan->flag &= ~ACHAN_SELECTED;
1381
1382                 for (conchan=chan->constraintChannels.first; conchan; conchan=conchan->next){
1383                         if (sel)
1384                                 conchan->flag |= CONSTRAINT_CHANNEL_SELECT;
1385                         else
1386                                 conchan->flag &= ~CONSTRAINT_CHANNEL_SELECT;
1387                 }
1388         }
1389
1390 }
1391
1392 static void hilight_channel (bAction *act, bActionChannel *chan, short select)
1393 {
1394         bActionChannel *curchan;
1395
1396         if (!act)
1397                 return;
1398
1399         for (curchan=act->chanbase.first; curchan; curchan=curchan->next){
1400                 if (curchan==chan && select)
1401                         curchan->flag |= ACHAN_HILIGHTED;
1402                 else
1403                         curchan->flag &= ~ACHAN_HILIGHTED;
1404         }
1405 }
1406
1407 /* select_mode = SELECT_REPLACE
1408  *             = SELECT_ADD
1409  *             = SELECT_SUBTRACT
1410  *             = SELECT_INVERT
1411  */
1412
1413 /* exported for outliner (ton) */
1414 /* apparently within active object context */
1415 int select_channel(bAction *act, bActionChannel *chan,
1416                           int selectmode) 
1417 {
1418         /* Select the channel based on the selection mode
1419          */
1420         int flag;
1421
1422         switch (selectmode) {
1423         case SELECT_ADD:
1424                 chan->flag |= ACHAN_SELECTED;
1425                 break;
1426         case SELECT_SUBTRACT:
1427                 chan->flag &= ~ACHAN_SELECTED;
1428                 break;
1429         case SELECT_INVERT:
1430                 chan->flag ^= ACHAN_SELECTED;
1431                 break;
1432         }
1433         flag = (chan->flag & ACHAN_SELECTED) ? 1 : 0;
1434
1435         hilight_channel(act, chan, flag);
1436         select_poseelement_by_name(chan->name, flag);
1437
1438         return flag;
1439 }
1440
1441 static int select_constraint_channel(bAction *act, 
1442                                      bConstraintChannel *conchan, 
1443                                      int selectmode) {
1444         /* Select the constraint channel based on the selection mode
1445          */
1446         int flag;
1447
1448         switch (selectmode) {
1449         case SELECT_ADD:
1450                 conchan->flag |= CONSTRAINT_CHANNEL_SELECT;
1451                 break;
1452         case SELECT_SUBTRACT:
1453                 conchan->flag &= ~CONSTRAINT_CHANNEL_SELECT;
1454                 break;
1455         case SELECT_INVERT:
1456                 conchan->flag ^= CONSTRAINT_CHANNEL_SELECT;
1457                 break;
1458         }
1459         flag = (conchan->flag & CONSTRAINT_CHANNEL_SELECT) ? 1 : 0;
1460
1461         return flag;
1462 }
1463
1464
1465 static void mouse_actionchannels(bAction *act, short *mval,
1466                                  short *mvalo, int selectmode) {
1467         /* Select action channels, based on mouse values.
1468          * If mvalo is NULL we assume it is a one click
1469          * action, other wise we treat it like it is a
1470          * border select with mval[0],mval[1] and
1471          * mvalo[0], mvalo[1] forming the corners of
1472          * a rectangle.
1473          */
1474         bActionChannel *chan;
1475         float   click, x,y;
1476         int   clickmin, clickmax;
1477         int             wsize, sel;
1478         bConstraintChannel *conchan;
1479
1480         if (!act)
1481                 return;
1482   
1483         if (selectmode == SELECT_REPLACE) {
1484                 deselect_actionchannels (act, 0);
1485                 selectmode = SELECT_ADD;
1486         }
1487
1488         /* wsize is the greatest possible height (in pixels) that would be
1489          * needed to draw all of the action channels and constraint
1490          * channels.
1491          */
1492         wsize =  count_action_levels(act)*(CHANNELHEIGHT+CHANNELSKIP);
1493         wsize += CHANNELHEIGHT/2;
1494
1495     areamouseco_to_ipoco(G.v2d, mval, &x, &y);
1496     clickmin = (int) ((wsize - y) / (CHANNELHEIGHT+CHANNELSKIP));
1497         
1498         /* Only one click */
1499         if (mvalo == NULL) {
1500                 clickmax = clickmin;
1501         }
1502         /* Two click values (i.e., border select */
1503         else {
1504                 areamouseco_to_ipoco(G.v2d, mvalo, &x, &y);
1505                 click = ((wsize - y) / (CHANNELHEIGHT+CHANNELSKIP));
1506
1507                 if ( ((int) click) < clickmin) {
1508                         clickmax = clickmin;
1509                         clickmin = (int) click;
1510                 }
1511                 else {
1512                         clickmax = (int) click;
1513                 }
1514         }
1515
1516         if (clickmax < 0) {
1517                 return;
1518         }
1519
1520         /* clickmin and clickmax now coorespond to indices into
1521          * the collection of channels and constraint channels.
1522          * What we need to do is apply the selection mode on all
1523          * channels and constraint channels between these indices.
1524          * This is done by traversing the channels and constraint
1525          * channels, for each item decrementing clickmin and clickmax.
1526          * When clickmin is less than zero we start selecting stuff,
1527          * until clickmax is less than zero or we run out of channels
1528          * and constraint channels.
1529          */
1530
1531         for (chan = act->chanbase.first; chan; chan=chan->next){
1532                 if (clickmax < 0) break;
1533
1534                 if ( clickmin <= 0) {
1535                         /* Select the channel with the given mode. If the
1536                          * channel is freshly selected then set it to the
1537                          * active channel for the action
1538                          */
1539                         sel = (chan->flag & ACHAN_SELECTED);
1540                         if ( select_channel(act, chan, selectmode) && !sel ) {
1541                                 act->achan = chan;
1542                         }
1543                 }
1544                 --clickmin;
1545                 --clickmax;
1546
1547                 /* Check for click in a constraint */
1548                 for (conchan=chan->constraintChannels.first; 
1549                          conchan; conchan=conchan->next){
1550                         if (clickmax < 0) break;
1551                         if ( clickmin <= 0) {
1552                                 select_constraint_channel(act, conchan, selectmode);
1553                         }
1554                         --clickmin;
1555                         --clickmax;
1556                 }
1557         }
1558
1559         allqueue (REDRAWIPO, 0);
1560         allqueue (REDRAWVIEW3D, 0);
1561         allqueue (REDRAWACTION, 0);
1562         allqueue (REDRAWNLA, 0);
1563 }
1564
1565 void delete_meshchannel_keys(Key *key)
1566 {
1567         if (!okee("Erase selected keys"))
1568                 return;
1569
1570         BIF_undo_push("Delete Action keys");
1571         delete_ipo_keys(key->ipo);
1572
1573         meshkey_do_redraw(key);
1574 }
1575
1576 void delete_actionchannel_keys(void)
1577 {
1578         bAction *act;
1579         bActionChannel *chan;
1580         bConstraintChannel *conchan;
1581
1582         act = G.saction->action;
1583         if (!act)
1584                 return;
1585
1586         if (!okee("Erase selected keys"))
1587                 return;
1588
1589         for (chan = act->chanbase.first; chan; chan=chan->next){
1590
1591                 /* Check action channel keys*/
1592                 delete_ipo_keys(chan->ipo);
1593
1594                 /* Delete constraint channel keys */
1595                 for (conchan=chan->constraintChannels.first; conchan; conchan=conchan->next)
1596                         delete_ipo_keys(conchan->ipo);
1597         }
1598
1599         remake_action_ipos (act);
1600         BIF_undo_push("Delete Action keys");
1601         allspace(REMAKEIPO, 0);
1602         allqueue(REDRAWACTION, 0);
1603         allqueue(REDRAWIPO, 0);
1604         allqueue(REDRAWNLA, 0);
1605
1606 }
1607 static void delete_actionchannels (void)
1608 {
1609         bConstraintChannel *conchan=NULL, *nextconchan;
1610         bActionChannel *chan, *next;
1611         bAction *act;
1612         int freechan;
1613
1614         act=G.saction->action;
1615
1616         if (!act)
1617                 return;
1618
1619         for (chan=act->chanbase.first; chan; chan=chan->next){
1620                 if (chan->flag & ACHAN_SELECTED)
1621                         break;
1622                 for (conchan=chan->constraintChannels.first; conchan; conchan=conchan->next)
1623                 {
1624                         if (conchan->flag & CONSTRAINT_CHANNEL_SELECT){
1625                                 chan=act->chanbase.last;
1626                                 break;
1627                         }
1628                 }
1629         }
1630
1631         if (!chan && !conchan)
1632                 return;
1633
1634         if (!okee("Erase selected channels"))
1635                 return;
1636
1637         for (chan=act->chanbase.first; chan; chan=next){
1638                 freechan = 0;
1639                 next=chan->next;
1640                 
1641                 /* Remove action channels */
1642                 if (chan->flag & ACHAN_SELECTED){
1643                         if (chan->ipo)
1644                                 chan->ipo->id.us--;     /* Release the ipo */
1645                         freechan = 1;
1646                 }
1647                 
1648                 /* Remove constraint channels */
1649                 for (conchan=chan->constraintChannels.first; conchan; conchan=nextconchan){
1650                         nextconchan=conchan->next;
1651                         if (freechan || conchan->flag & CONSTRAINT_CHANNEL_SELECT){
1652                                 if (conchan->ipo)
1653                                         conchan->ipo->id.us--;
1654                                 BLI_freelinkN(&chan->constraintChannels, conchan);
1655                         }
1656                 }
1657                 
1658                 if (freechan)
1659                         BLI_freelinkN (&act->chanbase, chan);
1660
1661         }
1662
1663         BIF_undo_push("Delete Action channels");
1664         allqueue (REDRAWACTION, 0);
1665         allqueue(REDRAWNLA, 0);
1666
1667 }
1668
1669 void sethandles_meshchannel_keys(int code, Key *key)
1670 {
1671     sethandles_ipo_keys(key->ipo, code);
1672
1673         BIF_undo_push("Set handles Action keys");
1674         meshkey_do_redraw(key);
1675 }
1676
1677 void sethandles_actionchannel_keys(int code)
1678 {
1679         bAction *act;
1680         bActionChannel *chan;
1681
1682         /* Get the selected action, exit if none are selected 
1683          */
1684         act = G.saction->action;
1685         if (!act)
1686                 return;
1687
1688         /* Loop through the channels and set the beziers
1689          * of the selected keys based on the integer code
1690          */
1691         for (chan = act->chanbase.first; chan; chan=chan->next){
1692                 sethandles_ipo_keys(chan->ipo, code);
1693         }
1694
1695         /* Clean up and redraw stuff
1696          */
1697         remake_action_ipos (act);
1698         BIF_undo_push("Set handles Action channel");
1699         allspace(REMAKEIPO, 0);
1700         allqueue(REDRAWACTION, 0);
1701         allqueue(REDRAWIPO, 0);
1702         allqueue(REDRAWNLA, 0);
1703 }
1704
1705 void set_ipotype_actionchannels(int ipotype) {
1706
1707         bAction *act; 
1708         bActionChannel *chan;
1709         short event;
1710
1711         /* Get the selected action, exit if none are selected 
1712          */
1713         act = G.saction->action;
1714         if (!act)
1715                 return;
1716
1717         if (ipotype == SET_IPO_POPUP) {
1718                 /* Present a popup menu asking the user what type
1719                  * of IPO curve he/she/GreenBTH wants. ;)
1720                  */
1721                 event
1722                         =  pupmenu("Channel Ipo Type %t|"
1723                                            "Constant %x1|"
1724                                            "Linear %x2|"
1725                                            "Bezier %x3");
1726                 if(event < 1) return;
1727                 ipotype = event;
1728         }
1729         
1730         /* Loop through the channels and for the selected ones set
1731          * the type for each Ipo curve in the channel Ipo (based on
1732          * the value from the popup).
1733          */
1734         for (chan = act->chanbase.first; chan; chan=chan->next){
1735                 if (chan->flag & ACHAN_SELECTED){
1736                         if (chan->ipo)
1737                                 setipotype_ipo(chan->ipo, ipotype);
1738                 }
1739         }
1740
1741         /* Clean up and redraw stuff
1742          */
1743         remake_action_ipos (act);
1744         BIF_undo_push("Set Ipo type Action channel");
1745         allspace(REMAKEIPO, 0);
1746         allqueue(REDRAWACTION, 0);
1747         allqueue(REDRAWIPO, 0);
1748         allqueue(REDRAWNLA, 0);
1749 }
1750
1751 static void select_all_keys_frames(bAction *act, short *mval, 
1752                                                         short *mvalo, int selectmode) {
1753         
1754         /* This function tries to select all action keys in
1755          * every channel for a given range of keyframes that
1756          * are within the mouse values mval and mvalo (usually
1757          * the result of a border select). If mvalo is passed as
1758          * NULL then the selection is treated as a one-click and
1759          * the function tries to select all keys within half a
1760          * frame of the click point.
1761          */
1762         
1763         rcti rect;
1764         rctf rectf;
1765         bActionChannel *chan;
1766         bConstraintChannel *conchan;
1767
1768         if (!act)
1769                 return;
1770
1771         if (selectmode == SELECT_REPLACE) {
1772                 deselect_actionchannel_keys(act, 0);
1773                 selectmode = SELECT_ADD;
1774         }
1775
1776         if (mvalo == NULL) {
1777                 rect.xmin = rect.xmax = mval[0];
1778                 rect.ymin = rect.ymax = mval[1];
1779         }
1780         else {
1781                 if (mval[0] < mvalo[0] ) {
1782                         rect.xmin = mval[0];
1783                         rect.xmax = mvalo[0];
1784                 }
1785                 else {
1786                         rect.xmin = mvalo[0];
1787                         rect.xmax = mval[0];
1788                 }
1789                 if (mval[1] < mvalo[1] ) {
1790                         rect.ymin = mval[1];
1791                         rect.ymax = mvalo[1];
1792                 }
1793                 else {
1794                         rect.ymin = mvalo[1];
1795                         rect.ymax = mval[1];
1796                 }
1797         }
1798
1799         mval[0]= rect.xmin;
1800         mval[1]= rect.ymin+2;
1801         areamouseco_to_ipoco(G.v2d, mval, &rectf.xmin, &rectf.ymin);
1802         mval[0]= rect.xmax;
1803         mval[1]= rect.ymax-2;
1804         areamouseco_to_ipoco(G.v2d, mval, &rectf.xmax, &rectf.ymax);
1805
1806         if (mvalo == NULL) {
1807                 rectf.xmin = rectf.xmin - 0.5;
1808                 rectf.xmax = rectf.xmax + 0.5;
1809         }
1810     
1811         for (chan=act->chanbase.first; chan; chan=chan->next){
1812                 borderselect_ipo_key(chan->ipo, rectf.xmin, rectf.xmax,
1813                                                          selectmode);
1814                 for (conchan=chan->constraintChannels.first; conchan; 
1815                          conchan=conchan->next){
1816                         borderselect_ipo_key(conchan->ipo, rectf.xmin, rectf.xmax,
1817                                                                  selectmode);
1818                 }
1819         }       
1820
1821         allqueue(REDRAWNLA, 0);
1822         allqueue(REDRAWACTION, 0);
1823         allqueue(REDRAWIPO, 0);
1824 }
1825
1826
1827 static void select_all_keys_channels(bAction *act, short *mval, 
1828                               short *mvalo, int selectmode) {
1829         bActionChannel    *chan;
1830         float              click, x,y;
1831         int                clickmin, clickmax;
1832         int                wsize;
1833         bConstraintChannel *conchan;
1834
1835         /* This function selects all the action keys that
1836          * are in the mouse selection range defined by
1837          * the ordered pairs mval and mvalo (usually
1838          * these 2 are obtained from a border select).
1839          * If mvalo is NULL, then the selection is
1840          * treated like a one-click action, and at most
1841          * one channel is selected.
1842          */
1843
1844         /* If the action is null then abort
1845          */
1846         if (!act)
1847                 return;
1848
1849         if (selectmode == SELECT_REPLACE) {
1850                 deselect_actionchannel_keys(act, 0);
1851                 selectmode = SELECT_ADD;
1852         }
1853
1854         /* wsize is the greatest possible height (in pixels) that would be
1855          * needed to draw all of the action channels and constraint
1856          * channels.
1857          */
1858
1859         wsize =  count_action_levels(act)*(CHANNELHEIGHT+CHANNELSKIP);
1860         wsize += CHANNELHEIGHT/2;
1861         
1862     areamouseco_to_ipoco(G.v2d, mval, &x, &y);
1863     clickmin = (int) ((wsize - y) / (CHANNELHEIGHT+CHANNELSKIP));
1864         
1865         /* Only one click */
1866         if (mvalo == NULL) {
1867                 clickmax = clickmin;
1868         }
1869         /* Two click values (i.e., border select) */
1870         else {
1871
1872                 areamouseco_to_ipoco(G.v2d, mvalo, &x, &y);
1873                 click = ((wsize - y) / (CHANNELHEIGHT+CHANNELSKIP));
1874                 
1875                 if ( ((int) click) < clickmin) {
1876                         clickmax = clickmin;
1877                         clickmin = (int) click;
1878                 }
1879                 else {
1880                         clickmax = (int) click;
1881                 }
1882         }
1883
1884         if (clickmax < 0) {
1885                 return;
1886         }
1887
1888         for (chan = act->chanbase.first; chan; chan=chan->next){
1889                 if (clickmax < 0) break;
1890
1891                 if ( clickmin <= 0) {
1892                         /* Select the channel with the given mode. If the
1893                          * channel is freshly selected then set it to the
1894                          * active channel for the action
1895                          */
1896                         select_ipo_bezier_keys(chan->ipo, selectmode);
1897                 }
1898                 --clickmin;
1899                 --clickmax;
1900
1901                 /* Check for click in a constraint */
1902                 for (conchan=chan->constraintChannels.first; 
1903                          conchan; conchan=conchan->next){
1904                         if (clickmax < 0) break;
1905                         if ( clickmin <= 0) {
1906                                 select_ipo_bezier_keys(chan->ipo, selectmode);
1907                         }
1908                         --clickmin;
1909                         --clickmax;
1910                 }
1911         }
1912   
1913         allqueue (REDRAWIPO, 0);
1914         allqueue (REDRAWVIEW3D, 0);
1915         allqueue (REDRAWACTION, 0);
1916         allqueue (REDRAWNLA, 0);
1917   
1918 }
1919
1920 static void borderselect_function(void (*select_func)(bAction *act, 
1921                                                      short *mval, 
1922                                                      short *mvalo, 
1923                                                      int selectmode)) {
1924         /* This function executes an arbitrary selection
1925          * function as part of a border select. This
1926          * way the same function that is used for
1927          * right click selection points can generally
1928          * be used as the argument to this function
1929          */
1930         rcti rect;
1931         short   mval[2], mvalo[2];
1932         bAction *act;
1933         int val;                
1934
1935         /* Get the selected action, exit if none are selected 
1936          */
1937         act=G.saction->action;
1938         if (!act)
1939                 return;
1940
1941         /* Let the user draw a border (or abort)
1942          */
1943         if ( (val=get_border (&rect, 3)) ) {
1944                 mval[0]= rect.xmin;
1945                 mval[1]= rect.ymin+2;
1946                 mvalo[0]= rect.xmax;
1947                 mvalo[1]= rect.ymax-2;
1948
1949                 /* if the left mouse was used, do an additive
1950                  * selection with the user defined selection
1951                  * function.
1952                  */
1953                 if (val == LEFTMOUSE)
1954                         select_func(act, mval, mvalo, SELECT_ADD);
1955                 
1956                 /* if the right mouse was used, do a subtractive
1957                  * selection with the user defined selection
1958                  * function.
1959                  */
1960                 else if (val == RIGHTMOUSE)
1961                         select_func(act, mval, mvalo, SELECT_SUBTRACT);
1962         }
1963         BIF_undo_push("Border select Action");
1964         
1965 }
1966
1967 static void clever_keyblock_names(Key *key, short* mval){
1968     int        but=0, i, keynum;
1969     char       str[64];
1970         float      x;
1971         KeyBlock   *kb;
1972         /* get the keynum cooresponding to the y value
1973          * of the mouse pointer, return if this is
1974          * an invalid key number (and we don't deal
1975          * with the speed ipo).
1976          */
1977
1978     keynum = get_nearest_key_num(key, mval, &x);
1979     if ( (keynum < 1) || (keynum >= key->totkey) )
1980         return;
1981
1982         kb= key->block.first;
1983         for (i=0; i<keynum; ++i) kb = kb->next; 
1984
1985         if (kb->name[0] == '\0') {
1986                 sprintf(str, "Key %d", keynum);
1987         }
1988         else {
1989                 strcpy(str, kb->name);
1990         }
1991
1992         if ( (kb->slidermin >= kb->slidermax) ) {
1993                 kb->slidermin = 0.0;
1994                 kb->slidermax = 1.0;
1995         }
1996
1997     add_numbut(but++, TEX, "KB: ", 0, 24, str, 
1998                "Does this really need a tool tip?");
1999         add_numbut(but++, NUM|FLO, "Slider Min:", 
2000                            -10000, kb->slidermax, &kb->slidermin, 0);
2001         add_numbut(but++, NUM|FLO, "Slider Max:", 
2002                            kb->slidermin, 10000, &kb->slidermax, 0);
2003
2004     if (do_clever_numbuts(str, but, REDRAW)) {
2005                 strcpy(kb->name, str);
2006         allqueue (REDRAWACTION, 0);
2007                 allspace(REMAKEIPO, 0);
2008         allqueue (REDRAWIPO, 0);
2009         }
2010
2011         
2012 }
2013
2014 static void numbuts_action(void)
2015 {
2016         /* now called from action window event loop, plus reacts on mouseclick */
2017         /* removed Hos grunts for that reason! :) (ton) */
2018     Key *key;
2019     short mval[2];
2020
2021     if ( (key = get_action_mesh_key()) ) {
2022         getmouseco_areawin (mval);
2023                 if (mval[0]<NAMEWIDTH) {
2024                         clever_keyblock_names(key, mval);
2025                 }
2026     }
2027 }
2028
2029 void winqreadactionspace(ScrArea *sa, void *spacedata, BWinEvent *evt)
2030 {
2031         extern void do_actionbuts(unsigned short event); // drawaction.c
2032         SpaceAction *saction;
2033         bAction *act;
2034         Key *key;
2035         float dx,dy;
2036         int doredraw= 0;
2037         int     cfra;
2038         short   mval[2];
2039         unsigned short event= evt->event;
2040         short val= evt->val;
2041         short mousebut = L_MOUSE;
2042
2043         if(curarea->win==0) return;
2044
2045         saction= curarea->spacedata.first;
2046         if (!saction)
2047                 return;
2048
2049         act=saction->action;
2050         if(val) {
2051                 
2052                 if( uiDoBlocks(&curarea->uiblocks, event)!=UI_NOTHING ) event= 0;
2053                 
2054                 /* swap mouse buttons based on user preference */
2055                 if (U.flag & USER_LMOUSESELECT) {
2056                         if (event == LEFTMOUSE) {
2057                                 event = RIGHTMOUSE;
2058                                 mousebut = L_MOUSE;
2059                         } else if (event == RIGHTMOUSE) {
2060                                 event = LEFTMOUSE;
2061                                 mousebut = R_MOUSE;
2062                         }
2063                 }
2064                 
2065                 getmouseco_areawin(mval);
2066
2067                 key = get_action_mesh_key();
2068
2069                 switch(event) {
2070                 case UI_BUT_EVENT:
2071                         do_actionbuts(val);     // window itself
2072                         break;
2073                 
2074                 case HOMEKEY:
2075                         do_action_buttons(B_ACTHOME);   // header
2076                         break;
2077
2078                 case AKEY:
2079                         if (key) {
2080                                 if (mval[0]<ACTWIDTH){
2081                                         /* to do ??? */
2082                                 }
2083                                 else{
2084                                         deselect_meshchannel_keys(key, 1);
2085                                         allqueue (REDRAWACTION, 0);
2086                                         allqueue(REDRAWNLA, 0);
2087                                         allqueue (REDRAWIPO, 0);
2088                                 }
2089                         }
2090                         else {
2091                                 if (mval[0]<NAMEWIDTH){
2092                                         deselect_actionchannels (act, 1);
2093                                         allqueue (REDRAWVIEW3D, 0);
2094                                         allqueue (REDRAWACTION, 0);
2095                                         allqueue(REDRAWNLA, 0);
2096                                         allqueue (REDRAWIPO, 0);
2097                                 }
2098                                 else if (mval[0]>ACTWIDTH){
2099                                         deselect_actionchannel_keys (act, 1);
2100                                         allqueue (REDRAWACTION, 0);
2101                                         allqueue(REDRAWNLA, 0);
2102                                         allqueue (REDRAWIPO, 0);
2103                                 }
2104                         }
2105                         break;
2106
2107                 case BKEY:
2108                         if (key) {
2109                                 if (mval[0]<ACTWIDTH){
2110                                         /* to do?? */
2111                                 }
2112                                 else {
2113                                         borderselect_mesh(key);
2114                                 }
2115                         }
2116                         else {
2117
2118                                 /* If the border select is initiated in the
2119                                  * part of the action window where the channel
2120                                  * names reside, then select the channels
2121                                  */
2122                                 if (mval[0]<NAMEWIDTH){
2123                                         borderselect_function(mouse_actionchannels);
2124                                         BIF_undo_push("Select Action");
2125                                 }
2126                                 else if (mval[0]>ACTWIDTH){
2127
2128                                         /* If the border select is initiated in the
2129                                          * vertical scrollbar, then (de)select all keys
2130                                          * for the channels in the selection region
2131                                          */
2132                                         if (IN_2D_VERT_SCROLL(mval)) {
2133                                                 borderselect_function(select_all_keys_channels);
2134                                         }
2135
2136                                         /* If the border select is initiated in the
2137                                          * horizontal scrollbar, then (de)select all keys
2138                                          * for the keyframes in the selection region
2139                                          */
2140                                         else if (IN_2D_HORIZ_SCROLL(mval)) {
2141                                                 borderselect_function(select_all_keys_frames);
2142                                         }
2143
2144                                         /* Other wise, select the action keys
2145                                          */
2146                                         else {
2147                                                 borderselect_action();
2148                                         }
2149                                 }
2150                         }
2151                         break;
2152
2153                 case CKEY:
2154                         /* scroll the window so the current
2155                          * frame is in the center.
2156                          */
2157                         center_currframe();
2158                         break;
2159
2160                 case DKEY:
2161                         if (key) {
2162                                 if (G.qual & LR_SHIFTKEY && mval[0]>ACTWIDTH) {
2163                                         duplicate_meshchannel_keys(key);
2164                                 }
2165                         }
2166                         else if (act) {
2167                                 if (G.qual & LR_SHIFTKEY && mval[0]>ACTWIDTH){
2168                                         duplicate_actionchannel_keys();
2169                                         remake_action_ipos(act);
2170                                 }
2171                         }
2172                         break;
2173
2174                 case GKEY:
2175                         if (mval[0]>=ACTWIDTH) {
2176                                 if (key) {
2177                                         transform_meshchannel_keys('g', key);
2178                                 }
2179                                 else if (act) {
2180                                         transform_actionchannel_keys ('g');
2181                                 }
2182                         }
2183                         break;
2184
2185                 case HKEY:
2186                         if (key) {
2187                                 if(G.qual & LR_SHIFTKEY) {
2188                                         sethandles_meshchannel_keys(HD_AUTO, key);
2189                                 }
2190                                 else { 
2191                                         sethandles_meshchannel_keys(HD_ALIGN, key);
2192                                 }
2193                         }
2194                         else {
2195                                 if(G.qual & LR_SHIFTKEY) {
2196                                         sethandles_actionchannel_keys(HD_AUTO);
2197                                 }
2198                                 else { 
2199                                         sethandles_actionchannel_keys(HD_ALIGN);
2200                                 }
2201                         }
2202                         break;
2203                 
2204                 case NKEY:
2205                         if(G.qual==0) {
2206                                 numbuts_action();
2207                                 
2208                                 /* no panel (yet). current numbuts are not easy to put in panel... */
2209                                 //add_blockhandler(curarea, ACTION_HANDLER_PROPERTIES, UI_PNL_TO_MOUSE);
2210                                 //scrarea_queue_winredraw(curarea);
2211                         }
2212                         break;
2213                         
2214                 case SKEY: 
2215                         if (mval[0]>=ACTWIDTH) {
2216                                 if (key) {
2217                                         transform_meshchannel_keys('s', key);
2218                                 }
2219                                 else if (act) {
2220                                         transform_actionchannel_keys ('s');
2221                                 }
2222                         }
2223                         break;
2224
2225                         /*** set the Ipo type  ***/
2226                 case TKEY:
2227                         if (key) {
2228                                 /* to do */
2229                         }
2230                         else {
2231                                 set_ipotype_actionchannels(SET_IPO_POPUP);
2232                         }
2233                         break;
2234
2235                 case VKEY:
2236                         if (key) {
2237                                 sethandles_meshchannel_keys(HD_VECT, key);
2238                                 /* to do */
2239                         }
2240                         else {
2241                                 sethandles_actionchannel_keys(HD_VECT);
2242                         }
2243                         break;
2244
2245                 case PAGEUPKEY:
2246                         if (key) {
2247                                 /* to do */
2248                         }
2249                         else {
2250                                 if(G.qual & LR_SHIFTKEY) {
2251                                         top_sel_action();
2252                                 }
2253                                 else
2254                                 {
2255                                         up_sel_action();
2256                                 }
2257                                 
2258                         }
2259                         break;
2260                 case PAGEDOWNKEY:
2261                         if (key) {
2262                                 /* to do */
2263                         }
2264                         else {
2265                                 if(G.qual & LR_SHIFTKEY) {
2266                                         bottom_sel_action();
2267                                 }
2268                                 else
2269                                 down_sel_action();
2270                                 
2271                         }
2272                         break;
2273                 case DELKEY:
2274                 case XKEY:
2275                         if (key) {
2276                                 delete_meshchannel_keys(key);
2277                         }
2278                         else {
2279                                 if (mval[0]<NAMEWIDTH)
2280                                         delete_actionchannels ();
2281                                 else
2282                                         delete_actionchannel_keys ();
2283                         }
2284                         break;
2285                 /* LEFTMOUSE and RIGHTMOUSE event codes can be swapped above,
2286                  * based on user preference USER_LMOUSESELECT
2287                  */
2288                 case LEFTMOUSE:
2289                         if(view2dmove(LEFTMOUSE)); // only checks for sliders
2290                         else if (mval[0]>ACTWIDTH){
2291                                 do {
2292                                         getmouseco_areawin(mval);
2293                                         
2294                                         areamouseco_to_ipoco(G.v2d, mval, &dx, &dy);
2295                                         
2296                                         cfra= (int)dx;
2297                                         if(cfra< 1) cfra= 1;
2298                                         
2299                                         if( cfra!=CFRA ) {
2300                                                 CFRA= cfra;
2301                                                 update_for_newframe();
2302                                                 force_draw_all(0);                                      }
2303                                         else PIL_sleep_ms(30);
2304                                         
2305                                 } while(get_mbut() & mousebut);
2306                         }
2307                         break;
2308                 case RIGHTMOUSE:
2309                         /* Clicking in the channel area selects the
2310                          * channel or constraint channel
2311                          */
2312                         if (mval[0]<NAMEWIDTH) {
2313                                 if(act) {
2314                                         if(G.qual & LR_SHIFTKEY)
2315                                                 mouse_actionchannels(act, mval, NULL,  SELECT_INVERT);
2316                                         else
2317                                                 mouse_actionchannels(act, mval, NULL,  SELECT_REPLACE);
2318                                         
2319                                         BIF_undo_push("Select Action");
2320                                 }
2321                                 else numbuts_action();
2322                         }
2323                         else if (mval[0]>ACTWIDTH) {
2324                 
2325                                 /* Clicking in the vertical scrollbar selects
2326                                  * all of the keys for that channel at that height
2327                                  */
2328                                 if (IN_2D_VERT_SCROLL(mval)) {
2329                                         if(G.qual & LR_SHIFTKEY)
2330                                                 select_all_keys_channels(act, mval, NULL, 
2331                                                                                                  SELECT_INVERT);
2332                                         else
2333                                                 select_all_keys_channels(act, mval, NULL, 
2334                                                                                                  SELECT_REPLACE);
2335                                 }
2336                 
2337                                 /* Clicking in the horizontal scrollbar selects
2338                                  * all of the keys within 0.5 of the nearest integer
2339                                  * frame
2340                                  */
2341                                 else if (IN_2D_HORIZ_SCROLL(mval)) {
2342                                         if(G.qual & LR_SHIFTKEY)
2343                                                 select_all_keys_frames(act, mval, NULL, 
2344                                                                                            SELECT_INVERT);
2345                                         else
2346                                                 select_all_keys_frames(act, mval, NULL, 
2347                                                                                            SELECT_REPLACE);
2348                                         BIF_undo_push("Select all Action");
2349                                 }
2350                                 
2351                                 /* Clicking in the main area of the action window
2352                                  * selects keys
2353                                  */
2354                                 else {
2355                                         if (key) {
2356                                                 if(G.qual & LR_SHIFTKEY)
2357                                                         mouse_mesh_action(SELECT_INVERT, key);
2358                                                 else
2359                                                         mouse_mesh_action(SELECT_REPLACE, key);
2360                                         }
2361                                         else {
2362                                                 if(G.qual & LR_SHIFTKEY)
2363                                                         mouse_action(SELECT_INVERT);
2364                                                 else
2365                                                         mouse_action(SELECT_REPLACE);
2366                                         }
2367                                 }
2368                         }
2369                         break;
2370                 case PADPLUSKEY:
2371                         view2d_zoom(G.v2d, 0.1154, sa->winx, sa->winy);
2372                         test_view2d(G.v2d, sa->winx, sa->winy);
2373                         doredraw= 1;
2374                         break;
2375                 case PADMINUS:
2376                         view2d_zoom(G.v2d, -0.15, sa->winx, sa->winy);
2377                         test_view2d(G.v2d, sa->winx, sa->winy);
2378                         doredraw= 1;
2379                         break;
2380                 case MIDDLEMOUSE:
2381                 case WHEELUPMOUSE:
2382                 case WHEELDOWNMOUSE:
2383                         view2dmove(event);      /* in drawipo.c */
2384                         break;
2385                 }
2386         }
2387
2388         if(doredraw) addqueue(curarea->win, REDRAW, 1);
2389         
2390 }
2391
2392 Key *get_action_mesh_key(void) 
2393 {
2394         /* gets the key data from the currently selected
2395          * mesh/lattice. If a mesh is not selected, or does not have
2396          * key data, then we return NULL (currently only
2397          * returns key data for RVK type meshes). If there
2398          * is an action that is pinned, return null
2399          */
2400     Object *ob;
2401     Key    *key;
2402
2403     ob = OBACT;
2404     if (!ob) return NULL;
2405
2406         if (G.saction->pin) return NULL;
2407
2408     if (ob->type==OB_MESH ) {
2409                 key = ((Mesh *)ob->data)->key;
2410     }
2411         else if (ob->type==OB_LATTICE ) {
2412                 key = ((Lattice *)ob->data)->key;
2413         }
2414         else return NULL;
2415
2416         if (key) {
2417                 if (key->type == KEY_RELATIVE)
2418                         return key;
2419         }
2420
2421     return NULL;
2422 }
2423
2424 int get_nearest_key_num(Key *key, short *mval, float *x) {
2425         /* returns the key num that cooresponds to the
2426          * y value of the mouse click. Does not check
2427          * if this is a valid keynum. Also gives the Ipo
2428          * x coordinate.
2429          */
2430     int num;
2431     float ybase, y;
2432
2433     areamouseco_to_ipoco(G.v2d, mval, x, &y);
2434
2435     ybase = key->totkey * (CHANNELHEIGHT + CHANNELSKIP);
2436     num = (int) ((ybase - y + CHANNELHEIGHT/2) / (CHANNELHEIGHT+CHANNELSKIP));
2437
2438     return (num + 1);
2439 }
2440
2441
2442
2443 void top_sel_action()
2444 {
2445         bAction *act;
2446         bActionChannel *chan;
2447         
2448         /* Get the selected action, exit if none are selected */
2449         act = G.saction->action;
2450         if (!act) return;
2451         
2452         for (chan=act->chanbase.first; chan; chan=chan->next){
2453                 if ((chan->flag & ACHAN_SELECTED) && !(chan->flag & ACHAN_MOVED)){
2454                         /* take it out off the chain keep data */
2455                         BLI_remlink (&act->chanbase, chan);
2456                         /* make it first element */
2457                         BLI_insertlinkbefore(&act->chanbase,act->chanbase.first, chan);
2458                         chan->flag |= ACHAN_MOVED;
2459                         /* restart with rest of list */
2460                         chan=chan->next;
2461                 }
2462         }
2463     /* clear temp flags */
2464         for (chan=act->chanbase.first; chan; chan=chan->next){
2465                 chan->flag = chan->flag & ~ACHAN_MOVED;
2466         }
2467         
2468         /* Clean up and redraw stuff */
2469         remake_action_ipos (act);
2470         BIF_undo_push("Top Action channel");
2471         allspace(REMAKEIPO, 0);
2472         allqueue(REDRAWACTION, 0);
2473         allqueue(REDRAWIPO, 0);
2474         allqueue(REDRAWNLA, 0);
2475 }
2476
2477 void up_sel_action()
2478 {
2479         bAction *act;
2480         bActionChannel *chan;
2481         bActionChannel *prev;
2482         
2483         /* Get the selected action, exit if none are selected */
2484         act = G.saction->action;
2485         if (!act) return;
2486         
2487         for (chan=act->chanbase.first; chan; chan=chan->next){
2488                 if ((chan->flag & ACHAN_SELECTED) && !(chan->flag & ACHAN_MOVED)){
2489                         prev = chan->prev;
2490                         if (prev){
2491                                 /* take it out off the chain keep data */
2492                                 BLI_remlink (&act->chanbase, chan);
2493                                 /* push it up */
2494                                 BLI_insertlinkbefore(&act->chanbase,prev, chan);
2495                                 chan->flag |= ACHAN_MOVED;
2496                                 /* restart with rest of list */
2497                                 chan=chan->next;
2498                         }
2499                 }
2500         }
2501         /* clear temp flags */
2502         for (chan=act->chanbase.first; chan; chan=chan->next){
2503                 chan->flag = chan->flag & ~ACHAN_MOVED;
2504         }
2505         
2506         /* Clean up and redraw stuff
2507         */
2508         remake_action_ipos (act);
2509         BIF_undo_push("Up Action channel");
2510         allspace(REMAKEIPO, 0);
2511         allqueue(REDRAWACTION, 0);
2512         allqueue(REDRAWIPO, 0);
2513         allqueue(REDRAWNLA, 0);
2514 }
2515
2516 void down_sel_action()
2517 {
2518         bAction *act;
2519         bActionChannel *chan;
2520         bActionChannel *next;
2521         
2522         /* Get the selected action, exit if none are selected */
2523         act = G.saction->action;
2524         if (!act) return;
2525         
2526         for (chan=act->chanbase.last; chan; chan=chan->prev){
2527                 if ((chan->flag & ACHAN_SELECTED) && !(chan->flag & ACHAN_MOVED)){
2528                         next = chan->next;
2529                         if (next) next = next->next;
2530                         if (next){
2531                                 /* take it out off the chain keep data */
2532                                 BLI_remlink (&act->chanbase, chan);
2533                                 /* move it down */
2534                                 BLI_insertlinkbefore(&act->chanbase,next, chan);
2535                                 chan->flag |= ACHAN_MOVED;
2536                         }
2537                         else {
2538                                 /* take it out off the chain keep data */
2539                                 BLI_remlink (&act->chanbase, chan);
2540                                 /* add at end */
2541                                 BLI_addtail(&act->chanbase,chan);
2542                                 chan->flag |= ACHAN_MOVED;
2543                         }
2544
2545                 }
2546         }
2547         /* clear temp flags */
2548         for (chan=act->chanbase.first; chan; chan=chan->next){
2549                 chan->flag = chan->flag & ~ACHAN_MOVED;
2550         }
2551         
2552         /* Clean up and redraw stuff
2553         */
2554         remake_action_ipos (act);
2555         BIF_undo_push("Down Action channel");
2556         allspace(REMAKEIPO, 0);
2557         allqueue(REDRAWACTION, 0);
2558         allqueue(REDRAWIPO, 0);
2559         allqueue(REDRAWNLA, 0);
2560 }
2561
2562 void bottom_sel_action()
2563 {
2564         bAction *act;
2565         bActionChannel *chan;
2566         
2567         /* Get the selected action, exit if none are selected */
2568         act = G.saction->action;
2569         if (!act) return;
2570         
2571         for (chan=act->chanbase.last; chan; chan=chan->prev){
2572                 if ((chan->flag & ACHAN_SELECTED) && !(chan->flag & ACHAN_MOVED)) {
2573                         /* take it out off the chain keep data */
2574                         BLI_remlink (&act->chanbase, chan);
2575                         /* add at end */
2576                         BLI_addtail(&act->chanbase,chan);
2577                         chan->flag |= ACHAN_MOVED;
2578                 }
2579                 
2580         }
2581         /* clear temp flags */
2582         for (chan=act->chanbase.first; chan; chan=chan->next){
2583                 chan->flag = chan->flag & ~ACHAN_MOVED;
2584         }
2585         
2586         /* Clean up and redraw stuff
2587         */
2588         remake_action_ipos (act);
2589         BIF_undo_push("Bottom Action channel");
2590         allspace(REMAKEIPO, 0);
2591         allqueue(REDRAWACTION, 0);
2592         allqueue(REDRAWIPO, 0);
2593         allqueue(REDRAWNLA, 0);
2594 }