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