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