SHIFT+S snap to frame for both Action and NLA window.
[blender.git] / source / blender / src / editnla.c
1 /**
2 * $Id$
3 *
4  * ***** BEGIN GPL/BL DUAL LICENSE BLOCK *****
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version. The Blender
10  * Foundation also sells licenses for use in proprietary software under
11  * the Blender License.  See http://www.blender.org/BL/ for information
12  * about this.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software Foundation,
21  * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
22  *
23  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
24  * All rights reserved.
25  *
26  * The Original Code is: all of this file.
27  *
28  * Contributor(s): none yet.
29  *
30  * ***** END GPL/BL DUAL LICENSE BLOCK *****
31 * This file is a horrible mess: An attmept to cram some
32 * final functionality into blender before it is too late.
33 *
34 * Hopefully it can be tidied up at a later date...
35 */
36
37 #include <stdlib.h>
38 #include <string.h>
39 #include <math.h>
40
41 #include "PIL_time.h"
42
43 #include "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 synchronize_action_strips(void)
180 {
181         Base *base;
182         bActionStrip *strip;
183         
184         for (base=G.scene->base.first; base; base=base->next) {
185                 for (strip = base->object->nlastrips.last; strip; strip=strip->prev) {
186                         if (strip->flag & ACTSTRIP_LOCK_ACTION) {
187                                 float actstart, actend;
188                                 
189                                 calc_action_range(strip->act, &actstart, &actend);
190                                 
191                                 if(strip->actstart!=actstart || strip->actend!=actend) {
192                                         float mapping= (strip->end - strip->start)/(strip->actend - strip->actstart);
193                                         
194                                         strip->start+= mapping*(actstart - strip->actstart);
195                                         strip->end+= mapping*(actend - strip->actend);
196                                         
197                                         strip->actstart= actstart;
198                                         strip->actend= actend;
199                                 }
200                         }
201                 }
202         }
203         
204 }
205
206 void reset_action_strips(int val)
207 {
208         Base *base;
209         bActionStrip *strip;
210         
211         for (base=G.scene->base.first; base; base=base->next) {
212                 for (strip = base->object->nlastrips.last; strip; strip=strip->prev) {
213                         if (strip->flag & ACTSTRIP_SELECT) {
214                                 if(val==2) {
215                                         calc_action_range(strip->act, &strip->actstart, &strip->actend);
216                                 }
217                                 else if(val==1) {
218                                         float mapping= (strip->actend - strip->actstart)/(strip->end - strip->start);
219                                         
220                                         strip->end= strip->start + mapping*(strip->end - strip->start);
221                                 }
222                                 base->object->ctime= -1234567.0f;       // eveil! 
223                                 DAG_object_flush_update(G.scene, base->object, OB_RECALC_OB|OB_RECALC_DATA);
224                         }
225                 }
226         }
227         BIF_undo_push("Reset NLA strips");
228         allqueue (REDRAWVIEW3D, 0);
229         allqueue (REDRAWACTION, 0);
230         allqueue (REDRAWNLA, 0);
231 }
232
233 void snap_action_strips(void)
234 {
235         Base *base;
236         bActionStrip *strip;
237         
238         for (base=G.scene->base.first; base; base=base->next) {
239                 for (strip = base->object->nlastrips.last; strip; strip=strip->prev) {
240                         if (strip->flag & ACTSTRIP_SELECT) {
241                                 strip->start= floor(strip->start+0.5);
242                         }
243                 }
244         }
245         BIF_undo_push("Snap NLA strips");
246         allqueue (REDRAWVIEW3D, 0);
247         allqueue (REDRAWACTION, 0);
248         allqueue (REDRAWNLA, 0);
249 }
250
251 void winqreadnlaspace(ScrArea *sa, void *spacedata, BWinEvent *evt)
252 {
253         unsigned short event= evt->event;
254         short val= evt->val;
255         SpaceNla *snla = curarea->spacedata.first;
256         int doredraw= 0;
257         short   mval[2];
258         float dx,dy;
259         int     cfra;
260         short mousebut = L_MOUSE;
261         
262         if (curarea->win==0) return;
263         if (!snla) return;
264         
265         if(val) {
266                 if( uiDoBlocks(&curarea->uiblocks, event)!=UI_NOTHING ) event= 0;
267                 
268                 /* swap mouse buttons based on user preference */
269                 if (U.flag & USER_LMOUSESELECT) {
270                         if (event == LEFTMOUSE) {
271                                 event = RIGHTMOUSE;
272                                 mousebut = L_MOUSE;
273                         } else if (event == RIGHTMOUSE) {
274                                 event = LEFTMOUSE;
275                                 mousebut = R_MOUSE;
276                         }
277                 }
278                 
279                 getmouseco_areawin(mval);
280                 
281                 switch(event) {
282                 case UI_BUT_EVENT:
283                         do_nlabuts(val); // in drawnla.c
284                         break;
285                 
286                 case HOMEKEY:
287                         do_nla_buttons(B_NLAHOME);
288                         break;
289
290                 case EQUALKEY:
291                 case PAGEUPKEY:
292                         shift_nlastrips_up();
293                         break;
294
295                 case MINUSKEY:
296                 case PAGEDOWNKEY:
297                         shift_nlastrips_down();
298                         break;
299
300                 case AKEY:
301                         if (G.qual & LR_SHIFTKEY){
302                                 add_nlablock(mval);
303                                 allqueue (REDRAWNLA, 0);
304                                 allqueue (REDRAWVIEW3D, 0);
305                         }
306                         else{
307                                 if (mval[0]>=NLAWIDTH)
308                                         deselect_nlachannel_keys(1);
309                                 else{
310                                         deselect_nlachannels(1);
311                                         allqueue (REDRAWVIEW3D, 0);
312                                 }
313                                 allqueue (REDRAWNLA, 0);
314                                 allqueue (REDRAWIPO, 0);
315                                 BIF_undo_push("(De)select all NLA");
316                         }
317                         break;
318
319                 case BKEY:
320                         borderselect_nla();
321                         break;
322
323                 case CKEY:
324                         convert_nla(mval);
325                         break;
326                         
327                 case DKEY:
328                         if (G.qual & LR_SHIFTKEY && mval[0]>=NLAWIDTH){
329                                 duplicate_nlachannel_keys();
330                                 update_for_newframe_muted();
331                         }
332                         break;
333
334                 case GKEY:
335                         if (mval[0]>=NLAWIDTH)
336                                 transform_nlachannel_keys ('g', 0);
337                         update_for_newframe_muted();
338                         break;
339
340                 case NKEY:
341                         if(G.qual==0) {
342                                 toggle_blockhandler(curarea, NLA_HANDLER_PROPERTIES, UI_PNL_TO_MOUSE);
343                                 scrarea_queue_winredraw(curarea);
344                         }
345                         break;
346
347                 case SKEY:
348                         if(G.qual==LR_ALTKEY) {
349                                 val= pupmenu("Action Strip Scale%t|Clear Strip Size%x1|Remap Start/End%x2");
350                                 if(val==1)
351                                         reset_action_strips(1);
352                                 else if(val==2)
353                                         reset_action_strips(2);
354                         }
355                         else if(G.qual & LR_SHIFTKEY) {
356                                 if(okee("Snap Strips to Frame"))
357                                         snap_action_strips();
358                         }
359                         else {
360                                 if (mval[0]>=NLAWIDTH)
361                                         transform_nlachannel_keys ('s', 0);
362                                 update_for_newframe_muted();
363                         }
364                         break;
365
366                 case DELKEY:
367                 case XKEY:
368                         if (mval[0]>=NLAWIDTH)
369                                 delete_nlachannel_keys ();
370
371                         update_for_newframe_muted();
372                         break;
373                         
374                 /* LEFTMOUSE and RIGHTMOUSE event codes can be swapped above,
375                  * based on user preference USER_LMOUSESELECT
376                  */
377                 case LEFTMOUSE:
378                         if(view2dmove(LEFTMOUSE))
379                                 break; // only checks for sliders
380                         else if (mval[0]>=snla->v2d.mask.xmin) {
381                                 do {
382                                         getmouseco_areawin(mval);
383                                         
384                                         areamouseco_to_ipoco(G.v2d, mval, &dx, &dy);
385                                         
386                                         cfra= (int)dx;
387                                         if(cfra< 1) cfra= 1;
388                                         
389                                         if( cfra!=CFRA ) {
390                                                 CFRA= cfra;
391                                                 update_for_newframe();
392                                                 force_draw_all(0);
393                                         }
394                                         else PIL_sleep_ms(30);
395                                         
396                                 } while(get_mbut() & mousebut);
397                                 break;
398                         }
399                         /* else pass on! */
400                 case RIGHTMOUSE:
401                         if (mval[0]>=snla->v2d.mask.xmin) {
402                                 if(G.qual & LR_SHIFTKEY)
403                                         mouse_nla(SELECT_INVERT);
404                                 else
405                                         mouse_nla(SELECT_REPLACE);
406                         }
407                         else
408                                 mouse_nlachannels(mval);
409                         break;
410
411                 case PADPLUSKEY:
412                         view2d_zoom(G.v2d, 0.1154, sa->winx, sa->winy);
413                         test_view2d(G.v2d, sa->winx, sa->winy);
414                         view2d_do_locks(curarea, V2D_LOCK_COPY);
415                         doredraw= 1;
416                         break;
417                 case PADMINUS:
418                         view2d_zoom(G.v2d, -0.15, sa->winx, sa->winy);
419                         test_view2d(G.v2d, sa->winx, sa->winy);
420                         view2d_do_locks(curarea, V2D_LOCK_COPY);
421                         doredraw= 1;
422                         break;
423                 case MIDDLEMOUSE:
424                 case WHEELUPMOUSE:
425                 case WHEELDOWNMOUSE:
426                         view2dmove(event);      /* in drawipo.c */
427                         break;
428                 }
429         }
430         
431         if(doredraw) scrarea_queue_winredraw(curarea);
432 }
433
434 static void set_active_strip(Object *ob, bActionStrip *act)
435 {
436         bActionStrip *strip;
437         
438         for (strip = ob->nlastrips.first; strip; strip=strip->next)
439                 strip->flag &= ~ACTSTRIP_ACTIVE;
440         
441         if(act) {
442                 act->flag |= ACTSTRIP_ACTIVE;
443         
444                 if(ob->action!=act->act) {
445                         if(ob->action) ob->action->id.us--;
446                         ob->action= act->act;
447                         ob->action->id.us++;
448                         
449                         allqueue(REDRAWIPO, 0);
450                         allqueue(REDRAWVIEW3D, 0);
451                         allqueue(REDRAWACTION, 0);
452                         allqueue(REDRAWNLA, 0);
453                         ob->ctime= -1234567.0f; // eveil! 
454                         DAG_object_flush_update(G.scene, ob, OB_RECALC_OB|OB_RECALC_DATA);
455                 }
456         }       
457 }
458
459 static void convert_nla(short mval[2])
460 {
461         bActionStrip *strip, *nstrip;
462         Base *base;
463         float x,y;
464         float   ymax, ymin;
465         int sel=0;
466         short event;
467         char str[128];
468         
469         /* Find out what strip we're over */
470         ymax = count_nla_levels() * (NLACHANNELSKIP+NLACHANNELHEIGHT);
471         ymax+= NLACHANNELHEIGHT/2;
472         
473         areamouseco_to_ipoco(G.v2d, mval, &x, &y);
474         
475         for (base=G.scene->base.first; base; base=base->next){
476                 if (nla_filter(base)) {
477                         
478                         /* Area that encloses object name (or ipo) */
479                         ymin=ymax-(NLACHANNELHEIGHT+NLACHANNELSKIP);
480                         ymax=ymin;
481                         
482                         /* Check action ipo */
483                         if (base->object->action) {
484                                 ymin=ymax-(NLACHANNELSKIP+NLACHANNELHEIGHT);
485                                 if (y>=ymin && y<=ymax)
486                                         break;
487                                 ymax=ymin;
488                         }                       
489                                 
490                         /* Check nlastrips */
491                         for (strip=base->object->nlastrips.first; strip; strip=strip->next){
492                                 ymin=ymax-(NLACHANNELSKIP+NLACHANNELHEIGHT);
493                                 if (y>=ymin && y<=ymax){
494                                         sel = 1;
495                                         break;
496                                 }
497                                 ymax=ymin;
498                         }
499                         if (sel)
500                                 break;
501                 }
502         }
503         
504         if (base==0 || base->object->action==NULL)
505                 return;
506         
507         sprintf(str, "Convert Action%%t|%s to NLA Strip%%x1", base->object->action->id.name+2);
508         event = pupmenu(str);
509         
510         switch (event){
511         case 1:
512                 if (base->object->action){
513                         /* Make new actionstrip */
514                         nstrip = MEM_callocN(sizeof(bActionStrip), "bActionStrip");
515                         
516                         deselect_nlachannel_keys(0);
517                         
518                         /* Link the action to the nstrip */
519                         nstrip->act = base->object->action;
520                         nstrip->act->id.us++;
521                         calc_action_range(nstrip->act, &nstrip->actstart, &nstrip->actend);
522                         nstrip->start = nstrip->actstart;
523                         nstrip->end = nstrip->actend;
524                         nstrip->flag = ACTSTRIP_SELECT|ACTSTRIP_LOCK_ACTION;
525                         set_active_strip(base->object, nstrip);
526
527                         nstrip->repeat = 1.0;
528                                                         
529                         BLI_addtail(&base->object->nlastrips, nstrip);
530                         
531                         BIF_undo_push("Convert NLA");
532                         allqueue (REDRAWNLA, 0);
533                 }
534                 
535                 
536                 break;
537         default:
538                 break;
539         }
540 }
541
542
543 static Base *nla_base=NULL;     /* global, bad, bad! put it in nla space later, or recode the 2 functions below (ton) */
544
545 static void add_nla_block(short event)
546 {
547         bAction *act=NULL;
548         bActionStrip *strip;
549         int             cur;
550
551         if (event!=-1){
552                 for (cur = 1, act=G.main->action.first; act; act=act->id.next, cur++){
553                         if (cur==event){
554                                 break;
555                         }
556                 }
557         }
558         
559         /* Bail out if no action was chosen */
560         if (!act){
561                 return;
562         }
563         
564         /* Initialize the new action block */
565         strip = MEM_callocN(sizeof(bActionStrip), "bActionStrip");
566         
567         deselect_nlachannel_keys(0);
568         
569         /* Link the action to the strip */
570         strip->act = act;
571         calc_action_range(strip->act, &strip->actstart, &strip->actend);
572         strip->start = G.scene->r.cfra;         /* could be mval[0] another time... */
573         strip->end = strip->start + (strip->actend-strip->actstart);
574                 /* simple prevention of zero strips */
575         if(strip->start>strip->end-2) 
576                 strip->end= strip->start+100;
577         
578         strip->flag = ACTSTRIP_SELECT|ACTSTRIP_LOCK_ACTION;
579         set_active_strip(nla_base->object, strip);
580         
581         strip->repeat = 1.0;
582         
583         act->id.us++;
584         
585         BLI_addtail(&nla_base->object->nlastrips, strip);
586
587         BIF_undo_push("Add NLA strip");
588 }
589
590 static void add_nla_databrowse_callback(unsigned short val)
591 {
592         /* val is not used, databrowse needs it to optional pass an event */
593         short event;
594         
595         if(nla_base==NULL) return;
596         
597         event= G.snla->menunr;  /* set by databrowse or pupmenu */
598         
599         add_nla_block(event);
600 }
601
602 static void add_nlablock(short mval[2])
603 {
604         /* Make sure we are over an object with action */
605         Base *base;
606         rctf    rectf;
607         float ymin, ymax;
608         float x, y;
609         short event;
610         short nr;
611         char *str;
612
613         areamouseco_to_ipoco(G.v2d, mval, &x, &y);
614         
615         mval[0]-=7;
616         areamouseco_to_ipoco(G.v2d, mval, &rectf.xmin, &rectf.ymin);
617         
618         mval[0]+=14;
619         areamouseco_to_ipoco(G.v2d, mval, &rectf.xmax, &rectf.ymax);
620
621         ymax = count_nla_levels();      
622         ymax*= (NLACHANNELHEIGHT + NLACHANNELSKIP);
623         ymax+= NLACHANNELHEIGHT/2;
624
625         for (base=G.scene->base.first; base; base=base->next){
626                 /* Handle object ipo selection */
627                 if (nla_filter(base)) {
628                         
629                         /* Area that encloses object name (or ipo) */
630                         ymin=ymax-(NLACHANNELHEIGHT+NLACHANNELSKIP);
631
632                         /* Area that encloses action */
633                         if (base->object->action)
634                                 ymin-=(NLACHANNELHEIGHT+NLACHANNELSKIP);
635
636                         /* Area that encloses nla strips */
637                         ymin-=(NLACHANNELHEIGHT+NLACHANNELSKIP)*
638                                 (BLI_countlist(&base->object->nlastrips));
639
640                         /* Test to see the mouse is in an action area */
641                         if (!((ymax < rectf.ymin) || (ymin > rectf.ymax)))
642                                 break;          
643                         
644                         ymax=ymin;
645                 }
646         }       
647         
648         /* global... for the call above, because the NLA system seems not to have an 'active strip' stored */
649         nla_base= base;
650         
651         /* Make sure we have an action */
652         if (!base){
653                 error ("Object has not an Action");
654                 return;
655         }
656         
657         /* Popup action menu */
658         IDnames_to_pupstring(&str, "Add Action", NULL, &G.main->action, (ID *)G.scene, &nr);
659         
660         if(strncmp(str+13, "DataBrow", 8)==0) {
661                 MEM_freeN(str);
662
663                 activate_databrowse((ID *)NULL, ID_AC, 0, 0, &G.snla->menunr, 
664                                                         add_nla_databrowse_callback );
665                 
666                 return;                 
667         }
668         else {
669                 event = pupmenu(str);
670                 MEM_freeN(str);
671                 add_nla_block(event);
672         }
673         
674         /* Ton: this is a callback for databrowse too
675            Hos: no, I don't think it is
676            add_nla_block(0);
677         */
678 }
679
680 /* Left hand side of channels display, selects objects */
681 static void mouse_nlachannels(short mval[2])
682 {
683         bActionStrip *strip= NULL;
684         Base    *base;
685         Object *ob=NULL;
686         float   x,y;
687         int             click, obclick=0;
688         int             wsize;
689         
690         wsize = (count_nla_levels ()*(NLACHANNELHEIGHT+NLACHANNELSKIP));
691         wsize+= NLACHANNELHEIGHT/2;
692
693         areamouseco_to_ipoco(G.v2d, mval, &x, &y);
694         click = (int)floor( ((float)wsize - y) / (NLACHANNELHEIGHT+NLACHANNELSKIP));
695         
696         if (click<0)
697                 return;
698
699         for (base = G.scene->base.first; base; base=base->next){
700                 if (nla_filter(base)) {
701                         ob= base->object;
702                         
703                         /* See if this is a base selected */
704                         if (click==0) {
705                                 obclick= 1;
706                                 break;
707                         }
708                         click--;
709                         
710                         /* See if this is an action */
711                         if (ob->action){
712                                 if (click==0) break;
713                                 click--;
714                         }
715
716                         /* See if this is an nla strip */
717                         if(ob->nlastrips.first) {
718                                 for (strip = ob->nlastrips.first; strip; strip=strip->next){
719                                         if (click==0) break;
720                                         click--;                                
721                                 }
722                                 if (strip && click==0) break;
723                         }
724                 }
725         }
726
727         if (!base)
728                 return;
729
730         /* Handle object strip selection */
731         if (G.qual & LR_SHIFTKEY) {
732                 if (base->flag & SELECT) base->flag &= ~SELECT;
733                 else base->flag |= SELECT;
734         }
735         else {
736                 deselect_nlachannels (0);       // Auto clear
737                 base->flag |= SELECT;
738         }
739         ob->flag= base->flag;
740         
741         if(base!=BASACT) set_active_base(base);
742         
743         /* set action */
744         if(strip)
745                 set_active_strip(ob, strip);
746
747         /* override option for NLA */
748         if(obclick && mval[0]<25) {
749                 ob->nlaflag ^= OB_NLA_OVERRIDE;
750                 ob->ctime= -1234567.0f; // eveil! 
751                 DAG_object_flush_update(G.scene, ob, OB_RECALC_OB|OB_RECALC_DATA);
752         }
753         
754         allqueue(REDRAWIPO, 0);
755         allqueue(REDRAWVIEW3D, 0);
756         allqueue(REDRAWACTION, 0);
757         allqueue(REDRAWNLA, 0);
758         
759 }
760
761 void deselect_nlachannel_keys (int test)
762 {
763         Base                    *base;
764         int                             sel=1;
765         bActionChannel  *chan;
766         bActionStrip    *strip;
767         bConstraintChannel *conchan;
768         
769         /* Determine if this is selection or deselection */
770         if (test){
771                 for (base=G.scene->base.first; base && sel; base=base->next){
772                         
773                         /* Test object ipos */
774                         if (is_ipo_key_selected(base->object->ipo)){
775                                 sel = 0;
776                                 break;
777                         }
778                         
779                         /* Test object constraint ipos */
780                         if (sel){
781                                 for (conchan=base->object->constraintChannels.first; conchan; conchan=conchan->next){
782                                         if (is_ipo_key_selected(conchan->ipo)){
783                                                 sel=0;
784                                                 break;
785                                         }
786                                 }
787                         }
788                         
789                         /* Test action ipos */
790                         if (sel){
791                                 if (base->object->action){
792                                         for (chan=base->object->action->chanbase.first; chan; chan=chan->next){
793                                                 if (is_ipo_key_selected(chan->ipo)){
794                                                         sel=0;
795                                                         break;
796                                                 }
797
798                                                 /* Test action constraints */
799                                                 if (sel){
800                                                         for (conchan=chan->constraintChannels.first; conchan; conchan=conchan->next){
801                                                                 if (is_ipo_key_selected(conchan->ipo)){
802                                                                         sel=0;
803                                                                         break;
804                                                                 }
805                                                         }
806                                                 }
807                                         }
808                                 }
809                         }
810                         
811                         /* Test NLA strips */
812                         if (sel){
813                                 for (strip=base->object->nlastrips.first; strip; strip=strip->next){
814                                         if (strip->flag & ACTSTRIP_SELECT){
815                                                 sel = 0;
816                                                 break;
817                                         }
818                                 }
819                         }
820                 }
821         }
822         else
823                 sel=0;
824         
825         
826         /* Set the flags */
827         for (base=G.scene->base.first; base; base=base->next){
828                 
829                 /* Set the object ipos */
830                 set_ipo_key_selection(base->object->ipo, sel);
831                 
832                 /* Set the object constraint ipos */
833                 for (conchan=base->object->constraintChannels.first; conchan; conchan=conchan->next){
834                         set_ipo_key_selection(conchan->ipo, sel);                       
835                 }
836
837                 /* Set the action ipos */
838                 if (base->object->action){
839                         for (chan=base->object->action->chanbase.first; chan; chan=chan->next){
840                                 set_ipo_key_selection(chan->ipo, sel);
841                                 /* Set the action constraint ipos */
842                                 for (conchan=chan->constraintChannels.first; conchan; conchan=conchan->next)
843                                         set_ipo_key_selection(conchan->ipo, sel);
844                         }
845                 }
846                 
847                 /* Set the nlastrips */
848                 for (strip=base->object->nlastrips.first; strip; strip=strip->next){
849                         if (sel)
850                                 strip->flag |= ACTSTRIP_SELECT;
851                         else
852                                 strip->flag &= ~ACTSTRIP_SELECT;
853                 }
854         }
855 }
856
857 /* very bad call! */
858 static void recalc_all_ipos(void)
859 {
860         Ipo *ipo;
861         IpoCurve *icu;
862         
863         /* Go to each ipo */
864         for (ipo=G.main->ipo.first; ipo; ipo=ipo->id.next){
865                 for (icu = ipo->curve.first; icu; icu=icu->next){
866                         sort_time_ipocurve(icu);
867                         testhandles_ipocurve(icu);
868                 }
869         }
870 }
871
872 void transform_nlachannel_keys(int mode, int dummy)
873 {
874         Base *base;
875         TransVert *tv;
876         bActionChannel *chan;
877         bActionStrip *strip;
878         bConstraintChannel *conchan;
879         float   sval[2], cval[2], lastcval[2];
880         float   fac=0.0F;
881         float   deltax, startx;
882         int i;
883         int             loop=1;
884         int             tvtot=0;
885         int             invert=0, firsttime=1;
886         short   mvals[2], mvalc[2];
887         short   cancel=0;
888         char    str[256];
889
890         /* Ensure that partial selections result in beztriple selections */
891         for (base=G.scene->base.first; base; base=base->next){
892
893                 /* Check object ipos */
894                 i= fullselect_ipo_keys(base->object->ipo);
895                 if(i) base->flag |= BA_HAS_RECALC_OB;
896                 tvtot+=i;
897                 
898                 /* Check object constraint ipos */
899                 for(conchan=base->object->constraintChannels.first; conchan; conchan=conchan->next)
900                         tvtot+=fullselect_ipo_keys(conchan->ipo);                       
901                 
902                 /* Check action ipos */
903                 if (base->object->action){
904                         /* exclude if strip is selected too */
905                         for (strip=base->object->nlastrips.first; strip; strip=strip->next){
906                                 if (strip->flag & ACTSTRIP_SELECT)
907                                         if(strip->act==base->object->action)
908                                                 break;
909                         }
910                         if(strip==NULL) {
911                                 
912                                 for (chan=base->object->action->chanbase.first; chan; chan=chan->next){
913                                         i= fullselect_ipo_keys(chan->ipo);
914                                         if(i) base->flag |= BA_HAS_RECALC_OB|BA_HAS_RECALC_DATA;
915                                         tvtot+=i;
916                                         
917                                         /* Check action constraint ipos */
918                                         for (conchan=chan->constraintChannels.first; conchan; conchan=conchan->next)
919                                                 tvtot+=fullselect_ipo_keys(conchan->ipo);
920                                 }
921                         }               
922                 }
923
924                 /* Check nlastrips */
925                 for (strip=base->object->nlastrips.first; strip; strip=strip->next){
926                         if (strip->flag & ACTSTRIP_SELECT) {
927                                 base->flag |= BA_HAS_RECALC_OB|BA_HAS_RECALC_DATA;
928                                 tvtot+=2;
929                         }
930                 }
931         }
932         
933         /* If nothing is selected, bail out */
934         if (!tvtot)
935                 return;
936         
937         
938         /* Build the transvert structure */
939         tv = MEM_callocN (sizeof(TransVert) * tvtot, "transVert");
940         tvtot=0;
941         for (base=G.scene->base.first; base; base=base->next){
942                 /* Manipulate object ipos */
943                 tvtot=add_trans_ipo_keys(base->object->ipo, tv, tvtot);
944
945                 /* Manipulate object constraint ipos */
946                 for (conchan=base->object->constraintChannels.first; conchan; conchan=conchan->next)
947                         tvtot=add_trans_ipo_keys(conchan->ipo, tv, tvtot);
948
949                 /* Manipulate action ipos */
950                 if (base->object->action){
951                         /* exclude if strip is selected too */
952                         for (strip=base->object->nlastrips.first; strip; strip=strip->next){
953                                 if (strip->flag & ACTSTRIP_SELECT)
954                                         if(strip->act==base->object->action)
955                                                 break;
956                         }
957                         if(strip==NULL) {
958                                 
959                                 for (chan=base->object->action->chanbase.first; chan; chan=chan->next){
960                                         tvtot=add_trans_ipo_keys(chan->ipo, tv, tvtot);
961
962                                         /* Manipulate action constraint ipos */
963                                         for (conchan=chan->constraintChannels.first; conchan; conchan=conchan->next)
964                                                 tvtot=add_trans_ipo_keys(conchan->ipo, tv, tvtot);
965                                 }
966                         }
967                 }
968
969                 /* Manipulate nlastrips */
970                 for (strip=base->object->nlastrips.first; strip; strip=strip->next){
971                         if (strip->flag & ACTSTRIP_SELECT){
972                                 tv[tvtot+0].val=&strip->start;
973                                 tv[tvtot+1].val=&strip->end;
974                                 
975                                 tv[tvtot+0].oldval = strip->start;
976                                 tv[tvtot+1].oldval = strip->end;
977                                 
978                                 tvtot+=2;
979                         }
980                 }
981         }
982         
983         /* Do the event loop */
984         //      cent[0] = curarea->winx + (G.snla->v2d.hor.xmax)/2;
985         //      cent[1] = curarea->winy + (G.snla->v2d.hor.ymax)/2;
986         
987         //      areamouseco_to_ipoco(cent, &cenf[0], &cenf[1]);
988         
989         getmouseco_areawin (mvals);
990         areamouseco_to_ipoco(G.v2d, mvals, &sval[0], &sval[1]);
991         
992         startx=sval[0];
993         while (loop) {
994                 /*              Get the input */
995                 /*              If we're cancelling, reset transformations */
996                 /*                      Else calc new transformation */
997                 /*              Perform the transformations */
998                 while (qtest()) {
999                         short val;
1000                         unsigned short event= extern_qread(&val);
1001                         
1002                         if (val) {
1003                                 switch (event) {
1004                                 case LEFTMOUSE:
1005                                 case SPACEKEY:
1006                                 case RETKEY:
1007                                         loop=0;
1008                                         break;
1009                                 case XKEY:
1010                                         break;
1011                                 case ESCKEY:
1012                                 case RIGHTMOUSE:
1013                                         cancel=1;
1014                                         loop=0;
1015                                         break;
1016                                 default:
1017                                         arrows_move_cursor(event);
1018                                         break;
1019                                 };
1020                         }
1021                 }
1022                 
1023                 if (cancel) {
1024                         for (i=0; i<tvtot; i++) {
1025                                 if (tv[i].loc){
1026                                         tv[i].loc[0]=tv[i].oldloc[0];
1027                                         tv[i].loc[1]=tv[i].oldloc[1];
1028                                 }
1029                                 if (tv[i].val)
1030                                         tv[i].val[0]=tv[i].oldval;
1031                         }
1032                 }
1033                 else {
1034                         getmouseco_areawin (mvalc);
1035                         areamouseco_to_ipoco(G.v2d, mvalc, &cval[0], &cval[1]);
1036                         
1037                         if (!firsttime && lastcval[0]==cval[0] && lastcval[1]==cval[1]) {
1038                                 PIL_sleep_ms(1);
1039                         }
1040                         else {
1041                                 for (i=0; i<tvtot; i++){
1042                                         if (tv[i].loc)
1043                                                 tv[i].loc[0]=tv[i].oldloc[0];
1044                                         if (tv[i].val)
1045                                                 tv[i].val[0]=tv[i].oldval;
1046                                         
1047                                         switch (mode){
1048                                         case 'g':
1049                                                 deltax = cval[0]-sval[0];
1050                                                 fac= deltax;
1051                                                 
1052                                                 apply_keyb_grid(&fac, 0.0F, 1.0F, 0.1F, U.flag & USER_AUTOGRABGRID);
1053                                                 
1054                                                 if (tv[i].loc)
1055                                                         tv[i].loc[0]+=fac;
1056                                                 if (tv[i].val)
1057                                                         tv[i].val[0]+=fac;
1058                                                 break;
1059                                         case 's': 
1060                                                 startx=mvals[0]-(NLAWIDTH/2+(curarea->winrct.xmax-curarea->winrct.xmin)/2);
1061                                                 deltax=mvalc[0]-(NLAWIDTH/2+(curarea->winrct.xmax-curarea->winrct.xmin)/2);
1062                                                 fac= (float)fabs(deltax/startx);
1063                                                 
1064                                                 apply_keyb_grid(&fac, 0.0F, 0.2F, 0.1F, U.flag & USER_AUTOSIZEGRID);
1065                                                 
1066                                                 if (invert){
1067                                                         if (i % 03 == 0){
1068                                                                 memcpy (tv[i].loc, tv[i].oldloc, sizeof(tv[i+2].oldloc));
1069                                                         }
1070                                                         if (i % 03 == 2){
1071                                                                 memcpy (tv[i].loc, tv[i].oldloc, sizeof(tv[i-2].oldloc));
1072                                                         }
1073                                                         
1074                                                         fac*=-1;
1075                                                 }
1076                                                 startx= (G.scene->r.cfra);
1077                                                 
1078                                                 if (tv[i].loc){
1079                                                         tv[i].loc[0]-= startx;
1080                                                         tv[i].loc[0]*=fac;
1081                                                         tv[i].loc[0]+= startx;
1082                                                 }
1083                                                 if (tv[i].val){
1084                                                         tv[i].val[0]-= startx;
1085                                                         tv[i].val[0]*=fac;
1086                                                         tv[i].val[0]+= startx;
1087                                                 }
1088                                                 
1089                                                 break;
1090                                         }
1091                                 }
1092                         }
1093                         
1094                         if (mode=='s'){
1095                                 sprintf(str, "sizeX: %.3f", fac);
1096                                 headerprint(str);
1097                         }
1098                         else if (mode=='g'){
1099                                 sprintf(str, "deltaX: %.3f", fac);
1100                                 headerprint(str);
1101                         }
1102                         
1103                         if (G.snla->lock) {
1104                                 for (base=G.scene->base.first; base; base=base->next){
1105                                         if(base->flag & BA_HAS_RECALC_OB)
1106                                                 base->object->recalc |= OB_RECALC_OB;
1107                                         if(base->flag & BA_HAS_RECALC_DATA)
1108                                                 base->object->recalc |= OB_RECALC_DATA;
1109                                         
1110                                         if(base->object->recalc) base->object->ctime= -1234567.0f;      // eveil! 
1111                                 }
1112                                 
1113                                 DAG_scene_flush_update(G.scene, screen_view3d_layers());
1114                                 
1115                 allqueue (REDRAWVIEW3D, 0);
1116                                 allqueue (REDRAWNLA, 0);
1117                                 allqueue (REDRAWIPO, 0);
1118                                 force_draw_all(0);
1119                         }
1120                         else {
1121                                 addqueue (curarea->win, REDRAWALL, 0);
1122                                 force_draw(0);
1123                         }
1124                 }
1125                 
1126                 lastcval[0]= cval[0];
1127                 lastcval[1]= cval[1];
1128                 firsttime= 0;
1129         }
1130         
1131         synchronize_action_strips();
1132         
1133         /* cleanup */
1134         for (base=G.scene->base.first; base; base=base->next)
1135                 base->flag &= ~(BA_HAS_RECALC_OB|BA_HAS_RECALC_DATA);
1136         
1137         if(cancel==0) BIF_undo_push("Select all NLA");
1138         recalc_all_ipos();      // bad
1139         allqueue (REDRAWVIEW3D, 0);
1140         allqueue (REDRAWNLA, 0);
1141         allqueue (REDRAWIPO, 0);
1142         MEM_freeN (tv);
1143 }
1144
1145 void delete_nlachannel_keys(void)
1146 {
1147         Base *base;
1148         bActionChannel *chan;
1149         bConstraintChannel *conchan;
1150         bActionStrip *strip, *nextstrip;
1151         
1152         if (!okee("Erase selected keys"))
1153                 return;
1154         
1155         for (base = G.scene->base.first; base; base=base->next){
1156
1157                 /* Delete object ipos */
1158                 delete_ipo_keys(base->object->ipo);
1159                 
1160                 /* Delete object constraint keys */
1161                 for(conchan=base->object->constraintChannels.first; conchan; conchan=conchan->next)
1162                         delete_ipo_keys(conchan->ipo);
1163
1164                 /* Delete NLA strips */
1165                 for (strip = base->object->nlastrips.first; strip; strip=nextstrip){
1166                         nextstrip=strip->next;
1167                         if (strip->flag & ACTSTRIP_SELECT){
1168                                 free_actionstrip(strip);
1169                                 BLI_remlink(&base->object->nlastrips, strip);
1170                                 MEM_freeN(strip);
1171                         }
1172                 }
1173                 
1174                 /* Delete action ipos */
1175                 if (base->object->action){
1176                         for (chan=base->object->action->chanbase.first; chan; chan=chan->next){
1177                                 delete_ipo_keys(chan->ipo);
1178                                 /* Delete action constraint keys */
1179                                 for(conchan=chan->constraintChannels.first; conchan; conchan=conchan->next)
1180                                         delete_ipo_keys(conchan->ipo);
1181                         }
1182                 }
1183         }
1184         
1185         synchronize_action_strips();
1186         
1187         BIF_undo_push("Delete NLA keys");
1188         recalc_all_ipos();      // bad
1189         allspace(REMAKEIPO,0);
1190         allqueue (REDRAWVIEW3D, 0);
1191         allqueue(REDRAWNLA, 0);
1192         allqueue(REDRAWIPO, 0);
1193 }
1194
1195 void duplicate_nlachannel_keys(void)
1196 {
1197         Base *base;
1198         bActionChannel *chan;
1199         bConstraintChannel *conchan;
1200         bActionStrip *strip, *laststrip;
1201         
1202         /* Find selected items */
1203         for (base = G.scene->base.first; base; base=base->next){
1204                 /* Duplicate object keys */
1205                 duplicate_ipo_keys(base->object->ipo);
1206                 
1207                 /* Duplicate object constraint keys */
1208                 for(conchan=base->object->constraintChannels.first; conchan; conchan=conchan->next)
1209                         duplicate_ipo_keys(conchan->ipo);
1210
1211                 /* Duplicate nla strips */
1212                 laststrip = base->object->nlastrips.last;
1213                 for (strip=base->object->nlastrips.first; strip; strip=strip->next){
1214                         if (strip->flag & ACTSTRIP_SELECT){
1215                                 bActionStrip *newstrip;
1216                                 
1217                                 copy_actionstrip(&newstrip, &strip);
1218                                 
1219                                 BLI_addtail(&base->object->nlastrips, newstrip);
1220                                 
1221                                 strip->flag &= ~ACTSTRIP_SELECT;
1222                                 newstrip->flag |= ACTSTRIP_SELECT;
1223                                 set_active_strip(base->object, newstrip);
1224
1225                         }
1226                         if (strip==laststrip)
1227                                 break;
1228                 }
1229                 
1230                 /* Duplicate actionchannel keys */
1231                 if (base->object->action){
1232                         for (chan=base->object->action->chanbase.first; chan; chan=chan->next){
1233                                 duplicate_ipo_keys(chan->ipo);
1234                                 /* Duplicate action constraint keys */
1235                                 for(conchan=chan->constraintChannels.first; conchan; conchan=conchan->next)
1236                                         duplicate_ipo_keys(conchan->ipo);
1237                         }
1238                 }
1239         }
1240         
1241         BIF_undo_push("Duplicate NLA");
1242         transform_nlachannel_keys ('g', 0);
1243 }
1244
1245 void borderselect_nla(void)
1246
1247         Base *base;
1248         rcti rect;
1249         rctf rectf;
1250         int  val, selectmode;
1251         short   mval[2];
1252         float   ymin, ymax;
1253         bActionStrip *strip;
1254         bConstraintChannel *conchan;
1255         
1256         if ( (val = get_border (&rect, 3)) ){
1257     if (val == LEFTMOUSE)
1258       selectmode = SELECT_ADD;
1259     else
1260       selectmode = SELECT_SUBTRACT;
1261
1262                 mval[0]= rect.xmin;
1263                 mval[1]= rect.ymin+2;
1264                 areamouseco_to_ipoco(G.v2d, mval, &rectf.xmin, &rectf.ymin);
1265                 mval[0]= rect.xmax;
1266                 mval[1]= rect.ymax-2;
1267                 areamouseco_to_ipoco(G.v2d, mval, &rectf.xmax, &rectf.ymax);
1268                 
1269                 ymax = count_nla_levels();
1270                 ymax*= (NLACHANNELHEIGHT+NLACHANNELSKIP);
1271                 ymax+= (NLACHANNELHEIGHT+NLACHANNELSKIP)/2;
1272         
1273                 for (base=G.scene->base.first; base; base=base->next){
1274                         if (nla_filter(base)) {
1275                                 
1276                                 ymin=ymax-(NLACHANNELHEIGHT+NLACHANNELSKIP);
1277                                 
1278                                 /* Check object ipos */
1279                                 if (base->object->ipo){
1280                                         if (!((ymax < rectf.ymin) || (ymin > rectf.ymax)))
1281                                                 borderselect_ipo_key(base->object->ipo, rectf.xmin, rectf.xmax,
1282                                  selectmode);
1283                                 }
1284                                 /* Check object constraint ipos */
1285                                 for (conchan=base->object->constraintChannels.first; conchan; conchan=conchan->next){
1286                                         if (!((ymax < rectf.ymin) || (ymin > rectf.ymax)))
1287                                                 borderselect_ipo_key(conchan->ipo, rectf.xmin, rectf.xmax,
1288                                  selectmode);
1289                                 }
1290                                 
1291                                 ymax=ymin;
1292
1293                                 /* Check action ipos */
1294                                 if (base->object->action){
1295                                         bActionChannel *chan;
1296                                         float xmin, xmax;
1297                                         
1298                                         ymin=ymax-(NLACHANNELHEIGHT+NLACHANNELSKIP);
1299                                         
1300                                         /* if action is mapped in NLA, it returns a correction */
1301                                         xmin= get_action_frame(base->object, rectf.xmin);
1302                                         xmax= get_action_frame(base->object, rectf.xmax);
1303                                         
1304                                         if (!((ymax < rectf.ymin) || (ymin > rectf.ymax))){
1305                                                 for (chan=base->object->action->chanbase.first; chan; chan=chan->next){
1306                                                         borderselect_ipo_key(chan->ipo, xmin, xmax, selectmode);
1307                                                         /* Check action constraint ipos */
1308                                                         for (conchan=chan->constraintChannels.first; conchan; conchan=conchan->next)
1309                                                                 borderselect_ipo_key(conchan->ipo, xmin, xmax, selectmode);
1310                                                 }
1311                                         }
1312
1313                                         ymax=ymin;
1314                                 }       /* End of if action */
1315                                 
1316                                 /* Skip nlastrips */
1317                                 for (strip=base->object->nlastrips.first; strip; strip=strip->next){
1318                                         ymin=ymax-(NLACHANNELHEIGHT+NLACHANNELSKIP);
1319                                         //
1320                                         if (!((ymax < rectf.ymin) || (ymin > rectf.ymax))){
1321                                                 if (!((rectf.xmax<strip->start) || (rectf.xmin>strip->end))){
1322                                                         if (val==1)
1323                                                                 strip->flag |= ACTSTRIP_SELECT;
1324                                                         else
1325                                                                 strip->flag &= ~ACTSTRIP_SELECT;
1326                                                 }
1327                                         }
1328                                         
1329                                         ymax=ymin;
1330                                 }
1331                         }
1332                 }       
1333                 BIF_undo_push("Border select NLA");
1334                 allqueue(REDRAWNLA, 0);
1335                 allqueue(REDRAWACTION, 0);
1336                 allqueue(REDRAWIPO, 0);
1337         }
1338 }
1339
1340 /* right hand side of window, does ipokeys, actionkeys or strips */
1341 static void mouse_nla(int selectmode)
1342 {
1343         Base *base;
1344         bAction *act;
1345         bActionChannel *chan;
1346         bActionStrip *rstrip;
1347         bConstraintChannel *conchan;
1348         float   selx;
1349         short   mval[2];
1350         short sel, isdone=0;
1351         
1352         getmouseco_areawin (mval);
1353         
1354         /* Try object ipo or ob-constraint ipo selection */
1355         base= get_nearest_nlachannel_ob_key(&selx, &sel);
1356         if (base) {
1357                 isdone= 1;
1358                 
1359                 if (selectmode == SELECT_REPLACE){
1360                         deselect_nlachannel_keys(0);
1361                         selectmode = SELECT_ADD;
1362                 }
1363                 
1364                 select_ipo_key(base->object->ipo, selx, selectmode);
1365                 
1366                 /* Try object constraint selection */
1367                 for (conchan=base->object->constraintChannels.first; conchan; conchan=conchan->next)
1368                         select_ipo_key(conchan->ipo, selx, selectmode);
1369         }
1370         else {
1371                 /* Try action ipo selection */
1372                 act= get_nearest_nlachannel_ac_key(&selx, &sel);
1373                 if (act) {
1374                         isdone= 1;
1375                         
1376                         if (selectmode == SELECT_REPLACE){
1377                                 deselect_nlachannel_keys(0);
1378                                 selectmode = SELECT_ADD;
1379                         }
1380                         
1381                         for (chan=act->chanbase.first; chan; chan=chan->next) {
1382                                 select_ipo_key(chan->ipo, selx, selectmode);
1383                                 /* Try action constraint selection */
1384                                 for (conchan=chan->constraintChannels.first; conchan; conchan=conchan->next)
1385                                         select_ipo_key(conchan->ipo, selx, selectmode);
1386                         }
1387                 }
1388                 else {
1389         
1390                         /* Try nla strip selection */
1391                         base= get_nearest_nlastrip(&rstrip, &sel);
1392                         if (base){
1393                                 isdone= 1;
1394                                 
1395                                 if (!(G.qual & LR_SHIFTKEY)){
1396                                         deselect_nlachannel_keys(0);
1397                                         sel = 0;
1398                                 }
1399                                 
1400                                 if (sel)
1401                                         rstrip->flag &= ~ACTSTRIP_SELECT;
1402                                 else
1403                                         rstrip->flag |= ACTSTRIP_SELECT;
1404                                 
1405                                 set_active_strip(base->object, rstrip);
1406                                 
1407                                 if(base!=BASACT) set_active_base(base);
1408                         }
1409                 }
1410         }
1411         
1412         if(isdone) {
1413                 std_rmouse_transform(transform_nlachannel_keys);
1414                 
1415                 allqueue(REDRAWIPO, 0);
1416                 allqueue(REDRAWVIEW3D, 0);
1417                 allqueue(REDRAWNLA, 0);
1418         }
1419 }
1420
1421 /* This function is currently more complicated than it seems like it should be.
1422 * However, this will be needed once the nla strip timeline is more complex */
1423 static Base *get_nearest_nlastrip (bActionStrip **rstrip, short *sel)
1424 {
1425         Base *base, *firstbase=NULL;
1426         bActionStrip *strip, *firststrip=NULL, *foundstrip=NULL;
1427         rctf    rectf;
1428         float ymin, ymax;
1429         short mval[2];
1430         short foundsel = 0;
1431
1432         getmouseco_areawin (mval);
1433         
1434         mval[0]-=7;
1435         areamouseco_to_ipoco(G.v2d, mval, &rectf.xmin, &rectf.ymin);
1436         
1437         mval[0]+=14;
1438         areamouseco_to_ipoco(G.v2d, mval, &rectf.xmax, &rectf.ymax);
1439         
1440         ymax = count_nla_levels();
1441         ymax*=(NLACHANNELHEIGHT + NLACHANNELSKIP);
1442         ymax+= NLACHANNELHEIGHT/2;
1443         
1444         for (base = G.scene->base.first; base; base=base->next){
1445                 if (nla_filter(base)) {
1446                         
1447                         /* Skip object ipos */
1448                         ymax-=(NLACHANNELHEIGHT+NLACHANNELSKIP);
1449
1450                         /* Skip action ipos */
1451                         if (base->object->action)
1452                                 ymax-=(NLACHANNELHEIGHT+NLACHANNELSKIP);
1453                         
1454                         /* the strips */
1455                         for (strip=base->object->nlastrips.first; strip; strip=strip->next){
1456                                 ymin=ymax-(NLACHANNELHEIGHT+NLACHANNELSKIP);
1457                                 /* Do Ytest */
1458                                 if (!((ymax < rectf.ymin) || (ymin > rectf.ymax))){
1459                                         /* Do XTest */
1460                                         if (!((rectf.xmax<strip->start) || (rectf.xmin>strip->end))){
1461                                                 if (!firstbase){
1462                                                         firstbase=base;
1463                                                         firststrip=strip;
1464                                                         *sel = strip->flag & ACTSTRIP_SELECT;
1465                                                 }
1466                                                 
1467                                                 if (strip->flag & ACTSTRIP_SELECT){ 
1468                                                         if (!foundsel){
1469                                                                 foundsel=1;
1470                                                                 foundstrip = strip;
1471                                                         }
1472                                                 }
1473                                                 else if (foundsel && strip != foundstrip){
1474                                                         *rstrip=strip;
1475                                                         *sel = 0;
1476                                                         return base;
1477                                                 }
1478                                         }
1479                                 }
1480                                 ymax=ymin;
1481                         }
1482                 }
1483         }
1484         *rstrip=firststrip;
1485         return firstbase;
1486 }
1487
1488 static Base *get_nearest_nlachannel_ob_key (float *index, short *sel)
1489 {
1490         Base *base;
1491         IpoCurve *icu;
1492         Base *firstbase=NULL;
1493         bConstraintChannel *conchan;
1494         int     foundsel=0;
1495         float firstvertx=-1, foundx=-1;
1496         int i;
1497         short mval[2];
1498         float ymin, ymax;
1499         rctf    rectf;
1500         
1501         *index=0;
1502         
1503         getmouseco_areawin (mval);
1504         
1505         mval[0]-=7;
1506         areamouseco_to_ipoco(G.v2d, mval, &rectf.xmin, &rectf.ymin);
1507         
1508         mval[0]+=14;
1509         areamouseco_to_ipoco(G.v2d, mval, &rectf.xmax, &rectf.ymax);
1510         
1511         ymax = count_nla_levels();
1512         
1513         ymax*= (NLACHANNELHEIGHT + NLACHANNELSKIP);
1514         ymax+= NLACHANNELHEIGHT/2;
1515         
1516         *sel=0;
1517         
1518         for (base=G.scene->base.first; base; base=base->next){
1519                 if (nla_filter(base)) {
1520                         
1521                         ymin=ymax-(NLACHANNELHEIGHT+NLACHANNELSKIP);
1522                         
1523                         /* Handle object ipo selection */
1524                         if (base->object->ipo){
1525                                 if (!((ymax < rectf.ymin) || (ymin > rectf.ymax))){
1526                                         for (icu=base->object->ipo->curve.first; icu; icu=icu->next){
1527                                                 for (i=0; i<icu->totvert; i++){
1528                                                         if (icu->bezt[i].vec[1][0] > rectf.xmin && icu->bezt[i].vec[1][0] <= rectf.xmax ){
1529                                                                 if (!firstbase){
1530                                                                         firstbase=base;
1531                                                                         firstvertx=icu->bezt[i].vec[1][0];
1532                                                                         *sel = icu->bezt[i].f2 & 1;     
1533                                                                 }
1534                                                                 
1535                                                                 if (icu->bezt[i].f2 & 1){ 
1536                                                                         if (!foundsel){
1537                                                                                 foundsel=1;
1538                                                                                 foundx = icu->bezt[i].vec[1][0];
1539                                                                         }
1540                                                                 }
1541                                                                 else if (foundsel && icu->bezt[i].vec[1][0] != foundx){
1542                                                                         *index=icu->bezt[i].vec[1][0];
1543                                                                         *sel = 0;
1544                                                                         return base;
1545                                                                 }
1546                                                         }
1547                                                 }
1548                                         }
1549                                 }
1550                         }
1551                         /* Handle object constraint ipos */
1552                         for (conchan=base->object->constraintChannels.first; conchan; conchan=conchan->next){
1553                                 if ( !((ymax < rectf.ymin) || (ymin > rectf.ymax)) && conchan->ipo){
1554                                         for (icu=conchan->ipo->curve.first; icu; icu=icu->next){
1555                                                 for (i=0; i<icu->totvert; i++){
1556                                                         if (icu->bezt[i].vec[1][0] > rectf.xmin && icu->bezt[i].vec[1][0] <= rectf.xmax ){
1557                                                                 if (!firstbase){
1558                                                                         firstbase=base;
1559                                                                         firstvertx=icu->bezt[i].vec[1][0];
1560                                                                         *sel = icu->bezt[i].f2 & 1;     
1561                                                                 }
1562                                                                 
1563                                                                 if (icu->bezt[i].f2 & 1){ 
1564                                                                         if (!foundsel){
1565                                                                                 foundsel=1;
1566                                                                                 foundx = icu->bezt[i].vec[1][0];
1567                                                                         }
1568                                                                 }
1569                                                                 else if (foundsel && icu->bezt[i].vec[1][0] != foundx){
1570                                                                         *index=icu->bezt[i].vec[1][0];
1571                                                                         *sel = 0;
1572                                                                         return base;
1573                                                                 }
1574                                                         }
1575                                                 }
1576                                         }
1577                                 }
1578                         }
1579
1580                         ymax=ymin;
1581                         
1582                         /* Skip action ipos */
1583                         if (base->object->action){
1584                                 ymax-=(NLACHANNELHEIGHT+NLACHANNELSKIP);
1585                         }
1586                         /* Skip nlastrips */
1587                         ymax-=(NLACHANNELHEIGHT+NLACHANNELSKIP)*BLI_countlist(&base->object->nlastrips);
1588                 }
1589         }       
1590         
1591         *index=firstvertx;
1592         return firstbase;
1593 }
1594
1595 static bAction *get_nearest_nlachannel_ac_key (float *index, short *sel)
1596 {
1597         Base *base;
1598         IpoCurve *icu;
1599         bAction *firstact=NULL;
1600         bActionChannel *chan;
1601         bConstraintChannel *conchan;
1602         rctf    rectf;
1603         float firstvert=-1, foundx=-1;
1604         float ymin, ymax, xmin, xmax;
1605         int i;
1606         int     foundsel=0;
1607         short mval[2];
1608         
1609         *index=0;
1610         
1611         getmouseco_areawin (mval);
1612         
1613         mval[0]-=7;
1614         areamouseco_to_ipoco(G.v2d, mval, &rectf.xmin, &rectf.ymin);
1615         
1616         mval[0]+=14;
1617         areamouseco_to_ipoco(G.v2d, mval, &rectf.xmax, &rectf.ymax);
1618         
1619         ymax = count_nla_levels();
1620         
1621         ymax*= (NLACHANNELHEIGHT + NLACHANNELSKIP);
1622         ymax+= NLACHANNELHEIGHT/2;
1623         
1624         *sel=0;
1625         
1626         for (base=G.scene->base.first; base; base=base->next){
1627                 /* Handle object ipo selection */
1628                 if (nla_filter(base)) {
1629                         
1630                         /* Skip object ipo and ob-constraint ipo */
1631                         ymin=ymax-(NLACHANNELHEIGHT+NLACHANNELSKIP);
1632                         ymax=ymin;
1633                         
1634                         /* Handle action ipos */
1635                         if (base->object->action){
1636                                 bAction *act= base->object->action;
1637                                 
1638                                 /* if action is mapped in NLA, it returns a correction */
1639                                 xmin= get_action_frame(base->object, rectf.xmin);
1640                                 xmax= get_action_frame(base->object, rectf.xmax);
1641                                 
1642                                 ymin=ymax-(NLACHANNELHEIGHT+NLACHANNELSKIP);
1643                                 if (!((ymax < rectf.ymin) || (ymin > rectf.ymax))){
1644                                         for (chan=act->chanbase.first; chan; chan=chan->next){
1645                                                 if(chan->ipo) {
1646                                                         for (icu=chan->ipo->curve.first; icu; icu=icu->next){
1647                                                                 for (i=0; i<icu->totvert; i++){
1648                                                                         if (icu->bezt[i].vec[1][0] > xmin && icu->bezt[i].vec[1][0] <= xmax ){
1649                                                                                 if (!firstact){
1650                                                                                         firstact= act;
1651                                                                                         firstvert=icu->bezt[i].vec[1][0];
1652                                                                                         *sel = icu->bezt[i].f2 & 1;     
1653                                                                                 }
1654                                                                                 
1655                                                                                 if (icu->bezt[i].f2 & 1){ 
1656                                                                                         if (!foundsel){
1657                                                                                                 foundsel=1;
1658                                                                                                 foundx = icu->bezt[i].vec[1][0];
1659                                                                                         }
1660                                                                                 }
1661                                                                                 else if (foundsel && icu->bezt[i].vec[1][0] != foundx){
1662                                                                                         *index=icu->bezt[i].vec[1][0];
1663                                                                                         *sel = 0;
1664                                                                                         return act;
1665                                                                                 }
1666                                                                         }
1667                                                                 }
1668                                                         }
1669                                                 }
1670                                                 
1671                                                 for (conchan=chan->constraintChannels.first; conchan; conchan=conchan->next){
1672                                                         ymin=ymax-(NLACHANNELHEIGHT+NLACHANNELSKIP);
1673                                                         if ( !((ymax < rectf.ymin) || (ymin > rectf.ymax)) && conchan->ipo){
1674                                                                 for (icu=conchan->ipo->curve.first; icu; icu=icu->next){
1675                                                                         for (i=0; i<icu->totvert; i++){
1676                                                                                 if (icu->bezt[i].vec[1][0] > xmin && icu->bezt[i].vec[1][0] <= xmax ){
1677                                                                                         if (!firstact){
1678                                                                                                 firstact=base->object->action;
1679                                                                                                 firstvert=icu->bezt[i].vec[1][0];
1680                                                                                                 *sel = icu->bezt[i].f2 & 1;     
1681                                                                                         }
1682                                                                                         
1683                                                                                         if (icu->bezt[i].f2 & 1){ 
1684                                                                                                 if (!foundsel){
1685                                                                                                         foundsel=1;
1686                                                                                                         foundx = icu->bezt[i].vec[1][0];
1687                                                                                                 }
1688                                                                                         }
1689                                                                                         else if (foundsel && icu->bezt[i].vec[1][0] != foundx){
1690                                                                                                 *index=icu->bezt[i].vec[1][0];
1691                                                                                                 *sel = 0;
1692                                                                                                 return base->object->action;
1693                                                                                         }
1694                                                                                 }
1695                                                                         }
1696                                                                 }
1697                                                         }
1698                                                         ymax=ymin;
1699                                                 }
1700                                         
1701                                         
1702                                         }
1703                                 }                       
1704                                 ymax=ymin;
1705                         }
1706                         
1707                         /* Skip nlastrips */
1708                         ymax-=(NLACHANNELHEIGHT+NLACHANNELSKIP)*BLI_countlist(&base->object->nlastrips);
1709                 }
1710         }       
1711         
1712         *index=firstvert;
1713         return firstact;
1714 }
1715
1716 void deselect_nlachannels(int test)
1717 {
1718         Base *base;
1719         int sel = 1;
1720
1721         if (test){
1722                 for (base=G.scene->base.first; base; base=base->next){
1723                         /* Check base flags for previous selection */
1724                         if (base->flag & SELECT){
1725                                 sel=0;
1726                                 break;
1727                         }
1728                 }
1729         }
1730         else
1731                 sel = 0;
1732
1733         /* Select objects */
1734         for (base=G.scene->base.first; base; base=base->next){
1735                 if (sel){
1736                         if (nla_filter(base))
1737                                 base->flag |= SELECT;
1738                 }
1739                 else
1740                         base->flag &= ~SELECT;
1741                 
1742                 base->object->flag= base->flag;
1743         }       
1744 }