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