Extra selection support for the action window, including:
[blender.git] / source / blender / src / editnla.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 * This file is a horrible mess: An attmept to cram some
32 * final functionality into blender before it is too late.
33 *
34 * Hopefully it can be tidied up at a later date...
35 */
36
37 #include <stdlib.h>
38 #include <string.h>
39 #include <math.h>
40
41 #include "PIL_time.h"
42
43 #include "BKE_global.h"
44 #include "BKE_main.h"
45 #include "BKE_library.h"
46 #include "BKE_nla.h"
47 #include "BKE_action.h"
48
49 #include "BIF_screen.h"
50 #include "BIF_interface.h"
51 #include "BIF_buttons.h"
52 #include "BIF_space.h"
53 #include "BIF_mywindow.h"
54 #include "BIF_editview.h"
55 #include "BIF_toolbox.h"
56
57 #include "MEM_guardedalloc.h"
58
59 #include "BLI_blenlib.h"
60
61 #include "DNA_screen_types.h"
62 #include "DNA_space_types.h"
63 #include "DNA_scene_types.h"
64 #include "DNA_ipo_types.h"
65 #include "DNA_curve_types.h"
66 #include "DNA_object_types.h"
67 #include "DNA_userdef_types.h"
68 #include "DNA_action_types.h"
69 #include "DNA_nla_types.h"
70 #include "DNA_constraint_types.h"
71
72 #include "BSE_editipo.h"
73 #include "BSE_editnla_types.h"
74 #include "BSE_headerbuttons.h"
75 #include "BSE_drawipo.h"
76 #include "BSE_trans_types.h"
77 #include "BSE_edit.h"
78 #include "BDR_editobject.h"
79
80 #include "interface.h"
81 #include "blendef.h"
82 #include "mydevice.h"
83 #include "blendertimer.h"
84
85 #ifdef HAVE_CONFIG_H
86 #include <config.h>
87 #endif
88
89 /* Note: A lot of these pretty much duplicate the behaviour of the
90 action windows.  The functions should be shared, not copy-pasted */
91 typedef struct NlaParam{
92         SpaceNla *snla;
93         unsigned short event;
94         short val;
95 }NlaParam;
96
97 static void deselect_nlachannel_keys (int test);
98 static void deselect_nlachannels(int test);
99 static void transform_nlachannel_keys(char mode);       
100 static void delete_nlachannel_keys(void);
101 static void delete_nlachannels(void);
102 static void duplicate_nlachannel_keys(void);
103 static void borderselect_nla(void);
104 static void mouse_nla(int selectmode);
105 static Base *get_nearest_nlachannel_ob_key (float *index, short *sel);
106 static bAction *get_nearest_nlachannel_ac_key (float *index, short *sel);
107 static Base *get_nearest_nlastrip (bActionStrip **rstrip, short *sel);
108
109 static void mouse_nlachannels(short mval[2]);
110 static void add_nlablock(short mval[2]);
111 static bActionStrip *get_active_nlastrip(void);
112 static void convert_nla(short mval[2]);
113
114 extern int count_nla_levels(void);      /* From drawnla.c */
115 extern int nla_filter (Base* base, int flags);  /* From drawnla.c */
116
117 /* ******************** SPACE: NLA ********************** */
118
119 /* Protected creator function */
120 int calc_memleak (void* ptr){
121         int doredraw= 0;
122         short   mval[2];
123         float dx,dy;
124         int     cfra;
125         SpaceNla *snla;
126         NlaParam *params=(NlaParam*) ptr;
127         unsigned short event;
128         short val ;
129
130         if (!ptr)
131                 return -1;
132
133         snla= params->snla;
134         event = params->event;
135         val = params->val;
136         
137         if(curarea->win==0) return 0;
138         
139         if (!snla)
140                 return 0;
141         
142         if(val) {
143                 if( uiDoBlocks(&curarea->uiblocks, event)!=UI_NOTHING ) event= 0;
144                 
145                 getmouseco_areawin(mval);
146                 
147                 switch(event) {
148                 case UI_BUT_EVENT:
149                         do_blenderbuttons(val);
150                         break;
151                 case HOMEKEY:
152                         do_nla_buttons(B_NLAHOME);
153                         break;
154                 case DKEY:
155                         if (G.qual & LR_SHIFTKEY && mval[0]>=NLAWIDTH){
156                                 duplicate_nlachannel_keys();
157                                 update_for_newframe();
158                         }
159                         break;
160                 case DELKEY:
161                 case XKEY:
162                         if (mval[0]>=NLAWIDTH)
163                                 delete_nlachannel_keys ();
164                         else
165                                 delete_nlachannels();
166                         update_for_newframe();
167                         break;
168                 case GKEY:
169                         if (mval[0]>=NLAWIDTH)
170                                 transform_nlachannel_keys ('g');
171                         update_for_newframe();
172                         break;
173                 case SKEY:
174                         if (mval[0]>=NLAWIDTH)
175                                 transform_nlachannel_keys ('s');
176                         update_for_newframe();
177                         break;
178                 case BKEY:
179                         borderselect_nla();
180                         break;
181                 case CKEY:
182                         convert_nla(mval);
183                         break;
184                         
185                 case AKEY:
186                         if (G.qual & LR_SHIFTKEY){
187                                 add_nlablock(mval);
188                                 allqueue (REDRAWNLA, 0);
189                                 allqueue (REDRAWVIEW3D, 0);
190                         }
191                         else{
192                                 if (mval[0]>=NLAWIDTH)
193                                         deselect_nlachannel_keys(1);
194                                 else{
195                                         deselect_nlachannels(1);
196                                         allqueue (REDRAWVIEW3D, 0);
197                                 }
198                                 allqueue (REDRAWNLA, 0);
199                                 allqueue (REDRAWIPO, 0);
200                         }
201                         break;
202                 case RIGHTMOUSE:
203                         if (mval[0]>=NLAWIDTH) {
204                                 if(G.qual & LR_SHIFTKEY)
205                                         mouse_nla(SELECT_INVERT);
206                                 else
207                                         mouse_nla(SELECT_REPLACE);
208                         }
209                         else
210                                 mouse_nlachannels(mval);
211                         break;
212                 case LEFTMOUSE:
213                         if (mval[0]>NLAWIDTH){
214                                 do {
215                                         getmouseco_areawin(mval);
216                                         
217                                         areamouseco_to_ipoco(G.v2d, mval, &dx, &dy);
218                                         
219                                         cfra= (int)dx;
220                                         if(cfra< 1) cfra= 1;
221                                         
222                                         if( cfra!=CFRA ) {
223                                                 CFRA= cfra;
224                                                 update_for_newframe();
225                                                 force_draw_plus(SPACE_VIEW3D);
226                                                 force_draw_plus(SPACE_IPO);
227                                         }
228                                         
229                                 } while(get_mbut()&L_MOUSE);
230                         }
231                         
232                         break;
233                 case MIDDLEMOUSE:
234                 case WHEELUPMOUSE:
235                 case WHEELDOWNMOUSE:
236                         view2dmove(event);      /* in drawipo.c */
237                         break;
238                 }
239         }
240         
241         if(doredraw) scrarea_queue_winredraw(curarea);
242         return 0;
243 }
244
245 void winqreadnlaspace(unsigned short event, short val, char ascii)
246 {
247         NlaParam param;
248         Base *base;
249         bActionStrip *strip, *next;
250         short mval[2];
251         float dx, dy;
252         int cfra;
253
254         param.event = event;
255         param.val = val;
256         param.snla = curarea->spacedata.first;
257
258
259         /* Call the protected (&obfuscated) eventloop function */
260         calc_memleak(&param); /* enable NLA */
261 }
262
263 static void convert_nla(short mval[2])
264 {
265         short event;
266         float   ymax, ymin;
267         Base *base;
268         float x,y;
269         int sel=0;
270         bActionStrip *strip, *nstrip;
271         /* Find out what strip we're over */
272         ymax = count_nla_levels() * (NLACHANNELSKIP+NLACHANNELHEIGHT);
273         areamouseco_to_ipoco(G.v2d, mval, &x, &y);
274         
275         for (base=G.scene->base.first; base; base=base->next){
276                 if (nla_filter(base, 0)){
277                         /* Check object ipo */
278                         ymin=ymax-(NLACHANNELSKIP+NLACHANNELHEIGHT);
279                         if (y>=ymin && y<=ymax)
280                                 break;
281                         ymax=ymin;
282                         
283                         if (base->object->type==OB_ARMATURE){
284                                 /* Check action ipo */
285                                 ymin=ymax-(NLACHANNELSKIP+NLACHANNELHEIGHT);
286                                 if (y>=ymin && y<=ymax)
287                                         break;
288                                 ymax=ymin;
289                                 
290                                 /* Check nlastrips */
291                                 for (strip=base->object->nlastrips.first; strip; strip=strip->next){
292                                         ymin=ymax-(NLACHANNELSKIP+NLACHANNELHEIGHT);
293                                         if (y>=ymin && y<=ymax){
294                                                 sel = 1;
295                                                 break;
296                                         }
297                                         ymax=ymin;
298                                 }
299                                 if (sel)
300                                         break;
301                         }
302                 }
303         }
304         
305         if (!base)
306                 return;
307         
308         if (base->object->type==OB_ARMATURE){
309                 event = pupmenu("Convert%t|Action to NLAstrip%x1");
310                 switch (event){
311                 case 1:
312                         if (base->object->action){
313                                 /* Make new actionstrip */
314                                 nstrip = MEM_callocN(sizeof(bActionStrip), "bActionStrip");
315                                 
316                                 deselect_nlachannel_keys(0);
317                                 
318                                 /* Link the action to the nstrip */
319                                 nstrip->act = base->object->action;
320                                 nstrip->actstart = calc_action_start(base->object->action);     /* MAKE THIS THE FIRST FRAME OF THE ACTION */
321                                 nstrip->actend = calc_action_end(base->object->action);
322                                 nstrip->start = nstrip->actstart;
323                                 nstrip->end = nstrip->actend;
324                                 nstrip->flag = ACTSTRIP_SELECT;
325                                 nstrip->repeat = 1.0;
326                                                                 
327                                 BLI_addtail(&base->object->nlastrips, nstrip);
328                                 
329                                 /* Unlink action */
330                                 base->object->action = NULL;
331                                 
332                                 allqueue (REDRAWNLA, 0);
333                         }
334                         
335                         
336                         break;
337                 default:
338                         break;
339                 }
340         }
341 }
342
343 static void add_nlablock(short mval[2])
344 {
345         /* Make sure we are over an armature */
346         Base *base;
347         bAction *act=NULL;
348         bActionStrip *strip;
349         float ymin, ymax;
350         float x, y;
351         rctf    rectf;
352         short event;
353         char *str;
354         short nr;
355         int             cur;
356         
357         areamouseco_to_ipoco(G.v2d, mval, &x, &y);
358         
359         mval[0]-=7;
360         areamouseco_to_ipoco(G.v2d, mval, &rectf.xmin, &rectf.ymin);
361         
362         mval[0]+=14;
363         areamouseco_to_ipoco(G.v2d, mval, &rectf.xmax, &rectf.ymax);
364         
365         ymax = count_nla_levels();      
366         ymax*=(NLACHANNELHEIGHT + NLACHANNELSKIP);
367         
368         for (base=G.scene->base.first; base; base=base->next){
369                 /* Handle object ipo selection */
370                 if (nla_filter(base, 0)){
371                         
372                         /* STUPID STUPID STUPID */
373                         ymin=ymax-(NLACHANNELHEIGHT+NLACHANNELSKIP);
374                         
375                         /* Handle object ipos */
376                         if (base->object->type==OB_ARMATURE){
377                                 if (!((ymax < rectf.ymin) || (ymin > rectf.ymax)))
378                                         break;          
379                         }
380                         
381                         ymax=ymin;
382                         
383                         /* Handle action ipos & Action strips */
384                         if (base->object->type==OB_ARMATURE){
385                                 ymin=ymax-(NLACHANNELHEIGHT+NLACHANNELSKIP)*(BLI_countlist(&base->object->nlastrips) + 1);
386                                 if (!((ymax < rectf.ymin) || (ymin > rectf.ymax)))
387                                         break;
388                                 ymax=ymin;
389                                 
390                                 
391                         }                       
392                 }
393         }       
394         
395         /* Make sure we have an armature */
396         if (!base){
397                 error ("Not an armature!");
398                 return;
399         }
400         
401         /* Popup action menu */
402         IDnames_to_pupstring(&str, "Add action", NULL, &G.main->action, (ID *)G.scene, &nr);
403
404         event = pupmenu(str);
405         
406         if (event!=-1){
407                 for (cur = 1, act=G.main->action.first; act; act=act->id.next, cur++){
408                         if (cur==event){
409                                 break;
410                         }
411                 }
412         }
413         
414         MEM_freeN(str);
415         
416         /* Bail out if no action was chosen */
417         if (!act){
418                 return;
419         }
420         
421         /* Initialize the new action block */
422         strip = MEM_callocN(sizeof(bActionStrip), "bActionStrip");
423         
424         deselect_nlachannel_keys(0);
425         
426         /* Link the action to the strip */
427         strip->act = act;
428         strip->actstart = 1.0;
429         strip->actend = calc_action_end(act);
430         strip->start = G.scene->r.cfra; /* Should be mval[0] */
431         strip->end = strip->start + (strip->actend-strip->actstart);
432         strip->flag = ACTSTRIP_SELECT;
433         strip->repeat = 1.0;
434         
435         act->id.us++;
436         
437         BLI_addtail(&base->object->nlastrips, strip);
438 }
439
440 static void mouse_nlachannels(short mval[2])
441 {
442         /* Find which strip has been clicked */
443 //      bActionChannel *chan;
444         bConstraintChannel *conchan=NULL;
445         bActionStrip *strip;
446         float   click;
447         int             wsize;
448         int             sel;
449         Base    *base;
450         
451         wsize = (count_nla_levels ()*(NLACHANNELHEIGHT+NLACHANNELSKIP));
452
453
454         click = (wsize-(mval[1]+G.v2d->cur.ymin));
455         click += NLACHANNELHEIGHT/2;
456         click /= (NLACHANNELHEIGHT+NLACHANNELSKIP);
457
458         if (click<0)
459                 return;
460
461         for (base = G.scene->base.first; base; base=base->next){
462                 if (nla_filter(base, 0)){
463                         /* See if this is a base selected */
464                         if ((int)click==0)
465                                 break;
466                         
467                         click--;
468                         
469                         /* Check for click in a constraint */
470                         for (conchan=base->object->constraintChannels.first; conchan; conchan=conchan->next){
471                                 if ((int)click==0){
472                                         base=G.scene->base.last;
473                                         break;
474                                 }
475                                 click--;
476                         }
477
478                         /* See if this is an action */
479                         if (base->object->type==OB_ARMATURE && base->object->action){
480                                 if ((int)click==0){
481                                         break;
482                                 }
483                                 click--;
484                         }
485
486                         /* See if this is an nla strip */
487                         for (strip = base->object->nlastrips.first; strip; strip=strip->next){
488                                 if ((int)click==0){
489                                         base=G.scene->base.last;
490                                         break;
491                                 }
492                                 click--;                                
493                         }
494                 }
495         }
496
497         if (!base && !conchan)
498                 return;
499
500         /* Handle constraint strip selection */
501         if (conchan){
502                 if (conchan->flag & CONSTRAINT_CHANNEL_SELECT)
503                         sel = 0;
504                 else
505                         sel =1;
506                 
507                 /* Channel names clicking */
508                 if (G.qual & LR_SHIFTKEY){
509                         //              select_poseelement_by_name(chan->name, !(chan->flag & ACHAN_SELECTED));
510                         if (conchan->flag & CONSTRAINT_CHANNEL_SELECT){
511                                 conchan->flag &= ~CONSTRAINT_CHANNEL_SELECT;
512                                 //      hilight_channel(act, chan, 0);
513                         }
514                         else{
515                                 conchan->flag |= CONSTRAINT_CHANNEL_SELECT;
516                                 //      hilight_channel(act, chan, 1);
517                         }
518                 }
519                 else{
520                         deselect_nlachannels (0);       // Auto clear
521                         conchan->flag |= CONSTRAINT_CHANNEL_SELECT;
522                         //      hilight_channel(act, chan, 1);
523                         //      act->achan = chan;
524                         //      select_poseelement_by_name(chan->name, 1);
525                 }
526                 
527         }
528         
529         /* Handle object strip selection */
530         else if (base)
531         {
532                 /* Choose the mode */
533                 if (base->flag & SELECT)
534                         sel = 0;
535                 else
536                         sel =1;
537                 
538                 /* Channel names clicking */
539                 if (G.qual & LR_SHIFTKEY){
540         //              select_poseelement_by_name(chan->name, !(chan->flag & ACHAN_SELECTED));
541                         if (base->flag & SELECT){
542                                 base->flag &= ~SELECT;
543                 //              hilight_channel(act, chan, 0);
544                         }
545                         else{
546                                 base->flag |= SELECT;
547                 //              hilight_channel(act, chan, 1);
548                         }
549                 }
550                 else{
551                         deselect_nlachannels (0);       // Auto clear
552                         base->flag |= SELECT;
553                 //      hilight_channel(act, chan, 1);
554                 //      act->achan = chan;
555                 //      select_poseelement_by_name(chan->name, 1);
556                 }
557                 
558         }
559         allqueue (REDRAWIPO, 0);
560         allqueue (REDRAWVIEW3D, 0);
561         allqueue (REDRAWACTION, 0);
562         allqueue(REDRAWNLA, 0);
563         
564 }
565
566 void init_nlaspace(ScrArea *sa)
567 {
568         SpaceNla *snla;
569         
570         snla= MEM_callocN(sizeof(SpaceNla), "initnlaspace");
571         BLI_addhead(&sa->spacedata, snla);
572         
573         snla->spacetype= SPACE_NLA;
574         
575         snla->v2d.tot.xmin= 1.0;
576         snla->v2d.tot.ymin=     0.0;
577         snla->v2d.tot.xmax= 1000.0;
578         snla->v2d.tot.ymax= 1000.0;
579         
580         snla->v2d.cur.xmin= -5.0;
581         snla->v2d.cur.ymin= 0.0;
582         snla->v2d.cur.xmax= 65.0;
583         snla->v2d.cur.ymax= 1000.0;
584         
585         snla->v2d.min[0]= 0.0;
586         snla->v2d.min[1]= 0.0;
587         
588         snla->v2d.max[0]= 1000.0;
589         snla->v2d.max[1]= 1000.0;
590         
591         snla->v2d.minzoom= 0.1F;
592         snla->v2d.maxzoom= 10;
593         
594         snla->v2d.scroll= R_SCROLL+B_SCROLL;
595         snla->v2d.keepaspect= 0;
596         snla->v2d.keepzoom= V2D_LOCKZOOM_Y;
597         snla->v2d.keeptot= 0;
598         
599         snla->lock = 0;
600 };
601
602 static void deselect_nlachannel_keys (int test)
603 {
604         Base                    *base;
605         int                             sel=1;
606         bActionChannel  *chan;
607         bActionStrip    *strip;
608         bConstraintChannel *conchan;
609         
610         /* Determine if this is selection or deselection */
611         if (test){
612                 for (base=G.scene->base.first; base && sel; base=base->next){
613                         
614                         /* Test object ipos */
615                         if (is_ipo_key_selected(base->object->ipo)){
616                                 sel = 0;
617                                 break;
618                         }
619                         
620                         /* Test object constraint ipos */
621                         if (sel){
622                                 for (conchan=base->object->constraintChannels.first; conchan; conchan=conchan->next){
623                                         if (is_ipo_key_selected(conchan->ipo)){
624                                                 sel=0;
625                                                 break;
626                                         }
627                                 }
628                         }
629                         
630                         /* Test action ipos */
631                         if (sel){
632                                 if (base->object->type==OB_ARMATURE && base->object->action){
633                                         for (chan=base->object->action->chanbase.first; chan; chan=chan->next){
634                                                 if (is_ipo_key_selected(chan->ipo)){
635                                                         sel=0;
636                                                         break;
637                                                 }
638
639                                                 /* Test action constraints */
640                                                 if (sel){
641                                                         for (conchan=chan->constraintChannels.first; conchan; conchan=conchan->next){
642                                                                 if (is_ipo_key_selected(conchan->ipo)){
643                                                                         sel=0;
644                                                                         break;
645                                                                 }
646                                                         }
647                                                 }
648                                         }
649                                 }
650                         }
651                         
652                         /* Test NLA strips */
653                         if (sel){
654                                 if (base->object->type==OB_ARMATURE){
655                                         for (strip=base->object->nlastrips.first; strip; strip=strip->next){
656                                                 if (strip->flag & ACTSTRIP_SELECT){
657                                                         sel = 0;
658                                                         break;
659                                                 }
660                                         }
661                                 }
662                         }
663                 }
664         }
665         else
666                 sel=0;
667         
668         
669         /* Set the flags */
670         for (base=G.scene->base.first; base; base=base->next){
671                 /* Set the object ipos */
672                 set_ipo_key_selection(base->object->ipo, sel);
673
674                 
675                 /* Set the object constraint ipos */
676                 for (conchan=base->object->constraintChannels.first; conchan; conchan=conchan->next){
677                         set_ipo_key_selection(conchan->ipo, sel);                       
678                 }
679
680                 /* Set the action ipos */
681                 if (base->object->type==OB_ARMATURE && base->object->action){
682                         for (chan=base->object->action->chanbase.first; chan; chan=chan->next){
683                                 set_ipo_key_selection(chan->ipo, sel);
684                                 /* Set the action constraint ipos */
685                                 for (conchan=chan->constraintChannels.first; conchan; conchan=conchan->next)
686                                         set_ipo_key_selection(conchan->ipo, sel);
687                         }
688                 }
689                 
690                 /* Set the nlastrips */
691                 if (base->object->type==OB_ARMATURE){
692                         for (strip=base->object->nlastrips.first; strip; strip=strip->next){
693                                 if (sel)
694                                         strip->flag |= ACTSTRIP_SELECT;
695                                 else
696                                         strip->flag &= ~ACTSTRIP_SELECT;
697                         }
698                 }
699         }
700 }
701
702
703 static void transform_nlachannel_keys(char mode)
704 {
705         Base *base;
706         TransVert *tv;
707         int /*sel=0,*/  i;
708         short   mvals[2], mvalc[2];
709         //      short    cent[2];
710         float   sval[2], cval[2], lastcval[2];
711         short   cancel=0;
712         float   fac=0.0F;
713         int             loop=1;
714         int             tvtot=0;
715         float   deltax, startx;
716         //      float   cenf[2];
717         int             invert=0, firsttime=1;
718         char    str[256];
719         bActionChannel *chan;
720         bActionStrip *strip;
721         bConstraintChannel *conchan;
722
723         /* Ensure that partial selections result in beztriple selections */
724         for (base=G.scene->base.first; base; base=base->next){
725
726                 /* Check object ipos */
727                 tvtot+=fullselect_ipo_keys(base->object->ipo);
728                 
729                 /* Check object constraint ipos */
730                 for(conchan=base->object->constraintChannels.first; conchan; conchan=conchan->next)
731                         tvtot+=fullselect_ipo_keys(conchan->ipo);                       
732                 
733                 /* Check action ipos */
734                 if (base->object->type == OB_ARMATURE && base->object->action){
735                         for (chan=base->object->action->chanbase.first; chan; chan=chan->next){
736                                 tvtot+=fullselect_ipo_keys(chan->ipo);
737                                 
738                                 /* Check action constraint ipos */
739                                 for (conchan=chan->constraintChannels.first; conchan; conchan=conchan->next)
740                                         tvtot+=fullselect_ipo_keys(conchan->ipo);
741                         }
742                 
743                 }
744
745                 /* Check nlastrips */
746                 if (base->object->type==OB_ARMATURE){
747                         for (strip=base->object->nlastrips.first; strip; strip=strip->next){
748                                 if (strip->flag & ACTSTRIP_SELECT)
749                                         tvtot+=2;
750                         }
751                 }
752         }
753         
754         /* If nothing is selected, bail out */
755         if (!tvtot)
756                 return;
757         
758         
759         /* Build the transvert structure */
760         tv = MEM_callocN (sizeof(TransVert) * tvtot, "transVert");
761         tvtot=0;
762         for (base=G.scene->base.first; base; base=base->next){
763                 /* Manipulate object ipos */
764                 tvtot=add_trans_ipo_keys(base->object->ipo, tv, tvtot);
765
766                 /* Manipulate object constraint ipos */
767                 for (conchan=base->object->constraintChannels.first; conchan; conchan=conchan->next)
768                         tvtot=add_trans_ipo_keys(conchan->ipo, tv, tvtot);
769
770                 /* Manipulate action ipos */
771                 if (base->object->type==OB_ARMATURE && base->object->action){
772                         for (chan=base->object->action->chanbase.first; chan; chan=chan->next){
773                                 tvtot=add_trans_ipo_keys(chan->ipo, tv, tvtot);
774
775                                 /* Manipulate action constraint ipos */
776                                 for (conchan=chan->constraintChannels.first; conchan; conchan=conchan->next)
777                                         tvtot=add_trans_ipo_keys(conchan->ipo, tv, tvtot);
778                         }
779                 }
780
781                 /* Manipulate nlastrips */
782                 for (strip=base->object->nlastrips.first; strip; strip=strip->next){
783                         if (strip->flag & ACTSTRIP_SELECT){
784                                 tv[tvtot+0].val=&strip->start;
785                                 tv[tvtot+1].val=&strip->end;
786                                 
787                                 tv[tvtot+0].oldval = strip->start;
788                                 tv[tvtot+1].oldval = strip->end;
789                                 
790                                 tvtot+=2;
791                         }
792                 }
793         }
794         
795         /* Do the event loop */
796         //      cent[0] = curarea->winx + (G.snla->v2d.hor.xmax)/2;
797         //      cent[1] = curarea->winy + (G.snla->v2d.hor.ymax)/2;
798         
799         //      areamouseco_to_ipoco(cent, &cenf[0], &cenf[1]);
800         
801         getmouseco_areawin (mvals);
802         areamouseco_to_ipoco(G.v2d, mvals, &sval[0], &sval[1]);
803         
804         startx=sval[0];
805         while (loop) {
806                 /*              Get the input */
807                 /*              If we're cancelling, reset transformations */
808                 /*                      Else calc new transformation */
809                 /*              Perform the transformations */
810                 while (qtest()) {
811                         short val;
812                         unsigned short event= extern_qread(&val);
813                         
814                         if (val) {
815                                 switch (event) {
816                                 case LEFTMOUSE:
817                                 case SPACEKEY:
818                                 case RETKEY:
819                                         loop=0;
820                                         break;
821                                 case XKEY:
822                                         break;
823                                 case ESCKEY:
824                                 case RIGHTMOUSE:
825                                         cancel=1;
826                                         loop=0;
827                                         break;
828                                 default:
829                                         arrows_move_cursor(event);
830                                         break;
831                                 };
832                         }
833                 }
834                 
835                 if (cancel) {
836                         for (i=0; i<tvtot; i++) {
837                                 if (tv[i].loc){
838                                         tv[i].loc[0]=tv[i].oldloc[0];
839                                         tv[i].loc[1]=tv[i].oldloc[1];
840                                 }
841                                 if (tv[i].val)
842                                         tv[i].val[0]=tv[i].oldval;
843                         }
844                 }
845                 else {
846                         getmouseco_areawin (mvalc);
847                         areamouseco_to_ipoco(G.v2d, mvalc, &cval[0], &cval[1]);
848                         
849                         if (!firsttime && lastcval[0]==cval[0] && lastcval[1]==cval[1]) {
850                                 PIL_sleep_ms(1);
851                         }
852                         else {
853                                 for (i=0; i<tvtot; i++){
854                                         if (tv[i].loc)
855                                                 tv[i].loc[0]=tv[i].oldloc[0];
856                                         if (tv[i].val)
857                                                 tv[i].val[0]=tv[i].oldval;
858                                         
859                                         switch (mode){
860                                         case 'g':
861                                                 deltax = cval[0]-sval[0];
862                                                 fac= deltax;
863                                                 
864                                                 apply_keyb_grid(&fac, 0.0F, 1.0F, 0.1F, U.flag & AUTOGRABGRID);
865                                                 
866                                                 if (tv[i].loc)
867                                                         tv[i].loc[0]+=fac;
868                                                 if (tv[i].val)
869                                                         tv[i].val[0]+=fac;
870                                                 break;
871                                         case 's': 
872                                                 startx=mvals[0]-(NLAWIDTH/2+(curarea->winrct.xmax-curarea->winrct.xmin)/2);
873                                                 deltax=mvalc[0]-(NLAWIDTH/2+(curarea->winrct.xmax-curarea->winrct.xmin)/2);
874                                                 fac= (float)fabs(deltax/startx);
875                                                 
876                                                 apply_keyb_grid(&fac, 0.0F, 0.2F, 0.1F, U.flag & AUTOSIZEGRID);
877                                                 
878                                                 if (invert){
879                                                         if (i % 03 == 0){
880                                                                 memcpy (tv[i].loc, tv[i].oldloc, sizeof(tv[i+2].oldloc));
881                                                         }
882                                                         if (i % 03 == 2){
883                                                                 memcpy (tv[i].loc, tv[i].oldloc, sizeof(tv[i-2].oldloc));
884                                                         }
885                                                         
886                                                         fac*=-1;
887                                                 }
888                                                 startx= (G.scene->r.cfra);
889                                                 
890                                                 if (tv[i].loc){
891                                                         tv[i].loc[0]-= startx;
892                                                         tv[i].loc[0]*=fac;
893                                                         tv[i].loc[0]+= startx;
894                                                 }
895                                                 if (tv[i].val){
896                                                         tv[i].val[0]-= startx;
897                                                         tv[i].val[0]*=fac;
898                                                         tv[i].val[0]+= startx;
899                                                 }
900                                                 
901                                                 break;
902                                         }
903                                 }
904                         }
905                         
906                         if (mode=='s'){
907                                 sprintf(str, "sizeX: %.3f", fac);
908                                 headerprint(str);
909                         }
910                         else if (mode=='g'){
911                                 sprintf(str, "deltaX: %.3f", fac);
912                                 headerprint(str);
913                         }
914                         
915                         if (G.snla->lock){
916                                 allqueue (REDRAWVIEW3D, 0);
917                                 allqueue (REDRAWNLA, 0);
918                                 allqueue (REDRAWIPO, 0);
919                                 force_draw_all();
920                         }
921                         else {
922                                 addqueue (curarea->win, REDRAWALL, 0);
923                                 force_draw ();
924                         }
925                 }
926                 
927                 lastcval[0]= cval[0];
928                 lastcval[1]= cval[1];
929                 firsttime= 0;
930         }
931         
932         allspace(REMAKEALLIPO, 0);
933         allqueue (REDRAWVIEW3D, 0);
934         allqueue (REDRAWNLA, 0);
935         allqueue (REDRAWIPO, 0);
936         MEM_freeN (tv);
937 }
938
939 static void delete_nlachannel_keys(void)
940 {
941         Base *base;
942         bActionChannel *chan;
943         bConstraintChannel *conchan;
944         bActionStrip *strip, *nextstrip;
945         
946         if (!okee("Erase selected keys"))
947                 return;
948         
949         for (base = G.scene->base.first; base; base=base->next){
950
951                 /* Delete object ipos */
952                 delete_ipo_keys(base->object->ipo);
953                 
954                 /* Delete object constraint keys */
955                 for(conchan=base->object->constraintChannels.first; conchan; conchan=conchan->next)
956                         delete_ipo_keys(conchan->ipo);
957
958                 /* Delete NLA strips */
959                 if (base->object->type==OB_ARMATURE){
960                         for (strip = base->object->nlastrips.first; strip; strip=nextstrip){
961                                 nextstrip=strip->next;
962                                 if (strip->flag & ACTSTRIP_SELECT){
963                                         free_actionstrip(strip);
964                                         BLI_remlink(&base->object->nlastrips, strip);
965                                         MEM_freeN(strip);
966                                 }
967                         }
968                 }
969                 
970                 /* Delete action ipos */
971                 if (base->object->type==OB_ARMATURE && base->object->action){
972                         for (chan=base->object->action->chanbase.first; chan; chan=chan->next){
973                                 delete_ipo_keys(chan->ipo);
974                                 /* Delete action constraint keys */
975                                 for(conchan=chan->constraintChannels.first; conchan; conchan=conchan->next)
976                                         delete_ipo_keys(conchan->ipo);
977                         }
978                 }
979         }
980         
981         allspace(REMAKEALLIPO, 0);
982         allqueue(REDRAWNLA, 0);
983         allqueue(REDRAWIPO, 0);
984 }
985
986 static void duplicate_nlachannel_keys(void)
987 {
988         Base *base;
989         bActionChannel *chan;
990         bConstraintChannel *conchan;
991         bActionStrip *strip, *laststrip;
992         
993         /* Find selected items */
994         for (base = G.scene->base.first; base; base=base->next){
995                 /* Duplicate object keys */
996                 duplicate_ipo_keys(base->object->ipo);
997                 
998                 /* Duplicate object constraint keys */
999                 for(conchan=base->object->constraintChannels.first; conchan; conchan=conchan->next)
1000                         duplicate_ipo_keys(conchan->ipo);
1001
1002                 /* Duplicate nla strips */
1003                 if (base->object->type == OB_ARMATURE){
1004                         laststrip = base->object->nlastrips.last;
1005                         for (strip=base->object->nlastrips.first; strip; strip=strip->next){
1006                                 if (strip->flag & ACTSTRIP_SELECT){
1007                                         bActionStrip *newstrip;
1008                                         
1009                                         copy_actionstrip(&newstrip, &strip);
1010                                         
1011                                         BLI_addtail(&base->object->nlastrips, newstrip);
1012                                         
1013                                         strip->flag &= ~ACTSTRIP_SELECT;
1014                                         newstrip->flag |= ACTSTRIP_SELECT;
1015                                         
1016                                 }
1017                                 if (strip==laststrip)
1018                                         break;
1019                         }
1020                 }
1021                 
1022                 /* Duplicate actionchannel keys */
1023                 if (base->object->type == OB_ARMATURE && base->object->action){
1024                         for (chan=base->object->action->chanbase.first; chan; chan=chan->next){
1025                                 duplicate_ipo_keys(chan->ipo);
1026                                 /* Duplicate action constraint keys */
1027                                 for(conchan=chan->constraintChannels.first; conchan; conchan=conchan->next)
1028                                         duplicate_ipo_keys(conchan->ipo);
1029                         }
1030                 }
1031         }
1032         
1033         transform_nlachannel_keys ('g');
1034 }
1035
1036 static void borderselect_nla(void)
1037
1038         Base *base;
1039         rcti rect;
1040         rctf rectf;
1041         int  val, selectmode;
1042         short   mval[2];
1043         float   ymin, ymax;
1044         bActionStrip *strip;
1045         bConstraintChannel *conchan;
1046         
1047         if ( (val = get_border (&rect, 3)) ){
1048     if (val == LEFTMOUSE)
1049       selectmode = SELECT_ADD;
1050     else
1051       selectmode = SELECT_SUBTRACT;
1052
1053                 mval[0]= rect.xmin;
1054                 mval[1]= rect.ymin+2;
1055                 areamouseco_to_ipoco(G.v2d, mval, &rectf.xmin, &rectf.ymin);
1056                 mval[0]= rect.xmax;
1057                 mval[1]= rect.ymax-2;
1058                 areamouseco_to_ipoco(G.v2d, mval, &rectf.xmax, &rectf.ymax);
1059                 
1060                 ymax = count_nla_levels();
1061                 ymax*= (NLACHANNELHEIGHT+NLACHANNELSKIP);
1062                 
1063                 for (base=G.scene->base.first; base; base=base->next){
1064                         /* Check object ipos */
1065                         if (nla_filter(base, 0)){
1066                                 ymin=ymax-(NLACHANNELHEIGHT+NLACHANNELSKIP);
1067                                 if (base->object->ipo){
1068                                         if (!((ymax < rectf.ymin) || (ymin > rectf.ymax)))
1069                                                 borderselect_ipo_key(base->object->ipo, rectf.xmin, rectf.xmax,
1070                                  selectmode);
1071                                 }
1072                                 ymax=ymin;
1073
1074                                 /* Check object constraint ipos */
1075                                 for (conchan=base->object->constraintChannels.first; conchan; conchan=conchan->next){
1076                                         ymin=ymax-(NLACHANNELHEIGHT+NLACHANNELSKIP);
1077                                         if (!((ymax < rectf.ymin) || (ymin > rectf.ymax)))
1078                                                 borderselect_ipo_key(conchan->ipo, rectf.xmin, rectf.xmax,
1079                                  selectmode);
1080                                         ymax=ymin;
1081                                 }
1082
1083                                 /* Check action ipos */
1084                                 if (ACTIVE_ARMATURE(base)){
1085                                         ymin=ymax-(NLACHANNELHEIGHT+NLACHANNELSKIP);
1086                                         if (base->object->action){
1087                                                 bActionChannel *chan;
1088                                                 
1089                                                 if (!((ymax < rectf.ymin) || (ymin > rectf.ymax))){
1090                                                         for (chan=base->object->action->chanbase.first; chan; chan=chan->next){
1091                                                                 borderselect_ipo_key(chan->ipo, rectf.xmin, rectf.xmax,
1092                                      selectmode);
1093                                                                 /* Check action constraint ipos */
1094                                                                 for (conchan=chan->constraintChannels.first; conchan; conchan=conchan->next)
1095                                                                         borderselect_ipo_key(conchan->ipo, rectf.xmin, rectf.xmax,
1096                                        selectmode);
1097                                                         }
1098                                                 }
1099                                         }
1100                                         ymax=ymin;
1101                                 }       /* End of if armature */
1102                                 
1103                                 /* Skip nlastrips */
1104                                 if (base->object->type==OB_ARMATURE){
1105                                         for (strip=base->object->nlastrips.first; strip; strip=strip->next){
1106                                                 ymin=ymax-(NLACHANNELHEIGHT+NLACHANNELSKIP);
1107                                                 //
1108                                                 if (!((ymax < rectf.ymin) || (ymin > rectf.ymax))){
1109                                                         if (!((rectf.xmax<strip->start) || (rectf.xmin>strip->end))){
1110                                                                 if (val==1)
1111                                                                         strip->flag |= ACTSTRIP_SELECT;
1112                                                                 else
1113                                                                         strip->flag &= ~ACTSTRIP_SELECT;
1114                                                         }
1115                                                 }
1116                                                 
1117                                                 ymax=ymin;
1118                                         }
1119                                 }
1120                                 
1121                         }       /* End of object filter */
1122                 }       
1123                 allqueue(REDRAWNLA, 0);
1124                 allqueue(REDRAWACTION, 0);
1125                 allqueue(REDRAWIPO, 0);
1126         }
1127 }
1128
1129 static void mouse_nla(int selectmode)
1130 {
1131         short sel;
1132         float   selx;
1133         short   mval[2];
1134         Base *base;
1135         bAction *act;
1136         bActionChannel *chan;
1137         bActionStrip *rstrip;
1138         bConstraintChannel *conchan;
1139         
1140         getmouseco_areawin (mval);
1141         
1142         /* Try object ipo selection */
1143         base=get_nearest_nlachannel_ob_key(&selx, &sel);
1144         if (base){
1145                 if (selectmode == SELECT_REPLACE){
1146                         deselect_nlachannel_keys(0);
1147                         selectmode = SELECT_ADD;
1148                 }
1149                 
1150                 select_ipo_key(base->object->ipo, selx, selectmode);
1151                 
1152                 /* Try object constraint selection */
1153                 for (conchan=base->object->constraintChannels.first; conchan; conchan=conchan->next)
1154                         select_ipo_key(conchan->ipo, selx, selectmode);
1155                 
1156                 
1157                 allqueue(REDRAWIPO, 0);
1158                 allqueue(REDRAWVIEW3D, 0);
1159                 allqueue(REDRAWNLA, 0);
1160                 return;
1161         }
1162
1163         /* Try action ipo selection */
1164         act=get_nearest_nlachannel_ac_key(&selx, &sel);
1165         if (act){
1166                 if (selectmode == SELECT_REPLACE){
1167                         deselect_nlachannel_keys(0);
1168                         selectmode = SELECT_ADD;
1169                 }
1170                 
1171                 for (chan=act->chanbase.first; chan; chan=chan->next){
1172                         select_ipo_key(chan->ipo, selx, selectmode);
1173                         /* Try action constraint selection */
1174                         for (conchan=chan->constraintChannels.first; conchan; conchan=conchan->next)
1175                                 select_ipo_key(conchan->ipo, selx, selectmode);
1176                 }
1177                 
1178                 allqueue(REDRAWIPO, 0);
1179                 allqueue(REDRAWVIEW3D, 0);
1180                 allqueue(REDRAWNLA, 0);
1181                 return;
1182         }
1183         
1184         /* Try nla strip selection */
1185         base=get_nearest_nlastrip(&rstrip, &sel);
1186         if (base){
1187                 if (!(G.qual & LR_SHIFTKEY)){
1188                         deselect_nlachannel_keys(0);
1189                         sel = 0;
1190                 }
1191                 
1192                 if (sel)
1193                         rstrip->flag &= ~ACTSTRIP_SELECT;
1194                 else
1195                         rstrip->flag |= ACTSTRIP_SELECT;
1196                 
1197                 allqueue(REDRAWIPO, 0);
1198                 allqueue(REDRAWVIEW3D, 0);
1199                 allqueue(REDRAWNLA, 0);
1200                 return;
1201                 
1202         }
1203         
1204 }
1205
1206 static Base *get_nearest_nlastrip (bActionStrip **rstrip, short *sel)
1207 /* This function is currently more complicated than it seems like it should be.
1208 * However, this will be needed once the nla strip timeline is more complex */
1209 {
1210         Base *base, *firstbase=NULL;
1211         short mval[2];
1212         short foundsel = 0;
1213         rctf    rectf;
1214         float ymin, ymax;
1215         bActionStrip *strip, *firststrip, *foundstrip;
1216         
1217         getmouseco_areawin (mval);
1218         
1219         mval[0]-=7;
1220         areamouseco_to_ipoco(G.v2d, mval, &rectf.xmin, &rectf.ymin);
1221         
1222         mval[0]+=14;
1223         areamouseco_to_ipoco(G.v2d, mval, &rectf.xmax, &rectf.ymax);
1224         
1225         ymax = count_nla_levels();
1226         ymax*=(NLACHANNELHEIGHT + NLACHANNELSKIP);
1227         
1228         for (base = G.scene->base.first; base; base=base->next){
1229                 if (nla_filter(base, 0)){
1230                         /* Skip object ipos */
1231                         //      if (base->object->ipo)
1232                         ymax-=(NLACHANNELHEIGHT+NLACHANNELSKIP);
1233                         
1234                         if (base->object->type==OB_ARMATURE){
1235                                 /* Skip action ipos */
1236                                 if (base->object->action)
1237                                         ymax-=(NLACHANNELHEIGHT+NLACHANNELSKIP);
1238                                 
1239                                 for (strip=base->object->nlastrips.first; strip; strip=strip->next){
1240                                         ymin=ymax-(NLACHANNELHEIGHT+NLACHANNELSKIP);
1241                                         /* Do Ytest */
1242                                         if (!((ymax < rectf.ymin) || (ymin > rectf.ymax))){
1243                                                 /* Do XTest */
1244                                                 if (!((rectf.xmax<strip->start) || (rectf.xmin>strip->end))){
1245                                                         if (!firstbase){
1246                                                                 firstbase=base;
1247                                                                 firststrip=strip;
1248                                                                 *sel = strip->flag & ACTSTRIP_SELECT;
1249                                                         }
1250                                                         
1251                                                         if (strip->flag & ACTSTRIP_SELECT){ 
1252                                                                 if (!foundsel){
1253                                                                         foundsel=1;
1254                                                                         foundstrip = strip;
1255                                                                 }
1256                                                         }
1257                                                         else if (foundsel && strip != foundstrip){
1258                                                                 *rstrip=strip;
1259                                                                 *sel = 0;
1260                                                                 return base;
1261                                                         }
1262                                                 }
1263                                         }
1264                                         ymax=ymin;
1265                                 }
1266                         }
1267                 }
1268         }
1269         *rstrip=firststrip;
1270         return firstbase;
1271 }
1272
1273 static Base *get_nearest_nlachannel_ob_key (float *index, short *sel)
1274 {
1275         Base *base;
1276         IpoCurve *icu;
1277         Base *firstbase=NULL;
1278         bConstraintChannel *conchan;
1279         int     foundsel=0;
1280         float firstvert=-1, foundx=-1;
1281         int i;
1282         short mval[2];
1283         float ymin, ymax;
1284         rctf    rectf;
1285         
1286         *index=0;
1287         
1288         getmouseco_areawin (mval);
1289         
1290         mval[0]-=7;
1291         areamouseco_to_ipoco(G.v2d, mval, &rectf.xmin, &rectf.ymin);
1292         
1293         mval[0]+=14;
1294         areamouseco_to_ipoco(G.v2d, mval, &rectf.xmax, &rectf.ymax);
1295         
1296         ymax = count_nla_levels();
1297         
1298         ymax*=(NLACHANNELHEIGHT + NLACHANNELSKIP);
1299         
1300         *sel=0;
1301         
1302         for (base=G.scene->base.first; base; base=base->next){
1303                 /* Handle object ipo selection */
1304                 if (nla_filter(base, 0)){
1305                         ymin=ymax-(NLACHANNELHEIGHT+NLACHANNELSKIP);
1306                         if (base->object->ipo){
1307                                 if (!((ymax < rectf.ymin) || (ymin > rectf.ymax))){
1308                                         for (icu=base->object->ipo->curve.first; icu; icu=icu->next){
1309                                                 for (i=0; i<icu->totvert; i++){
1310                                                         if (icu->bezt[i].vec[1][0] > rectf.xmin && icu->bezt[i].vec[1][0] <= rectf.xmax ){
1311                                                                 if (!firstbase){
1312                                                                         firstbase=base;
1313                                                                         firstvert=icu->bezt[i].vec[1][0];
1314                                                                         *sel = icu->bezt[i].f2 & 1;     
1315                                                                 }
1316                                                                 
1317                                                                 if (icu->bezt[i].f2 & 1){ 
1318                                                                         if (!foundsel){
1319                                                                                 foundsel=1;
1320                                                                                 foundx = icu->bezt[i].vec[1][0];
1321                                                                         }
1322                                                                 }
1323                                                                 else if (foundsel && icu->bezt[i].vec[1][0] != foundx){
1324                                                                         *index=icu->bezt[i].vec[1][0];
1325                                                                         *sel = 0;
1326                                                                         return base;
1327                                                                 }
1328                                                         }
1329                                                 }
1330                                         }
1331                                 }
1332                         }
1333                 
1334                         ymax=ymin;
1335
1336                         /* Handle object constraint ipos */
1337                         for (conchan=base->object->constraintChannels.first; conchan; conchan=conchan->next){
1338                                 ymin=ymax-(NLACHANNELHEIGHT+NLACHANNELSKIP);
1339                                 if (!((ymax < rectf.ymin) || (ymin > rectf.ymax))){
1340                                         for (icu=conchan->ipo->curve.first; icu; icu=icu->next){
1341                                                 for (i=0; i<icu->totvert; i++){
1342                                                         if (icu->bezt[i].vec[1][0] > rectf.xmin && icu->bezt[i].vec[1][0] <= rectf.xmax ){
1343                                                                 if (!firstbase){
1344                                                                         firstbase=base;
1345                                                                         firstvert=icu->bezt[i].vec[1][0];
1346                                                                         *sel = icu->bezt[i].f2 & 1;     
1347                                                                 }
1348                                                                 
1349                                                                 if (icu->bezt[i].f2 & 1){ 
1350                                                                         if (!foundsel){
1351                                                                                 foundsel=1;
1352                                                                                 foundx = icu->bezt[i].vec[1][0];
1353                                                                         }
1354                                                                 }
1355                                                                 else if (foundsel && icu->bezt[i].vec[1][0] != foundx){
1356                                                                         *index=icu->bezt[i].vec[1][0];
1357                                                                         *sel = 0;
1358                                                                         return base;
1359                                                                 }
1360                                                         }
1361                                                 }
1362                                         }
1363                                 }
1364                                 ymax=ymin;
1365                         }
1366
1367                         /* Skip action ipos */
1368                         if (ACTIVE_ARMATURE(base)){
1369                                 ymax-=(NLACHANNELHEIGHT+NLACHANNELSKIP);
1370                         }
1371                         /* Skip nlastrips */
1372                         if (base->object->type==OB_ARMATURE){
1373                                 ymax-=(NLACHANNELHEIGHT+NLACHANNELSKIP)*BLI_countlist(&base->object->nlastrips);
1374                         }
1375                 }
1376         }       
1377         
1378         *index=firstvert;
1379         return firstbase;
1380 }
1381
1382 static bAction *get_nearest_nlachannel_ac_key (float *index, short *sel)
1383 {
1384         Base *base;
1385         IpoCurve *icu;
1386         bAction *firstact=NULL;
1387         int     foundsel=0;
1388         float firstvert=-1, foundx=-1;
1389         int i;
1390         short mval[2];
1391         float ymin, ymax;
1392         rctf    rectf;
1393         bActionChannel *chan;
1394         bConstraintChannel *conchan;
1395         
1396         *index=0;
1397         
1398         getmouseco_areawin (mval);
1399         
1400         mval[0]-=7;
1401         areamouseco_to_ipoco(G.v2d, mval, &rectf.xmin, &rectf.ymin);
1402         
1403         mval[0]+=14;
1404         areamouseco_to_ipoco(G.v2d, mval, &rectf.xmax, &rectf.ymax);
1405         
1406         ymax = count_nla_levels();
1407         
1408         ymax*=(NLACHANNELHEIGHT + NLACHANNELSKIP);
1409         
1410         *sel=0;
1411         
1412         for (base=G.scene->base.first; base; base=base->next){
1413                 /* Handle object ipo selection */
1414                 if (nla_filter(base, 0)){
1415                         /* Skip object ipo */
1416                         ymin=ymax-(NLACHANNELHEIGHT+NLACHANNELSKIP);
1417                         ymax=ymin;
1418                         
1419                         /* Handle action ipos */
1420                         if (ACTIVE_ARMATURE(base)){
1421                                 ymin=ymax-(NLACHANNELHEIGHT+NLACHANNELSKIP);
1422                                 if (!((ymax < rectf.ymin) || (ymin > rectf.ymax))){
1423                                         for (chan=base->object->action->chanbase.first; chan; chan=chan->next){
1424                                                 for (icu=chan->ipo->curve.first; icu; icu=icu->next){
1425                                                         for (i=0; i<icu->totvert; i++){
1426                                                                 if (icu->bezt[i].vec[1][0] > rectf.xmin && icu->bezt[i].vec[1][0] <= rectf.xmax ){
1427                                                                         if (!firstact){
1428                                                                                 firstact=base->object->action;
1429                                                                                 firstvert=icu->bezt[i].vec[1][0];
1430                                                                                 *sel = icu->bezt[i].f2 & 1;     
1431                                                                         }
1432                                                                         
1433                                                                         if (icu->bezt[i].f2 & 1){ 
1434                                                                                 if (!foundsel){
1435                                                                                         foundsel=1;
1436                                                                                         foundx = icu->bezt[i].vec[1][0];
1437                                                                                 }
1438                                                                         }
1439                                                                         else if (foundsel && icu->bezt[i].vec[1][0] != foundx){
1440                                                                                 *index=icu->bezt[i].vec[1][0];
1441                                                                                 *sel = 0;
1442                                                                                 return base->object->action;
1443                                                                         }
1444                                                                 }
1445                                                         }
1446                                                 }
1447                                                 
1448                                                 
1449                                                 for (conchan=chan->constraintChannels.first; conchan; conchan=conchan->next){
1450                                                         ymin=ymax-(NLACHANNELHEIGHT+NLACHANNELSKIP);
1451                                                         if (!((ymax < rectf.ymin) || (ymin > rectf.ymax))){
1452                                                                 for (icu=conchan->ipo->curve.first; icu; icu=icu->next){
1453                                                                         for (i=0; i<icu->totvert; i++){
1454                                                                                 if (icu->bezt[i].vec[1][0] > rectf.xmin && icu->bezt[i].vec[1][0] <= rectf.xmax ){
1455                                                                                         if (!firstact){
1456                                                                                                 firstact=base->object->action;
1457                                                                                                 firstvert=icu->bezt[i].vec[1][0];
1458                                                                                                 *sel = icu->bezt[i].f2 & 1;     
1459                                                                                         }
1460                                                                                         
1461                                                                                         if (icu->bezt[i].f2 & 1){ 
1462                                                                                                 if (!foundsel){
1463                                                                                                         foundsel=1;
1464                                                                                                         foundx = icu->bezt[i].vec[1][0];
1465                                                                                                 }
1466                                                                                         }
1467                                                                                         else if (foundsel && icu->bezt[i].vec[1][0] != foundx){
1468                                                                                                 *index=icu->bezt[i].vec[1][0];
1469                                                                                                 *sel = 0;
1470                                                                                                 return base->object->action;
1471                                                                                         }
1472                                                                                 }
1473                                                                         }
1474                                                                 }
1475                                                         }
1476                                                         ymax=ymin;
1477                                                 }
1478                                         
1479                                         
1480                                         }
1481                                 }                       
1482                                 ymax=ymin;
1483                         }
1484                         
1485                         /* Skip nlastrips */
1486                         if (base->object->type==OB_ARMATURE){
1487                                 ymax-=(NLACHANNELHEIGHT+NLACHANNELSKIP)*BLI_countlist(&base->object->nlastrips);
1488                         }
1489                 }
1490         }       
1491         
1492         *index=firstvert;
1493         return firstact;
1494 }
1495
1496 static bActionStrip *get_active_nlastrip(void)
1497 /* For now just returns the first selected strip */
1498 {
1499         Base *base;
1500         bActionStrip *strip;
1501         
1502         for (base=G.scene->base.first; base; base=base->next){
1503                 if (nla_filter(base, 0) && base->object->type==OB_ARMATURE){
1504                         for (strip=base->object->nlastrips.first; strip; strip=strip->next){
1505                                 if (strip->flag & ACTSTRIP_SELECT)
1506                                         return strip;
1507                         }
1508                 }
1509         }
1510         
1511         return NULL;
1512 }
1513
1514 void clever_numbuts_nla(void){
1515         bActionStrip *strip;
1516         int but=0;
1517                 
1518         /* Determine if an nla strip has been selected */
1519         strip = get_active_nlastrip();
1520         if (!strip)
1521                 return;
1522         
1523         add_numbut(but++, LABEL, "Timeline Range:", 1.0, 18000.0, 0, 0);
1524         add_numbut(but++, NUM|FLO, "Strip Start:", 1.0, 18000.0, &strip->start, "First frame in the timeline");
1525         add_numbut(but++, NUM|FLO, "Strip End:", 1.0, 18000.0, &strip->end, "Last frame in the timeline");
1526         add_numbut(but++, LABEL, "Action Range:", 1.0, 18000.0, 0, 0);
1527         add_numbut(but++, NUM|FLO, "Action Start:", 1.0, 18000.0, &strip->actstart, "First frame of the action to map to the playrange");
1528         add_numbut(but++, NUM|FLO, "Action End:", 1.0, 18000.0, &strip->actend, "Last frame of the action to map to the playrange");
1529         add_numbut(but++, LABEL, "Blending:", 1.0, 18000.0, 0, 0);
1530         add_numbut(but++, NUM|FLO, "Blendin:", 0.0, 18000.0, &strip->blendin, "Number of frames of ease-in");
1531         add_numbut(but++, NUM|FLO, "Blendout:", 0.0, 18000.0, &strip->blendout, "Number of frames of ease-out");
1532         add_numbut(but++, LABEL, "Options:", 1.0, 18000.0, 0, 0);
1533         add_numbut(but++, NUM|FLO, "Repeat:", 0.0001, 18000.0, &strip->repeat, "Number of times the action should repeat");
1534         add_numbut(but++, NUM|FLO, "Stride:", 0.0001, 1000.0, &strip->stridelen, "Distance covered by one complete cycle of the action specified in the Action Range");
1535         {
1536                 /* STUPID HACK BECAUSE NUMBUTS ARE BROKEN WITH MULTIPLE TOGGLES */
1537                 short hold= (strip->flag & ACTSTRIP_HOLDLASTFRAME) ? 1 : 0;
1538                 short frompath=(strip->flag & ACTSTRIP_USESTRIDE) ? 1 : 0;
1539
1540                 add_numbut(but++, TOG|SHO, "Use Path", 0, 0, &frompath, "Plays action based on position on path & stride length.  Only valid for armatures that are parented to a path");
1541                 add_numbut(but++, TOG|SHO, "Hold", 0, 0, &hold, "Toggles whether or not to continue displaying the last frame past the end of the strip");
1542                 add_numbut(but++, TOG|SHO, "Add", 0, 0, &strip->mode, "Toggles additive blending mode");
1543                 
1544                 do_clever_numbuts("Action", but, REDRAW);
1545                 
1546                 /* STUPID HACK BECAUSE NUMBUTS ARE BROKEN WITH MULTIPLE TOGGLES */
1547                 if (hold) strip->flag |= ACTSTRIP_HOLDLASTFRAME;
1548                 else strip->flag &= ~ACTSTRIP_HOLDLASTFRAME;
1549
1550                 if (frompath) strip->flag |= ACTSTRIP_USESTRIDE;
1551                 else strip->flag &= ~ACTSTRIP_USESTRIDE;
1552                 
1553         }
1554
1555         if (strip->end<strip->start)
1556                 strip->end=strip->start;
1557
1558
1559         if (strip->blendin>(strip->end-strip->start))
1560                 strip->blendin = strip->end-strip->start;
1561
1562         if (strip->blendout>(strip->end-strip->start))
1563                 strip->blendout = strip->end-strip->start;
1564
1565         if (strip->blendin > (strip->end-strip->start-strip->blendout))
1566                 strip->blendin = (strip->end-strip->start-strip->blendout);
1567
1568         if (strip->blendout > (strip->end-strip->start-strip->blendin))
1569                 strip->blendout = (strip->end-strip->start-strip->blendin);
1570         
1571         
1572         update_for_newframe();
1573         allqueue (REDRAWNLA, 0);
1574         allqueue (REDRAWVIEW3D, 0);
1575 }       
1576
1577 static void deselect_nlachannels(int test){
1578         int sel = 1;
1579         Base *base;
1580         bConstraintChannel *conchan;
1581
1582         if (test){
1583                 for (base=G.scene->base.first; base; base=base->next){
1584                         /* Check base flags for previous selection */
1585                         if (base->flag & SELECT){
1586                                 sel=0;
1587                                 break;
1588                         }
1589
1590                         /* Check constraint flags for previous selection */
1591                         for (conchan=base->object->constraintChannels.first; conchan; conchan=conchan->next){
1592                                 if (conchan->flag & CONSTRAINT_CHANNEL_SELECT){
1593                                         sel=0;
1594                                         base = G.scene->base.last;
1595                                         break;
1596                                 }
1597                         }
1598                 }
1599         }
1600         else
1601                 sel = 0;
1602
1603         /* Select objects */
1604         for (base=G.scene->base.first; base; base=base->next){
1605                 if (sel){
1606                         if (nla_filter(base, 0))
1607                                 base->flag |= SELECT;
1608                 }
1609                 else
1610                         base->flag &= ~SELECT;
1611
1612                 /* Select constraint channels */
1613                 for (conchan=base->object->constraintChannels.first; conchan; conchan=conchan->next){
1614                         if (sel){
1615                                 if (nla_filter(base, 0))
1616                                         conchan->flag |= CONSTRAINT_CHANNEL_SELECT;
1617                         }
1618                         else
1619                                 conchan->flag &= ~CONSTRAINT_CHANNEL_SELECT;
1620                 }
1621         }       
1622 }
1623
1624 static void delete_nlachannels(void){
1625         Base *base;
1626         bConstraintChannel *conchan, *nextchan;
1627         int sel=0;
1628
1629         /* See if there is anything selected */
1630         for (base = G.scene->base.first; base && (!sel); base=base->next){
1631                 /* Check constraints */
1632                 for (conchan=base->object->constraintChannels.first; conchan; conchan=conchan->next){
1633                         if (conchan->flag & CONSTRAINT_CHANNEL_SELECT){
1634                                 sel = 1;
1635                                 break;
1636                         }
1637                 }
1638         }
1639
1640         if (!sel)
1641                 return;
1642
1643         if (okee ("Delete selected channels")){
1644                 for (base=G.scene->base.first; base; base=base->next){
1645                         for (conchan=base->object->constraintChannels.first; conchan; conchan=nextchan){
1646                                 nextchan = conchan->next;
1647                                 
1648                                 if (conchan->flag & CONSTRAINT_CHANNEL_SELECT){
1649                                         /* If we're the active constraint, unlink us */
1650                                         if (conchan==base->object->activecon)
1651                                                 base->object->activecon = NULL;
1652                                         
1653                                         if (conchan->ipo)
1654                                                 conchan->ipo->id.us--;
1655                                         BLI_freelinkN(&base->object->constraintChannels, conchan);
1656                                 }
1657                         }
1658                 }
1659         }
1660 }