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