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