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