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