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