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