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