svn merge -r 15392:15551 https://svn.blender.org/svnroot/bf-blender/trunk/blender
[blender.git] / source / blender / src / editnla.c
index e05d3b149ff71362eb6ef659c33fb28c30aff298..dbc0deecb2cae140def1e887f362347c3d67e03a 100644 (file)
 
 #include "BLI_blenlib.h"
 
+#include "DNA_action_types.h"
+#include "DNA_constraint_types.h"
+#include "DNA_curve_types.h"
+#include "DNA_ipo_types.h"
+#include "DNA_object_types.h"
+#include "DNA_nla_types.h"
 #include "DNA_screen_types.h"
 #include "DNA_space_types.h"
 #include "DNA_scene_types.h"
-#include "DNA_ipo_types.h"
-#include "DNA_curve_types.h"
-#include "DNA_object_types.h"
 #include "DNA_userdef_types.h"
-#include "DNA_action_types.h"
-#include "DNA_nla_types.h"
-#include "DNA_constraint_types.h"
 
 #include "BKE_action.h"
+#include "BKE_blender.h"
 #include "BKE_depsgraph.h"
 #include "BKE_group.h"
 #include "BKE_global.h"
@@ -61,6 +62,7 @@
 #include "BKE_library.h"
 #include "BKE_main.h"
 #include "BKE_nla.h"
+#include "BKE_utildefines.h"
 
 #include "BIF_screen.h"
 #include "BIF_interface.h"
 #include "BIF_editview.h"
 #include "BIF_toolbox.h"
 #include "BIF_editnla.h"
+#include "BIF_editaction.h"
+#include "BIF_transform.h"
 
 #include "BSE_editipo.h"
 #include "BSE_editnla_types.h"
 #include "BSE_headerbuttons.h"
 #include "BSE_drawipo.h"
+#include "BSE_editaction_types.h"
 #include "BSE_trans_types.h"
 #include "BSE_edit.h"
 #include "BSE_filesel.h"
 #include "BDR_editobject.h"
 #include "BSE_drawnla.h"
+#include "BSE_time.h"
 
 #include "blendef.h"
 #include "mydevice.h"
@@ -96,7 +102,6 @@ static Base *get_nearest_nlachannel_ob_key (float *index, short *sel);
 static bAction *get_nearest_nlachannel_ac_key (float *index, short *sel);
 static Base *get_nearest_nlastrip (bActionStrip **rstrip, short *sel);
 static void mouse_nlachannels(short mval[2]);
-static void convert_nla(short mval[2]);
 
 /* ******************** SPACE: NLA ********************** */
 
