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