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