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