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