Bug #1670
[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                 allqueue(REDRAWIPO, 0);
588                 allqueue(REDRAWVIEW3D, 0);
589                 allqueue(REDRAWACTION, 0);
590                 allqueue(REDRAWNLA, 0);
591
592         }
593 }
594
595 static void mouse_mesh_action(int selectmode, Key *key)
596 {
597         /* Handle a right mouse click selection in an
598          * action window displaying RVK data
599          */
600
601     IpoCurve *icu;
602     short  sel;
603     float  selx;
604     short  mval[2];
605
606     /* going to assume that the only reason 
607      * we got here is because it has been 
608      * determined that we are a mesh with
609      * the right properties (i.e., have key
610      * data, etc)
611      */
612
613         /* get the click location, and the cooresponding
614          * ipo curve and selection time value
615          */
616     getmouseco_areawin (mval);
617     icu = get_nearest_meshchannel_key(&selx, &sel);
618
619     if (icu){
620         if (selectmode == SELECT_REPLACE) {
621                         /* if we had planned to replace the
622                          * selection, then we will first deselect
623                          * all of the keys, and if the clicked on
624                          * key had been unselected, we will select 
625                          * it, otherwise, we are done.
626                          */
627             deselect_meshchannel_keys(key, 0);
628
629             if (sel == 0)
630                 selectmode = SELECT_ADD;
631             else
632                                 /* the key is selected so we should
633                                  * deselect -- but everything is now deselected
634                                  * so we are done.
635                                  */
636                                 return;
637         }
638                 
639                 /* select the key using the given mode
640                  * and redraw as mush stuff as needed.
641                  */
642                 select_icu_key(icu, selx, selectmode);
643
644         allqueue(REDRAWIPO, 0);
645         allqueue(REDRAWVIEW3D, 0);
646         allqueue(REDRAWACTION, 0);
647         allqueue(REDRAWNLA, 0);
648
649     }
650 }
651
652 void borderselect_action(void)
653
654         rcti rect;
655         rctf rectf;
656         int val, selectmode;            
657         short   mval[2];
658         bActionChannel *chan;
659         bConstraintChannel *conchan;
660         bAction *act;
661         float   ymin, ymax;
662
663         act=G.saction->action;
664
665
666         if (!act)
667                 return;
668
669         if ( (val = get_border(&rect, 3)) ){
670     if (val == LEFTMOUSE)
671       selectmode = SELECT_ADD;
672     else
673       selectmode = SELECT_SUBTRACT;
674
675                 mval[0]= rect.xmin;
676                 mval[1]= rect.ymin+2;
677                 areamouseco_to_ipoco(G.v2d, mval, &rectf.xmin, &rectf.ymin);
678                 mval[0]= rect.xmax;
679                 mval[1]= rect.ymax-2;
680                 areamouseco_to_ipoco(G.v2d, mval, &rectf.xmax, &rectf.ymax);
681                 
682                 ymax= count_action_levels(act) * (CHANNELHEIGHT+CHANNELSKIP);
683                 ymax += CHANNELHEIGHT/2;
684                 
685                 for (chan=act->chanbase.first; chan; chan=chan->next){
686                         
687                         /* Check action */
688                         ymin=ymax-(CHANNELHEIGHT+CHANNELSKIP);
689                         if (!((ymax < rectf.ymin) || (ymin > rectf.ymax)))
690           borderselect_ipo_key(chan->ipo, rectf.xmin, rectf.xmax,
691                                selectmode);
692
693                         ymax=ymin;
694
695                         /* Check constraints */
696                         for (conchan=chan->constraintChannels.first; conchan; conchan=conchan->next){
697                                 ymin=ymax-(CHANNELHEIGHT+CHANNELSKIP);
698                                 if (!((ymax < rectf.ymin) || (ymin > rectf.ymax)))
699                                         borderselect_ipo_key(conchan->ipo, rectf.xmin, rectf.xmax,
700                                selectmode);
701                                 
702                                 ymax=ymin;
703                         }
704                 }       
705                 allqueue(REDRAWNLA, 0);
706                 allqueue(REDRAWACTION, 0);
707                 allqueue(REDRAWIPO, 0);
708         }
709 }
710
711 void borderselect_mesh(Key *key)
712
713         rcti     rect;
714         int      val, adrcodemax, adrcodemin;
715         short    mval[2];
716         float    xmin, xmax;
717         int      (*select_function)(BezTriple *);
718         IpoCurve *icu;
719
720         if ( (val = get_border(&rect, 3)) ){
721                 /* set the selection function based on what
722                  * mouse button had been used in the border
723                  * select
724                  */
725                 if (val == LEFTMOUSE)
726                         select_function = select_bezier_add;
727                 else
728                         select_function = select_bezier_subtract;
729
730                 /* get the minimum and maximum adrcode numbers
731                  * for the IpoCurves (this is the number that
732                  * relates an IpoCurve to the keyblock it
733                  * controls).
734                  */
735                 mval[0]= rect.xmin;
736                 mval[1]= rect.ymin+2;
737                 adrcodemax = get_nearest_key_num(key, mval, &xmin);
738                 adrcodemax = (adrcodemax >= key->totkey) ? key->totkey : adrcodemax;
739
740                 mval[0]= rect.xmax;
741                 mval[1]= rect.ymax-2;
742                 adrcodemin = get_nearest_key_num(key, mval, &xmax);
743                 adrcodemin = (adrcodemin < 1) ? 1 : adrcodemin;
744
745                 /* Lets loop throug the IpoCurves and do borderselect
746                  * on the curves with adrcodes in our selected range.
747                  */
748                 for (icu = key->ipo->curve.first; icu ; icu = icu->next) {
749                         /* lets not deal with the "speed" Ipo
750                          */
751                         if (!icu->adrcode) continue;
752                         if ( (icu->adrcode >= adrcodemin) && 
753                                  (icu->adrcode <= adrcodemax) ) {
754                                 borderselect_icu_key(icu, xmin, xmax, select_function);
755                         }
756                 }
757
758                 /* redraw stuff */
759                 allqueue(REDRAWNLA, 0);
760                 allqueue(REDRAWACTION, 0);
761                 allqueue(REDRAWIPO, 0);
762         }
763 }
764
765 /* used by ipo, outliner, buttons to find the active channel */
766 bActionChannel* get_hilighted_action_channel(bAction* action)
767 {
768         bActionChannel *chan;
769
770         if (!action)
771                 return NULL;
772
773         for (chan=action->chanbase.first; chan; chan=chan->next){
774                 if (chan->flag & ACHAN_SELECTED && chan->flag & ACHAN_HILIGHTED)
775                         return chan;
776         }
777
778         return NULL;
779
780 }
781
782 void set_exprap_action(int mode)
783 {
784         if(G.saction->action && G.saction->action->id.lib) return;
785
786         error ("Not yet implemented!");
787 }
788
789 void free_posebuf(void) 
790 {
791         if (g_posebuf){
792                 clear_pose(g_posebuf);
793                 MEM_freeN (g_posebuf);
794         }
795         g_posebuf=NULL;
796 }
797
798 void copy_posebuf (void)
799 {
800         Object *ob;
801
802         free_posebuf();
803
804         ob=G.obpose;
805         if (!ob){
806                 error ("Copy buffer is empty");
807                 return;
808         }
809
810         filter_pose_keys();
811         copy_pose(&g_posebuf, ob->pose, 0);
812
813 }
814
815 static void flip_name (char *name)
816 {
817
818         char    prefix[128]={""};       /* The part before the facing */
819         char    suffix[128]={""};       /* The part after the facing */
820         char    replace[128]={""};      /* The replacement string */
821
822         char    *index=NULL;
823         /* Find the last period */
824
825         strcpy (prefix, name);
826
827         /* Check for an instance of .Right */
828         if (!index){
829                 index = strstr (prefix, "Right");
830                 if (index){
831                         *index=0;
832                         strcpy (replace, "Left");
833                         strcpy (suffix, index+6);
834                 }
835         }
836
837         /* Che ck for an instance of .RIGHT */
838         if (!index){
839                 index = strstr (prefix, "RIGHT");
840                 if (index){
841                         *index=0;
842                         strcpy (replace, "LEFT");
843                         strcpy (suffix, index+6);
844                 }
845         }
846
847
848         /* Check for an instance of .right */
849         if (!index){
850                 index = strstr (prefix, "right");
851                 if (index){
852                         *index=0;
853                         strcpy (replace, "left");
854                         strcpy (suffix, index+6);
855                 }
856         }
857
858         /* Check for an instance of .left */
859         if (!index){
860                 index = strstr (prefix, "left");
861                 if (index){
862                         *index=0;
863                         strcpy (replace, "right");
864                         strcpy (suffix, index+5);
865                 }
866         }
867
868         /* Check for an instance of .LEFT */
869         if (!index){
870                 index = strstr (prefix, "LEFT");
871                 if (index){
872                         *index=0;
873                         strcpy (replace, "RIGHT");
874                         strcpy (suffix, index+5);
875                 }
876         }
877
878         /* Check for an instance of .Left */
879         if (!index){
880                 index = strstr (prefix, "Left");
881                 if (index){
882                         *index=0;
883                         strcpy (replace, "Right");
884                         strcpy (suffix, index+5);
885                 }
886         }
887
888         /* check for an instance of .L */
889         if (!index){
890                 index = strstr (prefix, ".L");
891                 if (index){
892                         *index=0;
893                         strcpy (replace, ".R");
894                         strcpy (suffix, index+2);
895                 }
896         }
897
898         /* check for an instance of .l */
899         if (!index){
900                 index = strstr (prefix, ".l");
901                 if (index){
902                         *index=0;
903                         strcpy (replace, ".r");
904                         strcpy (suffix, index+2);
905                 }
906         }
907
908         /* Checl for an instance of .R */
909         if (!index){
910                 index = strstr (prefix, ".R");
911                 if (index){
912                         *index=0;
913                         strcpy (replace, ".L");
914                         strcpy (suffix, index+2);
915                 }
916         }
917
918         /* Checl for an instance of .r */
919         if (!index){
920                 index = strstr (prefix, ".r");
921                 if (index){
922                         *index=0;
923                         strcpy (replace, ".l");
924                         strcpy (suffix, index+2);
925                 }
926         }
927
928         sprintf (name, "%s%s%s", prefix, replace, suffix);
929 }
930
931 void paste_posebuf (int flip){
932         Object *ob;
933         bPoseChannel *temp, *chan;
934         float eul[4];
935         Base    *base;
936         int             newchan = 0;
937
938         ob=G.obpose;
939         if (!ob)
940                 return;
941
942         if (!g_posebuf){
943                 error ("Copy buffer is empty");
944                 return;
945         };
946         
947         collect_pose_garbage(ob);
948
949         /* Safely merge all of the channels in this pose into
950         any existing pose */
951         if (ob->pose){
952                 if (U.uiflag & USER_KEYINSERTACT){
953                         /* Display "Avail, all" dialog */
954                 }
955                 for (chan=g_posebuf->chanbase.first; chan; chan=chan->next){
956                         if (chan->flag & POSE_KEY){
957                                 temp = copy_pose_channel (chan);
958                                 if (flip){
959                                         flip_name (temp->name);
960                                         temp->loc[0]*=-1;
961
962                                         QuatToEul(temp->quat, eul);
963                                         eul[1]*=-1;
964                                         eul[2]*=-1;
965                                         EulToQuat(eul, temp->quat);
966                                 }
967
968                                 temp = set_pose_channel (ob->pose, temp);
969
970                                 if (U.uiflag & USER_KEYINSERTACT){
971                                         /* Set keys on pose */
972                                         if (chan->flag & POSE_ROT){
973                                                 set_action_key(ob->action, temp, AC_QUAT_X, newchan);
974                                                 set_action_key(ob->action, temp, AC_QUAT_Y, newchan);
975                                                 set_action_key(ob->action, temp, AC_QUAT_Z, newchan);
976                                                 set_action_key(ob->action, temp, AC_QUAT_W, newchan);
977                                         };
978                                         if (chan->flag & POSE_SIZE){
979                                                 set_action_key(ob->action, temp, AC_SIZE_X, newchan);
980                                                 set_action_key(ob->action, temp, AC_SIZE_Y, newchan);
981                                                 set_action_key(ob->action, temp, AC_SIZE_Z, newchan);
982                                         };
983                                         if (chan->flag & POSE_LOC){
984                                                 set_action_key(ob->action, temp, AC_LOC_X, newchan);
985                                                 set_action_key(ob->action, temp, AC_LOC_Y, newchan);
986                                                 set_action_key(ob->action, temp, AC_LOC_Z, newchan);
987                                         };                                      
988                                 }
989                         }
990                 }
991
992                 if (U.uiflag & USER_KEYINSERTACT){
993                         remake_action_ipos(ob->action);
994                         allqueue (REDRAWIPO, 0);
995                         allqueue (REDRAWVIEW3D, 0);
996                         allqueue (REDRAWACTION, 0);             
997                         allqueue(REDRAWNLA, 0);
998                 }
999
1000                 /* Update deformation children */
1001                 if (G.obpose->type == OB_ARMATURE){
1002                         for (base= FIRSTBASE; base; base= base->next){
1003                                 if (G.obpose==base->object->parent){
1004                                         if (base->object->partype==PARSKEL)
1005                                                 makeDispList(base->object);
1006                                 }
1007                         }
1008                 }
1009         }
1010 }
1011
1012 void set_action_key (struct bAction *act, struct bPoseChannel *chan, int adrcode, short makecurve)
1013 {
1014         set_action_key_time (act, chan, adrcode, makecurve, frame_to_float(CFRA));
1015 }
1016
1017 static void set_action_key_time (bAction *act, bPoseChannel *chan, int adrcode, short makecurve, float time)
1018 {
1019         bActionChannel  *achan;
1020         char    ipstr[256];
1021
1022         if (!act)
1023                 return;
1024
1025         if (!chan)
1026                 return;
1027         /* See if this action channel exists already */ 
1028         for (achan=act->chanbase.first; achan; achan=achan->next){
1029                 if (!strcmp (chan->name, achan->name))
1030                         break;
1031         }
1032
1033         if (!achan){
1034                 if (!makecurve)
1035                         return;
1036                 achan = MEM_callocN (sizeof(bActionChannel), "actionChannel");
1037                 strcpy (achan->name, chan->name);
1038                 BLI_addtail (&act->chanbase, achan);
1039         }
1040
1041         /* Ensure the channel appears selected in the action window */
1042         /* ton: added flag hilighted, for display in ipowin. dunno what the difference is between select/hilite */
1043         achan->flag |= ACHAN_SELECTED|ACHAN_HILIGHTED;
1044
1045         /* Ensure this action channel has a valid Ipo */
1046         if (!achan->ipo){
1047                 sprintf (ipstr, "%s.%s", act->id.name+2, chan->name);
1048                 ipstr[23]=0;
1049                 achan->ipo=     add_ipo(ipstr, ID_AC);  
1050         }
1051
1052         insertactionkey(act, achan, chan, adrcode, makecurve, time);
1053
1054 }
1055
1056 static void insertactionkey(bAction *act, bActionChannel *achan, bPoseChannel *chan, int adrcode, short makecurve, float cfra)
1057 {
1058         IpoCurve *icu;
1059         void *poin;
1060         float curval;
1061         int type;
1062         ID *id;
1063         
1064         if (!act){
1065                 return;
1066         }
1067         if (act->id.lib){
1068                 error ("Can't pose library actions");
1069                 return;
1070         }
1071         act->achan=achan;
1072         act->pchan=chan;
1073
1074         id=(ID*) act;
1075
1076         /* First see if this curve exists */
1077         if (!makecurve){
1078                 if (!achan->ipo)
1079                         return;
1080
1081                 for (icu = achan->ipo->curve.first; icu; icu=icu->next){
1082                         if (icu->adrcode == adrcode)
1083                                 break;
1084                 }
1085                 if (!icu)
1086                         return;
1087         }
1088
1089         
1090         icu = get_ipocurve (id, GS(id->name), adrcode, achan->ipo);
1091
1092         if(icu) {
1093                 poin= get_ipo_poin(id, icu, &type);
1094                 if(poin) {
1095                         curval= read_ipo_poin(poin, type);
1096         //              cfra= frame_to_float(CFRA);
1097                         insert_vert_ipo(icu, cfra, curval);
1098                 }
1099         }
1100         
1101 }
1102
1103 bAction *add_empty_action(void)
1104 {
1105         bAction *act;
1106
1107         act= alloc_libblock(&G.main->action, ID_AC, "Action");
1108         act->id.flag |= LIB_FAKEUSER;
1109         act->id.us++;
1110         return act;
1111 }
1112
1113 void transform_actionchannel_keys(char mode)
1114 {
1115         bAction *act;
1116         TransVert *tv;
1117         int /*sel=0,*/  i;
1118         bActionChannel  *chan;
1119         short   mvals[2], mvalc[2], cent[2];
1120         float   sval[2], cval[2], lastcval[2];
1121         short   cancel=0;
1122         float   fac=0.0F;
1123         int             loop=1;
1124         int             tvtot=0;
1125         float   deltax, startx;
1126         float   cenf[2];
1127         int             invert=0, firsttime=1;
1128         char    str[256];
1129         bConstraintChannel *conchan;
1130
1131         act=G.saction->action;
1132
1133         /* Ensure that partial selections result in beztriple selections */
1134         for (chan=act->chanbase.first; chan; chan=chan->next){
1135                 tvtot+=fullselect_ipo_keys(chan->ipo);
1136
1137                 for (conchan=chan->constraintChannels.first; conchan; conchan=conchan->next)
1138                         tvtot+=fullselect_ipo_keys(conchan->ipo);
1139         }
1140         
1141         /* If nothing is selected, bail out */
1142         if (!tvtot)
1143                 return;
1144         
1145         
1146         /* Build the transvert structure */
1147         tv = MEM_callocN (sizeof(TransVert) * tvtot, "transVert");
1148         tvtot=0;
1149         for (chan=act->chanbase.first; chan; chan=chan->next){
1150                 /* Add the actionchannel */
1151                 tvtot = add_trans_ipo_keys(chan->ipo, tv, tvtot);
1152                 for (conchan=chan->constraintChannels.first; conchan; conchan=conchan->next)
1153                         tvtot = add_trans_ipo_keys(conchan->ipo, tv, tvtot);
1154         }
1155
1156         /* Do the event loop */
1157         cent[0] = curarea->winx + (G.saction->v2d.hor.xmax)/2;
1158         cent[1] = curarea->winy + (G.saction->v2d.hor.ymax)/2;
1159         areamouseco_to_ipoco(G.v2d, cent, &cenf[0], &cenf[1]);
1160
1161         getmouseco_areawin (mvals);
1162         areamouseco_to_ipoco(G.v2d, mvals, &sval[0], &sval[1]);
1163
1164         startx=sval[0];
1165         while (loop) {
1166                 /*              Get the input */
1167                 /*              If we're cancelling, reset transformations */
1168                 /*                      Else calc new transformation */
1169                 /*              Perform the transformations */
1170                 while (qtest()) {
1171                         short val;
1172                         unsigned short event= extern_qread(&val);
1173
1174                         if (val) {
1175                                 switch (event) {
1176                                 case LEFTMOUSE:
1177                                 case SPACEKEY:
1178                                 case RETKEY:
1179                                         loop=0;
1180                                         break;
1181                                 case XKEY:
1182                                         break;
1183                                 case ESCKEY:
1184                                 case RIGHTMOUSE:
1185                                         cancel=1;
1186                                         loop=0;
1187                                         break;
1188                                 default:
1189                                         arrows_move_cursor(event);
1190                                         break;
1191                                 };
1192                         }
1193                 }
1194
1195                 if (cancel) {
1196                         for (i=0; i<tvtot; i++) {
1197                                 tv[i].loc[0]=tv[i].oldloc[0];
1198                                 tv[i].loc[1]=tv[i].oldloc[1];
1199                         }
1200                 } else {
1201                         getmouseco_areawin (mvalc);
1202                         areamouseco_to_ipoco(G.v2d, mvalc, &cval[0], &cval[1]);
1203
1204                         if (!firsttime && lastcval[0]==cval[0] && lastcval[1]==cval[1]) {
1205                                 PIL_sleep_ms(1);
1206                         } else {
1207                                 for (i=0; i<tvtot; i++){
1208                                         tv[i].loc[0]=tv[i].oldloc[0];
1209
1210                                         switch (mode){
1211                                         case 'g':
1212                                                 deltax = cval[0]-sval[0];
1213                                                 fac= deltax;
1214                                                 
1215                                                 apply_keyb_grid(&fac, 0.0, 1.0, 0.1, U.flag & USER_AUTOGRABGRID);
1216
1217                                                 tv[i].loc[0]+=fac;
1218                                                 break;
1219                                         case 's':
1220                                                 startx=mvals[0]-(ACTWIDTH/2+(curarea->winrct.xmax-curarea->winrct.xmin)/2);
1221                                                 deltax=mvalc[0]-(ACTWIDTH/2+(curarea->winrct.xmax-curarea->winrct.xmin)/2);
1222                                                 fac= fabs(deltax/startx);
1223                                                 
1224                                                 apply_keyb_grid(&fac, 0.0, 0.2, 0.1, U.flag & USER_AUTOSIZEGRID);
1225                 
1226                                                 if (invert){
1227                                                         if (i % 03 == 0){
1228                                                                 memcpy (tv[i].loc, tv[i].oldloc, sizeof(tv[i+2].oldloc));
1229                                                         }
1230                                                         if (i % 03 == 2){
1231                                                                 memcpy (tv[i].loc, tv[i].oldloc, sizeof(tv[i-2].oldloc));
1232                                                         }
1233         
1234                                                         fac*=-1;
1235                                                 }
1236                                                 startx= (G.scene->r.cfra);
1237                                         
1238                                                 tv[i].loc[0]-= startx;
1239                                                 tv[i].loc[0]*=fac;
1240                                                 tv[i].loc[0]+= startx;
1241                 
1242                                                 break;
1243                                         }
1244                                 }
1245                         }
1246         
1247                         if (mode=='s'){
1248                                 sprintf(str, "sizeX: %.3f", fac);
1249                                 headerprint(str);
1250                         }
1251                         else if (mode=='g'){
1252                                 sprintf(str, "deltaX: %.3f", fac);
1253                                 headerprint(str);
1254                         }
1255         
1256                         if (G.saction->lock){
1257                                 do_all_actions();
1258                                 allqueue (REDRAWVIEW3D, 0);
1259                                 allqueue (REDRAWACTION, 0);
1260                                 allqueue (REDRAWIPO, 0);
1261                                 allqueue(REDRAWNLA, 0);
1262                                 force_draw_all();
1263                         }
1264                         else {
1265                                 addqueue (curarea->win, REDRAWALL, 0);
1266                                 force_draw ();
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         allqueue (REDRAWVIEW3D, 0);
1281         allqueue (REDRAWACTION, 0);
1282         allqueue(REDRAWNLA, 0);
1283         allqueue (REDRAWIPO, 0);
1284         MEM_freeN (tv);
1285 }
1286
1287 void transform_meshchannel_keys(char mode, Key *key)
1288 {
1289         /* this is the function that determines what happens
1290          * to those little blocky rvk key things you have selected 
1291          * after you press a 'g' or an 's'. I'd love to say that
1292          * I have an intimate knowledge of all of what this function
1293          * is doing, but instead I'm just going to pretend.
1294          */
1295     TransVert *tv;
1296     int /*sel=0,*/  i;
1297     short       mvals[2], mvalc[2], cent[2];
1298     float       sval[2], cval[2], lastcval[2];
1299     short       cancel=0;
1300     float       fac=0.0F;
1301     int         loop=1;
1302     int         tvtot=0;
1303     float       deltax, startx;
1304     float       cenf[2];
1305     int         invert=0, firsttime=1;
1306     char        str[256];
1307
1308         /* count all of the selected beziers, and
1309          * set all 3 control handles to selected
1310          */
1311     tvtot=fullselect_ipo_keys(key->ipo);
1312     
1313     /* If nothing is selected, bail out 
1314          */
1315     if (!tvtot)
1316         return;
1317         
1318         
1319     /* Build the transvert structure 
1320          */
1321     tv = MEM_callocN (sizeof(TransVert) * tvtot, "transVert");
1322     tvtot=0;
1323
1324     tvtot = add_trans_ipo_keys(key->ipo, tv, tvtot);
1325
1326     /* Do the event loop 
1327          */
1328     cent[0] = curarea->winx + (G.saction->v2d.hor.xmax)/2;
1329     cent[1] = curarea->winy + (G.saction->v2d.hor.ymax)/2;
1330     areamouseco_to_ipoco(G.v2d, cent, &cenf[0], &cenf[1]);
1331
1332     getmouseco_areawin (mvals);
1333     areamouseco_to_ipoco(G.v2d, mvals, &sval[0], &sval[1]);
1334
1335     startx=sval[0];
1336     while (loop) {
1337         /* Get the input
1338                  * If we're cancelling, reset transformations
1339                  * Else calc new transformation
1340                  * Perform the transformations 
1341                  */
1342         while (qtest()) {
1343             short val;
1344             unsigned short event= extern_qread(&val);
1345
1346             if (val) {
1347                 switch (event) {
1348                 case LEFTMOUSE:
1349                 case SPACEKEY:
1350                 case RETKEY:
1351                     loop=0;
1352                     break;
1353                 case XKEY:
1354                     break;
1355                 case ESCKEY:
1356                 case RIGHTMOUSE:
1357                     cancel=1;
1358                     loop=0;
1359                     break;
1360                 default:
1361                     arrows_move_cursor(event);
1362                     break;
1363                 };
1364             }
1365         }
1366         
1367         if (cancel) {
1368             for (i=0; i<tvtot; i++) {
1369                 tv[i].loc[0]=tv[i].oldloc[0];
1370                 tv[i].loc[1]=tv[i].oldloc[1];
1371             }
1372         } 
1373                 else {
1374             getmouseco_areawin (mvalc);
1375             areamouseco_to_ipoco(G.v2d, mvalc, &cval[0], &cval[1]);
1376                         
1377             if (!firsttime && lastcval[0]==cval[0] && lastcval[1]==cval[1]) {
1378                 PIL_sleep_ms(1);
1379             } else {
1380                 for (i=0; i<tvtot; i++){
1381                     tv[i].loc[0]=tv[i].oldloc[0];
1382
1383                     switch (mode){
1384                     case 'g':
1385                         deltax = cval[0]-sval[0];
1386                         fac= deltax;
1387                                                 
1388                         apply_keyb_grid(&fac, 0.0, 1.0, 0.1, 
1389                                         U.flag & USER_AUTOGRABGRID);
1390
1391                         tv[i].loc[0]+=fac;
1392                         break;
1393                     case 's':
1394                         startx=mvals[0]-(ACTWIDTH/2+(curarea->winrct.xmax
1395                                                      -curarea->winrct.xmin)/2);
1396                         deltax=mvalc[0]-(ACTWIDTH/2+(curarea->winrct.xmax
1397                                                      -curarea->winrct.xmin)/2);
1398                         fac= fabs(deltax/startx);
1399                                                 
1400                         apply_keyb_grid(&fac, 0.0, 0.2, 0.1, 
1401                                         U.flag & USER_AUTOSIZEGRID);
1402                 
1403                         if (invert){
1404                             if (i % 03 == 0){
1405                                 memcpy (tv[i].loc, tv[i].oldloc, 
1406                                         sizeof(tv[i+2].oldloc));
1407                             }
1408                             if (i % 03 == 2){
1409                                 memcpy (tv[i].loc, tv[i].oldloc, 
1410                                         sizeof(tv[i-2].oldloc));
1411                             }
1412                                                         
1413                             fac*=-1;
1414                         }
1415                         startx= (G.scene->r.cfra);
1416                         
1417                         tv[i].loc[0]-= startx;
1418                         tv[i].loc[0]*=fac;
1419                         tv[i].loc[0]+= startx;
1420                 
1421                         break;
1422                     }
1423                 }
1424             }
1425                         /* Display a message showing the magnitude of
1426                          * the grab/scale we are performing
1427                          */
1428             if (mode=='s'){
1429                 sprintf(str, "sizeX: %.3f", fac);
1430                 headerprint(str);
1431             }
1432             else if (mode=='g'){
1433                 sprintf(str, "deltaX: %.3f", fac);
1434                 headerprint(str);
1435             }
1436         
1437             if (G.saction->lock){
1438                                 /* doubt any of this code ever gets
1439                                  * executed, but it might in the
1440                                  * future
1441                                  */
1442                                  
1443                 do_all_actions();
1444                 allqueue (REDRAWVIEW3D, 0);
1445                 allqueue (REDRAWACTION, 0);
1446                 allqueue (REDRAWIPO, 0);
1447                 allqueue(REDRAWNLA, 0);
1448                 force_draw_all();
1449             }
1450             else {
1451                 addqueue (curarea->win, REDRAWALL, 0);
1452                 force_draw ();
1453             }
1454         }
1455                 
1456         lastcval[0]= cval[0];
1457         lastcval[1]= cval[1];
1458         firsttime= 0;
1459     }
1460         
1461         /* fix up the Ipocurves and redraw stuff
1462          */
1463     meshkey_do_redraw(key);
1464
1465     MEM_freeN (tv);
1466
1467         /* did you understand all of that? I pretty much understand
1468          * what it does, but the specifics seem a little weird and crufty.
1469          */
1470 }
1471
1472
1473 void deselect_actionchannel_keys (bAction *act, int test)
1474 {
1475         bActionChannel  *chan;
1476         bConstraintChannel *conchan;
1477         int             sel=1;;
1478
1479         if (!act)
1480                 return;
1481
1482         /* Determine if this is selection or deselection */
1483         
1484         if (test){
1485                 for (chan=act->chanbase.first; chan; chan=chan->next){
1486                         /* Test the channel ipos */
1487                         if (is_ipo_key_selected(chan->ipo)){
1488                                 sel = 0;
1489                                 break;
1490                         }
1491
1492                         /* Test the constraint ipos */
1493                         for (conchan=chan->constraintChannels.first; conchan; conchan=conchan->next){
1494                                 if (is_ipo_key_selected(conchan->ipo)){
1495                                         sel = 0;
1496                                         break;
1497                                 }
1498                         }
1499
1500                         if (sel == 0)
1501                                 break;
1502                 }
1503         }
1504         else
1505                 sel=0;
1506         
1507         /* Set the flags */
1508         for (chan=act->chanbase.first; chan; chan=chan->next){
1509                 set_ipo_key_selection(chan->ipo, sel);
1510                 for (conchan=chan->constraintChannels.first; conchan; conchan=conchan->next)
1511                         set_ipo_key_selection(conchan->ipo, sel);
1512         }
1513 }
1514
1515 void deselect_meshchannel_keys (Key *key, int test)
1516 {
1517         /* should deselect the rvk keys
1518          */
1519     int         sel=1;
1520
1521     /* Determine if this is selection or deselection */
1522     if (test){
1523         if (is_ipo_key_selected(key->ipo)){
1524             sel = 0;
1525         }
1526     }
1527     else {
1528         sel=0;
1529     }
1530         
1531     /* Set the flags */
1532     set_ipo_key_selection(key->ipo, sel);
1533 }
1534
1535 void deselect_actionchannels (bAction *act, int test)
1536 {
1537         bActionChannel *chan;
1538         bConstraintChannel *conchan;
1539         int                     sel=1;  
1540
1541         if (!act)
1542                 return;
1543
1544         /* See if we should be selecting or deselecting */
1545         if (test){
1546                 for (chan=act->chanbase.first; chan; chan=chan->next){
1547                         if (!sel)
1548                                 break;
1549
1550                         if (chan->flag & ACHAN_SELECTED){
1551                                 sel=0;
1552                                 break;
1553                         }
1554                         if (sel){
1555                                 for (conchan=chan->constraintChannels.first; conchan; conchan=conchan->next){
1556                                         if (conchan->flag & CONSTRAINT_CHANNEL_SELECT){
1557                                                 sel=0;
1558                                                 break;
1559                                         }
1560                                 }
1561                         }
1562                 }
1563         }
1564         else
1565                 sel=0;
1566
1567         /* Now set the flags */
1568         for (chan=act->chanbase.first; chan; chan=chan->next){
1569                 select_poseelement_by_name(chan->name, sel);
1570
1571                 if (sel)
1572                         chan->flag |= ACHAN_SELECTED;
1573                 else
1574                         chan->flag &= ~ACHAN_SELECTED;
1575
1576                 for (conchan=chan->constraintChannels.first; conchan; conchan=conchan->next){
1577                         if (sel)
1578                                 conchan->flag |= CONSTRAINT_CHANNEL_SELECT;
1579                         else
1580                                 conchan->flag &= ~CONSTRAINT_CHANNEL_SELECT;
1581                 }
1582         }
1583
1584 }
1585
1586 static void hilight_channel (bAction *act, bActionChannel *chan, short select)
1587 {
1588         bActionChannel *curchan;
1589
1590         if (!act)
1591                 return;
1592
1593         for (curchan=act->chanbase.first; curchan; curchan=curchan->next){
1594                 if (curchan==chan && select)
1595                         curchan->flag |= ACHAN_HILIGHTED;
1596                 else
1597                         curchan->flag &= ~ACHAN_HILIGHTED;
1598         }
1599 }
1600
1601 /* select_mode = SELECT_REPLACE
1602  *             = SELECT_ADD
1603  *             = SELECT_SUBTRACT
1604  *             = SELECT_INVERT
1605  */
1606
1607 /* exported for outliner (ton) */
1608 int select_channel(bAction *act, bActionChannel *chan,
1609                           int selectmode) {
1610         /* Select the channel based on the selection mode
1611          */
1612         int flag;
1613
1614         switch (selectmode) {
1615         case SELECT_ADD:
1616                 chan->flag |= ACHAN_SELECTED;
1617                 break;
1618         case SELECT_SUBTRACT:
1619                 chan->flag &= ~ACHAN_SELECTED;
1620                 break;
1621         case SELECT_INVERT:
1622                 chan->flag ^= ACHAN_SELECTED;
1623                 break;
1624         }
1625         flag = (chan->flag & ACHAN_SELECTED) ? 1 : 0;
1626
1627         hilight_channel(act, chan, flag);
1628         select_poseelement_by_name(chan->name, flag);
1629
1630         return flag;
1631 }
1632
1633 static int select_constraint_channel(bAction *act, 
1634                                      bConstraintChannel *conchan, 
1635                                      int selectmode) {
1636         /* Select the constraint channel based on the selection mode
1637          */
1638         int flag;
1639
1640         switch (selectmode) {
1641         case SELECT_ADD:
1642                 conchan->flag |= CONSTRAINT_CHANNEL_SELECT;
1643                 break;
1644         case SELECT_SUBTRACT:
1645                 conchan->flag &= ~CONSTRAINT_CHANNEL_SELECT;
1646                 break;
1647         case SELECT_INVERT:
1648                 conchan->flag ^= CONSTRAINT_CHANNEL_SELECT;
1649                 break;
1650         }
1651         flag = (conchan->flag & CONSTRAINT_CHANNEL_SELECT) ? 1 : 0;
1652
1653         return flag;
1654 }
1655
1656
1657 static void mouse_actionchannels(bAction *act, short *mval,
1658                                  short *mvalo, int selectmode) {
1659         /* Select action channels, based on mouse values.
1660          * If mvalo is NULL we assume it is a one click
1661          * action, other wise we treat it like it is a
1662          * border select with mval[0],mval[1] and
1663          * mvalo[0], mvalo[1] forming the corners of
1664          * a rectangle.
1665          */
1666         bActionChannel *chan;
1667         float   click, x,y;
1668         int   clickmin, clickmax;
1669         int             wsize, sel;
1670         bConstraintChannel *conchan;
1671
1672         if (!act)
1673                 return;
1674   
1675         if (selectmode == SELECT_REPLACE) {
1676                 deselect_actionchannels (act, 0);
1677                 selectmode = SELECT_ADD;
1678         }
1679
1680         /* wsize is the greatest possible height (in pixels) that would be
1681          * needed to draw all of the action channels and constraint
1682          * channels.
1683          */
1684         wsize =  count_action_levels(act)*(CHANNELHEIGHT+CHANNELSKIP);
1685         wsize += CHANNELHEIGHT/2;
1686
1687     areamouseco_to_ipoco(G.v2d, mval, &x, &y);
1688     clickmin = (int) ((wsize - y) / (CHANNELHEIGHT+CHANNELSKIP));
1689         
1690         /* Only one click */
1691         if (mvalo == NULL) {
1692                 clickmax = clickmin;
1693         }
1694         /* Two click values (i.e., border select */
1695         else {
1696                 areamouseco_to_ipoco(G.v2d, mvalo, &x, &y);
1697                 click = ((wsize - y) / (CHANNELHEIGHT+CHANNELSKIP));
1698
1699                 if ( ((int) click) < clickmin) {
1700                         clickmax = clickmin;
1701                         clickmin = (int) click;
1702                 }
1703                 else {
1704                         clickmax = (int) click;
1705                 }
1706         }
1707
1708         if (clickmax < 0) {
1709                 return;
1710         }
1711
1712         /* clickmin and clickmax now coorespond to indices into
1713          * the collection of channels and constraint channels.
1714          * What we need to do is apply the selection mode on all
1715          * channels and constraint channels between these indices.
1716          * This is done by traversing the channels and constraint
1717          * channels, for each item decrementing clickmin and clickmax.
1718          * When clickmin is less than zero we start selecting stuff,
1719          * until clickmax is less than zero or we run out of channels
1720          * and constraint channels.
1721          */
1722
1723         for (chan = act->chanbase.first; chan; chan=chan->next){
1724                 if (clickmax < 0) break;
1725
1726                 if ( clickmin <= 0) {
1727                         /* Select the channel with the given mode. If the
1728                          * channel is freshly selected then set it to the
1729                          * active channel for the action
1730                          */
1731                         sel = (chan->flag & ACHAN_SELECTED);
1732                         if ( select_channel(act, chan, selectmode) && !sel ) {
1733                                 act->achan = chan;
1734                         }
1735                 }
1736                 --clickmin;
1737                 --clickmax;
1738
1739                 /* Check for click in a constraint */
1740                 for (conchan=chan->constraintChannels.first; 
1741                          conchan; conchan=conchan->next){
1742                         if (clickmax < 0) break;
1743                         if ( clickmin <= 0) {
1744                                 select_constraint_channel(act, conchan, selectmode);
1745                         }
1746                         --clickmin;
1747                         --clickmax;
1748                 }
1749         }
1750
1751         allqueue (REDRAWIPO, 0);
1752         allqueue (REDRAWVIEW3D, 0);
1753         allqueue (REDRAWACTION, 0);
1754         allqueue (REDRAWNLA, 0);
1755 }
1756
1757 void delete_meshchannel_keys(Key *key)
1758 {
1759         if (!okee("Erase selected keys"))
1760                 return;
1761
1762         delete_ipo_keys(key->ipo);
1763
1764         meshkey_do_redraw(key);
1765 }
1766
1767 void delete_actionchannel_keys(void)
1768 {
1769         bAction *act;
1770         bActionChannel *chan;
1771         bConstraintChannel *conchan;
1772
1773         act = G.saction->action;
1774         if (!act)
1775                 return;
1776
1777         if (!okee("Erase selected keys"))
1778                 return;
1779
1780         for (chan = act->chanbase.first; chan; chan=chan->next){
1781
1782                 /* Check action channel keys*/
1783                 delete_ipo_keys(chan->ipo);
1784
1785                 /* Delete constraint channel keys */
1786                 for (conchan=chan->constraintChannels.first; conchan; conchan=conchan->next)
1787                         delete_ipo_keys(conchan->ipo);
1788         }
1789
1790         remake_action_ipos (act);
1791         allspace(REMAKEIPO, 0);
1792         allqueue(REDRAWACTION, 0);
1793         allqueue(REDRAWIPO, 0);
1794         allqueue(REDRAWNLA, 0);
1795
1796 }
1797 static void delete_actionchannels (void)
1798 {
1799         bConstraintChannel *conchan=NULL, *nextconchan;
1800         bActionChannel *chan, *next;
1801         bAction *act;
1802         int freechan;
1803
1804         act=G.saction->action;
1805
1806         if (!act)
1807                 return;
1808
1809         for (chan=act->chanbase.first; chan; chan=chan->next){
1810                 if (chan->flag & ACHAN_SELECTED)
1811                         break;
1812                 for (conchan=chan->constraintChannels.first; conchan; conchan=conchan->next)
1813                 {
1814                         if (conchan->flag & CONSTRAINT_CHANNEL_SELECT){
1815                                 chan=act->chanbase.last;
1816                                 break;
1817                         }
1818                 }
1819         }
1820
1821         if (!chan && !conchan)
1822                 return;
1823
1824         if (!okee("Erase selected channels"))
1825                 return;
1826
1827         for (chan=act->chanbase.first; chan; chan=next){
1828                 freechan = 0;
1829                 next=chan->next;
1830                 
1831                 /* Remove action channels */
1832                 if (chan->flag & ACHAN_SELECTED){
1833                         if (chan->ipo)
1834                                 chan->ipo->id.us--;     /* Release the ipo */
1835                         freechan = 1;
1836                 }
1837                 
1838                 /* Remove constraint channels */
1839                 for (conchan=chan->constraintChannels.first; conchan; conchan=nextconchan){
1840                         nextconchan=conchan->next;
1841                         if (freechan || conchan->flag & CONSTRAINT_CHANNEL_SELECT){
1842                                 if (conchan->ipo)
1843                                         conchan->ipo->id.us--;
1844                                 BLI_freelinkN(&chan->constraintChannels, conchan);
1845                         }
1846                 }
1847                 
1848                 if (freechan)
1849                         BLI_freelinkN (&act->chanbase, chan);
1850
1851         }
1852
1853         allqueue (REDRAWACTION, 0);
1854         allqueue(REDRAWNLA, 0);
1855
1856 }
1857
1858 void sethandles_meshchannel_keys(int code, Key *key)
1859 {
1860     sethandles_ipo_keys(key->ipo, code);
1861
1862         meshkey_do_redraw(key);
1863 }
1864
1865 void sethandles_actionchannel_keys(int code)
1866 {
1867         bAction *act;
1868         bActionChannel *chan;
1869
1870         /* Get the selected action, exit if none are selected 
1871          */
1872         act = G.saction->action;
1873         if (!act)
1874                 return;
1875
1876         /* Loop through the channels and set the beziers
1877          * of the selected keys based on the integer code
1878          */
1879         for (chan = act->chanbase.first; chan; chan=chan->next){
1880                 sethandles_ipo_keys(chan->ipo, code);
1881         }
1882
1883         /* Clean up and redraw stuff
1884          */
1885         remake_action_ipos (act);
1886         allspace(REMAKEIPO, 0);
1887         allqueue(REDRAWACTION, 0);
1888         allqueue(REDRAWIPO, 0);
1889         allqueue(REDRAWNLA, 0);
1890 }
1891
1892 void set_ipotype_actionchannels(int ipotype) {
1893
1894         bAction *act; 
1895         bActionChannel *chan;
1896         short event;
1897
1898         /* Get the selected action, exit if none are selected 
1899          */
1900         act = G.saction->action;
1901         if (!act)
1902                 return;
1903
1904         if (ipotype == SET_IPO_POPUP) {
1905                 /* Present a popup menu asking the user what type
1906                  * of IPO curve he/she/GreenBTH wants. ;)
1907                  */
1908                 event
1909                         =  pupmenu("Channel Ipo Type %t|"
1910                                            "Constant %x1|"
1911                                            "Linear %x2|"
1912                                            "Bezier %x3");
1913                 if(event < 1) return;
1914                 ipotype = event;
1915         }
1916         
1917         /* Loop through the channels and for the selected ones set
1918          * the type for each Ipo curve in the channel Ipo (based on
1919          * the value from the popup).
1920          */
1921         for (chan = act->chanbase.first; chan; chan=chan->next){
1922                 if (chan->flag & ACHAN_SELECTED){
1923                         if (chan->ipo)
1924                                 setipotype_ipo(chan->ipo, ipotype);
1925                 }
1926         }
1927
1928         /* Clean up and redraw stuff
1929          */
1930         remake_action_ipos (act);
1931         allspace(REMAKEIPO, 0);
1932         allqueue(REDRAWACTION, 0);
1933         allqueue(REDRAWIPO, 0);
1934         allqueue(REDRAWNLA, 0);
1935 }
1936
1937 void select_all_keys_frames(bAction *act, short *mval, 
1938                                                         short *mvalo, int selectmode) {
1939         
1940         /* This function tries to select all action keys in
1941          * every channel for a given range of keyframes that
1942          * are within the mouse values mval and mvalo (usually
1943          * the result of a border select). If mvalo is passed as
1944          * NULL then the selection is treated as a one-click and
1945          * the function tries to select all keys within half a
1946          * frame of the click point.
1947          */
1948         
1949         rcti rect;
1950         rctf rectf;
1951         bActionChannel *chan;
1952         bConstraintChannel *conchan;
1953
1954         if (!act)
1955                 return;
1956
1957         if (selectmode == SELECT_REPLACE) {
1958                 deselect_actionchannel_keys(act, 0);
1959                 selectmode = SELECT_ADD;
1960         }
1961
1962         if (mvalo == NULL) {
1963                 rect.xmin = rect.xmax = mval[0];
1964                 rect.ymin = rect.ymax = mval[1];
1965         }
1966         else {
1967                 if (mval[0] < mvalo[0] ) {
1968                         rect.xmin = mval[0];
1969                         rect.xmax = mvalo[0];
1970                 }
1971                 else {
1972                         rect.xmin = mvalo[0];
1973                         rect.xmax = mval[0];
1974                 }
1975                 if (mval[1] < mvalo[1] ) {
1976                         rect.ymin = mval[1];
1977                         rect.ymax = mvalo[1];
1978                 }
1979                 else {
1980                         rect.ymin = mvalo[1];
1981                         rect.ymax = mval[1];
1982                 }
1983         }
1984
1985         mval[0]= rect.xmin;
1986         mval[1]= rect.ymin+2;
1987         areamouseco_to_ipoco(G.v2d, mval, &rectf.xmin, &rectf.ymin);
1988         mval[0]= rect.xmax;
1989         mval[1]= rect.ymax-2;
1990         areamouseco_to_ipoco(G.v2d, mval, &rectf.xmax, &rectf.ymax);
1991
1992         if (mvalo == NULL) {
1993                 rectf.xmin = rectf.xmin - 0.5;
1994                 rectf.xmax = rectf.xmax + 0.5;
1995         }
1996     
1997         for (chan=act->chanbase.first; chan; chan=chan->next){
1998                 borderselect_ipo_key(chan->ipo, rectf.xmin, rectf.xmax,
1999                                                          selectmode);
2000                 for (conchan=chan->constraintChannels.first; conchan; 
2001                          conchan=conchan->next){
2002                         borderselect_ipo_key(conchan->ipo, rectf.xmin, rectf.xmax,
2003                                                                  selectmode);
2004                 }
2005         }       
2006         allqueue(REDRAWNLA, 0);
2007         allqueue(REDRAWACTION, 0);
2008         allqueue(REDRAWIPO, 0);
2009 }
2010
2011
2012 void select_all_keys_channels(bAction *act, short *mval, 
2013                               short *mvalo, int selectmode) {
2014         bActionChannel    *chan;
2015         float              click, x,y;
2016         int                clickmin, clickmax;
2017         int                wsize;
2018         bConstraintChannel *conchan;
2019
2020         /* This function selects all the action keys that
2021          * are in the mouse selection range defined by
2022          * the ordered pairs mval and mvalo (usually
2023          * these 2 are obtained from a border select).
2024          * If mvalo is NULL, then the selection is
2025          * treated like a one-click action, and at most
2026          * one channel is selected.
2027          */
2028
2029         /* If the action is null then abort
2030          */
2031         if (!act)
2032                 return;
2033
2034         if (selectmode == SELECT_REPLACE) {
2035                 deselect_actionchannel_keys(act, 0);
2036                 selectmode = SELECT_ADD;
2037         }
2038
2039         /* wsize is the greatest possible height (in pixels) that would be
2040          * needed to draw all of the action channels and constraint
2041          * channels.
2042          */
2043
2044         wsize =  count_action_levels(act)*(CHANNELHEIGHT+CHANNELSKIP);
2045         wsize += CHANNELHEIGHT/2;
2046         
2047     areamouseco_to_ipoco(G.v2d, mval, &x, &y);
2048     clickmin = (int) ((wsize - y) / (CHANNELHEIGHT+CHANNELSKIP));
2049         
2050         /* Only one click */
2051         if (mvalo == NULL) {
2052                 clickmax = clickmin;
2053         }
2054         /* Two click values (i.e., border select) */
2055         else {
2056
2057                 areamouseco_to_ipoco(G.v2d, mvalo, &x, &y);
2058                 click = ((wsize - y) / (CHANNELHEIGHT+CHANNELSKIP));
2059                 
2060                 if ( ((int) click) < clickmin) {
2061                         clickmax = clickmin;
2062                         clickmin = (int) click;
2063                 }
2064                 else {
2065                         clickmax = (int) click;
2066                 }
2067         }
2068
2069         if (clickmax < 0) {
2070                 return;
2071         }
2072
2073         for (chan = act->chanbase.first; chan; chan=chan->next){
2074                 if (clickmax < 0) break;
2075
2076                 if ( clickmin <= 0) {
2077                         /* Select the channel with the given mode. If the
2078                          * channel is freshly selected then set it to the
2079                          * active channel for the action
2080                          */
2081                         select_ipo_bezier_keys(chan->ipo, selectmode);
2082                 }
2083                 --clickmin;
2084                 --clickmax;
2085
2086                 /* Check for click in a constraint */
2087                 for (conchan=chan->constraintChannels.first; 
2088                          conchan; conchan=conchan->next){
2089                         if (clickmax < 0) break;
2090                         if ( clickmin <= 0) {
2091                                 select_ipo_bezier_keys(chan->ipo, selectmode);
2092                         }
2093                         --clickmin;
2094                         --clickmax;
2095                 }
2096         }
2097   
2098         allqueue (REDRAWIPO, 0);
2099         allqueue (REDRAWVIEW3D, 0);
2100         allqueue (REDRAWACTION, 0);
2101         allqueue (REDRAWNLA, 0);
2102   
2103 }
2104
2105 static void borderselect_function(void (*select_func)(bAction *act, 
2106                                                      short *mval, 
2107                                                      short *mvalo, 
2108                                                      int selectmode)) {
2109         /* This function executes an arbitrary selection
2110          * function as part of a border select. This
2111          * way the same function that is used for
2112          * right click selection points can generally
2113          * be used as the argument to this function
2114          */
2115         rcti rect;
2116         short   mval[2], mvalo[2];
2117         bAction *act;
2118         int val;                
2119
2120         /* Get the selected action, exit if none are selected 
2121          */
2122         act=G.saction->action;
2123         if (!act)
2124                 return;
2125
2126         /* Let the user draw a border (or abort)
2127          */
2128         if ( (val=get_border (&rect, 3)) ) {
2129                 mval[0]= rect.xmin;
2130                 mval[1]= rect.ymin+2;
2131                 mvalo[0]= rect.xmax;
2132                 mvalo[1]= rect.ymax-2;
2133
2134                 /* if the left mouse was used, do an additive
2135                  * selection with the user defined selection
2136                  * function.
2137                  */
2138                 if (val == LEFTMOUSE)
2139                         select_func(act, mval, mvalo, SELECT_ADD);
2140                 
2141                 /* if the right mouse was used, do a subtractive
2142                  * selection with the user defined selection
2143                  * function.
2144                  */
2145                 else if (val == RIGHTMOUSE)
2146                         select_func(act, mval, mvalo, SELECT_SUBTRACT);
2147         }
2148         
2149 }
2150
2151 static void clever_keyblock_names(Key *key, short* mval){
2152     int        but=0, i, keynum;
2153     char       str[64];
2154         float      x;
2155         KeyBlock   *kb;
2156         /* get the keynum cooresponding to the y value
2157          * of the mouse pointer, return if this is
2158          * an invalid key number (and we don't deal
2159          * with the speed ipo).
2160          */
2161
2162     keynum = get_nearest_key_num(key, mval, &x);
2163     if ( (keynum < 1) || (keynum >= key->totkey) )
2164         return;
2165
2166         kb= key->block.first;
2167         for (i=0; i<keynum; ++i) kb = kb->next; 
2168
2169         if (kb->name[0] == '\0') {
2170                 sprintf(str, "Key %d", keynum);
2171         }
2172         else {
2173                 strcpy(str, kb->name);
2174         }
2175
2176         if ( (kb->slidermin >= kb->slidermax) ) {
2177                 kb->slidermin = 0.0;
2178                 kb->slidermax = 1.0;
2179         }
2180
2181     add_numbut(but++, TEX, "KB: ", 0, 24, str, 
2182                "Does this really need a tool tip?");
2183         add_numbut(but++, NUM|FLO, "Slider Min:", 
2184                            -10000, kb->slidermax, &kb->slidermin, 0);
2185         add_numbut(but++, NUM|FLO, "Slider Max:", 
2186                            kb->slidermin, 10000, &kb->slidermax, 0);
2187
2188     if (do_clever_numbuts(str, but, REDRAW)) {
2189                 strcpy(kb->name, str);
2190         allqueue (REDRAWACTION, 0);
2191                 allspace(REMAKEIPO, 0);
2192         allqueue (REDRAWIPO, 0);
2193         }
2194
2195         
2196 }
2197
2198 static void numbuts_action(void)
2199 {
2200         /* now called from action window event loop, plus reacts on mouseclick */
2201         /* removed Hos grunts for that reason! :) (ton) */
2202     Key *key;
2203     short mval[2];
2204
2205     if ( (key = get_action_mesh_key()) ) {
2206         getmouseco_areawin (mval);
2207                 if (mval[0]<NAMEWIDTH) {
2208                         clever_keyblock_names(key, mval);
2209                 }
2210     }
2211 }
2212
2213 void winqreadactionspace(ScrArea *sa, void *spacedata, BWinEvent *evt)
2214 {
2215         extern void do_actionbuts(unsigned short event); // drawaction.c
2216         SpaceAction *saction;
2217         bAction *act;
2218         Key *key;
2219         float dx,dy;
2220         int doredraw= 0;
2221         int     cfra;
2222         short   mval[2];
2223         unsigned short event= evt->event;
2224         short val= evt->val;
2225         short mousebut = L_MOUSE;
2226
2227         if(curarea->win==0) return;
2228
2229         saction= curarea->spacedata.first;
2230         if (!saction)
2231                 return;
2232
2233         act=saction->action;
2234         if(val) {
2235                 
2236                 if( uiDoBlocks(&curarea->uiblocks, event)!=UI_NOTHING ) event= 0;
2237                 
2238                 /* swap mouse buttons based on user preference */
2239                 if (U.flag & USER_LMOUSESELECT) {
2240                         if (evt->event == LEFTMOUSE) {
2241                                 event = RIGHTMOUSE;
2242                                 mousebut = L_MOUSE;
2243                         } else if (evt->event == RIGHTMOUSE) {
2244                                 event = LEFTMOUSE;
2245                                 mousebut = R_MOUSE;
2246                         }
2247                 }
2248                 
2249                 getmouseco_areawin(mval);
2250
2251                 key = get_action_mesh_key();
2252
2253                 switch(event) {
2254                 case UI_BUT_EVENT:
2255                         do_actionbuts(val);     // window itself
2256                         break;
2257                 
2258                 case HOMEKEY:
2259                         do_action_buttons(B_ACTHOME);   // header
2260                         break;
2261
2262                 case AKEY:
2263                         if (key) {
2264                                 if (mval[0]<ACTWIDTH){
2265                                         /* to do ??? */
2266                                 }
2267                                 else{
2268                                         deselect_meshchannel_keys(key, 1);
2269                                         allqueue (REDRAWACTION, 0);
2270                                         allqueue(REDRAWNLA, 0);
2271                                         allqueue (REDRAWIPO, 0);
2272                                 }
2273                         }
2274                         else {
2275                                 if (mval[0]<NAMEWIDTH){
2276                                         deselect_actionchannels (act, 1);
2277                                         allqueue (REDRAWVIEW3D, 0);
2278                                         allqueue (REDRAWACTION, 0);
2279                                         allqueue(REDRAWNLA, 0);
2280                                         allqueue (REDRAWIPO, 0);
2281                                 }
2282                                 else if (mval[0]>ACTWIDTH){
2283                                         deselect_actionchannel_keys (act, 1);
2284                                         allqueue (REDRAWACTION, 0);
2285                                         allqueue(REDRAWNLA, 0);
2286                                         allqueue (REDRAWIPO, 0);
2287                                 }
2288                         }
2289                         break;
2290
2291                 case BKEY:
2292                         if (key) {
2293                                 if (mval[0]<ACTWIDTH){
2294                                         /* to do?? */
2295                                 }
2296                                 else {
2297                                         borderselect_mesh(key);
2298                                 }
2299                         }
2300                         else {
2301
2302                                 /* If the border select is initiated in the
2303                                  * part of the action window where the channel
2304                                  * names reside, then select the channels
2305                                  */
2306                                 if (mval[0]<NAMEWIDTH){
2307                                         borderselect_function(mouse_actionchannels);
2308                                 }
2309                                 else if (mval[0]>ACTWIDTH){
2310
2311                                         /* If the border select is initiated in the
2312                                          * vertical scrollbar, then (de)select all keys
2313                                          * for the channels in the selection region
2314                                          */
2315                                         if (IN_2D_VERT_SCROLL(mval)) {
2316                                                 borderselect_function(select_all_keys_channels);
2317                                         }
2318
2319                                         /* If the border select is initiated in the
2320                                          * horizontal scrollbar, then (de)select all keys
2321                                          * for the keyframes in the selection region
2322                                          */
2323                                         else if (IN_2D_HORIZ_SCROLL(mval)) {
2324                                                 borderselect_function(select_all_keys_frames);
2325                                         }
2326
2327                                         /* Other wise, select the action keys
2328                                          */
2329                                         else {
2330                                                 borderselect_action();
2331                                         }
2332                                 }
2333                         }
2334                         break;
2335
2336                 case CKEY:
2337                         /* scroll the window so the current
2338                          * frame is in the center.
2339                          */
2340                         center_currframe();
2341                         break;
2342
2343                 case DKEY:
2344                         if (key) {
2345                                 if (G.qual & LR_SHIFTKEY && mval[0]>ACTWIDTH) {
2346                                         duplicate_meshchannel_keys(key);
2347                                 }
2348                         }
2349                         else if (act) {
2350                                 if (G.qual & LR_SHIFTKEY && mval[0]>ACTWIDTH){
2351                                         duplicate_actionchannel_keys();
2352                                         remake_action_ipos(act);
2353                                 }
2354                         }
2355                         break;
2356
2357                 case GKEY:
2358                         if (mval[0]>=ACTWIDTH) {
2359                                 if (key) {
2360                                         transform_meshchannel_keys('g', key);
2361                                 }
2362                                 else if (act) {
2363                                         transform_actionchannel_keys ('g');
2364                                 }
2365                         }
2366                         break;
2367
2368                 case HKEY:
2369                         if (key) {
2370                                 if(G.qual & LR_SHIFTKEY) {
2371                                         sethandles_meshchannel_keys(HD_AUTO, key);
2372                                 }
2373                                 else { 
2374                                         sethandles_meshchannel_keys(HD_ALIGN, key);
2375                                 }
2376                         }
2377                         else {
2378                                 if(G.qual & LR_SHIFTKEY) {
2379                                         sethandles_actionchannel_keys(HD_AUTO);
2380                                 }
2381                                 else { 
2382                                         sethandles_actionchannel_keys(HD_ALIGN);
2383                                 }
2384                         }
2385                         break;
2386                 
2387                 case NKEY:
2388                         if(G.qual==0) {
2389                                 numbuts_action();
2390                                 
2391                                 /* no panel (yet). current numbuts are not easy to put in panel... */
2392                                 //add_blockhandler(curarea, ACTION_HANDLER_PROPERTIES, UI_PNL_TO_MOUSE);
2393                                 //scrarea_queue_winredraw(curarea);
2394                         }
2395                         break;
2396                         
2397                 case SKEY: 
2398                         if (mval[0]>=ACTWIDTH) {
2399                                 if (key) {
2400                                         transform_meshchannel_keys('s', key);
2401                                 }
2402                                 else if (act) {
2403                                         transform_actionchannel_keys ('s');
2404                                 }
2405                         }
2406                         break;
2407
2408                         /*** set the Ipo type  ***/
2409                 case TKEY:
2410                         if (key) {
2411                                 /* to do */
2412                         }
2413                         else {
2414                                 set_ipotype_actionchannels(SET_IPO_POPUP);
2415                         }
2416                         break;
2417
2418                 case VKEY:
2419                         if (key) {
2420                                 sethandles_meshchannel_keys(HD_VECT, key);
2421                                 /* to do */
2422                         }
2423                         else {
2424                                 sethandles_actionchannel_keys(HD_VECT);
2425                         }
2426                         break;
2427
2428                 case DELKEY:
2429                 case XKEY:
2430                         if (key) {
2431                                 delete_meshchannel_keys(key);
2432                         }
2433                         else {
2434                                 if (mval[0]<NAMEWIDTH)
2435                                         delete_actionchannels ();
2436                                 else
2437                                         delete_actionchannel_keys ();
2438                         }
2439                         break;
2440                 /* LEFTMOUSE and RIGHTMOUSE event codes can be swapped above,
2441                  * based on user preference USER_LMOUSESELECT
2442                  */
2443                 case LEFTMOUSE:
2444                         if (mval[0]>ACTWIDTH){
2445                                 do {
2446                                         getmouseco_areawin(mval);
2447                                         
2448                                         areamouseco_to_ipoco(G.v2d, mval, &dx, &dy);
2449                                         
2450                                         cfra= (int)dx;
2451                                         if(cfra< 1) cfra= 1;
2452                                         
2453                                         if( cfra!=CFRA ) {
2454                                                 CFRA= cfra;
2455                                                 update_for_newframe();
2456                                                 force_draw_plus(SPACE_VIEW3D);
2457                                                 force_draw_plus(SPACE_IPO);
2458                                                 force_draw_plus(SPACE_BUTS);
2459                                         }
2460                                         
2461                                 } while(get_mbut() & mousebut);
2462                         }
2463                         break;
2464                 case RIGHTMOUSE:
2465                         /* Clicking in the channel area selects the
2466                          * channel or constraint channel
2467                          */
2468                         if (mval[0]<NAMEWIDTH) {
2469                                 if(act) {
2470                                         if(G.qual & LR_SHIFTKEY)
2471                                                 mouse_actionchannels(act, mval, NULL,  SELECT_INVERT);
2472                                         else
2473                                                 mouse_actionchannels(act, mval, NULL,  SELECT_REPLACE);
2474                                 }
2475                                 else numbuts_action();
2476                         }
2477                         else if (mval[0]>ACTWIDTH) {
2478                 
2479                                 /* Clicking in the vertical scrollbar selects
2480                                  * all of the keys for that channel at that height
2481                                  */
2482                                 if (IN_2D_VERT_SCROLL(mval)) {
2483                                         if(G.qual & LR_SHIFTKEY)
2484                                                 select_all_keys_channels(act, mval, NULL, 
2485                                                                                                  SELECT_INVERT);
2486                                         else
2487                                                 select_all_keys_channels(act, mval, NULL, 
2488                                                                                                  SELECT_REPLACE);
2489                                 }
2490                 
2491                                 /* Clicking in the horizontal scrollbar selects
2492                                  * all of the keys within 0.5 of the nearest integer
2493                                  * frame
2494                                  */
2495                                 else if (IN_2D_HORIZ_SCROLL(mval)) {
2496                                         if(G.qual & LR_SHIFTKEY)
2497                                                 select_all_keys_frames(act, mval, NULL, 
2498                                                                                            SELECT_INVERT);
2499                                         else
2500                                                 select_all_keys_frames(act, mval, NULL, 
2501                                                                                            SELECT_REPLACE);
2502                                 }
2503                                 
2504                                 /* Clicking in the main area of the action window
2505                                  * selects keys
2506                                  */
2507                                 else {
2508                                         if (key) {
2509                                                 if(G.qual & LR_SHIFTKEY)
2510                                                         mouse_mesh_action(SELECT_INVERT, key);
2511                                                 else
2512                                                         mouse_mesh_action(SELECT_REPLACE, key);
2513                                         }
2514                                         else {
2515                                                 if(G.qual & LR_SHIFTKEY)
2516                                                         mouse_action(SELECT_INVERT);
2517                                                 else
2518                                                         mouse_action(SELECT_REPLACE);
2519                                         }
2520                                 }
2521                         }
2522                         break;
2523                 case MIDDLEMOUSE:
2524                 case WHEELUPMOUSE:
2525                 case WHEELDOWNMOUSE:
2526                         view2dmove(event);      /* in drawipo.c */
2527                         break;
2528                 }
2529         }
2530
2531         if(doredraw) addqueue(curarea->win, REDRAW, 1);
2532         
2533 }
2534
2535 Key *get_action_mesh_key(void) {
2536         /* gets the key data from the currently selected
2537          * mesh/lattice. If a mesh is not selected, or does not have
2538          * key data, then we return NULL (currently only
2539          * returns key data for RVK type meshes). If there
2540          * is an action that is pinned, return null
2541          */
2542     Object *ob;
2543     Key    *key;
2544
2545     ob = OBACT;
2546     if (!ob) return NULL;
2547
2548         if (G.saction->pin) return NULL;
2549
2550     if (ob->type==OB_MESH ) {
2551                 key = ((Mesh *)ob->data)->key;
2552     }
2553         else if (ob->type==OB_LATTICE ) {
2554                 key = ((Lattice *)ob->data)->key;
2555         }
2556         else return NULL;
2557
2558         if (key) {
2559                 if (key->type == KEY_RELATIVE)
2560                         return key;
2561         }
2562
2563     return NULL;
2564 }
2565
2566 int get_nearest_key_num(Key *key, short *mval, float *x) {
2567         /* returns the key num that cooresponds to the
2568          * y value of the mouse click. Does not check
2569          * if this is a valid keynum. Also gives the Ipo
2570          * x coordinate.
2571          */
2572     int num;
2573     float ybase, y;
2574
2575     areamouseco_to_ipoco(G.v2d, mval, x, &y);
2576
2577     ybase = key->totkey * (CHANNELHEIGHT + CHANNELSKIP);
2578     num = (int) ((ybase - y + CHANNELHEIGHT/2) / (CHANNELHEIGHT+CHANNELSKIP));
2579
2580     return (num + 1);
2581 }
2582
2583