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