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