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