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