@@ -106,6 +111,9 @@ void shift_nlastrips_up(void) {
        bActionStrip *strip, *prevstrip;
 
        for (base=G.scene->base.first; base; base=base->next) {
+               if (base->object->nlaflag & OB_NLA_COLLAPSED)
+                       continue;
+               
                for (strip = base->object->nlastrips.first; 
                         strip; strip=strip->next){
                        if (strip->flag & ACTSTRIP_SELECT) {
@@ -143,6 +151,9 @@ void shift_nlastrips_down(void) {
        bActionStrip *strip, *nextstrip;
 
        for (base=G.scene->base.first; base; base=base->next) {
+               if (base->object->nlaflag & OB_NLA_COLLAPSED)
+                       continue;
+               
                for (strip = base->object->nlastrips.last; 
                         strip; strip=strip->prev){
                        if (strip->flag & ACTSTRIP_SELECT) {
@@ -177,26 +188,180 @@ void shift_nlastrips_down(void) {
 void synchronize_action_strips(void)
 {
        Base *base;
+       Object *ob;
        bActionStrip *strip;
        
        for (base=G.scene->base.first; base; base=base->next) {
-               for (strip = base->object->nlastrips.last; strip; strip=strip->prev) {
+               /* get object first */
+               ob= base->object;
+               
+               /* step 1: adjust strip-lengths */
+               //      FIXME: this seems very buggy
+               for (strip = ob->nlastrips.last; strip; strip=strip->prev) {
                        if (strip->flag & ACTSTRIP_LOCK_ACTION) {
                                float actstart, actend;
                                
-                               calc_action_range(strip->act, &actstart, &actend);
+                               calc_action_range(strip->act, &actstart, &actend, 1);
                                
-                               if(strip->actstart!=actstart || strip->actend!=actend) {
-                                       float mapping= (strip->end - strip->start)/(strip->actend - strip->actstart);
+                               if ((strip->actstart!=actstart) || (strip->actend!=actend)) {           
+                                       float offset = strip->scale * (actstart - strip->actstart);
+                                       float actlen = actend - actstart;
                                        
-                                       strip->start+= mapping*(actstart - strip->actstart);
-                                       strip->end+= mapping*(actend - strip->actend);
+                                       strip->start += offset;
+                                       strip->end = (strip->scale * strip->repeat * actlen) + strip->start;
                                        
                                        strip->actstart= actstart;
                                        strip->actend= actend;
                                }
                        }
                }
+               
+               /* step 2: adjust blendin/out values for each strip if option is turned on */
+               for (strip= ob->nlastrips.first; strip; strip=strip->next) {
+                       if (strip->flag & ACTSTRIP_AUTO_BLENDS) {
+                               bActionStrip *prev= strip->prev;
+                               bActionStrip *next= strip->next;
+                               float pr[2], nr[2];
+                               
+                               strip->blendin = 0.0f;
+                               strip->blendout = 0.0f;
+                               
+                               /* setup test ranges */
+                               if (prev && next) {
+                                       /* init range for previous strip */
+                                       pr[0]= prev->start;
+                                       pr[1]= prev->end;
+                                       
+                                       /* init range for next strip */
+                                       nr[0]= next->start;
+                                       nr[1]= next->end;
+                               }
+                               else if (prev) {
+                                       /* next strip's range is same as previous strip's range  */
+                                       pr[0] = nr[0] = prev->start;
+                                       pr[1] = nr[1] = prev->end;
+                               }
+                               else if (next) {
+                                       /* previous strip's range is same as next strip's range */
+                                       pr[0] = nr[0] = next->start;
+                                       pr[1] = nr[1] = next->end;
+                               }
+                               else {
+                                       /* there shouldn't be any more strips to loop through for this operation */
+                                       break;
+                               }
+                               
+                               /* test various cases */
+                               if ( IN_RANGE(pr[1], strip->start, strip->end) && 
+                                       (IN_RANGE(pr[0], strip->start, strip->end)==0) )
+                               {
+                                       /* previous strip intersects start of current */
+                                       
+                                       if ( IN_RANGE(nr[1], strip->start, strip->end) && 
+                                               (IN_RANGE(nr[0], strip->start, strip->end)==0) )
+                                       {
+                                               /* next strip also intersects start of current */
+                                               if (nr[1] < pr[1])
+                                                       strip->blendin= nr[1] - strip->start;
+                                               else
+                                                       strip->blendin= pr[1] - strip->start;
+                                       }
+                                       else if (IN_RANGE(nr[0], strip->start, strip->end) && 
+                                                       (IN_RANGE(nr[1], strip->start, strip->end)==0))
+                                       {
+                                               /* next strip intersects end of current */
+                                               strip->blendout= strip->end - nr[0];
+                                               strip->blendin= pr[1] - strip->start;
+                                       }
+                                       else {
+                                               /* only previous strip intersects current */
+                                               strip->blendin= pr[1] - strip->start;
+                                       }
+                               }
+                               else if (IN_RANGE(pr[0], strip->start, strip->end) && 
+                                               (IN_RANGE(pr[1], strip->start, strip->end)==0) )
+                               {
+                                       /* previous strip intersects end of current */
+                                       
+                                       if ( IN_RANGE(nr[0], strip->start, strip->end) && 
+                                               (IN_RANGE(nr[1], strip->start, strip->end)==0) )
+                                       {
+                                               /* next strip also intersects end of current */
+                                               if (nr[1] > pr[1])
+                                                       strip->blendout= strip->end - nr[0];
+                                               else
+                                                       strip->blendout= strip->end - pr[0];
+                                       }
+                                       else if (IN_RANGE(nr[1], strip->start, strip->end) && 
+                                                       (IN_RANGE(nr[0], strip->start, strip->end)==0))
+                                       {
+                                               /* next strip intersects start of current */
+                                               strip->blendin= nr[1] - strip->start;
+                                               strip->blendout= strip->end - pr[0];
+                                       }
+                                       else {
+                                               /* only previous strip intersects current */
+                                               strip->blendout= strip->end - pr[0];
+                                       }
+                               }
+                               else if (IN_RANGE(nr[1], strip->start, strip->end) && 
+                                               (IN_RANGE(nr[0], strip->start, strip->end)==0) )
+                               {
+                                       /* next strip intersects start of current */
+                                       
+                                       if ( IN_RANGE(pr[1], strip->start, strip->end) && 
+                                               (IN_RANGE(pr[0], strip->start, strip->end)==0) )
+                                       {
+                                               /* previous strip also intersects start of current */
+                                               if (pr[1] < nr[1])
+                                                       strip->blendin= pr[1] - strip->start;
+                                               else
+                                                       strip->blendin= nr[1] - strip->start;
+                                       }
+                                       else if (IN_RANGE(pr[0], strip->start, strip->end) && 
+                                                       (IN_RANGE(pr[1], strip->start, strip->end)==0))
+                                       {
+                                               /* previous strip intersects end of current */
+                                               strip->blendout= strip->end - pr[0];
+                                               strip->blendin= nr[1] - strip->start;
+                                       }
+                                       else {
+                                               /* only next strip intersects current */
+                                               strip->blendin= nr[1] - strip->start;
+                                       }
+                               }
+                               else if (IN_RANGE(nr[0], strip->start, strip->end) && 
+                                               (IN_RANGE(nr[1], strip->start, strip->end)==0) )
+                               {
+                                       /* next strip intersects end of current */
+                                       
+                                       if ( IN_RANGE(pr[0], strip->start, strip->end) && 
+                                               (IN_RANGE(pr[1], strip->start, strip->end)==0) )
+                                       {
+                                               /* previous strip also intersects end of current */
+                                               if (pr[1] > nr[1])
+                                                       strip->blendout= strip->end - pr[0];
+                                               else
+                                                       strip->blendout= strip->end - nr[0];
+                                       }
+                                       else if (IN_RANGE(pr[1], strip->start, strip->end) && 
+                                                       (IN_RANGE(pr[0], strip->start, strip->end)==0))
+                                       {
+                                               /* previous strip intersects start of current */
+                                               strip->blendin= pr[1] - strip->start;
+                                               strip->blendout= strip->end - nr[0];
+                                       }
+                                       else {
+                                               /* only next strip intersects current */
+                                               strip->blendout= strip->end - nr[0];
+                                       }
+                               }
+                               
+                               /* make sure blending stays in ranges */
+                               CLAMP(strip->blendin, 0, (strip->end-strip->start));
+                               CLAMP(strip->blendout, 0, (strip->end-strip->start));
+                       }
+               }
        }
        
 }
@@ -207,17 +372,46 @@ void reset_action_strips(int val)
        bActionStrip *strip;
        
        for (base=G.scene->base.first; base; base=base->next) {
+               if (base->object->nlaflag & OB_NLA_COLLAPSED)
+                       continue;
+               
                for (strip = base->object->nlastrips.last; strip; strip=strip->prev) {
                        if (strip->flag & ACTSTRIP_SELECT) {
-                               if(val==2) {
-                                       calc_action_range(strip->act, &strip->actstart, &strip->actend);
-                               }
-                               else if(val==1) {
-                                       float mapping= (strip->actend - strip->actstart)/(strip->end - strip->start);
-                                       
-                                       strip->end= strip->start + mapping*(strip->end - strip->start);
+                               switch (val) {
+                                       case 1:
+                                       {
+                                               /* clear scaling - reset to 1.0 without touching keys */
+                                               float actlen= (strip->actend - strip->actstart);
+                                               
+                                               strip->scale= 1.0f;
+                                               strip->end= (strip->repeat * actlen) + strip->start;
+                                       }
+                                               break;
+                                       case 2:
+                                       {
+                                               /* reset action-range */
+                                               calc_action_range(strip->act, &strip->actstart, &strip->actend, 1);
+                                       }
+                                               break;
+                                       case 3:
+                                       {
+                                               /* apply scale to keys - scale is reset to 1.0f, but keys stay at the same times */
+                                               bActionChannel *achan;
+                                               
+                                               if (strip->act) {
+                                                       for (achan= strip->act->chanbase.first; achan; achan= achan->next) {
+                                                               actstrip_map_ipo_keys(base->object, achan->ipo, 0, 0);
+                                                       }
+                                                       
+                                                       /* now we can reset scale */
+                                                       calc_action_range(strip->act, &strip->actstart, &strip->actend, 1);
+                                                       strip->scale= 1.0f;
+                                                       strip->end = (strip->repeat * (strip->actend - strip->actstart)) + strip->start;
+                                               }
+                                       }
+                                               break;
                                }
-                               base->object->ctime= -1234567.0f;       // eveil! 
+                               base->object->ctime= -1234567.0f;       // evil! 
                                DAG_object_flush_update(G.scene, base->object, OB_RECALC_OB|OB_RECALC_DATA);
                        }
                }
@@ -228,21 +422,77 @@ void reset_action_strips(int val)
        allqueue (REDRAWNLA, 0);
 }
 
-void snap_action_strips(void)
+void snap_action_strips(int snap_mode)
 {
        Base *base;
        bActionStrip *strip;
        
        for (base=G.scene->base.first; base; base=base->next) {
+               /* object has ipo - these keyframes should be able to be snapped, even if strips are collapsed */
+               if (base->object->ipo) {
+                       snap_ipo_keys(base->object->ipo, snap_mode);
+               }
+               
+               /* object is collapsed - action and nla strips not shown/editable */
+               if (base->object->nlaflag & OB_NLA_COLLAPSED)
+                       continue;
+               
+               /* snap action strips */
                for (strip = base->object->nlastrips.last; strip; strip=strip->prev) {
                        if (strip->flag & ACTSTRIP_SELECT) {
-                               strip->start= floor(strip->start+0.5);
-                               strip->end= floor(strip->end+0.5);
+                               if (snap_mode==1) {
+                                       /* nearest frame */
+                                       strip->start= floor(strip->start+0.5);
+                                       strip->end= floor(strip->end+0.5);
+                               }
+                               else if (snap_mode==2) {
+                                       /* current frame */
+                                       float diff;
+                                       if (CFRA < strip->start) {
+                                               diff = (strip->start - CFRA);
+                                               strip->start -= diff;
+                                               strip->end -= diff;
+                                       }
+                                       else {
+                                               diff = (CFRA - strip->start);
+                                               strip->start += diff;
+                                               strip->end += diff;
+                                       }
+                               }
+                               else if (snap_mode==3) {
+                                       /* nearest second */
+                                       float secf = FPS;
+                                       strip->start= (float)(floor(strip->start/secf + 0.5f) * secf);
+                                       strip->end= (float)(floor(strip->end/secf + 0.5f) * secf);
+                               }
+                       }
+               }
+               
+               /* object has action */
+               if (base->object->action) {
+                       ListBase act_data = {NULL, NULL};
+                       bActListElem *ale;
+                       int filter;
+                       
+                       /* filter action data */
+                       filter= (ACTFILTER_VISIBLE | ACTFILTER_FOREDIT | ACTFILTER_IPOKEYS);
+                       actdata_filter(&act_data, filter, base->object->action, ACTCONT_ACTION);
+                       
+                       /* snap to frame */
+                       for (ale= act_data.first; ale; ale= ale->next) {
+                               actstrip_map_ipo_keys(base->object, ale->key_data, 0, 1); 
+                               snap_ipo_keys(ale->key_data, snap_mode);
+                               actstrip_map_ipo_keys(base->object, ale->key_data, 1, 1);
                        }
+                       BLI_freelistN(&act_data);
+                       
+                       remake_action_ipos(base->object->action);
                }
        }
        BIF_undo_push("Snap NLA strips");
        allqueue (REDRAWVIEW3D, 0);
+       allqueue (REMAKEIPO, 0);
+       allqueue (REDRAWIPO, 0);
        allqueue (REDRAWACTION, 0);
        allqueue (REDRAWNLA, 0);
 }
@@ -251,127 +501,75 @@ static void set_active_strip(Object *ob, bActionStrip *act)
 {
        bActionStrip *strip;
        
+       /* make sure all other strips are not active */
        for (strip = ob->nlastrips.first; strip; strip=strip->next)
                strip->flag &= ~ACTSTRIP_ACTIVE;
        
-       if(act) {
+       /* act is new active strip */
+       if (act) {
+               /* set active flag for this strip */
                act->flag |= ACTSTRIP_ACTIVE;
-       
-               if(ob->action!=act->act) {
-                       if(ob->action) ob->action->id.us--;
-                       if(act->act->id.lib) {
+               
+               /* check if active action will still be the same one */
+               if (ob->action != act->act) {
+                       /* clear object's links with its current action (if present) */
+                       if (ob->action) {
+                               ob->action->id.us--;
+                       }
+                       
+                       /* only set object's action to active strip's action if possible */
+                       if (act->act->id.lib) {
                                ob->action= NULL;
                        }
                        else {
                                ob->action= act->act;
                                id_us_plus(&ob->action->id);
-                       }                       
+                       }       
+                       
+                       /* request redrawing in relevant spaces */
                        allqueue(REDRAWIPO, 0);
                        allqueue(REDRAWVIEW3D, 0);
                        allqueue(REDRAWACTION, 0);
                        allqueue(REDRAWNLA, 0);
-                       ob->ctime= -1234567.0f; // eveil! 
+                       
+                       /* when only showing action (i.e. nla-override off), 
+                        * reset pose to restpose for armatures 
+                        */
+                       if ((ob->nlaflag & OB_NLA_OVERRIDE)==0) {
+                               if (ob->type == OB_ARMATURE)
+                                       rest_pose(ob->pose);
+                       }
+                       
+                       /* flush depsgraph */
+                       ob->ctime= -1234567.0f; // evil! 
                        DAG_object_flush_update(G.scene, ob, OB_RECALC_OB|OB_RECALC_DATA);
                }
        }       
 }
 
-static void find_stridechannel(Object *ob, bActionStrip *strip)
-{
-       if(ob && ob->pose) {
-               bPoseChannel *pchan;
-               for(pchan= ob->pose->chanbase.first; pchan; pchan= pchan->next)
-                       if(pchan->flag & POSE_STRIDE)
-                               break;
-               if(pchan)
-                       BLI_strncpy(strip->stridechannel, pchan->name, 32);
-               else
-                       strip->stridechannel[0]= 0;
-       }
-}
-
-static void convert_nla(short mval[2])
+void convert_nla(void)
 {
-       bActionStrip *strip, *nstrip;
-       Base *base;
-       float x,y;
-       float   ymax, ymin;
-       int sel=0;
-       short event;
+       bActionStrip *strip;
+       Object *ob= OBACT;
        char str[128];
+       short event;
        
-       /* Find out what strip we're over */
-       ymax = count_nla_levels() * (NLACHANNELSKIP+NLACHANNELHEIGHT);
-       ymax+= NLACHANNELHEIGHT/2;
-       
-       areamouseco_to_ipoco(G.v2d, mval, &x, &y);
-       
-       for (base=G.scene->base.first; base; base=base->next){
-               if (nla_filter(base)) {
-                       
-                       /* Area that encloses object name (or ipo) */
-                       ymin=ymax-(NLACHANNELHEIGHT+NLACHANNELSKIP);
-                       ymax=ymin;
-                       
-                       /* Check action ipo */
-                       if (base->object->action) {
-                               ymin=ymax-(NLACHANNELSKIP+NLACHANNELHEIGHT);
-                               if (y>=ymin && y<=ymax)
-                                       break;
-                               ymax=ymin;
-                       }                       
-                               
-                       /* Check nlastrips */
-                       for (strip=base->object->nlastrips.first; strip; strip=strip->next){
-                               ymin=ymax-(NLACHANNELSKIP+NLACHANNELHEIGHT);
-                               if (y>=ymin && y<=ymax){
-                                       sel = 1;
-                                       break;
-                               }
-                               ymax=ymin;
-                       }
-                       if (sel)
-                               break;
-               }
-       }
-       
-       if (base==0 || base->object->action==NULL)
+       if ((ob==NULL)||(ob->action==NULL)) {
+               error("Need active Object to convert Action to NLA Strip");
                return;
+       }
        
-       sprintf(str, "Convert Action%%t|%s to NLA Strip%%x1", base->object->action->id.name+2);
+       sprintf(str, "Convert Action%%t|%s to NLA Strip%%x1", ob->action->id.name+2);
        event = pupmenu(str);
        
-       switch (event){
-       case 1:
-               if (base->object->action){
-                       /* Make new actionstrip */
-                       nstrip = MEM_callocN(sizeof(bActionStrip), "bActionStrip");
-                       
+       if (event==1) {
+               if (ob->action) {
                        deselect_nlachannel_keys(0);
-                       
-                       /* Link the action to the nstrip */
-                       nstrip->act = base->object->action;
-                       nstrip->act->id.us++;
-                       calc_action_range(nstrip->act, &nstrip->actstart, &nstrip->actend);
-                       nstrip->start = nstrip->actstart;
-                       nstrip->end = nstrip->actend;
-                       nstrip->flag = ACTSTRIP_SELECT|ACTSTRIP_LOCK_ACTION;
-                       
-                       find_stridechannel(base->object, nstrip);
-                       set_active_strip(base->object, nstrip);
-                       
-                       nstrip->repeat = 1.0;
-                                                       
-                       BLI_addtail(&base->object->nlastrips, nstrip);
-                       
+                       strip = convert_action_to_strip(ob); //creates a new NLA strip from the action in given object
+                       set_active_strip(ob, strip);
                        BIF_undo_push("Convert NLA");
                        allqueue (REDRAWNLA, 0);
                }
-               
-               
-               break;
-       default:
-               break;
        }
 }
 
@@ -403,13 +601,13 @@ static void add_nla_block(short event)
        /* Link the action to the strip */
        strip->act = act;
        id_us_plus(&act->id);
-       calc_action_range(strip->act, &strip->actstart, &strip->actend);
+       calc_action_range(strip->act, &strip->actstart, &strip->actend, 1);
        strip->start = G.scene->r.cfra;         /* could be mval[0] another time... */
        strip->end = strip->start + (strip->actend-strip->actstart);
                /* simple prevention of zero strips */
        if(strip->start>strip->end-2) 
                strip->end= strip->start+100;
-       strip->repeat = 1.0;
+       strip->repeat = strip->scale= 1.0f;
        
        strip->flag = ACTSTRIP_SELECT|ACTSTRIP_LOCK_ACTION;
        
@@ -419,11 +617,70 @@ static void add_nla_block(short event)
        if(strip->object)
                id_lib_extern(&strip->object->id);      /* checks lib data, sets correct flag for saving then */
 
+       if(ob->nlastrips.first == NULL)
+               ob->nlaflag |= OB_NLA_OVERRIDE;
+       
        BLI_addtail(&ob->nlastrips, strip);
 
        BIF_undo_push("Add NLA strip");
 }
 
+static void add_nla_block_by_name(char name[32], Object *ob, short hold, short add, float repeat)
+{
+       bAction *act=NULL;
+       bActionStrip *strip;
+       int             cur;
+
+       if (name){
+               for (cur = 1, act=G.main->action.first; act; act=act->id.next, cur++){
+                       if (strcmp(name,act->id.name)==0) {
+                               break;
+                       }
+               }
+       }
+       
+       /* Bail out if no action was chosen */
+       if (!act){
+               return;
+       }
+       
+       /* Initialize the new action block */
+       strip = MEM_callocN(sizeof(bActionStrip), "bActionStrip");
+       strip->scale= 1.0f;
+       
+       deselect_nlachannel_keys(0);
+       
+       /* Link the action to the strip */
+       strip->act = act;
+       calc_action_range(strip->act, &strip->actstart, &strip->actend, 1);
+       strip->start = G.scene->r.cfra;         /* could be mval[0] another time... */
+       strip->end = strip->start + (strip->actend-strip->actstart);
+               /* simple prevention of zero strips */
+       if(strip->start>strip->end-2) 
+               strip->end= strip->start+100;
+       
+       strip->flag = ACTSTRIP_SELECT|ACTSTRIP_LOCK_ACTION; //|ACTSTRIP_USEMATCH;
+       
+       if (hold==1)
+               strip->flag = strip->flag|ACTSTRIP_HOLDLASTFRAME;
+               
+       if (add==1)
+               strip->mode = ACTSTRIPMODE_ADD;
+       
+       find_stridechannel(ob, strip);
+       
+       set_active_strip(ob, strip);
+       
+       strip->repeat = repeat;
+       
+       act->id.us++;
+       
+       if(ob->nlastrips.first == NULL)
+               ob->nlaflag |= OB_NLA_OVERRIDE;
+               
+       BLI_addtail(&ob->nlastrips, strip);
+}
+
 static void add_nla_databrowse_callback(unsigned short val)
 {
        /* val is not used, databrowse needs it to optional pass an event */
@@ -437,11 +694,11 @@ static void add_nla_databrowse_callback(unsigned short val)
 }
 
 /* Adds strip to to active Object */
-static void add_nlablock(void)
+void add_nlablock(void)
 {
        Object *ob= OBACT;
        short event;
-       short nr;
+       short nr=0;
        char *str, title[64];
        
        if(ob==NULL) {
@@ -449,11 +706,12 @@ static void add_nlablock(void)
                return;
        }
        
+       sprintf(title, "Add Action strip to: %s", ob->id.name+2);
+       
        /* Popup action menu */
-       sprintf(title, "Add Action strip to %s", ob->id.name+2);
        IDnames_to_pupstring(&str, title, NULL, &G.main->action, (ID *)G.scene, &nr);
        
-       if(strncmp(str+13, "DataBrow", 8)==0) {
+       if(nr==-2) {
                MEM_freeN(str);
 
                activate_databrowse((ID *)NULL, ID_AC, 0, 0, &G.snla->menunr, 
@@ -462,12 +720,101 @@ static void add_nlablock(void)
                return;                 
        }
        else {
-               event = pupmenu(str);
+               event = pupmenu_col(str, 20);
                MEM_freeN(str);
                add_nla_block(event);
        }
 }
 
+/* Creates a new action, and makes a new actionstrip of that */
+void add_empty_nlablock(void)
+{
+       Object *ob= OBACT;
+       bAction *act= NULL;
+       bActionStrip *strip;
+       
+       /* check for active object first - will add strip to active object */
+       if (ob == NULL) 
+               return;
+               
+       /* make new action */
+       if ((ob->type == OB_ARMATURE) && (ob->flag & OB_POSEMODE))
+               act= add_empty_action("ObAction");
+       else
+               act= add_empty_action("Action");
+               
+       /* make a new strip for it */
+       add_nla_block_by_name(act->id.name, ob, 0, 1, 1.0f);
+       strip= ob->nlastrips.last; 
+       
+       /* change some settings of the strip - try to avoid bad scaling */
+       if ((EFRA-CFRA) < 100) {
+               strip->flag |= ACTSTRIP_AUTO_BLENDS;
+               strip->flag &= ~ACTSTRIP_LOCK_ACTION;
+               strip->actstart = CFRA;
+               strip->actend = CFRA + 100;
+               
+               strip->start = CFRA;
+               strip->end = CFRA + 100;
+       }
+       else {
+               strip->flag |= ACTSTRIP_AUTO_BLENDS;
+               strip->flag &= ~ACTSTRIP_LOCK_ACTION;
+               strip->actstart = CFRA;
+               strip->actend = EFRA;
+               
+               strip->start = CFRA;
+               strip->end = EFRA;
+       }
+       
+       BIF_undo_push("Add NLA strip");
+}
+
+/* Adds strip to to active Object */
+static void relink_active_strip(void)
+{
+       Object *ob= OBACT;
+       bActionStrip *strip;
+       bAction *act;
+       short event;
+       short cur;
+       char *str;
+       
+       if(ob==NULL) return;
+       if(ob->nlaflag & OB_NLA_COLLAPSED) return;
+       
+       for (strip = ob->nlastrips.first; strip; strip=strip->next)
+               if(strip->flag & ACTSTRIP_ACTIVE)
+                       break;
+       
+       if(strip==NULL) return;
+       
+       /* Popup action menu */
+       IDnames_to_pupstring(&str, "Relink Action strip", NULL, &G.main->action, (ID *)G.scene, NULL);
+       if(str) {
+               event = pupmenu_col(str, 20);
+               MEM_freeN(str);
+               
+               for (cur = 1, act=G.main->action.first; act; act=act->id.next, cur++){
+                       if (cur==event){
+                               break;
+                       }
+               }
+               
+               if(act) {
+                       if(strip->act) strip->act->id.us--;
+                       strip->act = act;
+                       id_us_plus(&act->id);
+                       
+                       allqueue(REDRAWVIEW3D, 0);
+                       allqueue(REDRAWACTION, 0);
+                       allqueue(REDRAWNLA, 0);
+               }
+       }
+}
+
+
+
 /* Left hand side of channels display, selects objects */
 static void mouse_nlachannels(short mval[2])
 {
@@ -498,22 +845,25 @@ static void mouse_nlachannels(short mval[2])
                        }
                        click--;
                        
-                       /* See if this is an action */
-                       if (ob->action){
-                               if (click==0) {
-                                       actclick= 1;
-                                       break;
+                       /* see if any strips under object */
+                       if ((ob->nlaflag & OB_NLA_COLLAPSED)==0) {
+                               /* See if this is an action */
+                               if (ob->action){
+                                       if (click==0) {
+                                               actclick= 1;
+                                               break;
+                                       }
+                                       click--;
                                }
-                               click--;
-                       }
 
-                       /* See if this is an nla strip */
-                       if(ob->nlastrips.first) {
-                               for (strip = ob->nlastrips.first; strip; strip=strip->next){
-                                       if (click==0) break;
-                                       click--;                                
+                               /* See if this is an nla strip */
+                               if(ob->nlastrips.first) {
+                                       for (strip = ob->nlastrips.first; strip; strip=strip->next){
+                                               if (click==0) break;
+                                               click--;                                
+                                       }
+                                       if (strip && click==0) break;
                                }
-                               if (strip && click==0) break;
                        }
                }
        }
@@ -536,12 +886,26 @@ static void mouse_nlachannels(short mval[2])
        
        if(actclick) /* de-activate all strips */
                set_active_strip(ob, NULL);
-       else if(strip) /* set action */
-               set_active_strip(ob, strip);
+       else if(strip) {
+               if(mval[0] >= (NLAWIDTH-16)) /* toggle strip muting */
+                       strip->flag ^= ACTSTRIP_MUTE;
+               else /* set action */
+                       set_active_strip(ob, strip);
+       }
 
-       /* override option for NLA */
-       if(obclick && mval[0]<25)
+       /* icon toggles beside strip */
+       if (obclick && mval[0]<20) {
+               /* collapse option for NLA object strip */
+               ob->nlaflag ^= OB_NLA_COLLAPSED;
+       }
+       else if(obclick && mval[0]<36) {
+               /* override option for NLA */
                ob->nlaflag ^= OB_NLA_OVERRIDE;
+       }
+       else if((obclick) && (ob->ipo) && (mval[0] >= (NLAWIDTH-16))) {
+               /* mute Object IPO-block */
+               ob->ipo->muteipo = (ob->ipo->muteipo)? 0: 1;
+       }
        
        ob->ctime= -1234567.0f; // eveil! 
        DAG_object_flush_update(G.scene, ob, OB_RECALC_OB|OB_RECALC_DATA);
@@ -563,8 +927,7 @@ void deselect_nlachannel_keys (int test)
        
        /* Determine if this is selection or deselection */
        if (test){
-               for (base=G.scene->base.first; base && sel; base=base->next){
-                       
+               for (base=G.scene->base.first; base && sel; base=base->next){           
                        /* Test object ipos */
                        if (is_ipo_key_selected(base->object->ipo)){
                                sel = 0;
@@ -581,6 +944,10 @@ void deselect_nlachannel_keys (int test)
                                }
                        }
                        
+                       /* check if collapsed */
+                       if (base->object->nlaflag & OB_NLA_COLLAPSED)
+                               continue;
+                       
                        /* Test action ipos */
                        if (sel){
                                if (base->object->action){
@@ -619,8 +986,7 @@ void deselect_nlachannel_keys (int test)
        
        
        /* Set the flags */
-       for (base=G.scene->base.first; base; base=base->next){
-               
+       for (base=G.scene->base.first; base; base=base->next){          
                /* Set the object ipos */
                set_ipo_key_selection(base->object->ipo, sel);
                
@@ -629,6 +995,10 @@ void deselect_nlachannel_keys (int test)
                        set_ipo_key_selection(conchan->ipo, sel);                       
                }
 
+               /* check if collapsed */
+               if (base->object->nlaflag & OB_NLA_COLLAPSED)
+                       continue;
+               
                /* Set the action ipos */
                if (base->object->action){
                        for (chan=base->object->action->chanbase.first; chan; chan=chan->next){
@@ -666,271 +1036,28 @@ static void recalc_all_ipos(void)
 
 void transform_nlachannel_keys(int mode, int dummy)
 {
-       Base *base;
-       TransVert *tv;
-       bActionChannel *chan;
-       bActionStrip *strip;
-       bConstraintChannel *conchan;
-       float   sval[2], cval[2], lastcval[2];
-       float   fac=0.0F;
-       float   deltax, startx;
-       int i;
-       int             loop=1;
-       int             tvtot=0;
-       int             invert=0, firsttime=1;
-       short   mvals[2], mvalc[2];
-       short   cancel=0;
-       char    str[256];
-
-       /* Ensure that partial selections result in beztriple selections */
-       for (base=G.scene->base.first; base; base=base->next){
+       short context = (U.flag & USER_DRAGIMMEDIATE)?CTX_TWEAK:CTX_NONE;
 
-               /* Check object ipos */
-               i= fullselect_ipo_keys(base->object->ipo);
-               if(i) base->flag |= BA_HAS_RECALC_OB;
-               tvtot+=i;
-               
-               /* Check object constraint ipos */
-               for(conchan=base->object->constraintChannels.first; conchan; conchan=conchan->next)
-                       tvtot+=fullselect_ipo_keys(conchan->ipo);                       
-               
-               /* Check action ipos */
-               if (base->object->action){
-                       /* exclude if strip is selected too */
-                       for (strip=base->object->nlastrips.first; strip; strip=strip->next){
-                               if (strip->flag & ACTSTRIP_SELECT)
-                                       if(strip->act==base->object->action)
-                                               break;
-                       }
-                       if(strip==NULL) {
-                               
-                               for (chan=base->object->action->chanbase.first; chan; chan=chan->next){
-                                       i= fullselect_ipo_keys(chan->ipo);
-                                       if(i) base->flag |= BA_HAS_RECALC_OB|BA_HAS_RECALC_DATA;
-                                       tvtot+=i;
-                                       
-                                       /* Check action constraint ipos */
-                                       for (conchan=chan->constraintChannels.first; conchan; conchan=conchan->next)
-                                               tvtot+=fullselect_ipo_keys(conchan->ipo);
-                               }
-                       }               
-               }
-
-               /* Check nlastrips */
-               for (strip=base->object->nlastrips.first; strip; strip=strip->next){
-                       if (strip->flag & ACTSTRIP_SELECT) {
-                               base->flag |= BA_HAS_RECALC_OB|BA_HAS_RECALC_DATA;
-                               tvtot+=2;
-                       }
-               }
-       }
-       
-       /* If nothing is selected, bail out */
-       if (!tvtot)
-               return;
-       
-       
-       /* Build the transvert structure */
-       tv = MEM_callocN (sizeof(TransVert) * tvtot, "transVert");
-       tvtot=0;
-       for (base=G.scene->base.first; base; base=base->next){
-               /* Manipulate object ipos */
-               tvtot=add_trans_ipo_keys(base->object->ipo, tv, tvtot);
-
-               /* Manipulate object constraint ipos */
-               for (conchan=base->object->constraintChannels.first; conchan; conchan=conchan->next)
-                       tvtot=add_trans_ipo_keys(conchan->ipo, tv, tvtot);
-
-               /* Manipulate action ipos */
-               if (base->object->action){
-                       /* exclude if strip is selected too */
-                       for (strip=base->object->nlastrips.first; strip; strip=strip->next){
-                               if (strip->flag & ACTSTRIP_SELECT)
-                                       if(strip->act==base->object->action)
-                                               break;
-                       }
-                       if(strip==NULL) {
-                               
-                               for (chan=base->object->action->chanbase.first; chan; chan=chan->next){
-                                       tvtot=add_trans_ipo_keys(chan->ipo, tv, tvtot);
-
-                                       /* Manipulate action constraint ipos */
-                                       for (conchan=chan->constraintChannels.first; conchan; conchan=conchan->next)
-                                               tvtot=add_trans_ipo_keys(conchan->ipo, tv, tvtot);
-                               }
-                       }
-               }
-
-               /* Manipulate nlastrips */
-               for (strip=base->object->nlastrips.first; strip; strip=strip->next){
-                       if (strip->flag & ACTSTRIP_SELECT){
-                               tv[tvtot+0].val=&strip->start;
-                               tv[tvtot+1].val=&strip->end;
-                               
-                               tv[tvtot+0].oldval = strip->start;
-                               tv[tvtot+1].oldval = strip->end;
-                               
-                               tvtot+=2;
-                       }
+       switch (mode) {
+               case 'g':
+               {
+                       initTransform(TFM_TIME_TRANSLATE, context);
+                       Transform();
                }
-       }
-       
-       /* Do the event loop */
-       //      cent[0] = curarea->winx + (G.snla->v2d.hor.xmax)/2;
-       //      cent[1] = curarea->winy + (G.snla->v2d.hor.ymax)/2;
-       
-       //      areamouseco_to_ipoco(cent, &cenf[0], &cenf[1]);
-       
-       getmouseco_areawin (mvals);
-       areamouseco_to_ipoco(G.v2d, mvals, &sval[0], &sval[1]);
-       
-       startx=sval[0];
-       while (loop) {
-               /*              Get the input */
-               /*              If we're cancelling, reset transformations */
-               /*                      Else calc new transformation */
-               /*              Perform the transformations */
-               while (qtest()) {
-                       short val;
-                       unsigned short event= extern_qread(&val);
-                       
-                       if (val) {
-                               switch (event) {
-                               case LEFTMOUSE:
-                               case SPACEKEY:
-                               case RETKEY:
-                                       loop=0;
-                                       break;
-                               case XKEY:
-                                       break;
-                               case ESCKEY:
-                               case RIGHTMOUSE:
-                                       cancel=1;
-                                       loop=0;
-                                       break;
-                               default:
-                                       arrows_move_cursor(event);
-                                       break;
-                               };
-                       }
+                       break;
+               case 's':
+               {
+                       initTransform(TFM_TIME_SCALE, context);
+                       Transform();
                }
-               
-               if (cancel) {
-                       for (i=0; i<tvtot; i++) {
-                               if (tv[i].loc){
-                                       tv[i].loc[0]=tv[i].oldloc[0];
-                                       tv[i].loc[1]=tv[i].oldloc[1];
-                               }
-                               if (tv[i].val)
-                                       tv[i].val[0]=tv[i].oldval;
-                       }
+                       break;
+               case 'e':
+               {
+                       initTransform(TFM_TIME_EXTEND, context);
+                       Transform();
                }
-               else {
-                       getmouseco_areawin (mvalc);
-                       areamouseco_to_ipoco(G.v2d, mvalc, &cval[0], &cval[1]);
-                       
-                       if (!firsttime && lastcval[0]==cval[0] && lastcval[1]==cval[1]) {
-                               PIL_sleep_ms(10);
-                       }
-                       else {
-                               for (i=0; i<tvtot; i++){
-                                       if (tv[i].loc)
-                                               tv[i].loc[0]=tv[i].oldloc[0];
-                                       if (tv[i].val)
-                                               tv[i].val[0]=tv[i].oldval;
-                                       
-                                       switch (mode){
-                                       case 'g':
-                                               deltax = cval[0]-sval[0];
-                                               fac= deltax;
-                                               
-                                               apply_keyb_grid(&fac, 0.0F, 1.0F, 0.1F, U.flag & USER_AUTOGRABGRID);
-                                               
-                                               if (tv[i].loc)
-                                                       tv[i].loc[0]+=fac;
-                                               if (tv[i].val)
-                                                       tv[i].val[0]+=fac;
-                                               break;
-                                       case 's': 
-                                               startx=mvals[0]-(NLAWIDTH/2+(curarea->winrct.xmax-curarea->winrct.xmin)/2);
-                                               deltax=mvalc[0]-(NLAWIDTH/2+(curarea->winrct.xmax-curarea->winrct.xmin)/2);
-                                               fac= (float)fabs(deltax/startx);
-                                               
-                                               apply_keyb_grid(&fac, 0.0F, 0.2F, 0.1F, U.flag & USER_AUTOSIZEGRID);
-                                               
-                                               if (invert){
-                                                       if (i % 03 == 0){
-                                                               memcpy (tv[i].loc, tv[i].oldloc, sizeof(tv[i+2].oldloc));
-                                                       }
-                                                       if (i % 03 == 2){
-                                                               memcpy (tv[i].loc, tv[i].oldloc, sizeof(tv[i-2].oldloc));
-                                                       }
-                                                       
-                                                       fac*=-1;
-                                               }
-                                               startx= (G.scene->r.cfra);
-                                               
-                                               if (tv[i].loc){
-                                                       tv[i].loc[0]-= startx;
-                                                       tv[i].loc[0]*=fac;
-                                                       tv[i].loc[0]+= startx;
-                                               }
-                                               if (tv[i].val){
-                                                       tv[i].val[0]-= startx;
-                                                       tv[i].val[0]*=fac;
-                                                       tv[i].val[0]+= startx;
-                                               }
-                                               
-                                               break;
-                                       }
-                               }
-                       
-                               if (mode=='s'){
-                                       sprintf(str, "sizeX: %.3f", fac);
-                                       headerprint(str);
-                               }
-                               else if (mode=='g'){
-                                       sprintf(str, "deltaX: %.3f", fac);
-                                       headerprint(str);
-                               }
-                               
-                               if (G.snla->lock) {
-                                       for (base=G.scene->base.first; base; base=base->next){
-                                               if(base->flag & BA_HAS_RECALC_OB)
-                                                       base->object->recalc |= OB_RECALC_OB;
-                                               if(base->flag & BA_HAS_RECALC_DATA)
-                                                       base->object->recalc |= OB_RECALC_DATA;
-                                               
-                                               if(base->object->recalc) base->object->ctime= -1234567.0f;      // eveil! 
-                                       }
-                                       
-                                       DAG_scene_flush_update(G.scene, screen_view3d_layers());
-                                       
-                                       force_draw_all(0);
-                               }
-                               else {
-                                       force_draw(0);
-                               }
-                       }
-               }
-               
-               lastcval[0]= cval[0];
-               lastcval[1]= cval[1];
-               firsttime= 0;
+                       break;
        }
-       
-       synchronize_action_strips();
-       
-       /* cleanup */
-       for (base=G.scene->base.first; base; base=base->next)
-               base->flag &= ~(BA_HAS_RECALC_OB|BA_HAS_RECALC_DATA);
-       
-       if(cancel==0) BIF_undo_push("Select all NLA");
-       recalc_all_ipos();      // bad
-       allqueue (REDRAWVIEW3D, 0);
-       allqueue (REDRAWNLA, 0);
-       allqueue (REDRAWIPO, 0);
-       MEM_freeN (tv);
 }
 
 void delete_nlachannel_keys(void)
@@ -939,12 +1066,8 @@ void delete_nlachannel_keys(void)
        bActionChannel *chan;
        bConstraintChannel *conchan;
        bActionStrip *strip, *nextstrip;
-       
-       if (!okee("Erase selected keys"))
-               return;
-       
+               
        for (base = G.scene->base.first; base; base=base->next){
-
                /* Delete object ipos */
                delete_ipo_keys(base->object->ipo);
                
@@ -952,6 +1075,10 @@ void delete_nlachannel_keys(void)
                for(conchan=base->object->constraintChannels.first; conchan; conchan=conchan->next)
                        delete_ipo_keys(conchan->ipo);
 
+               /* skip actions and nlastrips if object collapsed */
+               if (base->object->nlaflag & OB_NLA_COLLAPSED)
+                       continue;
+                       
                /* Delete NLA strips */
                for (strip = base->object->nlastrips.first; strip; strip=nextstrip){
                        nextstrip=strip->next;
@@ -965,18 +1092,22 @@ void delete_nlachannel_keys(void)
                /* Delete action ipos */
                if (base->object->action){
                        for (chan=base->object->action->chanbase.first; chan; chan=chan->next){
-                               delete_ipo_keys(chan->ipo);
+                               if (EDITABLE_ACHAN(chan))
+                                       delete_ipo_keys(chan->ipo);
+                                       
                                /* Delete action constraint keys */
-                               for(conchan=chan->constraintChannels.first; conchan; conchan=conchan->next)
-                                       delete_ipo_keys(conchan->ipo);
+                               for(conchan=chan->constraintChannels.first; conchan; conchan=conchan->next){
+                                       if (EDITABLE_CONCHAN(conchan))
+                                               delete_ipo_keys(conchan->ipo);
+                               }
                        }
                }
        }
        
+       recalc_all_ipos();      // bad
        synchronize_action_strips();
        
        BIF_undo_push("Delete NLA keys");
-       recalc_all_ipos();      // bad
        allspace(REMAKEIPO,0);
        allqueue (REDRAWVIEW3D, 0);
        allqueue(REDRAWNLA, 0);
@@ -991,14 +1122,18 @@ void duplicate_nlachannel_keys(void)
        bActionStrip *strip, *laststrip;
        
        /* Find selected items */
-       for (base = G.scene->base.first; base; base=base->next){
+       for (base = G.scene->base.first; base; base=base->next){        
                /* Duplicate object keys */
                duplicate_ipo_keys(base->object->ipo);
                
                /* Duplicate object constraint keys */
                for(conchan=base->object->constraintChannels.first; conchan; conchan=conchan->next)
                        duplicate_ipo_keys(conchan->ipo);
-
+               
+               /* skip actions and nlastrips if object collapsed */
+               if (base->object->nlaflag & OB_NLA_COLLAPSED)
+                       continue;
+                       
                /* Duplicate nla strips */
                laststrip = base->object->nlastrips.last;
                for (strip=base->object->nlastrips.first; strip; strip=strip->next){
@@ -1021,10 +1156,14 @@ void duplicate_nlachannel_keys(void)
                /* Duplicate actionchannel keys */
                if (base->object->action){
                        for (chan=base->object->action->chanbase.first; chan; chan=chan->next){
-                               duplicate_ipo_keys(chan->ipo);
+                               if (EDITABLE_ACHAN(chan))
+                                       duplicate_ipo_keys(chan->ipo);
+                                       
                                /* Duplicate action constraint keys */
-                               for(conchan=chan->constraintChannels.first; conchan; conchan=conchan->next)
-                                       duplicate_ipo_keys(conchan->ipo);
+                               for(conchan=chan->constraintChannels.first; conchan; conchan=conchan->next) {
+                                       if (EDITABLE_CONCHAN(conchan))
+                                               duplicate_ipo_keys(conchan->ipo);
+                               }
                        }
                }
        }
@@ -1045,11 +1184,11 @@ void borderselect_nla(void)
        bConstraintChannel *conchan;
        
        if ( (val = get_border (&rect, 3)) ){
-    if (val == LEFTMOUSE)
-      selectmode = SELECT_ADD;
-    else
-      selectmode = SELECT_SUBTRACT;
-
+           if (val == LEFTMOUSE)
+                       selectmode = SELECT_ADD;
+           else
+                       selectmode = SELECT_SUBTRACT;
+         
                mval[0]= rect.xmin;
                mval[1]= rect.ymin+2;
                areamouseco_to_ipoco(G.v2d, mval, &rectf.xmin, &rectf.ymin);
@@ -1080,7 +1219,11 @@ void borderselect_nla(void)
                                }
                                
                                ymax=ymin;
-
+                               
+                               /* skip actions and nlastrips if object collapsed */
+                               if (base->object->nlaflag & OB_NLA_COLLAPSED)
+                                       continue;
+                               
                                /* Check action ipos */
                                if (base->object->action){
                                        bActionChannel *chan;
@@ -1122,9 +1265,7 @@ void borderselect_nla(void)
                        }
                }       
                BIF_undo_push("Border select NLA");
-               allqueue(REDRAWNLA, 0);
-               allqueue(REDRAWACTION, 0);
-               allqueue(REDRAWIPO, 0);
+               allqueue(REDRAWMARKER, 0);
        }
 }
 
@@ -1136,6 +1277,7 @@ static void mouse_nla(int selectmode)
        bActionChannel *chan;
        bActionStrip *rstrip;
        bConstraintChannel *conchan;
+       TimeMarker *marker;
        float   selx;
        short   mval[2];
        short sel, isdone=0;
@@ -1144,6 +1286,7 @@ static void mouse_nla(int selectmode)
        
        /* Try object ipo or ob-constraint ipo selection */
        base= get_nearest_nlachannel_ob_key(&selx, &sel);
+       marker=find_nearest_marker(SCE_MARKERS, 1);
        if (base) {
                isdone= 1;
                
@@ -1158,6 +1301,27 @@ static void mouse_nla(int selectmode)
                for (conchan=base->object->constraintChannels.first; conchan; conchan=conchan->next)
                        select_ipo_key(conchan->ipo, selx, selectmode);
        }
+       else if (marker) {
+               /* marker */            
+               if (selectmode == SELECT_REPLACE) {                     
+                       deselect_markers(0, 0);
+                       marker->flag |= SELECT;
+               }
+               else if (selectmode == SELECT_INVERT) {
+                       if (marker->flag & SELECT)
+                               marker->flag &= ~SELECT;
+                       else
+                               marker->flag |= SELECT;
+               }
+               else if (selectmode == SELECT_ADD) 
+                       marker->flag |= SELECT;
+               else if (selectmode == SELECT_SUBTRACT)
+                       marker->flag &= ~SELECT;
+               
+               std_rmouse_transform(transform_markers);
+               
+               allqueue(REDRAWMARKER, 0);
+       }
        else {
                /* Try action ipo selection */
                act= get_nearest_nlachannel_ac_key(&selx, &sel);
@@ -1237,6 +1401,10 @@ static Base *get_nearest_nlastrip (bActionStrip **rstrip, short *sel)
                        
                        /* Skip object ipos */
                        ymax-=(NLACHANNELHEIGHT+NLACHANNELSKIP);
+                       
+                       /* check if skip strips if collapsed */
+                       if (base->object->nlaflag & OB_NLA_COLLAPSED)
+                               continue;
 
                        /* Skip action ipos */
                        if (base->object->action)
@@ -1370,6 +1538,10 @@ static Base *get_nearest_nlachannel_ob_key (float *index, short *sel)
 
                        ymax=ymin;
                        
+                       /* Skip actions and nlastrips if object is collapsed */
+                       if (base->object->nlaflag & OB_NLA_COLLAPSED)
+                               continue;
+                       
                        /* Skip action ipos */
                        if (base->object->action){
                                ymax-=(NLACHANNELHEIGHT+NLACHANNELSKIP);
@@ -1420,6 +1592,11 @@ static bAction *get_nearest_nlachannel_ac_key (float *index, short *sel)
                        
                        /* Skip object ipo and ob-constraint ipo */
                        ymin=ymax-(NLACHANNELHEIGHT+NLACHANNELSKIP);
+                       
+                       /* skip this object if it is collapsed */
+                       if (base->object->nlaflag & OB_NLA_COLLAPSED)
+                               continue;
+                       
                        ymax=ymin;
                        
                        /* Handle action ipos */
@@ -1534,6 +1711,25 @@ void deselect_nlachannels(int test)
        }       
 }
 
+static Object *get_object_from_active_strip(void) {
+
+       Base *base;
+       bActionStrip *strip;
+       
+       for (base=G.scene->base.first; base; base=base->next) {
+               if ((base->object->nlaflag & OB_NLA_COLLAPSED)==0) {
+                       for (strip = base->object->nlastrips.first; 
+                                strip; strip=strip->next){
+                               if (strip->flag & ACTSTRIP_SELECT) {
+                                       return base->object;
+                               }
+                       }
+               }
+       }
+       return NULL;
+}
+
+
 void winqreadnlaspace(ScrArea *sa, void *spacedata, BWinEvent *evt)
 {
        unsigned short event= evt->event;
@@ -1544,12 +1740,13 @@ void winqreadnlaspace(ScrArea *sa, void *spacedata, BWinEvent *evt)
        float dx,dy;
        int     cfra;
        short mousebut = L_MOUSE;
+       Object          *ob; //in shift-B / bake
        
        if (curarea->win==0) return;
        if (!snla) return;
        
        if(val) {
-               if( uiDoBlocks(&curarea->uiblocks, event)!=UI_NOTHING ) event= 0;
+               if( uiDoBlocks(&curarea->uiblocks, event, 1)!=UI_NOTHING ) event= 0;
                
                /* swap mouse buttons based on user preference */
                if (U.flag & USER_LMOUSESELECT) {
@@ -1574,21 +1771,41 @@ void winqreadnlaspace(ScrArea *sa, void *spacedata, BWinEvent *evt)
                                break;
                                
                        case EQUALKEY:
-                       case PAGEUPKEY:
                                shift_nlastrips_up();
                                break;
+                       
+                       case PAGEUPKEY:
+                               if (G.qual & LR_CTRLKEY)
+                                       shift_nlastrips_up();
+                               else {
+                                       nextprev_marker(1);
+                                       allqueue(REDRAWMARKER, 0);
+                               }                               
+                               break;
                                
                        case MINUSKEY:
-                       case PAGEDOWNKEY:
                                shift_nlastrips_down();
                                break;
                                
+                       case PAGEDOWNKEY:
+                               if (G.qual & LR_CTRLKEY)
+                                       shift_nlastrips_down();
+                               else {
+                                       nextprev_marker(-1);
+                                       allqueue(REDRAWMARKER, 0);
+                               }
+                               break;
+                               
                        case AKEY:
                                if (G.qual & LR_SHIFTKEY){
                                        add_nlablock();
                                        allqueue (REDRAWNLA, 0);
                                        allqueue (REDRAWVIEW3D, 0);
                                }
+                               else if (G.qual & LR_CTRLKEY) {
+                                       deselect_markers(1, 0);
+                                       allqueue(REDRAWMARKER, 0);
+                               }
                                else{
                                        if (mval[0]>=NLAWIDTH)
                                                deselect_nlachannel_keys(1);
@@ -1603,44 +1820,103 @@ void winqreadnlaspace(ScrArea *sa, void *spacedata, BWinEvent *evt)
                                break;
                                
                        case BKEY:
-                               borderselect_nla();
+                               if (G.qual & LR_SHIFTKEY){
+                                       bake_all_to_action();
+                                       allqueue (REDRAWNLA, 0);
+                                   allqueue (REDRAWVIEW3D, 0);
+                                   BIF_undo_push("Bake All To Action");
+                                   ob = get_object_from_active_strip();
+                                   //build_match_caches(ob);
+                               }
+                               else if (G.qual & LR_CTRLKEY) 
+                                       borderselect_markers();
+                               else
+                                       borderselect_nla();
                                break;
                                
                        case CKEY:
-                               convert_nla(mval);
+                               if(G.qual==LR_CTRLKEY) {
+                                       if(okee("Copy Modifiers"))
+                                               copy_action_modifiers();
+                               }
+                               else convert_nla();
                                break;
                                
                        case DKEY:
-                               if (G.qual & LR_SHIFTKEY && mval[0]>=NLAWIDTH){
+                               if (G.qual == (LR_CTRLKEY|LR_SHIFTKEY) && mval[0]>=NLAWIDTH) {
+                                       duplicate_marker();
+                               }
+                               else if (G.qual & LR_SHIFTKEY && mval[0]>=NLAWIDTH){
                                        duplicate_nlachannel_keys();
                                        update_for_newframe_muted();
                                }
+                               
                                break;
                                
-                       case GKEY:
-                               if (mval[0]>=NLAWIDTH)
-                                       transform_nlachannel_keys ('g', 0);
-                               update_for_newframe_muted();
+                       case EKEY:
+                               if (mval[0] >= NLAWIDTH) {
+                                       transform_nlachannel_keys ('e', 0);
+                                       update_for_newframe_muted();
+                               }
                                break;
                                
+                       case GKEY:
+                               if (mval[0]>=NLAWIDTH) {
+                                       if (G.qual & LR_CTRLKEY) {
+                                               transform_markers('g', 0);
+                                       }
+                                       else {
+                                               transform_nlachannel_keys ('g', 0);
+                                               update_for_newframe_muted();
+                                       }
+                               }
+                               break;
+                       
+                       case MKEY:
+                               /* marker operations */
+                               if (G.qual == 0)
+                                       add_marker(CFRA);
+                               else if (G.qual == LR_CTRLKEY)
+                                       rename_marker();
+                               else 
+                                       break;
+                               allqueue(REDRAWMARKER, 0);
+                               break;                          
+                       
                        case NKEY:
                                if(G.qual==0) {
                                        toggle_blockhandler(curarea, NLA_HANDLER_PROPERTIES, UI_PNL_TO_MOUSE);
                                        scrarea_queue_winredraw(curarea);
                                }
+                               else if (G.qual & LR_SHIFTKEY) {
+                                       add_empty_nlablock();
+                               }
+                               break;
+                       case LKEY:
+                               relink_active_strip();
+                               break;
+                               
+                       case PKEY:
+                               if (G.qual & LR_CTRLKEY) /* set preview range */
+                                       anim_previewrange_set();
+                               else if (G.qual & LR_ALTKEY) /* clear preview range */
+                                       anim_previewrange_clear();
+                               allqueue(REDRAWMARKER, 0);
                                break;
                                
                        case SKEY:
-                               if(G.qual==LR_ALTKEY) {
-                                       val= pupmenu("Action Strip Scale%t|Clear Strip Size%x1|Remap Start/End%x2");
-                                       if(val==1)
-                                               reset_action_strips(1);
-                                       else if(val==2)
-                                               reset_action_strips(2);
+                               if (G.qual==LR_ALTKEY) {
+                                       val= pupmenu("Action Strip Scale%t|Reset Strip Scale%x1|Remap Action Start/End%x2|Apply Scale%x3");
+                                       if (val > 0)
+                                               reset_action_strips(val);
                                }
-                               else if(G.qual & LR_SHIFTKEY) {
-                                       if(okee("Snap Strips to Frame"))
-                                               snap_action_strips();
+                               else if (G.qual & LR_SHIFTKEY) {
+                                       if (snla->flag & SNLA_DRAWTIME)
+                                               val= pupmenu("Snap To%t|Nearest Second%x3|Current Time%x2");
+                                       else
+                                               val= pupmenu("Snap To%t|Nearest Frame%x1|Current Frame%x2");
+                                       if (ELEM3(val, 1, 2, 3))
+                                               snap_action_strips(val);
                                }
                                else {
                                        if (mval[0]>=NLAWIDTH)
@@ -1649,12 +1925,31 @@ void winqreadnlaspace(ScrArea *sa, void *spacedata, BWinEvent *evt)
                                }
                                break;
                                
+                       case TKEY:
+                               if (G.qual & LR_CTRLKEY) {
+                                       val= pupmenu("Time value%t|Frames %x1|Seconds%x2");
+                                       
+                                       if (val > 0) {
+                                               if (val == 2) snla->flag |= SNLA_DRAWTIME;
+                                               else snla->flag &= ~SNLA_DRAWTIME;
+                                               
+                                               doredraw= 1;
+                                       }
+                               }                               
+                               break;
+                               
                        case DELKEY:
                        case XKEY:
-                               if (mval[0]>=NLAWIDTH)
-                                       delete_nlachannel_keys ();
-                               
-                               update_for_newframe_muted();
+                               if (mval[0]>=NLAWIDTH) {
+                                       if (okee("Erase selected?")) {
+                                               delete_nlachannel_keys();
+                                               update_for_newframe_muted();
+                                               
+                                               remove_marker();
+                                               
+                                               allqueue(REDRAWMARKER, 0);
+                                       }
+                               }
                                break;
                                
                                /* LEFTMOUSE and RIGHTMOUSE event codes can be swapped above,
@@ -1669,7 +1964,7 @@ void winqreadnlaspace(ScrArea *sa, void *spacedata, BWinEvent *evt)
                                                
                                                areamouseco_to_ipoco(G.v2d, mval, &dx, &dy);
                                                
-                                               cfra= (int)dx;
+                                               cfra= (int)(dx+0.5f);
                                                if(cfra< 1) cfra= 1;
                                                
                                                if( cfra!=CFRA ) {
@@ -1717,3 +2012,72 @@ void winqreadnlaspace(ScrArea *sa, void *spacedata, BWinEvent *evt)
        if(doredraw) scrarea_queue_winredraw(curarea);
 }
 
+void bake_all_to_action(void)
+{
+       Object          *ob;
+       bAction         *newAction;
+       Ipo             *ipo;
+       ID              *id;
+       short           hold, add;
+       float           repeat;
+
+       /* burn object-level motion into a new action */
+       ob = get_object_from_active_strip();
+       if (ob) {
+               if (ob->flag&OB_ARMATURE) {
+                       //newAction = bake_obIPO_to_action(ob);
+                       newAction = NULL;
+                       if (newAction) {
+                               /* unlink the object's IPO */
+                               ipo=ob->ipo;
+                               if (ipo) {
+                                       id = &ipo->id;
+                                       if (id->us > 0)
+                                               id->us--;
+                                       ob->ipo = NULL;
+                               }
+                               
+                               /* add the new Action to NLA as a strip */
+                               hold=1;
+                               add=1;
+                               repeat=1.0;
+                               printf("about to add nla block...\n");
+                               add_nla_block_by_name(newAction->id.name, ob, hold, add, repeat);
+                               BIF_undo_push("Add NLA strip");
+                       }
+               }
+       }
+}
+
+void copy_action_modifiers(void)
+{
+       bActionStrip *strip, *actstrip;
+       Object *ob= OBACT;
+       
+       if(ob==NULL)
+               return;
+       
+       /* active strip */
+       for (actstrip=ob->nlastrips.first; actstrip; actstrip=actstrip->next)
+               if(actstrip->flag & ACTSTRIP_ACTIVE)
+                       break;
+       if(actstrip==NULL)
+               return;
+       
+       /* copy to selected items */
+       for (strip=ob->nlastrips.first; strip; strip=strip->next){
+               if (strip->flag & ACTSTRIP_SELECT) {
+                       if(strip!=actstrip) {
+                               if (strip->modifiers.first)
+                                       BLI_freelistN(&strip->modifiers);
+                               if (actstrip->modifiers.first)
+                                       duplicatelist (&strip->modifiers, &actstrip->modifiers);
+                       }
+               }
+       }
+       
+       BIF_undo_push("Copy Action Modifiers");
+       allqueue(REDRAWNLA, 0);
+       DAG_scene_flush_update(G.scene, screen_view3d_layers(), 0);
+}
+