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