== Action Editor - Copy and Paste Tools ==
[blender.git] / source / blender / src / editaction.c
index aa6bf38d2fcf163038c48d5b1c8d11e303466c61..9dcb7f683638aac92fb8f8093986c5e45d5baed9 100644 (file)
@@ -324,6 +324,10 @@ static void actdata_filter_action (ListBase *act_data, bAction *act, int filter_
                                                if (ale) BLI_addtail(act_data, ale);
                                        }
                                }
+                               else {
+                                       /* only consider selected channels - achan not selected */
+                                       continue;
+                               }       
                                
                                /* check if expanded - if not, continue on to next action channel */
                                if (EXPANDED_ACHAN(achan) == 0 && (filter_mode & ACTFILTER_ONLYICU)==0) 
@@ -380,19 +384,53 @@ static void actdata_filter_action (ListBase *act_data, bAction *act, int filter_
 static void actdata_filter_shapekey (ListBase *act_data, Key *key, int filter_mode)
 {
        bActListElem *ale;
+       KeyBlock *kb;
        IpoCurve *icu;
+       int i;
        
-       /* loop over ipo curves if present */
-       if (key->ipo) {
-               if (filter_mode & ACTFILTER_IPOKEYS) {
-                       ale= make_new_actlistelem(key->ipo, ACTTYPE_IPO, key, ACTTYPE_SHAPEKEY);
-                       if (ale) BLI_addtail(act_data, ale);
+       /* are we filtering for display or editing */
+       if (filter_mode & ACTFILTER_FORDRAWING) {
+               /* for display - loop over shapekeys, adding ipo-curve references where needed */
+               kb= key->block.first;
+               
+               /* loop through possible shapekeys, manually creating entries */
+               for (i= 1; i < key->totkey; i++) {
+                       ale= MEM_callocN(sizeof(bActListElem), "bActListElem");
+                       kb = kb->next;
+                       
+                       ale->data= kb;
+                       ale->type= ACTTYPE_SHAPEKEY; /* 'abused' usage of this type */
+                       ale->owner= key;
+                       ale->ownertype= ACTTYPE_SHAPEKEY;
+                       ale->datatype= ALE_NONE;
+                       ale->index = i;
+                       
+                       if (key->ipo) {
+                               for (icu= key->ipo->curve.first; icu; icu=icu->next) {
+                                       if (icu->adrcode == i) {
+                                               ale->key_data= icu;
+                                               ale->datatype= ALE_ICU;
+                                               break;
+                                       }
+                               }
+                       }
+                       
+                       BLI_addtail(act_data, ale);
                }
-               else {
-                       for (icu= key->ipo->curve.first; icu; icu=icu->next) {
-                               ale= make_new_actlistelem(icu, ACTTYPE_ICU, key, ACTTYPE_SHAPEKEY);
+       }
+       else {
+               /* loop over ipo curves if present - for editing */
+               if (key->ipo) {
+                       if (filter_mode & ACTFILTER_IPOKEYS) {
+                               ale= make_new_actlistelem(key->ipo, ACTTYPE_IPO, key, ACTTYPE_SHAPEKEY);
                                if (ale) BLI_addtail(act_data, ale);
                        }
+                       else {
+                               for (icu= key->ipo->curve.first; icu; icu=icu->next) {
+                                       ale= make_new_actlistelem(icu, ACTTYPE_ICU, key, ACTTYPE_SHAPEKEY);
+                                       if (ale) BLI_addtail(act_data, ale);
+                               }
+                       }
                }
        }
 }
@@ -446,6 +484,7 @@ void actdata_filter (ListBase *act_data, int filter_mode, void *data, short data
  * returns key data for RVK type meshes). If there
  * is an action that is pinned, return null
  */
+/* Note: there's a similar function in key.c (ob_get_key) */
 Key *get_action_mesh_key(void) 
 {
     Object *ob;
@@ -588,7 +627,7 @@ static void *get_nearest_action_key (float *selx, short *sel, short *ret_type, b
        areamouseco_to_ipoco(G.v2d, mval, &rectf.xmax, &rectf.ymax);
 
        /* if action is mapped in NLA, it returns a correction */
-       if (G.saction->pin==0 && OBACT && datatype==ACTCONT_ACTION) {
+       if (NLA_ACTION_SCALED && datatype==ACTCONT_ACTION) {
                xmin= get_action_frame(OBACT, rectf.xmin);
                xmax= get_action_frame(OBACT, rectf.xmax);
        }
@@ -603,7 +642,7 @@ static void *get_nearest_action_key (float *selx, short *sel, short *ret_type, b
        }
                
        /* filter data */
-       filter= (ACTFILTER_VISIBLE | ACTFILTER_CHANNELS);
+       filter= (ACTFILTER_FORDRAWING | ACTFILTER_VISIBLE | ACTFILTER_CHANNELS);
        actdata_filter(&act_data, filter, data, datatype);
        
        for (ale= act_data.first; ale; ale= ale->next) {
@@ -611,7 +650,6 @@ static void *get_nearest_action_key (float *selx, short *sel, short *ret_type, b
                        break;
                if (clickmin <= 0) {
                        /* found match */
-                       *ret_type= ale->type;
                        
                        /* make list of keyframes */
                        if (ale->key_data) {
@@ -644,9 +682,15 @@ static void *get_nearest_action_key (float *selx, short *sel, short *ret_type, b
                                *selx= ((xmax+xmin) / 2);
                        
                        /* figure out what to return */
-                       if (datatype == ACTCONT_ACTION)
+                       if (datatype == ACTCONT_ACTION) {
                                *par= ale->owner; /* assume that this is an action channel */
-                       data = ale->data;
+                               *ret_type= ale->type;
+                               data = ale->data;
+                       }
+                       else if (datatype == ACTCONT_SHAPEKEY) {
+                               data = ale->key_data;
+                               *ret_type= ACTTYPE_ICU;
+                       }
                        
                        /* cleanup tempolary lists */
                        BLI_freelistN(&act_keys);
@@ -737,7 +781,7 @@ static TransVert *transform_action_init (int *tvtot, float *minx, float *maxx)
                
        /* min max, only every other three */
        min= max= tv[1].loc[0];
-       for (i=1; i<count; i+=3){
+       for (i=1; i<count; i+=3) {
                if(min>tv[i].loc[0]) min= tv[i].loc[0];
                if(max<tv[i].loc[0]) max= tv[i].loc[0];
        }
@@ -751,14 +795,17 @@ static TransVert *transform_action_init (int *tvtot, float *minx, float *maxx)
        return tv;
 } 
 
-/* main transform loop for action editor */
+/* main transform loop for action editor 
+ * NOTE: yes, this is a very long function that really should be converted to
+ * using the transform system proper 
+ */
 static short transform_action_loop (TransVert *tv, int tvtot, char mode, short context, float minx, float maxx)
 {
        Object *ob= OBACT;
        float deltax, startx;
        float cenf[2];
        float sval[2], cval[2], lastcval[2]={0,0};
-       float fac=0.0f;
+       float fac=0.0f, secf= ((float)G.scene->r.frs_sec);
        int     loop=1, invert=0;
        int     i;
        short cancel=0, firsttime=1;
@@ -773,24 +820,25 @@ static short transform_action_loop (TransVert *tv, int tvtot, char mode, short c
        getmouseco_areawin (mvals);
        areamouseco_to_ipoco(G.v2d, mvals, &sval[0], &sval[1]);
 
-       if(G.saction->pin==0 && OBACT)
+       if (NLA_ACTION_SCALED)
                sval[0]= get_action_frame(OBACT, sval[0]);
        
        /* used for drawing */
-       if(mode=='t') {
+       if (mode=='t') {
                G.saction->flag |= SACTION_MOVING;
                G.saction->timeslide= sval[0];
        }
        
        startx=sval[0];
        while (loop) {
-               if(mode=='t' && minx==maxx)
+               if (mode=='t' && minx==maxx)
                        break;
                
-               /*              Get the input */
-               /*              If we're cancelling, reset transformations */
-               /*                      Else calc new transformation */
-               /*              Perform the transformations */
+               /* 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);
@@ -826,10 +874,10 @@ static short transform_action_loop (TransVert *tv, int tvtot, char mode, short c
                        getmouseco_areawin (mvalc);
                        areamouseco_to_ipoco(G.v2d, mvalc, &cval[0], &cval[1]);
                        
-                       if(G.saction->pin==0 && OBACT)
+                       if (NLA_ACTION_SCALED)
                                cval[0]= get_action_frame(OBACT, cval[0]);
 
-                       if(mode=='t')
+                       if (mode=='t')
                                G.saction->timeslide= cval[0];
                        
                        if (!firsttime && lastcval[0]==cval[0] && lastcval[1]==cval[1]) {
@@ -878,12 +926,16 @@ static short transform_action_loop (TransVert *tv, int tvtot, char mode, short c
                                                }
                                                break;
                                        case 'g':
-                                               if (G.saction->pin==0 && OBACT && context==ACTCONT_ACTION) {
+                                               if (NLA_ACTION_SCALED && context==ACTCONT_ACTION) {
                                                        deltax = get_action_frame_inv(OBACT, cval[0]);
                                                        deltax -= get_action_frame_inv(OBACT, sval[0]);
                                                        
-                                                       if (autosnap == SACTSNAP_STEP) 
-                                                               deltax= 1.0f*floor(deltax/1.0f + 0.5f);
+                                                       if (autosnap == SACTSNAP_STEP) {
+                                                               if (G.saction->flag & SACTION_DRAWTIME) 
+                                                                       deltax= (float)( floor((deltax/secf) + 0.5f) * secf );
+                                                               else
+                                                                       deltax= (float)( floor(deltax + 0.5f) );
+                                                       }
                                                        
                                                        fac = get_action_frame_inv(OBACT, tv[i].loc[0]);
                                                        fac += deltax;
@@ -893,8 +945,12 @@ static short transform_action_loop (TransVert *tv, int tvtot, char mode, short c
                                                        deltax = cval[0] - sval[0];
                                                        fac= deltax;
                                                        
-                                                       if (autosnap == SACTSNAP_STEP)
-                                                               fac= 1.0f*floor(fac/1.0f + 0.5f);
+                                                       if (autosnap == SACTSNAP_STEP) {
+                                                               if (G.saction->flag & SACTION_DRAWTIME)
+                                                                       fac= (float)( floor((deltax/secf) + 0.5f) * secf );
+                                                               else
+                                                                       fac= (float)( floor(fac + 0.5f) );
+                                                       }
                                                        
                                                        tv[i].loc[0]+=fac;
                                                }
@@ -905,27 +961,30 @@ static short transform_action_loop (TransVert *tv, int tvtot, char mode, short c
                                                fac= fabs(deltax/startx);
                                                
                                                if (autosnap == SACTSNAP_STEP) {
-                                                       fac= 1.0f*floor(fac/1.0f + 0.5f);
+                                                       if (G.saction->flag & SACTION_DRAWTIME)
+                                                               fac= (float)( floor(fac/secf + 0.5f) * secf );
+                                                       else
+                                                               fac= (float)( floor(fac + 0.5f) );
                                                }
                                                
-                                               if (invert){
-                                                       if (i % 03 == 0){
+                                               if (invert) {
+                                                       if (i % 03 == 0) {
                                                                memcpy (tv[i].loc, tv[i].oldloc, sizeof(tv[i+2].oldloc));
                                                        }
-                                                       if (i % 03 == 2){
+                                                       if (i % 03 == 2) {
                                                                memcpy (tv[i].loc, tv[i].oldloc, sizeof(tv[i-2].oldloc));
                                                        }
        
-                                                       fac*=-1;
+                                                       fac *= -1;
                                                }
                                                startx= (G.scene->r.cfra);
-                                               if(G.saction->pin==0 && OBACT && context==ACTCONT_ACTION)
+                                               if (NLA_ACTION_SCALED && context==ACTCONT_ACTION)
                                                        startx= get_action_frame(OBACT, startx);
                                                        
                                                tv[i].loc[0]-= startx;
                                                tv[i].loc[0]*=fac;
                                                tv[i].loc[0]+= startx;
-               
+                                               
                                                break;
                                        }
                                        
@@ -934,16 +993,19 @@ static short transform_action_loop (TransVert *tv, int tvtot, char mode, short c
                                                float snapval;
                                                
                                                /* convert frame to nla-action time (if needed) */
-                                               if (G.saction->pin==0 && OBACT && context==ACTCONT_ACTION) 
+                                               if (NLA_ACTION_SCALED && context==ACTCONT_ACTION) 
                                                        snapval= get_action_frame_inv(OBACT, tv[i].loc[0]);
                                                else
                                                        snapval= tv[i].loc[0];
                                                
                                                /* snap to nearest frame */
-                                               snapval= (float)(floor(snapval+0.5));
+                                               if (G.saction->flag & SACTION_DRAWTIME)
+                                                       snapval= (float)( floor((snapval/secf) + 0.5f) * secf );
+                                               else
+                                                       snapval= (float)( floor(snapval+0.5f) );
                                                        
                                                /* convert frame out of nla-action time */
-                                               if (G.saction->pin==0 && OBACT && context==ACTCONT_ACTION)
+                                               if (NLA_ACTION_SCALED && context==ACTCONT_ACTION)
                                                        tv[i].loc[0]= get_action_frame(OBACT, snapval);
                                                else
                                                        tv[i].loc[0]= snapval;
@@ -955,20 +1017,30 @@ static short transform_action_loop (TransVert *tv, int tvtot, char mode, short c
                                        headerprint(str);
                                }
                                else if (mode=='g') {
-                                       if(G.saction->pin==0 && OBACT && context==ACTCONT_ACTION) {
+                                       if (NLA_ACTION_SCALED && context==ACTCONT_ACTION) {
                                                /* recalculate the delta based on 'visual' times */
                                                fac = get_action_frame_inv(OBACT, cval[0]);
                                                fac -= get_action_frame_inv(OBACT, sval[0]);
-                                               
-                                               if (autosnap == SACTSNAP_STEP) 
-                                                       fac= 1.0f*floor(fac/1.0f + 0.5f);
                                        }
+                                       
+                                       if (autosnap == SACTSNAP_STEP) {
+                                               if (G.saction->flag & SACTION_DRAWTIME)
+                                                       fac= floor(fac/secf + 0.5f);
+                                               else
+                                                       fac= floor(fac + 0.5f);
+                                       }
+                                       else if (autosnap == SACTSNAP_FRAME) {
+                                               if (G.saction->flag & SACTION_DRAWTIME)
+                                                       fac= fac / secf;
+                                       }
+                                       
                                        sprintf(str, "deltaX: %.3f", fac);
                                        headerprint(str);
                                }
                                else if (mode=='t') {
-                                       float fac= 2.0*(cval[0]-sval[0])/(maxx-minx);
+                                       fac= 2.0*(cval[0]-sval[0])/(maxx-minx);
                                        CLAMP(fac, -1.0f, 1.0f);
+                                       
                                        sprintf(str, "TimeSlide: %.3f", fac);
                                        headerprint(str);
                                }
@@ -1118,11 +1190,17 @@ void snap_action_keys(short mode)
                        strcpy(str, "Snap Keys To Nearest Frame");
                        break;
                case 2:
-                       strcpy(str, "Snap Keys To Current Frame");
+                       if (G.saction->flag & SACTION_DRAWTIME)
+                               strcpy(str, "Snap Keys To Current Time");
+                       else
+                               strcpy(str, "Snap Keys To Current Frame");
                        break;
                case 3:
                        strcpy(str, "Snap Keys To Nearest Marker");
                        break;
+               case 4:
+                       strcpy(str, "Snap Keys To Nearest Second");
+                       break;
                default:
                        return;
        }
@@ -1133,7 +1211,7 @@ void snap_action_keys(short mode)
        
        /* snap to frame */
        for (ale= act_data.first; ale; ale= ale->next) {
-               if (datatype==ACTCONT_ACTION && G.saction->pin==0 && OBACT) {
+               if (NLA_ACTION_SCALED && datatype==ACTCONT_ACTION) {
                        actstrip_map_ipo_keys(OBACT, ale->key_data, 0, 1); 
                        snap_ipo_keys(ale->key_data, mode);
                        actstrip_map_ipo_keys(OBACT, ale->key_data, 1, 1);
@@ -1191,7 +1269,7 @@ void mirror_action_keys(short mode)
        
        /* mirror */
        for (ale= act_data.first; ale; ale= ale->next) {
-               if (datatype==ACTCONT_ACTION && G.saction->pin==0 && OBACT) {
+               if (NLA_ACTION_SCALED && datatype==ACTCONT_ACTION) {
                        actstrip_map_ipo_keys(OBACT, ale->key_data, 0, 1); 
                        mirror_ipo_keys(ale->key_data, mode);
                        actstrip_map_ipo_keys(OBACT, ale->key_data, 1, 1);
@@ -1239,7 +1317,7 @@ void insertkey_action(void)
        
                /* ask user what to keyframe */
                mode = pupmenu("Insert Key%t|All Channels%x1|Only Selected Channels%x2");
-               if (mode == 0) return;
+               if (mode <= 0) return;
                
                /* filter data */
                filter= (ACTFILTER_VISIBLE | ACTFILTER_FOREDIT | ACTFILTER_ONLYICU );
@@ -1257,7 +1335,7 @@ void insertkey_action(void)
                                if (ob)
                                        insertkey((ID *)ob, icu->blocktype, achan->name, NULL, icu->adrcode);
                                else
-                                       insert_vert_ipo(icu, cfra, icu->curval);
+                                       insert_vert_icu(icu, cfra, icu->curval);
                        }
                }
                
@@ -1270,11 +1348,11 @@ void insertkey_action(void)
                
                /* ask user if they want to insert a keyframe */
                mode = okee("Insert Keyframe?");
-               if (mode == 0) return;
+               if (mode <= 0) return;
                
                if (key->ipo) {
                        for (icu= key->ipo->curve.first; icu; icu=icu->next) {
-                               insert_vert_ipo(icu, cfra, icu->curval);
+                               insert_vert_icu(icu, cfra, icu->curval);
                        }
                }
        }
@@ -1416,19 +1494,182 @@ void clean_action (void)
        allqueue(REDRAWNLA, 0);
 }
 
+/* **************************************************** */
+/* COPY/PASTE FOR ACTIONS */
+/* - The copy/paste buffer currently stores a set of IPO curves, with no
+ *   repeating curve-types (i.e.  no curves with the same adrcode). 
+ * - Only selected keyframes from the source curves are placed here. 
+ * - Only 'compatible' pastes are done.
+ */
+
+/* globals for copy/paste data (like for other copy/paste buffers) */
+ListBase actcopybuf = {NULL, NULL};
+
+/* This function frees any MEM_calloc'ed copy/paste buffer data */
+void free_actcopybuf ()
+{
+       IpoCurve *icu;
+       
+       while( (icu= actcopybuf.first) ) {
+               BLI_remlink(&actcopybuf, icu);
+               free_ipo_curve(icu);
+       }
+}
+
+/* This function adds data to the copy/paste buffer, freeing existing data first
+ * Only the active action channel gets its selected keyframes copied.
+ */
+void copy_actdata ()
+{
+       ListBase act_data = {NULL, NULL};
+       bActListElem *ale;
+       int filter;
+       void *data;
+       short datatype;
+       
+       /* clear buffer first */
+       free_actcopybuf();
+       
+       /* get data */
+       data= get_action_context(&datatype);
+       if (data == NULL) return;
+       
+       /* filter data */
+       filter= (ACTFILTER_VISIBLE | ACTFILTER_SEL | ACTFILTER_ONLYICU);
+       actdata_filter(&act_data, filter, data, datatype);
+       
+       /* each of these entries should be an ipo curve */
+       for (ale= act_data.first; ale; ale= ale->next) {
+               IpoCurve *icu= ale->key_data;
+               IpoCurve *icn;
+               BezTriple *bezt;
+               short nin_buffer= 1;
+               int i;
+               
+               /* check if a curve like this exists already in buffer */
+               for (icn= actcopybuf.first; icn; icn= icn->next) {
+                       if ((icn->blocktype==icu->blocktype) && (icn->adrcode==icu->adrcode)) {
+                               nin_buffer= 0;
+                               break;
+                       }
+               }
+               /* allocate memory for a new curve if a valid one wasn't found */
+               if (nin_buffer) {
+                       icn= MEM_callocN(sizeof(IpoCurve), "actcopybuf");
+                       
+                       *icn= *icu;
+                       icn->totvert= 0;
+                       icn->bezt = NULL;
+                       icn->driver = NULL;
+                       
+                       BLI_addtail(&actcopybuf, icn);
+               }
+               
+               /* find selected BezTriples to add to the buffer */
+               for (i=0, bezt=icu->bezt; i < icu->totvert; i++, bezt++) {
+                       if (BEZSELECTED(bezt))
+                               insert_bezt_icu(icn, bezt);
+               }
+       }
+       
+       /* check if anything ended up in the buffer */
+       if (actcopybuf.first==NULL || actcopybuf.last==NULL)
+               error("Nothing copied to buffer");
+       
+       /* free temp memory */
+       BLI_freelistN(&act_data);
+}
+
+void paste_actdata ()
+{
+       ListBase act_data = {NULL, NULL};
+       bActListElem *ale;
+       int filter;
+       void *data;
+       short datatype;
+       
+       /* check if buffer is empty */
+       if (actcopybuf.first==NULL || actcopybuf.last==NULL) {
+               error("No data in buffer to paste");
+               return;
+       }
+       
+       /* get data */
+       data= get_action_context(&datatype);
+       if (data == NULL) return;
+       
+       /* filter data */
+       filter= (ACTFILTER_VISIBLE | ACTFILTER_SEL | ACTFILTER_FOREDIT | ACTFILTER_ONLYICU);
+       actdata_filter(&act_data, filter, data, datatype);
+       
+       /* from selected channels */
+       for (ale= act_data.first; ale; ale= ale->next) {
+               IpoCurve *icu= ale->key_data;
+               IpoCurve *ico;
+               BezTriple *bezt;
+               int i;
+               float offset= 0.0f;
+               short offsetInit= 1;
+               
+               /* find matching ipo-curve */
+               for (ico= actcopybuf.first; ico; ico= ico->next) {
+                       if ((ico->blocktype==icu->blocktype) && (ico->adrcode==icu->adrcode)) {
+                               /* just start pasting, with the the first keyframe on the current frame, and so on */
+                               for (i=0, bezt=ico->bezt; i < ico->totvert; i++, bezt++) {
+                                       /* initialise offset (if not already done) */
+                                       if (offsetInit) {
+                                               offset= CFRA - bezt->vec[1][0];
+                                               offsetInit= 0;
+                                       }
+                                       
+                                       /* temporarily apply offset to src beztriple while copying */
+                                       bezt->vec[0][0] += offset;
+                                       bezt->vec[1][0] += offset;
+                                       bezt->vec[2][0] += offset;
+                                       
+                                       /* insert the keyframe */
+                                       insert_bezt_icu(icu, bezt);
+                                       
+                                       /* un-apply offset from src beztriple after copying */
+                                       bezt->vec[0][0] -= offset;
+                                       bezt->vec[1][0] -= offset;
+                                       bezt->vec[2][0] -= offset;
+                               }
+                               
+                               /* recalculate channel's handles? */
+                               calchandles_ipocurve(icu);
+                               
+                               /* done for this channel */
+                               break;
+                       }
+               }
+       }
+       
+       /* free temp memory */
+       BLI_freelistN(&act_data);
+       
+       /* undo and redraw stuff */
+       allqueue(REDRAWVIEW3D, 0);
+       allspace(REMAKEIPO, 0);
+       allqueue(REDRAWACTION, 0);
+       allqueue(REDRAWIPO, 0);
+       allqueue(REDRAWNLA, 0);
+       BIF_undo_push("Paste Action Keyframes");
+}
+
 /* **************************************************** */
 /* VARIOUS SETTINGS */
 
-/* this function combines several features related to setting 
+/* This function combines several features related to setting 
  * various ipo extrapolation/interpolation
  */
-void action_set_ipo_flags (int mode)
+void action_set_ipo_flags (short mode, short event)
 {
        ListBase act_data = {NULL, NULL};
        bActListElem *ale;
        void *data;
        short datatype;
-       int filter, event;
+       int filter;
        
        /* determine what type of data we are operating on */
        data = get_action_context(&datatype);
@@ -1445,7 +1686,7 @@ void action_set_ipo_flags (int mode)
                                                   "Extrapolation %x12|"
                                                   "Cyclic %x13|"
                                                   "Cyclic extrapolation %x14");
-                       if(event < 1) return;
+                       if (event < 1) return;
                }
                        break;
                case SET_IPO_POPUP:
@@ -1456,16 +1697,20 @@ void action_set_ipo_flags (int mode)
                                                   "Constant %x1|"
                                                   "Linear %x2|"
                                                   "Bezier %x3");
-                       if(event < 1) return;
+                       if (event < 1) return;
                }
                        break;
                        
+               case SET_IPO_MENU:      /* called from menus */
+               case SET_EXTEND_MENU:
+                       break;
+                       
                default: /* weird, unhandled case */
                        return;
        }
        
        /* filter data */
-       filter= (ACTFILTER_VISIBLE | ACTFILTER_FOREDIT | ACTFILTER_IPOKEYS);
+       filter= (ACTFILTER_VISIBLE | ACTFILTER_SEL | ACTFILTER_FOREDIT | ACTFILTER_IPOKEYS);
        actdata_filter(&act_data, filter, data, datatype);
        
        /* loop through setting flags */
@@ -1475,6 +1720,7 @@ void action_set_ipo_flags (int mode)
                /* depending on the mode */
                switch (mode) {
                        case SET_EXTEND_POPUP: /* extrapolation */
+                       case SET_EXTEND_MENU:
                        {
                                switch (event) {
                                        case SET_EXTEND_CONSTANT:
@@ -1493,6 +1739,7 @@ void action_set_ipo_flags (int mode)
                        }
                                break;
                        case SET_IPO_POPUP: /* interpolation */
+                       case SET_IPO_MENU:
                        {
                                setipotype_ipo(ipo, event);
                        }
@@ -1552,7 +1799,7 @@ void sethandles_action_keys (int code)
 static void clever_keyblock_names (Key *key, short *mval)
 {
     KeyBlock   *kb;
-       int        but=0, i, keynum;
+       int        but=0, keynum;
     char       str[64];
        float      x;
        
@@ -1561,20 +1808,19 @@ static void clever_keyblock_names (Key *key, short *mval)
         * an invalid key number (and we don't deal
         * with the speed ipo).
         */
-
+       
     keynum = get_nearest_key_num(key, mval, &x);
     if ( (keynum < 1) || (keynum >= key->totkey) )
         return;
-
-       kb= key->block.first;
-       for (i=0; i<keynum; ++i) kb = kb->next; 
-
-       if (kb->name[0] == '\0') {
+               
+       kb= key_get_keyblock(key, keynum);
+       if (kb == NULL)
+               return;
+       
+       if (kb->name[0] == '\0')
                sprintf(str, "Key %d", keynum);
-       }
-       else {
+       else
                strcpy(str, kb->name);
-       }
 
        if ( (kb->slidermin >= kb->slidermax) ) {
                kb->slidermin = 0.0;
@@ -1640,7 +1886,10 @@ static void clever_achannel_names (short *mval)
        else if (chantype == ACTTYPE_ICU) {
                icu= (IpoCurve *)act_channel;
                
-               strcpy(str, getname_ipocurve(icu));
+               if (G.saction->pin)
+                       sprintf(str, getname_ipocurve(icu, NULL));
+               else
+                       sprintf(str, getname_ipocurve(icu, OBACT));
                
                if (IS_EQ(icu->slide_max, icu->slide_min)) {
                        if (IS_EQ(icu->ymax, icu->ymin)) {
@@ -2120,7 +2369,7 @@ void markers_selectkeys_between (void)
                
        /* select keys in-between */
        for (ale= act_data.first; ale; ale= ale->next) {
-               if(G.saction->pin==0 && OBACT && datatype==ACTCONT_ACTION) {
+               if(NLA_ACTION_SCALED && datatype==ACTCONT_ACTION) {
                        actstrip_map_ipo_keys(OBACT, ale->key_data, 0, 1);
                        borderselect_ipo_key(ale->key_data, min, max, SELECT_ADD);
                        actstrip_map_ipo_keys(OBACT, ale->key_data, 1, 1);
@@ -2189,7 +2438,7 @@ void column_select_action_keys(int mode)
                        make_marker_cfra_list(&elems, 1);
                        
                        /* apply scaled action correction if needed */
-                       if (G.saction->pin==0 && OBACT && datatype==ACTCONT_ACTION) {
+                       if (NLA_ACTION_SCALED && datatype==ACTCONT_ACTION) {
                                for (ce= elems.first; ce; ce= ce->next) 
                                        ce->cfra= get_action_frame(OBACT, ce->cfra);
                        }
@@ -2222,6 +2471,11 @@ void column_select_action_keys(int mode)
        BLI_freelistN(&elems);
 }
 
+/* some quick defines for borderselect modes */
+#define ACTEDIT_BORDERSEL_ALL 0
+#define ACTEDIT_BORDERSEL_FRA 1
+#define ACTEDIT_BORDERSEL_CHA 2
+
 /* borderselect: for keyframes only */
 void borderselect_action (void)
 {
@@ -2233,15 +2487,24 @@ void borderselect_action (void)
        
        rcti rect;
        rctf rectf;
-       int val, selectmode;
+       int val, selectmode, mode;
        int (*select_function)(BezTriple *);
-       short   mval[2];
-       float   ymin, ymax;
+       short mval[2];
+       float ymin, ymax;
        
        /* determine what type of data we are operating on */
        data = get_action_context(&datatype);
        if (data == NULL) return;
        
+       /* what should be selected (based on the starting location of cursor) */
+       getmouseco_areawin(mval);
+       if (IN_2D_VERT_SCROLL(mval)) 
+               mode = ACTEDIT_BORDERSEL_CHA;
+       else if (IN_2D_HORIZ_SCROLL(mval))
+               mode = ACTEDIT_BORDERSEL_FRA;
+       else
+               mode = ACTEDIT_BORDERSEL_ALL;
+       
        /* draw and handle the borderselect stuff (ui) and get the select rect */
        if ( (val = get_border(&rect, 3)) ) {
                if (val == LEFTMOUSE) {
@@ -2261,7 +2524,7 @@ void borderselect_action (void)
                areamouseco_to_ipoco(G.v2d, mval, &rectf.xmax, &rectf.ymax);
                
                /* if action is mapped in NLA, it returns a correction */
-               if (G.saction->pin==0 && OBACT && datatype==ACTCONT_ACTION) {
+               if (NLA_ACTION_SCALED && datatype==ACTCONT_ACTION) {
                        rectf.xmin= get_action_frame(OBACT, rectf.xmin);
                        rectf.xmax= get_action_frame(OBACT, rectf.xmax);
                }
@@ -2275,14 +2538,38 @@ void borderselect_action (void)
                /* loop over data, doing border select */
                for (ale= act_data.first; ale; ale= ale->next) {
                        ymin=ymax-(CHANNELHEIGHT+CHANNELSKIP);
-                       if (!((ymax < rectf.ymin) || (ymin > rectf.ymax))) {
+                       
+                       /* what gets selected depends on the mode (based on initial position of cursor) */
+                       switch (mode) {
+                       case ACTEDIT_BORDERSEL_FRA: /* all in frame(s) */
                                if (ale->key_data) {
                                        if (ale->datatype == ALE_IPO)
                                                borderselect_ipo_key(ale->key_data, rectf.xmin, rectf.xmax, selectmode);
                                        else if (ale->datatype == ALE_ICU)
                                                borderselect_icu_key(ale->key_data, rectf.xmin, rectf.xmax, select_function);
                                }
+                               break;
+                       case ACTEDIT_BORDERSEL_CHA: /* all in channel(s) */
+                               if (!((ymax < rectf.ymin) || (ymin > rectf.ymax))) {
+                                       if (ale->key_data) {
+                                               if (ale->datatype == ALE_IPO)
+                                                       select_ipo_bezier_keys(ale->key_data, selectmode);
+                                               else if (ale->datatype == ALE_ICU)
+                                                       select_icu_bezier_keys(ale->key_data, selectmode);
+                                       }
+                               }
+                               break;
+                       default: /* any keyframe inside region defined by region */
+                               if (!((ymax < rectf.ymin) || (ymin > rectf.ymax))) {
+                                       if (ale->key_data) {
+                                               if (ale->datatype == ALE_IPO)
+                                                       borderselect_ipo_key(ale->key_data, rectf.xmin, rectf.xmax, selectmode);
+                                               else if (ale->datatype == ALE_ICU)
+                                                       borderselect_icu_key(ale->key_data, rectf.xmin, rectf.xmax, select_function);
+                                       }
+                               }
                        }
+                       
                        ymax=ymin;
                }
                
@@ -2304,11 +2591,13 @@ static void mouse_action (int selectmode)
 {
        void *data;
        short datatype;
+       
        bAction *act= NULL;
        bActionChannel *achan= NULL;
        bConstraintChannel *conchan= NULL;
        IpoCurve *icu= NULL;
        TimeMarker *marker;
+       
        void *act_channel;
        short sel, act_type;
        float selx;
@@ -2329,8 +2618,11 @@ static void mouse_action (int selectmode)
                        case ACTTYPE_CONCHAN:
                                conchan= (bConstraintChannel *)act_channel;
                                break;
-                       default:
+                       case ACTTYPE_ACHAN:
                                achan= (bActionChannel *)act_channel;
+                               break;
+                       default:
+                               return;
                }
                
                if (selectmode == SELECT_REPLACE) {
@@ -2693,7 +2985,7 @@ void winqreadactionspace(ScrArea *sa, void *spacedata, BWinEvent *evt)
        short val= evt->val;
        short mousebut = L_MOUSE;
 
-       if(curarea->win==0) return;
+       if (curarea->win==0) return;
 
        saction= curarea->spacedata.first;
        if (!saction)
@@ -2789,12 +3081,12 @@ void winqreadactionspace(ScrArea *sa, void *spacedata, BWinEvent *evt)
                        break;
                
                case HKEY:
-                       if(G.qual & LR_SHIFTKEY) {
-                               if(okee("Set Keys to Auto Handle"))
+                       if (G.qual & LR_SHIFTKEY) {
+                               if (okee("Set Keys to Auto Handle"))
                                        sethandles_action_keys(HD_AUTO);
                        }
                        else {
-                               if(okee("Toggle Keys Aligned Handle"))
+                               if (okee("Toggle Keys Aligned Handle"))
                                        sethandles_action_keys(HD_ALIGN);
                        }
                        break;
@@ -2818,7 +3110,11 @@ void winqreadactionspace(ScrArea *sa, void *spacedata, BWinEvent *evt)
                        if (G.qual & LR_SHIFTKEY) {
                                /* mirror keyframes */
                                if (data) {
-                                       val = pupmenu("Mirror Keys Over%t|Current Frame%x1|Vertical Axis%x2|Horizontal Axis %x3|Selected Marker %x4");
+                                       if (G.saction->flag & SACTION_DRAWTIME)
+                                               val = pupmenu("Mirror Keys Over%t|Current Time%x1|Vertical Axis%x2|Horizontal Axis %x3|Selected Marker %x4");
+                                       else
+                                               val = pupmenu("Mirror Keys Over%t|Current Frame%x1|Vertical Axis%x2|Horizontal Axis %x3|Selected Marker %x4");
+                                       
                                        mirror_action_keys(val);
                                }
                        }
@@ -2857,6 +3153,7 @@ void winqreadactionspace(ScrArea *sa, void *spacedata, BWinEvent *evt)
                                anim_previewrange_set();
                        else if (G.qual & LR_ALTKEY) /* clear preview range */
                                anim_previewrange_clear();
+                               
                        allqueue(REDRAWTIME, 0);
                        allqueue(REDRAWBUTSALL, 0);
                        allqueue(REDRAWACTION, 0);
@@ -2866,29 +3163,44 @@ void winqreadactionspace(ScrArea *sa, void *spacedata, BWinEvent *evt)
                        
                case SKEY: 
                        if (mval[0]>=ACTWIDTH) {
-                               if(G.qual & LR_SHIFTKEY) {
+                               if (G.qual & LR_SHIFTKEY) {
                                        if (data) {
-                                               val = pupmenu("Snap Keys To%t|Nearest Frame%x1|Current Frame%x2|Nearest Marker %x3");
+                                               if (G.saction->flag & SACTION_DRAWTIME)
+                                                       val = pupmenu("Snap Keys To%t|Nearest Second%x4|Current Time%x2|Nearest Marker %x3");
+                                               else
+                                                       val = pupmenu("Snap Keys To%t|Nearest Frame%x1|Current Frame%x2|Nearest Marker %x3");
+                                               
                                                snap_action_keys(val);
                                        }
                                }
-                               else
-                                       transform_action_keys ('s', 0); 
+                               else {
+                                       transform_action_keys('s', 0);  
+                               }
                        }
                        break;
                
                case TKEY:
-                       if(G.qual & LR_SHIFTKEY)
-                               action_set_ipo_flags(SET_IPO_POPUP);
+                       if (G.qual & LR_SHIFTKEY)
+                               action_set_ipo_flags(SET_IPO_POPUP, 0);
+                       else if (G.qual & LR_CTRLKEY) {
+                               val= pupmenu("Time value%t|Frames %x1|Seconds%x2");
+                               
+                               if (val > 0) {
+                                       if (val == 2) saction->flag |= SACTION_DRAWTIME;
+                                       else saction->flag &= ~SACTION_DRAWTIME;
+                                       
+                                       doredraw= 1;
+                               }
+                       }                               
                        else
                                transform_action_keys ('t', 0);
                        break;
-
+               
                case VKEY:
-                       if(okee("Set Keys to Vector Handle"))
+                       if (okee("Set Keys to Vector Handle"))
                                sethandles_action_keys(HD_VECT);
                        break;
-
+               
                case PAGEUPKEY:
                        if (datatype == ACTCONT_ACTION) {
                                if(G.qual & LR_SHIFTKEY)
@@ -2943,7 +3255,7 @@ void winqreadactionspace(ScrArea *sa, void *spacedata, BWinEvent *evt)
                case LEFTMOUSE:
                        if(view2dmove(LEFTMOUSE)) // only checks for sliders
                                break;
-                       else if (mval[0]>ACTWIDTH) {
+                       else if ((G.v2d->mask.xmin==0) || (mval[0]>ACTWIDTH)) {
                                do {
                                        getmouseco_areawin(mval);
                                        
@@ -2965,7 +3277,7 @@ void winqreadactionspace(ScrArea *sa, void *spacedata, BWinEvent *evt)
                        /* passed on as selection */
                case RIGHTMOUSE:
                        /* Clicking in the channel area */
-                       if (mval[0]<NAMEWIDTH) {
+                       if ((G.v2d->mask.xmin) && (mval[0]<NAMEWIDTH)) {
                                if (datatype == ACTCONT_ACTION) {
                                        /* mouse is over action channels */
                                        if (G.qual & LR_CTRLKEY)
@@ -2975,7 +3287,7 @@ void winqreadactionspace(ScrArea *sa, void *spacedata, BWinEvent *evt)
                                }
                                else numbuts_action();
                        }
-                       else if (mval[0]>ACTWIDTH) {
+                       else {
                                short select_mode= (G.qual & LR_SHIFTKEY)? SELECT_INVERT: SELECT_REPLACE;
                                
                                /* Clicking in the vertical scrollbar selects
@@ -3002,7 +3314,7 @@ void winqreadactionspace(ScrArea *sa, void *spacedata, BWinEvent *evt)
                        view2d_zoom(G.v2d, 0.1154, sa->winx, sa->winy);
                        test_view2d(G.v2d, sa->winx, sa->winy);
                        view2d_do_locks(curarea, V2D_LOCK_COPY);
-
+                       
                        doredraw= 1;
                        break;
                case PADMINUS: