== Action Editor - Copy and Paste Tools ==
[blender.git] / source / blender / src / editaction.c
index 683e5e3c2160c5f38304acdca6a12f24a4cb3a95..9dcb7f683638aac92fb8f8093986c5e45d5baed9 100644 (file)
@@ -25,7 +25,7 @@
  *
  * The Original Code is: all of this file.
  *
- * Contributor(s): none yet.
+ * Contributor(s): 2007, Joshua Leung (major rewrite of Action Editor)
  *
  * ***** END GPL/BL DUAL LICENSE BLOCK *****
  */
@@ -84,6 +84,7 @@
 #include "BSE_edit.h"
 #include "BSE_drawipo.h"
 #include "BSE_headerbuttons.h"
+#include "BSE_editaction_types.h"
 #include "BSE_editipo.h"
 #include "BSE_time.h"
 #include "BSE_trans_types.h"
 #include "blendef.h"
 #include "nla.h"
 
-/* Local Function prototypes, are forward needed */
-static void hilight_channel (bAction *act, bActionChannel *chan, short hilight);
+/* **************************************************** */
+/* ACTION API */
 
-/* messy call... */
-static void select_poseelement_by_name (char *name, int select)
+/* this function adds a new Action block */
+bAction *add_empty_action (char *name)
 {
-       /* Syncs selection of channels with selection of object elements in posemode */
-       Object *ob= OBACT;
-       bPoseChannel *pchan;
+       bAction *act;
        
-       if (!ob || ob->type!=OB_ARMATURE)
-               return;
+       act= alloc_libblock(&G.main->action, ID_AC, name);
+       act->id.flag |= LIB_FAKEUSER;
+       act->id.us++;
        
-       if(select==2) {
-               for(pchan= ob->pose->chanbase.first; pchan; pchan= pchan->next)
-                       pchan->bone->flag &= ~(BONE_ACTIVE);
-       }
+       return act;
+}
+
+/* generic get current action call, for action window context */
+bAction *ob_get_action (Object *ob)
+{
+       bActionStrip *strip;
        
-       pchan= get_pose_channel(ob->pose, name);
-       if(pchan) {
-               if(select)
-                       pchan->bone->flag |= (BONE_SELECTED);
-               else 
-                       pchan->bone->flag &= ~(BONE_SELECTED);
-               if(select==2)
-                       pchan->bone->flag |= (BONE_ACTIVE);
+       if(ob->action)
+               return ob->action;
+       
+       for (strip=ob->nlastrips.first; strip; strip=strip->next) {
+               if (strip->flag & ACTSTRIP_SELECT)
+                       return strip->act;
        }
+       return NULL;
 }
 
-/* apparently within active object context */
-/* called extern, like on bone selection */
-void select_actionchannel_by_name (bAction *act, char *name, int select)
+/* used by ipo, outliner, buttons to find the active channel */
+bActionChannel *get_hilighted_action_channel (bAction *action)
 {
-       bActionChannel *chan;
+       bActionChannel *achan;
 
-       if (!act)
-               return;
+       if (!action)
+               return NULL;
 
-       for (chan = act->chanbase.first; chan; chan=chan->next){
-               if (!strcmp (chan->name, name)){
-                       if (select){
-                               chan->flag |= ACHAN_SELECTED;
-                               hilight_channel (act, chan, 1);
-                       }
-                       else{
-                               chan->flag &= ~ACHAN_SELECTED;
-                               hilight_channel (act, chan, 0);
-                       }
-                       return;
+       for (achan= action->chanbase.first; achan; achan= achan->next) {
+               if(VISIBLE_ACHAN(achan)) {
+                       if (SEL_ACHAN(achan) && (achan->flag & ACHAN_HILIGHTED))
+                               return achan;
                }
        }
+
+       return NULL;
 }
 
-/* called on changing action ipos or keys */
-void remake_action_ipos(bAction *act)
+/* ----------------------------------------- */
+
+void remake_action_ipos (bAction *act)
 {
-       bActionChannel *chan;
+       bActionChannel *achan;
        bConstraintChannel *conchan;
-       IpoCurve                *icu;
+       IpoCurve *icu;
 
-       for (chan= act->chanbase.first; chan; chan=chan->next){
-               if (chan->ipo){
-                       for (icu = chan->ipo->curve.first; icu; icu=icu->next){
+       for (achan= act->chanbase.first; achan; achan= achan->next) {
+               if (achan->ipo) {
+                       for (icu = achan->ipo->curve.first; icu; icu=icu->next) {
                                sort_time_ipocurve(icu);
                                testhandles_ipocurve(icu);
                        }
                }
-               for (conchan=chan->constraintChannels.first; conchan; conchan=conchan->next){
-                       if (conchan->ipo){
+               for (conchan=achan->constraintChannels.first; conchan; conchan=conchan->next){
+                       if (conchan->ipo) {
                                for (icu = conchan->ipo->curve.first; icu; icu=icu->next){
                                        sort_time_ipocurve(icu);
                                        testhandles_ipocurve(icu);
@@ -175,7 +172,7 @@ void remake_action_ipos(bAction *act)
        synchronize_action_strips();
 }
 
-static void remake_meshaction_ipos(Ipo *ipo)
+static void remake_meshaction_ipos (Ipo *ipo)
 {
        /* this puts the bezier triples in proper
         * order and makes sure the bezier handles
@@ -183,13 +180,13 @@ static void remake_meshaction_ipos(Ipo *ipo)
         */
        IpoCurve *icu;
 
-       for (icu = ipo->curve.first; icu; icu=icu->next){
+       for (icu = ipo->curve.first; icu; icu=icu->next) {
                sort_time_ipocurve(icu);
                testhandles_ipocurve(icu);
        }
 }
 
-static void meshkey_do_redraw(Key *key)
+static void meshkey_do_redraw (Key *key)
 {
        if(key->ipo)
                remake_meshaction_ipos(key->ipo);
@@ -203,824 +200,617 @@ static void meshkey_do_redraw(Key *key)
 
 }
 
-void duplicate_meshchannel_keys(Key *key)
-{
-       duplicate_ipo_keys(key->ipo);
-       transform_meshchannel_keys ('g', key);
-}
-
+/* **************************************************** */
+/* FILTER->EDIT STRUCTURES */
+/* 
+ * This method involves generating a list of edit structures which enable
+ * tools to naively perform the actions they require without all the boiler-plate
+ * associated with loops within loops and checking for cases to ignore. 
+ */
 
-void duplicate_actionchannel_keys(void)
+/* this function allocates memory for a new bActListElem struct for the 
+ * provided action channel-data. 
+ */
+bActListElem *make_new_actlistelem (void *data, short datatype, void *owner, short ownertype)
 {
-       bAction *act;
-       bActionChannel *achan;
-       bConstraintChannel *conchan;
-
-       act=G.saction->action;
-       if (!act)
-               return;
-
-       /* Find selected items */
-       for (achan = act->chanbase.first; achan; achan= achan->next){
-               if(EDITABLE_ACHAN(achan))
-                       duplicate_ipo_keys(achan->ipo);
+       bActListElem *ale= NULL;
+       
+       /* only allocate memory if there is data to convert */
+       if (data) {
+               /* allocate and set generic data */
+               ale= MEM_callocN(sizeof(bActListElem), "bActListElem");
                
-               if (VISIBLE_ACHAN(achan)) {
-                       for (conchan=achan->constraintChannels.first; conchan; conchan=conchan->next) {
-                               if (EDITABLE_CONCHAN(conchan))
-                                       duplicate_ipo_keys(conchan->ipo);
+               ale->data= data;
+               ale->type= datatype;
+               ale->owner= owner;
+               ale->ownertype= ownertype;
+               
+               /* do specifics */
+               switch (datatype) {
+                       case ACTTYPE_ACHAN:
+                       {
+                               bActionChannel *achan= (bActionChannel *)data;
+                               
+                               ale->flag= achan->flag;
+                               
+                               if (achan->ipo) {
+                                       ale->key_data= achan->ipo;
+                                       ale->datatype= ALE_IPO;
+                               }
+                               else {
+                                       ale->key_data= NULL;
+                                       ale->datatype= ALE_NONE;
+                               }
+                       }       
+                               break;
+                       case ACTTYPE_CONCHAN:
+                       {
+                               bConstraintChannel *conchan= (bConstraintChannel *)data;
+                               
+                               ale->flag= conchan->flag;
+                               
+                               if (conchan->ipo && conchan->ipo->curve.first) {
+                                       /* we assume that constraint ipo blocks only have 1 curve:
+                                        * INFLUENCE, so we pretend that a constraint channel is 
+                                        * really just a Ipo-Curve channel instead.
+                                        */
+                                       ale->key_data= conchan->ipo->curve.first;
+                                       ale->datatype= ALE_ICU;
+                               }
+                               else {
+                                       ale->key_data= NULL;
+                                       ale->datatype= ALE_NONE;
+                               }
+                       }
+                               break;
+                       case ACTTYPE_ICU:
+                       {
+                               IpoCurve *icu= (IpoCurve *)data;
+                               
+                               ale->flag= icu->flag;
+                               ale->key_data= icu;
+                               ale->datatype= ALE_ICU;
+                       }
+                               break;
+                               
+                       case ACTTYPE_FILLIPO:
+                       case ACTTYPE_FILLCON:
+                       {
+                               bActionChannel *achan= (bActionChannel *)data;
+                               
+                               if (datatype == ACTTYPE_FILLIPO)
+                                       ale->flag= FILTER_IPO_ACHAN(achan);
+                               else
+                                       ale->flag= FILTER_CON_ACHAN(achan);
+                                       
+                               ale->key_data= NULL;
+                               ale->datatype= ALE_NONE;
                        }
+                               break;
+                       case ACTTYPE_IPO:
+                       {
+                               ale->flag= 0;
+                               ale->key_data= data;
+                               ale->datatype= ALE_IPO;
+                       }
+                               break;
                }
        }
-
-       transform_actionchannel_keys ('g', 0);
+       
+       /* return created datatype */
+       return ale;
 }
-
-static bActionChannel *get_nearest_actionchannel_key (float *index, short *sel, bConstraintChannel **rchan)
+/* ----------------------------------------- */
+static void actdata_filter_action (ListBase *act_data, bAction *act, int filter_mode)
 {
-       bAction *act;
-       bActionChannel *chan;
+       bActListElem *ale;
+       bActionChannel *achan;
+       bConstraintChannel *conchan;
        IpoCurve *icu;
-       bActionChannel *firstchan=NULL;
-       bConstraintChannel *conchan, *firstconchan=NULL;
-       rctf    rectf;
-       float firstvert=-1, foundx=-1;
-       float ymin, ymax, xmin, xmax;
-       int i;
-       int     foundsel=0;
-       short mval[2];
-       
-       *index=0;
-
-       *rchan=NULL;
-       act= G.saction->action; /* We presume that we are only called during a valid action */
-       
-       getmouseco_areawin (mval);
-
-       mval[0]-=7;
-       areamouseco_to_ipoco(G.v2d, mval, &rectf.xmin, &rectf.ymin);
-       mval[0]+=14;
-       areamouseco_to_ipoco(G.v2d, mval, &rectf.xmax, &rectf.ymax);
-
-       ymax = count_action_levels(act) * (CHANNELHEIGHT + CHANNELSKIP);
-       ymax += CHANNELHEIGHT/2;
-       
-       /* if action is mapped in NLA, it returns a correction */
-       if(G.saction->pin==0 && OBACT) {
-               xmin= get_action_frame(OBACT, rectf.xmin);
-               xmax= get_action_frame(OBACT, rectf.xmax);
-       }
-       else {
-               xmin= rectf.xmin;
-               xmax= rectf.xmax;
-       }
        
-       *sel=0;
-
-       for (chan=act->chanbase.first; chan; chan=chan->next){
-               if((chan->flag & ACHAN_HIDDEN)==0) {
-
-                       /* Check action channel */
-                       ymin= ymax-(CHANNELHEIGHT+CHANNELSKIP);
-                       if (!((ymax < rectf.ymin) || (ymin > rectf.ymax)) && chan->ipo){
-                               for (icu=chan->ipo->curve.first; icu; icu=icu->next){
-                                       for (i=0; i<icu->totvert; i++){
-                                               if (icu->bezt[i].vec[1][0] > xmin && icu->bezt[i].vec[1][0] <= xmax ){
-                                                       if (!firstchan){
-                                                               firstchan=chan;
-                                                               firstvert=icu->bezt[i].vec[1][0];
-                                                               *sel = icu->bezt[i].f2 & 1;     
-                                                       }
-                                                       
-                                                       if (icu->bezt[i].f2 & 1){ 
-                                                               if (!foundsel){
-                                                                       foundsel=1;
-                                                                       foundx = icu->bezt[i].vec[1][0];
-                                                               }
-                                                       }
-                                                       else if (foundsel && icu->bezt[i].vec[1][0] != foundx){
-                                                               *index=icu->bezt[i].vec[1][0];
-                                                               *sel = 0;
-                                                               return chan;
-                                                       }
+       /* loop over action channels, performing the necessary checks */
+       for (achan= act->chanbase.first; achan; achan= achan->next) {
+               /* only work with this channel and its subchannels if it is visible */
+               if (!(filter_mode & ACTFILTER_VISIBLE) || VISIBLE_ACHAN(achan)) {
+                       /* only work with this channel and its subchannels if it is editable */
+                       if (!(filter_mode & ACTFILTER_FOREDIT) || EDITABLE_ACHAN(achan)) {
+                               /* check if this achan should only be included if it is selected */
+                               if (!(filter_mode & ACTFILTER_SEL) || SEL_ACHAN(achan)) {
+                                       /* are we only interested in the ipo-curves? */
+                                       if ((filter_mode & ACTFILTER_ONLYICU)==0) {
+                                               ale= make_new_actlistelem(achan, ACTTYPE_ACHAN, achan, ACTTYPE_ACHAN);
+                                               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) 
+                                       continue;
+                                       
+                               /* ipo channels */
+                               if (achan->ipo) {
+                                       /* include ipo-expand widget? */
+                                       if ((filter_mode & ACTFILTER_CHANNELS) && (filter_mode & ACTFILTER_ONLYICU)==0) {
+                                               ale= make_new_actlistelem(achan, ACTTYPE_FILLIPO, achan, ACTTYPE_ACHAN);
+                                               if (ale) BLI_addtail(act_data, ale);
+                                       }
+                                       
+                                       /* add ipo-curve channels? */
+                                       if (FILTER_IPO_ACHAN(achan) || (filter_mode & ACTFILTER_ONLYICU)) {
+                                               /* loop through ipo-curve channels, adding them */
+                                               for (icu= achan->ipo->curve.first; icu; icu=icu->next) {
+                                                       ale= make_new_actlistelem(icu, ACTTYPE_ICU, achan, ACTTYPE_ACHAN);
+                                                       if (ale) BLI_addtail(act_data, ale); 
                                                }
                                        }
                                }
-                       }
-                       ymax=ymin;
-                       
-                       /* Check constraint channels */
-                       for (conchan=chan->constraintChannels.first; conchan; conchan=conchan->next){
-                               ymin=ymax-(CHANNELHEIGHT+CHANNELSKIP);
-                               if (!((ymax < rectf.ymin) || (ymin > rectf.ymax)) && conchan->ipo) {
-                                       for (icu=conchan->ipo->curve.first; icu; icu=icu->next){
-                                               for (i=0; i<icu->totvert; i++){
-                                                       if (icu->bezt[i].vec[1][0] > xmin && icu->bezt[i].vec[1][0] <= xmax ){
-                                                               if (!firstchan){
-                                                                       firstchan=chan;
-                                                                       firstconchan=conchan;
-                                                                       firstvert=icu->bezt[i].vec[1][0];
-                                                                       *sel = icu->bezt[i].f2 & 1;     
-                                                               }
-                                                               
-                                                               if (icu->bezt[i].f2 & 1){ 
-                                                                       if (!foundsel){
-                                                                               foundsel=1;
-                                                                               foundx = icu->bezt[i].vec[1][0];
+                               
+                               /* constraint channels */
+                               if (achan->constraintChannels.first) {
+                                       /* include constraint-expand widget? */
+                                       if ((filter_mode & ACTFILTER_CHANNELS) && (filter_mode & ACTFILTER_ONLYICU)==0) {
+                                               ale= make_new_actlistelem(achan, ACTTYPE_FILLCON, achan, ACTTYPE_ACHAN);
+                                               if (ale) BLI_addtail(act_data, ale);
+                                       }
+                                       
+                                       /* add constaint channels? */
+                                       if (FILTER_CON_ACHAN(achan)) {
+                                               /* loop through constraint channels, checking and adding them */
+                                               for (conchan=achan->constraintChannels.first; conchan; conchan=conchan->next) {
+                                                       /* only work with this channel and its subchannels if it is editable */
+                                                       if (!(filter_mode & ACTFILTER_FOREDIT) || EDITABLE_CONCHAN(conchan)) {
+                                                               /* check if this conchan should only be included if it is selected */
+                                                               if (!(filter_mode & ACTFILTER_SEL) || SEL_CONCHAN(conchan)) {
+                                                                       if ((filter_mode & ACTFILTER_ONLYICU)==0) {
+                                                                               ale= make_new_actlistelem(conchan, ACTTYPE_CONCHAN, achan, ACTTYPE_ACHAN);
+                                                                               if (ale) BLI_addtail(act_data, ale); 
                                                                        }
                                                                }
-                                                               else if (foundsel && icu->bezt[i].vec[1][0] != foundx){
-                                                                       *index=icu->bezt[i].vec[1][0];
-                                                                       *sel = 0;
-                                                                       *rchan = conchan;
-                                                                       return chan;
-                                                               }
                                                        }
                                                }
                                        }
                                }
-                               ymax=ymin;
-                       }
+                       }               
                }
-       }       
-       
-       *rchan = firstconchan;
-       *index=firstvert;
-       return firstchan;
-}
-
-static IpoCurve *get_nearest_meshchannel_key (float *index, short *sel)
-{
-       /* This function tries to find the RVK key that is
-        * closest to the user's mouse click
-        */
-    Key      *key;
-    IpoCurve *icu; 
-    IpoCurve *firsticu=NULL;
-    int             foundsel=0;
-    float    firstvert=-1, foundx=-1;
-       int      i;
-    short    mval[2];
-    float    ymin, ymax, ybase;
-    rctf     rectf;
-
-    *index=0;
-
-    key = get_action_mesh_key();
-       
-    /* lets get the mouse position and process it so 
-     * we can start testing selections
-     */
-    getmouseco_areawin (mval);
-    mval[0]-=7;
-    areamouseco_to_ipoco(G.v2d, mval, &rectf.xmin, &rectf.ymin);
-    mval[0]+=14;
-    areamouseco_to_ipoco(G.v2d, mval, &rectf.xmax, &rectf.ymax);
-
-    ybase = key->totkey * (CHANNELHEIGHT + CHANNELSKIP);
-       ybase += CHANNELHEIGHT/2;
-    *sel=0;
-
-    /* lets loop through the IpoCurves trying to find the closest
-     * bezier
-     */
-       if (!key->ipo) return NULL;
-    for (icu = key->ipo->curve.first; icu ; icu = icu->next) {
-        /* lets not deal with the "speed" Ipo
-         */
-        if (!icu->adrcode) continue;
-
-        ymax = ybase   - (CHANNELHEIGHT+CHANNELSKIP)*(icu->adrcode-1);
-        ymin=ymax-(CHANNELHEIGHT+CHANNELSKIP);
-
-        /* Does this curve coorespond to the right
-         * strip?
-         */
-        if (!((ymax < rectf.ymin) || (ymin > rectf.ymax))){
-                       
-            /* loop through the beziers in the curve
-             */
-            for (i=0; i<icu->totvert; i++){
-
-                /* Is this bezier in the right area?
-                 */
-                if (icu->bezt[i].vec[1][0] > rectf.xmin && 
-                    icu->bezt[i].vec[1][0] <= rectf.xmax ){
-
-                    /* if no other curves have been picked ...
-                     */
-                    if (!firsticu){
-                        /* mark this curve/bezier as the first
-                         * selected
-                         */
-                        firsticu=icu;
-                        firstvert=icu->bezt[i].vec[1][0];
-
-                        /* sel = (is the bezier is already selected) ? 1 : 0;
-                         */
-                        *sel = icu->bezt[i].f2 & 1;    
-                    }
-
-                    /* if the bezier is selected ...
-                     */
-                    if (icu->bezt[i].f2 & 1){ 
-                        /* if we haven't found a selected one yet ...
-                         */
-                        if (!foundsel){
-                            /* record the found x value
-                             */
-                            foundsel=1;
-                            foundx = icu->bezt[i].vec[1][0];
-                        }
-                    }
-
-                    /* if the bezier is unselected and not at the x
-                     * position of a previous found selected bezier ...
-                     */
-                    else if (foundsel && icu->bezt[i].vec[1][0] != foundx){
-                        /* lets return this found curve/bezier
-                         */
-                        *index=icu->bezt[i].vec[1][0];
-                        *sel = 0;
-                        return icu;
-                    }
-                }
-            }
-        }
-       }
-       
-    /* return what we've found
-     */
-    *index=firstvert;
-    return firsticu;
+       }
 }
 
-/* This function makes a list of the selected keyframes
- * in the ipo curves it has been passed
- */
-static void make_sel_cfra_list(Ipo *ipo, ListBase *elems)
+static void actdata_filter_shapekey (ListBase *act_data, Key *key, int filter_mode)
 {
+       bActListElem *ale;
+       KeyBlock *kb;
        IpoCurve *icu;
-       BezTriple *bezt;
-       int a;
+       int i;
        
-       for(icu= ipo->curve.first; icu; icu= icu->next) {
-
-               bezt= icu->bezt;
-               if(bezt) {
-                       a= icu->totvert;
-                       while(a--) {
-                               if(bezt->f2 & 1) {
-                                       add_to_cfra_elem(elems, bezt);
+       /* 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 {
+               /* 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);
                                }
-                               bezt++;
                        }
                }
        }
 }
-
-/* This function selects all key frames in the same column(s) as a already selected key(s)
- * this version only works for Shape Keys, Key should be not NULL
+/* This function filters the active data source to leave only the desired
+ * data types. 'Public' api call.
+ *     *act_data: is a pointer to a ListBase, to which the filtered action data 
+ *             will be placed for use.
+ *     filter_mode: how should the data be filtered - bitmapping accessed flags
  */
-void column_select_shapekeys(Key *key, int mode)
+void actdata_filter (ListBase *act_data, int filter_mode, void *data, short datatype)
 {
-       if(key->ipo) {
-               IpoCurve *icu;
-               ListBase elems= {NULL, NULL};
-               CfraElem *ce;
+       /* only filter data if there's somewhere to put it */
+       if (data && act_data) {
+               bActListElem *ale, *next;
                
-               /* build list of columns */
-               switch (mode) {
-                       case 1:
-                               /* create a list of all selected keys */
-                               make_sel_cfra_list(key->ipo, &elems);
+               /* firstly filter the data */
+               switch (datatype) {
+                       case ACTCONT_ACTION:
+                               actdata_filter_action(act_data, data, filter_mode);
                                break;
-                       case 2:
-                               /* create a list of all selected markers */
-                               make_marker_cfra_list(&elems, 1);
+                       case ACTCONT_SHAPEKEY:
+                               actdata_filter_shapekey(act_data, data, filter_mode);
                                break;
                }
-               
-               /* loop through all of the keys and select additional keyframes
-                       * based on the keys found to be selected above
-                       */
-               for(ce= elems.first; ce; ce= ce->next) {
-                       for (icu = key->ipo->curve.first; icu ; icu = icu->next) {
-                               BezTriple *bezt= icu->bezt;
-                               if(bezt) {
-                                       int verts = icu->totvert;
-                                       while(verts--) {
-                                               if( ((int)ce->cfra) == ((int)bezt->vec[1][0]) ) {
-                                                       bezt->f2 |= 1;
-                                               }
-                                               bezt++;
-                                       }
-                               }
+                       
+               /* remove any weedy entries */
+               for (ale= act_data->first; ale; ale= next) {
+                       next= ale->next;
+                       
+                       if (ale->type == ACTTYPE_NONE)
+                               BLI_freelinkN(act_data, ale);
+                       
+                       if (filter_mode & ACTFILTER_IPOKEYS) {
+                               if (ale->datatype != ALE_IPO)
+                                       BLI_freelinkN(act_data, ale);
+                               else if (ale->key_data == NULL)
+                                       BLI_freelinkN(act_data, ale);
                        }
                }
-
-               BLI_freelistN(&elems);
        }
 }
 
-/* This function selects all key frames in the same column(s) as a already selected key(s)
- * this version only works for on Action. *act should be not NULL
+/* **************************************************** */
+/* GENERAL ACTION TOOLS */
+
+
+/* gets the key data from the currently selected
+ * mesh/lattice. If a mesh is not selected, or does not have
+ * key data, then we return NULL (currently only
+ * returns key data for RVK type meshes). If there
+ * is an action that is pinned, return null
  */
-void column_select_actionkeys(bAction *act, int mode)
+/* Note: there's a similar function in key.c (ob_get_key) */
+Key *get_action_mesh_key(void) 
 {
-       IpoCurve *icu;
-       BezTriple *bezt;
-       ListBase elems= {NULL, NULL};
-       CfraElem *ce;
-       bActionChannel *achan;
-       bConstraintChannel *conchan;
+    Object *ob;
+    Key    *key;
 
-       /* build list of columns */
-       switch (mode) {
-               case 1:
-                       /* create a list of all selected keys */
-                       for (achan=act->chanbase.first; achan; achan=achan->next){
-                               if((achan->flag & ACHAN_HIDDEN)==0) {
-                                       if (achan->ipo)
-                                               make_sel_cfra_list(achan->ipo, &elems);
-                                       for (conchan=achan->constraintChannels.first; conchan; conchan=conchan->next) {
-                                               if (conchan->ipo)
-                                                       make_sel_cfra_list(conchan->ipo, &elems);
-                                       }
-                               }
-                       }
-                       break;
-               case 2:
-                       /* create a list of all selected markers */
-                       make_marker_cfra_list(&elems, 1);
-                       
-                       /* apply scaled action correction if needed */
-                       if (G.saction->pin==0 && OBACT) {
-                               for (ce= elems.first; ce; ce= ce->next) 
-                                       ce->cfra= get_action_frame(OBACT, ce->cfra);
-                       }
-                       break;
-       }
-       
-       /* loop through all of the keys and select additional keyframes
-        * based on the keys found to be selected above
-        */
-       for (achan=act->chanbase.first; achan; achan=achan->next){
-               if((achan->flag & ACHAN_HIDDEN)==0) {
-                       if (achan->ipo) {
-                               for(ce= elems.first; ce; ce= ce->next) {
-                                       for (icu = achan->ipo->curve.first; icu; icu = icu->next){
-                                               bezt= icu->bezt;
-                                               if(bezt) {
-                                                       int verts = icu->totvert;
-                                                       while(verts--) {
-                                               
-                                                               if( (int)(ce->cfra) == (int)(bezt->vec[1][0]) ) {
-                                                                       bezt->f2 |= 1;
-                                                               }
-                                                               bezt++;
-                                                       }
-                                               }
-                                       }
-                               }
-                       }
+    ob = OBACT;
+    if (!ob) return NULL;
 
+       if (G.saction->pin) return NULL;
 
-                       for (conchan=achan->constraintChannels.first; conchan; conchan=conchan->next) {
-                               if (conchan->ipo) {
-                                       for(ce= elems.first; ce; ce= ce->next) {
-                                               for (icu = conchan->ipo->curve.first; icu; icu = icu->next){
-                                                       bezt= icu->bezt;
-                                                       if(bezt) {
-                                                               int verts = icu->totvert;
-                                                               while(verts--) {
-                                                       
-                                                                       if( ((int)ce->cfra) == ((int)bezt->vec[1][0]) ) {
-                                                                               bezt->f2 |= 1;
-                                                                       }
-                                                                       bezt++;
-                                                               }
-                                                       }
-                                               }
-                                       }
-                               }
-                       }               
-               }
+    if (ob->type==OB_MESH ) {
+               key = ((Mesh *)ob->data)->key;
+    }
+       else if (ob->type==OB_LATTICE ) {
+               key = ((Lattice *)ob->data)->key;
        }
-       BLI_freelistN(&elems);
+       else return NULL;
+
+       if (key) {
+               if (key->type == KEY_RELATIVE)
+                       return key;
+       }
+
+    return NULL;
 }
 
-/* apparently within active object context */
-static void mouse_action(int selectmode)
+/* TODO: kill this! */
+int get_nearest_key_num (Key *key, short *mval, float *x) 
 {
-       bAction *act;
-       short sel;
-       float   selx;
-       bActionChannel *chan;
-       bConstraintChannel *conchan;
-       TimeMarker *marker;
-       short   mval[2];
+       /* returns the key num that cooresponds to the
+        * y value of the mouse click. Does not check
+        * if this is a valid keynum. Also gives the Ipo
+        * x coordinate.
+        */
+    int num;
+    float y;
 
-       act=G.saction->action;
-       if (!act)
-               return;
+    areamouseco_to_ipoco(G.v2d, mval, x, &y);
+    num = (int) ((CHANNELHEIGHT/2 - y) / (CHANNELHEIGHT+CHANNELSKIP));
 
-       getmouseco_areawin (mval);
+    return (num + 1);
+}
 
-       chan=get_nearest_actionchannel_key(&selx, &sel, &conchan);
-       marker=find_nearest_marker(1);
+/* this function is used to get a pointer to an action or shapekey 
+ * datablock, thus simplying that process.
+ */
+/* this function is intended for use */
+void *get_nearest_act_channel (short mval[], short *ret_type)
+{
+       ListBase act_data = {NULL, NULL};
+       bActListElem *ale;
+       void *data;
+       short datatype;
+       int filter;
        
-       if (chan){
-               if (selectmode == SELECT_REPLACE) {
-                       selectmode = SELECT_ADD;
+       int clickmin, clickmax;
+       float x,y;
+       
+       /* determine what type of data we are operating on */
+       data = get_action_context(&datatype);
+       if (data == NULL) {
+               *ret_type= ACTTYPE_NONE;
+               return NULL;
+       }
+       
+    areamouseco_to_ipoco(G.v2d, mval, &x, &y);
+       clickmin = (int) (((CHANNELHEIGHT/2) - y) / (CHANNELHEIGHT+CHANNELSKIP));
+       clickmax = clickmin;
+       
+       if (clickmax < 0) {
+               *ret_type= ACTTYPE_NONE;
+               return NULL;
+       }
+       
+       /* filter data */
+       filter= (ACTFILTER_VISIBLE | ACTFILTER_CHANNELS);
+       actdata_filter(&act_data, filter, data, datatype);
+       
+       for (ale= act_data.first; ale; ale= ale->next) {
+               if (clickmax < 0) 
+                       break;
+               if (clickmin <= 0) {
+                       /* found match */
+                       *ret_type= ale->type;
+                       data= ale->data;
                        
-                       deselect_actionchannel_keys(act, 0, 0);
-                       deselect_actionchannels(act, 0);
+                       BLI_freelistN(&act_data);
                        
-                       chan->flag |= ACHAN_SELECTED;
-                       hilight_channel (act, chan, 1);
-                       select_poseelement_by_name(chan->name, 2);      /* 2 is activate */
-               }
-               
-               if (conchan)
-                       select_ipo_key(conchan->ipo, selx, selectmode);
-               else
-                       select_ipo_key(chan->ipo, selx, selectmode);
-
-               
-               std_rmouse_transform(transform_actionchannel_keys);
-               
-               allqueue(REDRAWIPO, 0);
-               allqueue(REDRAWVIEW3D, 0);
-               allqueue(REDRAWACTION, 0);
-               allqueue(REDRAWNLA, 0);
-               allqueue(REDRAWOOPS, 0);
-               allqueue(REDRAWBUTSALL, 0);
-       }
-       else if (marker) {
-               /* not channel, so maybe 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;
+                       return data;
                }
-               else if (selectmode == SELECT_ADD) 
-                       marker->flag |= SELECT;
-               else if (selectmode == SELECT_SUBTRACT)
-                       marker->flag &= ~SELECT;
-               
-               std_rmouse_transform(transform_markers);
-               
-               allqueue(REDRAWTIME, 0);
-               allqueue(REDRAWIPO, 0);
-               allqueue(REDRAWACTION, 0);
-               allqueue(REDRAWNLA, 0);
-               allqueue(REDRAWSOUND, 0);
+               --clickmin;
+               --clickmax;
        }
+       
+       /* cleanup */
+       BLI_freelistN(&act_data);
+       
+       *ret_type= ACTTYPE_NONE;
+       return NULL;
 }
 
-static void mouse_mesh_action(int selectmode, Key *key)
+/* used only by mouse_action. It is used to find the location of the nearest 
+ * keyframe to where the mouse clicked, 
+ */
+static void *get_nearest_action_key (float *selx, short *sel, short *ret_type, bActionChannel **par)
 {
-       /* Handle a right mouse click selection in an
-        * action window displaying RVK data
-        */
-
-    IpoCurve *icu;
-       TimeMarker *marker;
-    short  sel;
-    float  selx;
-    short  mval[2];
-
-    /* going to assume that the only reason 
-     * we got here is because it has been 
-     * determined that we are a mesh with
-     * the right properties (i.e., have key
-     * data, etc)
-     */
-
-       marker= find_nearest_marker(1);
-        
-       /* get the click location, and the cooresponding
-        * ipo curve and selection time value
-        */
-    getmouseco_areawin (mval);
-    icu = get_nearest_meshchannel_key(&selx, &sel);
-
-    if (icu){
-        if (selectmode == SELECT_REPLACE) {
-                       /* if we had planned to replace the
-                        * selection, then we will first deselect
-                        * all of the keys, and if the clicked on
-                        * key had been unselected, we will select 
-                        * it, otherwise, we are done.
-                        */
-            deselect_meshchannel_keys(key, 0, 0);
-
-            if (sel == 0)
-                selectmode = SELECT_ADD;
-            else
-                               /* the key is selected so we should
-                                * deselect -- but everything is now deselected
-                                * so we are done.
-                                */
-                               return;
-        }
+       ListBase act_data = {NULL, NULL};
+       ListBase act_keys = {NULL, NULL};
+       bActListElem *ale;
+       ActKeyColumn *ak;
+       void *data;
+       short datatype;
+       int filter;
+       
+       rctf rectf;
+       float xmin, xmax, x, y;
+       int clickmin, clickmax;
+       short mval[2];
+       short found = 0;
                
-               /* select the key using the given mode
-                * and redraw as mush stuff as needed.
-                */
-               select_icu_key(icu, selx, selectmode);
-
-               BIF_undo_push("Select Action key");
-        allqueue(REDRAWIPO, 0);
-        allqueue(REDRAWVIEW3D, 0);
-        allqueue(REDRAWACTION, 0);
-        allqueue(REDRAWNLA, 0);
+       getmouseco_areawin (mval);
 
-    }
-       else if (marker) {
-               /* not channel, so maybe 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(REDRAWTIME, 0);
-               allqueue(REDRAWIPO, 0);
-               allqueue(REDRAWACTION, 0);
-               allqueue(REDRAWNLA, 0);
-               allqueue(REDRAWSOUND, 0);
+       /* action-channel */
+       *par= NULL;
+       
+       /* determine what type of data we are operating on */
+       data = get_action_context(&datatype);
+       if (data == NULL) {
+               *ret_type= ACTTYPE_NONE;
+               return NULL;
        }
-}
-
-void borderselect_action(void)
-{ 
-       rcti rect;
-       rctf rectf;
-       int val, selectmode;            
-       short   mval[2];
-       bActionChannel *chan;
-       bConstraintChannel *conchan;
-       bAction *act;
-       float   ymin, ymax;
 
-       act=G.saction->action;
-
-       if (!act)
-               return;
+    areamouseco_to_ipoco(G.v2d, mval, &x, &y);
+    clickmin = (int) (((CHANNELHEIGHT/2) - y) / (CHANNELHEIGHT+CHANNELSKIP));
+       clickmax = clickmin;
+       
+       mval[0]-=7;
+       areamouseco_to_ipoco(G.v2d, mval, &rectf.xmin, &rectf.ymin);
+       mval[0]+=14;
+       areamouseco_to_ipoco(G.v2d, mval, &rectf.xmax, &rectf.ymax);
 
-       if ( (val = get_border(&rect, 3)) ){
-               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);
-               mval[0]= rect.xmax;
-               mval[1]= rect.ymax-2;
-               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) {
-                       rectf.xmin= get_action_frame(OBACT, rectf.xmin);
-                       rectf.xmax= get_action_frame(OBACT, rectf.xmax);
-               }
-               
-               ymax= count_action_levels(act) * (CHANNELHEIGHT+CHANNELSKIP);
-               ymax += CHANNELHEIGHT/2;
+       /* if action is mapped in NLA, it returns a correction */
+       if (NLA_ACTION_SCALED && datatype==ACTCONT_ACTION) {
+               xmin= get_action_frame(OBACT, rectf.xmin);
+               xmax= get_action_frame(OBACT, rectf.xmax);
+       }
+       else {
+               xmin= rectf.xmin;
+               xmax= rectf.xmax;
+       }
+       
+       if (clickmax < 0) {
+               *ret_type= ACTTYPE_NONE;
+               return NULL;
+       }
                
-               for (chan=act->chanbase.first; chan; chan=chan->next){
-                       if((chan->flag & ACHAN_HIDDEN)==0) {
-                               
-                               /* Check action */
-                               ymin=ymax-(CHANNELHEIGHT+CHANNELSKIP);
-                               if (!((ymax < rectf.ymin) || (ymin > rectf.ymax)))
-                                       borderselect_ipo_key(chan->ipo, rectf.xmin, rectf.xmax,
-                                                                  selectmode);
-
-                               ymax=ymin;
-
-                               /* Check constraints */
-                               for (conchan=chan->constraintChannels.first; conchan; conchan=conchan->next){
-                                       ymin=ymax-(CHANNELHEIGHT+CHANNELSKIP);
-                                       if (!((ymax < rectf.ymin) || (ymin > rectf.ymax)))
-                                               borderselect_ipo_key(conchan->ipo, rectf.xmin, rectf.xmax,
-                                                                  selectmode);
-                                       
-                                       ymax=ymin;
+       /* filter data */
+       filter= (ACTFILTER_FORDRAWING | ACTFILTER_VISIBLE | ACTFILTER_CHANNELS);
+       actdata_filter(&act_data, filter, data, datatype);
+       
+       for (ale= act_data.first; ale; ale= ale->next) {
+               if (clickmax < 0) 
+                       break;
+               if (clickmin <= 0) {
+                       /* found match */
+                       
+                       /* make list of keyframes */
+                       if (ale->key_data) {
+                               switch (ale->datatype) {
+                                       case ALE_IPO:
+                                       {
+                                               Ipo *ipo= (Ipo *)ale->key_data;
+                                               ipo_to_keylist(ipo, &act_keys, NULL);
+                                       }
+                                               break;
+                                       case ALE_ICU:
+                                       {
+                                               IpoCurve *icu= (IpoCurve *)ale->key_data;
+                                               icu_to_keylist(icu, &act_keys, NULL);
+                                       }
+                                               break;
                                }
                        }
-               }       
-               
-               BIF_undo_push("Border Select Action");
-               allqueue(REDRAWIPO, 0);
-               allqueue(REDRAWACTION, 0);
-               allqueue(REDRAWNLA, 0);
-       }
-}
-
-void borderselect_mesh(Key *key)
-{ 
-       rcti     rect;
-       int      val, adrcodemax, adrcodemin;
-       short    mval[2];
-       float    xmin, xmax;
-       int      selectmode;
-       int      (*select_function)(BezTriple *);
-       IpoCurve *icu;
-
-               
-       if ( (val = get_border(&rect, 3)) ){
-               /* set the selection function based on what
-                * mouse button had been used in the border
-                * select
-                */
-               if (val == LEFTMOUSE) {
-                       selectmode = SELECT_ADD;
-                       select_function = select_bezier_add;
-               }
-               else {
-                       selectmode = SELECT_SUBTRACT;
-                       select_function = select_bezier_subtract;
-               }       
-                               
-               /* get the minimum and maximum adrcode numbers
-                * for the IpoCurves (this is the number that
-                * relates an IpoCurve to the keyblock it
-                * controls).
-                */
-               mval[0]= rect.xmin;
-               mval[1]= rect.ymin+2;
-               adrcodemax = get_nearest_key_num(key, mval, &xmin);
-               adrcodemax = (adrcodemax >= key->totkey) ? key->totkey : adrcodemax;
-
-               mval[0]= rect.xmax;
-               mval[1]= rect.ymax-2;
-               adrcodemin = get_nearest_key_num(key, mval, &xmax);
-               adrcodemin = (adrcodemin < 1) ? 1 : adrcodemin;
-               
-               /* Lets loop throug the IpoCurves and do borderselect
-                * on the curves with adrcodes in our selected range.
-                */
-               if(key->ipo) {
-                       for (icu = key->ipo->curve.first; icu ; icu = icu->next) {
-                               /* lets not deal with the "speed" Ipo
-                                */
-                               if (!icu->adrcode) continue;
-                               if ( (icu->adrcode >= adrcodemin) && 
-                                        (icu->adrcode <= adrcodemax) ) {
-                                       borderselect_icu_key(icu, xmin, xmax, select_function);
+                       
+                       /* loop through keyframes, finding one that was clicked on */
+                       for (ak= act_keys.first; ak; ak= ak->next) {
+                               if (IN_RANGE(ak->cfra, xmin, xmax)) {
+                                       *selx= ak->cfra;
+                                       found= 1;
+                                       break;
                                }
                        }
+                       /* no matching keyframe found - set to mean frame value so it doesn't actually select anything */
+                       if (found == 0)
+                               *selx= ((xmax+xmin) / 2);
+                       
+                       /* figure out what to return */
+                       if (datatype == ACTCONT_ACTION) {
+                               *par= ale->owner; /* assume that this is an action channel */
+                               *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);
+                       act_keys.first = act_keys.last = NULL;
+                       
+                       BLI_freelistN(&act_data);
+                       
+                       return data;
                }
-               
-               /* redraw stuff */
-               BIF_undo_push("Border select Action Key");
-               allqueue(REDRAWIPO, 0);
-               allqueue(REDRAWACTION, 0);
-               allqueue(REDRAWNLA, 0);
+               --clickmin;
+               --clickmax;
        }
-}
-
-/* ******************** action API ***************** */
-
-/* generic get current action call, for action window context */
-bAction *ob_get_action(Object *ob)
-{
-       bActionStrip *strip;
        
-       if(ob->action)
-               return ob->action;
+       /* cleanup */
+       BLI_freelistN(&act_data);
        
-       for (strip=ob->nlastrips.first; strip; strip=strip->next){
-               if (strip->flag & ACTSTRIP_SELECT)
-                       return strip->act;
-       }
+       *ret_type= ACTTYPE_NONE;
        return NULL;
 }
 
-/* used by ipo, outliner, buttons to find the active channel */
-bActionChannel* get_hilighted_action_channel(bAction* action)
+void *get_action_context (short *datatype)
 {
-       bActionChannel *chan;
-
-       if (!action)
+       bAction *act;
+       Key *key;
+       
+       /* get pointers to active action/shapekey blocks */
+       act = (G.saction)? G.saction->action: NULL;
+       key = get_action_mesh_key();
+       
+       if (act) {
+               *datatype= ACTCONT_ACTION;
+               return act;
+       }
+       else if (key) {
+               *datatype= ACTCONT_SHAPEKEY;
+               return key;
+       }
+       else {
+               *datatype= ACTCONT_NONE;
                return NULL;
-
-       for (chan=action->chanbase.first; chan; chan=chan->next){
-               if((chan->flag & ACHAN_HIDDEN)==0)
-                       if (chan->flag & ACHAN_SELECTED && chan->flag & ACHAN_HILIGHTED)
-                               return chan;
        }
-
-       return NULL;
-
 }
 
-void set_exprap_action(int mode)
-{
-       if(G.saction->action && G.saction->action->id.lib) return;
-
-       error ("Not yet implemented!");
-}
+/* **************************************************** */
+/* TRANSFORM TOOLS */
 
-bAction *add_empty_action(int blocktype)
+/* initialise the transform data - create transverts */
+static TransVert *transform_action_init (int *tvtot, float *minx, float *maxx)
 {
-       bAction *act;
-       char *str= "Action";
+       ListBase act_data = {NULL, NULL};
+       bActListElem *ale;
+       TransVert *tv;
+       void *data;
+       short datatype;
+       int filter;
+       int count= 0;
+       float min= 0, max= 0;
+       int i;
        
-       if(blocktype==ID_OB)
-               str= "ObAction";
-       else if(blocktype==ID_KE)
-               str= "ShapeAction";
+       /* initialise the values being passed by reference */
+       *tvtot = *minx = *maxx = 0;
        
-       act= alloc_libblock(&G.main->action, ID_AC, str);
-       act->id.flag |= LIB_FAKEUSER;
-       act->id.us++;
-       return act;
-}
-
-void transform_actionchannel_keys(int mode, int dummy)
-{
-       bAction *act;
-       TransVert *tv;
-       Object *ob= OBACT;
-       bActionChannel  *achan;
-       bConstraintChannel *conchan;
-       float   deltax, startx;
-       float   minx, maxx, cenf[2];
-       float   sval[2], cval[2], lastcval[2]={0,0};
-       float   fac=0.0f;
-       int             loop=1;
-       int             tvtot=0;
-       int             invert=0, firsttime=1;
-       int             i;
-       short   cancel=0;
-       short   mvals[2], mvalc[2], cent[2];
-       char    str[256];
-
-       act=G.saction->action;
-       if(act==NULL) return;
-       
-       /* Ensure that partial selections result in beztriple selections */
-       for (achan=act->chanbase.first; achan; achan= achan->next){
-               if (EDITABLE_ACHAN(achan))
-                       tvtot+=fullselect_ipo_keys(achan->ipo);
-
-               if (VISIBLE_ACHAN(achan)) {
-                       for (conchan=achan->constraintChannels.first; conchan; conchan=conchan->next) {
-                               if (EDITABLE_CONCHAN(conchan))
-                                       tvtot+=fullselect_ipo_keys(conchan->ipo);
-                       }
-               }
-       }
+       /* determine what type of data we are operating on */
+       data = get_action_context(&datatype);
+       if (data == NULL) return NULL;
        
-       /* If nothing is selected, bail out */
-       if (!tvtot)
-               return;
+       /* filter data */
+       filter= (ACTFILTER_VISIBLE | ACTFILTER_FOREDIT | ACTFILTER_IPOKEYS);
+       actdata_filter(&act_data, filter, data, datatype);
        
+       /* loop 1: fully select ipo-keys and count how many BezTriples are selected */
+       for (ale= act_data.first; ale; ale= ale->next)
+               count += fullselect_ipo_keys(ale->key_data);
        
-       /* Build the transvert structure */
-       tv = MEM_callocN (sizeof(TransVert) * tvtot, "transVert");
-       
-       tvtot=0;
-       for (achan=act->chanbase.first; achan; achan= achan->next){
-               if(EDITABLE_ACHAN(achan))
-                       tvtot = add_trans_ipo_keys(achan->ipo, tv, tvtot);
-                       
-               if (VISIBLE_ACHAN(achan)) {
-                       for (conchan=achan->constraintChannels.first; conchan; conchan=conchan->next) {
-                               if (EDITABLE_CONCHAN(conchan))
-                                       tvtot = add_trans_ipo_keys(conchan->ipo, tv, tvtot);
-                       }
-               }
+       /* stop if trying to build list if nothing selected */
+       if (count == 0) {
+               /* cleanup temp list */
+               BLI_freelistN(&act_data);
+               return NULL;
        }
+               
+       /* Build the transvert structure */
+       tv = MEM_callocN (sizeof(TransVert) * count, "transVert");
        
+       /* loop 2: build transvert structure */
+       for (ale= act_data.first; ale; ale= ale->next)
+               *tvtot = add_trans_ipo_keys(ale->key_data, tv, *tvtot);
+               
        /* min max, only every other three */
-       minx= maxx= tv[1].loc[0];
-       for (i=1; i<tvtot; i+=3){
-               if(minx>tv[i].loc[0]) minx= tv[i].loc[0];
-               if(maxx<tv[i].loc[0]) maxx= tv[i].loc[0];
+       min= max= tv[1].loc[0];
+       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];
        }
+       *minx= min;
+       *maxx= max;
+       
+       /* cleanup temp list */
+       BLI_freelistN(&act_data);
+       
+       /* return created transverts */
+       return tv;
+} 
+
+/* 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, secf= ((float)G.scene->r.frs_sec);
+       int     loop=1, invert=0;
+       int     i;
+       short cancel=0, firsttime=1;
+       short mvals[2], mvalc[2], cent[2];
+       char str[256];
        
        /* Do the event loop */
        cent[0] = curarea->winx + (G.saction->v2d.hor.xmax)/2;
@@ -1030,25 +820,25 @@ void transform_actionchannel_keys(int mode, int dummy)
        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);
@@ -1079,24 +869,47 @@ void transform_actionchannel_keys(int mode, int dummy)
                                tv[i].loc[0]=tv[i].oldloc[0];
                                tv[i].loc[1]=tv[i].oldloc[1];
                        }
-               } else {
+               } 
+               else {
                        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]) {
                                PIL_sleep_ms(1);
-                       } else {
+                       } 
+                       else {
+                               short autosnap= 0;
+                               
+                               /* determine mode of keyframe snapping/autosnap */
+                               if (mode != 't') {
+                                       switch (G.saction->autosnap) {
+                                       case SACTSNAP_OFF:
+                                               if (G.qual == LR_CTRLKEY) 
+                                                       autosnap= SACTSNAP_STEP;
+                                               else if (G.qual == LR_SHIFTKEY)
+                                                       autosnap= SACTSNAP_FRAME;
+                                               else
+                                                       autosnap= SACTSNAP_OFF;
+                                               break;
+                                       case SACTSNAP_STEP:
+                                               autosnap= (G.qual==LR_CTRLKEY)? SACTSNAP_OFF: SACTSNAP_STEP;
+                                               break;
+                                       case SACTSNAP_FRAME:
+                                               autosnap= (G.qual==LR_SHIFTKEY)? SACTSNAP_OFF: SACTSNAP_FRAME;
+                                               break;
+                                       }
+                               }
                                
-                               for (i=0; i<tvtot; i++){
+                               for (i=0; i<tvtot; i++) {
                                        tv[i].loc[0]=tv[i].oldloc[0];
 
-                                       switch (mode){
+                                       switch (mode) {
                                        case 't':
                                                if( sval[0] > minx && sval[0] < maxx) {
                                                        float timefac, cvalc= CLAMPIS(cval[0], minx, maxx);
@@ -1113,71 +926,145 @@ void transform_actionchannel_keys(int mode, int dummy)
                                                }
                                                break;
                                        case 'g':
-                                               deltax = cval[0]-sval[0];
-                                               fac= deltax;
-                                               
-                                               apply_keyb_grid(&fac, 0.0, 1.0, 0.1, U.flag & USER_AUTOGRABGRID);
-
-                                               tv[i].loc[0]+=fac;
+                                               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) {
+                                                               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;
+                                                       tv[i].loc[0] = get_action_frame(OBACT, fac);
+                                               }
+                                               else {
+                                                       deltax = cval[0] - sval[0];
+                                                       fac= deltax;
+                                                       
+                                                       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;
+                                               }
                                                break;
                                        case 's':
                                                startx=mvals[0]-(ACTWIDTH/2+(curarea->winrct.xmax-curarea->winrct.xmin)/2);
                                                deltax=mvalc[0]-(ACTWIDTH/2+(curarea->winrct.xmax-curarea->winrct.xmin)/2);
                                                fac= fabs(deltax/startx);
                                                
-                                               apply_keyb_grid(&fac, 0.0, 0.2, 0.1, U.flag & USER_AUTOSIZEGRID);
-               
-                                               if (invert){
-                                                       if (i % 03 == 0){
+                                               if (autosnap == SACTSNAP_STEP) {
+                                                       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) {
                                                                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)
+                                               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;
                                        }
+                                       
+                                       /* snap key to nearest frame? */
+                                       if (autosnap == SACTSNAP_FRAME) {
+                                               float snapval;
+                                               
+                                               /* convert frame to nla-action time (if needed) */
+                                               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 */
+                                               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 (NLA_ACTION_SCALED && context==ACTCONT_ACTION)
+                                                       tv[i].loc[0]= get_action_frame(OBACT, snapval);
+                                               else
+                                                       tv[i].loc[0]= snapval;
+                                       }
                                }
        
-                               if (mode=='s'){
+                               if (mode=='s') {
                                        sprintf(str, "scaleX: %.3f", fac);
                                        headerprint(str);
                                }
-                               else if (mode=='g'){
-                                       if(G.saction->pin==0 && OBACT) {
+                               else if (mode=='g') {
+                                       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) {
+                                               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);
                                }
                
                                if (G.saction->lock) {
-                                       if(ob) {
-                                               ob->ctime= -1234567.0f;
-                                               if(ob->pose || ob_get_key(ob))
-                                                       DAG_object_flush_update(G.scene, ob, OB_RECALC);
-                                               else
-                                                       DAG_object_flush_update(G.scene, ob, OB_RECALC_OB);
+                                       if (context == ACTCONT_ACTION) {
+                                               if(ob) {
+                                                       ob->ctime= -1234567.0f;
+                                                       if(ob->pose || ob_get_key(ob))
+                                                               DAG_object_flush_update(G.scene, ob, OB_RECALC);
+                                                       else
+                                                               DAG_object_flush_update(G.scene, ob, OB_RECALC_OB);
+                                               }
+                                               force_draw_plus(SPACE_VIEW3D, 0);
+                                       }
+                                       else if (context == ACTCONT_SHAPEKEY) {
+                                               DAG_object_flush_update(G.scene, OBACT, OB_RECALC_OB|OB_RECALC_DATA);
+                               allqueue (REDRAWVIEW3D, 0);
+                               allqueue (REDRAWACTION, 0);
+                               allqueue (REDRAWIPO, 0);
+                               allqueue(REDRAWNLA, 0);
+                                               allqueue(REDRAWTIME, 0);
+                               force_draw_all(0);
                                        }
-                                       force_draw_plus(SPACE_VIEW3D, 0);
                                }
                                else {
                                        force_draw(0);
@@ -1190,1680 +1077,2046 @@ void transform_actionchannel_keys(int mode, int dummy)
                firsttime= 0;
        }
        
-       /*              Update the curve */
-       /*              Depending on the lock status, draw necessary views */
-
-       if(ob) {
-               ob->ctime= -1234567.0f;
-
-               if(ob->pose || ob_get_key(ob))
-                       DAG_object_flush_update(G.scene, ob, OB_RECALC);
-               else
-                       DAG_object_flush_update(G.scene, ob, OB_RECALC_OB);
-       }
-       
-       remake_action_ipos(act);
-
-       G.saction->flag &= ~SACTION_MOVING;
-       
-       if(cancel==0) BIF_undo_push("Transform Action");
-       allqueue (REDRAWVIEW3D, 0);
-       allqueue (REDRAWACTION, 0);
-       allqueue(REDRAWNLA, 0);
-       allqueue (REDRAWIPO, 0);
-       allqueue(REDRAWTIME, 0);
-       MEM_freeN (tv);
+       return cancel;
 }
 
-void transform_meshchannel_keys(char mode, Key *key)
+/* main call to start transforming keyframes */
+/*     NOTE: someday, this should be integrated with the transform system
+ *             instead of relying on our own methods
+ */
+void transform_action_keys (int mode, int dummy)
 {
-       /* this is the function that determines what happens
-        * to those little blocky rvk key things you have selected 
-        * after you press a 'g' or an 's'. I'd love to say that
-        * I have an intimate knowledge of all of what this function
-        * is doing, but instead I'm just going to pretend.
-        */
-    TransVert *tv;
-    int /*sel=0,*/  i;
-    short      mvals[2], mvalc[2], cent[2];
-    float      sval[2], cval[2], lastcval[2]={0,0};
-    short      cancel=0;
-    float      fac=0.0F;
-    int                loop=1;
-    int                tvtot=0;
-    float      deltax, startx;
-    float      cenf[2];
-    int                invert=0, firsttime=1;
-    char       str[256];
-
-       /* count all of the selected beziers, and
-        * set all 3 control handles to selected
-        */
-    tvtot=fullselect_ipo_keys(key->ipo);
-    
-    /* If nothing is selected, bail out 
-        */
-    if (!tvtot)
-        return;
+       Object *ob= OBACT;
+       TransVert *tv;
+       int tvtot= 0;
+       short cancel;
+       float minx, maxx;
        
+       void *data;
+       short datatype;
        
-    /* Build the transvert structure 
-        */
-    tv = MEM_callocN (sizeof(TransVert) * tvtot, "transVert");
-    tvtot=0;
+       /* determine what type of data we are operating on */
+       data = get_action_context(&datatype);
+       if (data == NULL) return;
+       
+       /* initialise transform */
+       tv= transform_action_init(&tvtot, &minx, &maxx);
+       if (tv == NULL) return;
+       
+       /* do transform loop */
+       cancel= transform_action_loop(tv, tvtot, mode, datatype, minx, maxx); 
+       
+       /* cleanup */
+       if (datatype == ACTCONT_ACTION) {
+               /* Update the curve */
+               /* Depending on the lock status, draw necessary views */
+               if(ob) {
+                       ob->ctime= -1234567.0f;
 
-    tvtot = add_trans_ipo_keys(key->ipo, tv, tvtot);
+                       if(ob->pose || ob_get_key(ob))
+                               DAG_object_flush_update(G.scene, ob, OB_RECALC);
+                       else
+                               DAG_object_flush_update(G.scene, ob, OB_RECALC_OB);
+               }
+               
+               remake_action_ipos((bAction *)data);
 
-    /* Do the event loop 
-        */
-    cent[0] = curarea->winx + (G.saction->v2d.hor.xmax)/2;
-    cent[1] = curarea->winy + (G.saction->v2d.hor.ymax)/2;
-    areamouseco_to_ipoco(G.v2d, 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;
-                };
-            }
-        }
-        
-        if (cancel) {
-            for (i=0; i<tvtot; i++) {
-                tv[i].loc[0]=tv[i].oldloc[0];
-                tv[i].loc[1]=tv[i].oldloc[1];
-            }
-        } 
-               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(1);
-            } else {
-                for (i=0; i<tvtot; i++){
-                    tv[i].loc[0]=tv[i].oldloc[0];
-
-                    switch (mode){
-                    case 'g':
-                        deltax = cval[0]-sval[0];
-                        fac= deltax;
-                                               
-                        apply_keyb_grid(&fac, 0.0, 1.0, 0.1, 
-                                        U.flag & USER_AUTOGRABGRID);
-
-                        tv[i].loc[0]+=fac;
-                        break;
-                    case 's':
-                        startx=mvals[0]-(ACTWIDTH/2+(curarea->winrct.xmax
-                                                     -curarea->winrct.xmin)/2);
-                        deltax=mvalc[0]-(ACTWIDTH/2+(curarea->winrct.xmax
-                                                     -curarea->winrct.xmin)/2);
-                        fac= fabs(deltax/startx);
-                                               
-                        apply_keyb_grid(&fac, 0.0, 0.2, 0.1, 
-                                        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);
-                        
-                        tv[i].loc[0]-= startx;
-                        tv[i].loc[0]*=fac;
-                        tv[i].loc[0]+= startx;
-               
-                        break;
-                    }
-                }
-            }
-                       /* Display a message showing the magnitude of
-                        * the grab/scale we are performing
-                        */
-            if (mode=='s'){
-                sprintf(str, "scaleX: %.3f", fac);
-                headerprint(str);
-            }
-            else if (mode=='g'){
-                sprintf(str, "deltaX: %.3f", fac);
-                headerprint(str);
-            }
-       
-            if (G.saction->lock){
-                               /* doubt any of this code ever gets
-                                * executed, but it might in the
-                                * future
-                                */
-                                
-                               DAG_object_flush_update(G.scene, OBACT, OB_RECALC_OB|OB_RECALC_DATA);
-                allqueue (REDRAWVIEW3D, 0);
-                allqueue (REDRAWACTION, 0);
-                allqueue (REDRAWIPO, 0);
-                allqueue(REDRAWNLA, 0);
-                               allqueue(REDRAWTIME, 0);
-                force_draw_all(0);
-            }
-            else {
-                addqueue (curarea->win, REDRAWALL, 0);
-                force_draw(0);
-            }
-        }
-               
-        lastcval[0]= cval[0];
-        lastcval[1]= cval[1];
-        firsttime= 0;
-    }
+               G.saction->flag &= ~SACTION_MOVING;
+               
+               if (cancel==0) BIF_undo_push("Transform Action"); // does it have to be here?
+               allqueue(REDRAWVIEW3D, 0);
+               allqueue(REDRAWACTION, 0);
+               allqueue(REDRAWNLA, 0);
+               allqueue(REDRAWIPO, 0);
+               allqueue(REDRAWTIME, 0);
+       }
+       else if (datatype == ACTCONT_SHAPEKEY) {
+               /* fix up the Ipocurves and redraw stuff */
+           meshkey_do_redraw((Key *)data);
+               if (cancel==0) BIF_undo_push("Transform Action");
+       }
        
-       /* fix up the Ipocurves and redraw stuff
-        */
-    meshkey_do_redraw(key);
-       BIF_undo_push("Transform Action Keys");
+       MEM_freeN(tv);
+}
 
-    MEM_freeN (tv);
+/* ----------------------------------------- */
 
-       /* did you understand all of that? I pretty much understand
-        * what it does, but the specifics seem a little weird and crufty.
-        */
+/* duplicate keyframes */
+void duplicate_action_keys (void)
+{
+       ListBase act_data = {NULL, NULL};
+       bActListElem *ale;
+       void *data;
+       short datatype;
+       int filter;
+       
+       /* determine what type of data we are operating on */
+       data = get_action_context(&datatype);
+       if (data == NULL) return;
+       
+       /* filter data */
+       filter= (ACTFILTER_VISIBLE | ACTFILTER_FOREDIT | ACTFILTER_IPOKEYS);
+       actdata_filter(&act_data, filter, data, datatype);
+       
+       /* loop through filtered data and duplicate selected keys */
+       for (ale= act_data.first; ale; ale= ale->next) {
+               duplicate_ipo_keys((Ipo *)ale->key_data);
+       }
+       
+       /* free filtered list */
+       BLI_freelistN(&act_data);
+       
+       /* now, go into transform-grab mode, to move keys */
+       transform_action_keys('g', 0);
 }
 
-void deselect_actionchannel_keys (bAction *act, int test, int sel)
+/* this function is responsible for snapping keyframes to frame-times */
+void snap_action_keys(short mode) 
 {
-       bActionChannel  *chan;
-       bConstraintChannel *conchan;
-
-       if (!act)
-               return;
-
-       /* Determine if this is selection or deselection */
+       ListBase act_data = {NULL, NULL};
+       bActListElem *ale;
+       int filter;
+       void *data;
+       short datatype;
+       char str[32];
+               
+       /* get data */
+       data= get_action_context(&datatype);
+       if (data == NULL) return;
        
-       if (test){
-               for (chan=act->chanbase.first; chan; chan=chan->next){
-                       if((chan->flag & ACHAN_HIDDEN)==0) {
-                               /* Test the channel ipos */
-                               if (is_ipo_key_selected(chan->ipo)){
-                                       sel = 0;
-                                       break;
-                               }
-
-                               /* Test the constraint ipos */
-                               for (conchan=chan->constraintChannels.first; conchan; conchan=conchan->next){
-                                       if (is_ipo_key_selected(conchan->ipo)){
-                                               sel = 0;
-                                               break;
-                                       }
-                               }
-
-                               if (sel == 0)
-                                       break;
-                       }
-               }
+       /* determine mode */
+       switch (mode) {
+               case 1:
+                       strcpy(str, "Snap Keys To Nearest Frame");
+                       break;
+               case 2:
+                       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;
        }
        
-       /* Set the flags */
-       for (chan=act->chanbase.first; chan; chan=chan->next){
-               if((chan->flag & ACHAN_HIDDEN)==0) {
-                       set_ipo_key_selection(chan->ipo, sel);
-                       for (conchan=chan->constraintChannels.first; conchan; conchan=conchan->next)
-                               set_ipo_key_selection(conchan->ipo, sel);
+       /* filter data */
+       filter= (ACTFILTER_VISIBLE | ACTFILTER_FOREDIT | ACTFILTER_IPOKEYS);
+       actdata_filter(&act_data, filter, data, datatype);
+       
+       /* snap to frame */
+       for (ale= act_data.first; ale; ale= ale->next) {
+               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);
                }
+               else 
+                       snap_ipo_keys(ale->key_data, mode);
        }
+       BLI_freelistN(&act_data);
+       
+       if (datatype == ACTCONT_ACTION)
+               remake_action_ipos(data);
+       
+       BIF_undo_push(str);
+       allspace(REMAKEIPO, 0);
+       allqueue(REDRAWACTION, 0);
+       allqueue(REDRAWIPO, 0);
+       allqueue(REDRAWNLA, 0);
 }
 
-void deselect_meshchannel_keys (Key *key, int test, int sel)
+/* this function is responsible for snapping keyframes to frame-times */
+void mirror_action_keys(short mode) 
 {
-       /* should deselect the rvk keys
-        */
-
-    /* Determine if this is selection or deselection */
-    if (test){
-        if (is_ipo_key_selected(key->ipo)){
-            sel = 0;
-        }
-    }
+       ListBase act_data = {NULL, NULL};
+       bActListElem *ale;
+       int filter;
+       void *data;
+       short datatype;
+       char str[32];
+               
+       /* get data */
+       data= get_action_context(&datatype);
+       if (data == NULL) return;
+       
+       /* determine mode */
+       switch (mode) {
+               case 1:
+                       strcpy(str, "Mirror Keys Over Current Frame");
+                       break;
+               case 2:
+                       strcpy(str, "Mirror Keys Over Y-Axis");
+                       break;
+               case 3:
+                       strcpy(str, "Mirror Keys Over X-Axis");
+                       break;
+               case 4:
+                       strcpy(str, "Mirror Keys Over Marker");
+                       break;
+               default:
+                       return;
+       }
        
-    /* Set the flags */
-    set_ipo_key_selection(key->ipo, sel);
+       /* filter data */
+       filter= (ACTFILTER_VISIBLE | ACTFILTER_FOREDIT | ACTFILTER_IPOKEYS);
+       actdata_filter(&act_data, filter, data, datatype);
+       
+       /* mirror */
+       for (ale= act_data.first; ale; ale= ale->next) {
+               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);
+               }
+               else 
+                       mirror_ipo_keys(ale->key_data, mode);
+       }
+       BLI_freelistN(&act_data);
+       
+       if (datatype == ACTCONT_ACTION)
+               remake_action_ipos(data);
+       
+       BIF_undo_push(str);
+       allspace(REMAKEIPO, 0);
+       allqueue(REDRAWACTION, 0);
+       allqueue(REDRAWIPO, 0);
+       allqueue(REDRAWNLA, 0);
 }
 
-/* apparently within active object context */
-void deselect_actionchannels (bAction *act, int test)
-{
-       bActionChannel *chan;
-       bConstraintChannel *conchan;
-       int                     sel=1;  
-
-       if (!act)
-               return;
-
-       /* See if we should be selecting or deselecting */
-       if (test){
-               for (chan=act->chanbase.first; chan; chan=chan->next){
-                       if((chan->flag & ACHAN_HIDDEN)==0) {
-                               if (!sel)
-                                       break;
+/* **************************************************** */
+/* ADD/REMOVE KEYFRAMES */
 
-                               if (chan->flag & ACHAN_SELECTED){
-                                       sel=0;
-                                       break;
-                               }
-                               if (sel){
-                                       for (conchan=chan->constraintChannels.first; conchan; conchan=conchan->next){
-                                               if (conchan->flag & CONSTRAINT_CHANNEL_SELECT){
-                                                       sel=0;
-                                                       break;
-                                               }
-                                       }
-                               }
+/* This function allows the user to insert keyframes on the current
+ * frame from the Action Editor, using the current values of the channels
+ * to be keyframed.  
+ */
+void insertkey_action(void)
+{
+       void *data;
+       short datatype;
+       
+       Object *ob= OBACT;
+       short mode;
+       float cfra;
+       
+       /* get data */
+       data= get_action_context(&datatype);
+       if (data == NULL) return;
+       cfra = frame_to_float(CFRA);
+               
+       if (datatype == ACTCONT_ACTION) {
+               ListBase act_data = {NULL, NULL};
+               bActListElem *ale;
+               int filter;
+       
+               /* ask user what to keyframe */
+               mode = pupmenu("Insert Key%t|All Channels%x1|Only Selected Channels%x2");
+               if (mode <= 0) return;
+               
+               /* filter data */
+               filter= (ACTFILTER_VISIBLE | ACTFILTER_FOREDIT | ACTFILTER_ONLYICU );
+               if (mode == 2) filter |= ACTFILTER_SEL;
+               
+               actdata_filter(&act_data, filter, data, datatype);
+               
+               /* loop through ipo curves retrieved */
+               for (ale= act_data.first; ale; ale= ale->next) {
+                       /* verify that this is indeed an ipo curve */
+                       if (ale->key_data && ale->owner) {
+                               bActionChannel *achan= (bActionChannel *)ale->owner;
+                               IpoCurve *icu= (IpoCurve *)ale->key_data;
+                               
+                               if (ob)
+                                       insertkey((ID *)ob, icu->blocktype, achan->name, NULL, icu->adrcode);
+                               else
+                                       insert_vert_icu(icu, cfra, icu->curval);
                        }
                }
+               
+               /* cleanup */
+               BLI_freelistN(&act_data);
        }
-       else
-               sel=0;
-
-       /* Now set the flags */
-       for (chan=act->chanbase.first; chan; chan=chan->next){
-               if((chan->flag & ACHAN_HIDDEN)==0) {
-                       select_poseelement_by_name(chan->name, sel);
-
-                       if (sel)
-                               chan->flag |= ACHAN_SELECTED;
-                       else
-                               chan->flag &= ~ACHAN_SELECTED;
-
-                       for (conchan=chan->constraintChannels.first; conchan; conchan=conchan->next){
-                               if (sel)
-                                       conchan->flag |= CONSTRAINT_CHANNEL_SELECT;
-                               else
-                                       conchan->flag &= ~CONSTRAINT_CHANNEL_SELECT;
+       else if (datatype == ACTCONT_SHAPEKEY) {
+               Key *key= (Key *)data;
+               IpoCurve *icu;
+               
+               /* ask user if they want to insert a keyframe */
+               mode = okee("Insert Keyframe?");
+               if (mode <= 0) return;
+               
+               if (key->ipo) {
+                       for (icu= key->ipo->curve.first; icu; icu=icu->next) {
+                               insert_vert_icu(icu, cfra, icu->curval);
                        }
                }
        }
-
+       
+       BIF_undo_push("Insert Key");
+       allspace(REMAKEIPO, 0);
+       allqueue(REDRAWACTION, 0);
+       allqueue(REDRAWIPO, 0);
+       allqueue(REDRAWNLA, 0);
 }
 
-static void hilight_channel (bAction *act, bActionChannel *chan, short select)
+/* delete selected keyframes */
+void delete_action_keys (void)
 {
-       bActionChannel *curchan;
-
-       if (!act)
-               return;
-
-       for (curchan=act->chanbase.first; curchan; curchan=curchan->next){
-               if (curchan==chan && select)
-                       curchan->flag |= ACHAN_HILIGHTED;
-               else
-                       curchan->flag &= ~ACHAN_HILIGHTED;
+       ListBase act_data = {NULL, NULL};
+       bActListElem *ale;
+       void *data;
+       short datatype;
+       int filter;
+       
+       /* determine what type of data we are operating on */
+       data = get_action_context(&datatype);
+       if (data == NULL) return;
+       
+       /* filter data */
+       filter= (ACTFILTER_VISIBLE | ACTFILTER_FOREDIT | ACTFILTER_IPOKEYS);
+       actdata_filter(&act_data, filter, data, datatype);
+       
+       /* loop through filtered data and delete selected keys */
+       for (ale= act_data.first; ale; ale= ale->next) {
+               delete_ipo_keys((Ipo *)ale->key_data);
        }
+       
+       /* free filtered list */
+       BLI_freelistN(&act_data);
+       
+       if (datatype == ACTCONT_ACTION)
+               remake_action_ipos(data);
+       
+       BIF_undo_push("Delete Action Keys");
+       allspace(REMAKEIPO, 0);
+       allqueue(REDRAWACTION, 0);
+       allqueue(REDRAWIPO, 0);
+       allqueue(REDRAWNLA, 0);
 }
 
-/* select_mode = SELECT_REPLACE
- *             = SELECT_ADD
- *             = SELECT_SUBTRACT
- *             = SELECT_INVERT
- */
-
-/* exported for outliner (ton) */
-/* apparently within active object context */
-int select_channel(bAction *act, bActionChannel *chan,
-                          int selectmode) 
+/* delete selected keyframes */
+void delete_action_channels (void)
 {
-       /* Select the channel based on the selection mode
-        */
-       int flag;
-
-       switch (selectmode) {
-       case SELECT_ADD:
-               chan->flag |= ACHAN_SELECTED;
-               break;
-       case SELECT_SUBTRACT:
-               chan->flag &= ~ACHAN_SELECTED;
-               break;
-       case SELECT_INVERT:
-               chan->flag ^= ACHAN_SELECTED;
-               break;
+       ListBase act_data = {NULL, NULL};
+       bActListElem *ale, *next;
+       bAction *act;
+       void *data;
+       short datatype;
+       int filter;
+       
+       /* determine what type of data we are operating on */
+       data = get_action_context(&datatype);
+       if (data == NULL) return;
+       if (datatype != ACTCONT_ACTION) return;
+       act= (bAction *)data;
+       
+       /* filter data */
+       filter= (ACTFILTER_VISIBLE | ACTFILTER_FOREDIT | ACTFILTER_CHANNELS | ACTFILTER_SEL);
+       actdata_filter(&act_data, filter, data, datatype);
+       
+       /* remove irrelevant entries */
+       for (ale= act_data.first; ale; ale= next) {
+               next= ale->next;
+               
+               if (ale->type != ACTTYPE_ACHAN)
+                       BLI_freelinkN(&act_data, ale);
        }
-       flag = (chan->flag & ACHAN_SELECTED) ? 1 : 0;
-
-       hilight_channel(act, chan, flag);
-       select_poseelement_by_name(chan->name, flag);
-
-       return flag;
+       
+       /* clean up action channels */
+       for (ale= act_data.first; ale; ale= next) {
+               bActionChannel *achan= (bActionChannel *)ale->data;
+               bConstraintChannel *conchan, *cnext;
+               next= ale->next;
+               
+               /* release reference to ipo users */
+               if (achan->ipo)
+                       achan->ipo->id.us--;
+                       
+               for (conchan= achan->constraintChannels.first; conchan; conchan=cnext) {
+                       cnext= conchan->next;
+                       
+                       if (conchan->ipo)
+                               conchan->ipo->id.us--;
+               }
+               
+               /* free memory */
+               BLI_freelistN(&achan->constraintChannels);
+               BLI_freelinkN(&act->chanbase, achan);
+               BLI_freelinkN(&act_data, ale);
+       }
+               
+       remake_action_ipos(data);
+       
+       BIF_undo_push("Delete Action Channels");
+       allspace(REMAKEIPO, 0);
+       allqueue(REDRAWACTION, 0);
+       allqueue(REDRAWIPO, 0);
+       allqueue(REDRAWNLA, 0);
 }
 
-static int select_constraint_channel(bAction *act, 
-                                     bConstraintChannel *conchan, 
-                                     int selectmode) {
-       /* Select the constraint channel based on the selection mode
-        */
-       int flag;
-
-       switch (selectmode) {
-       case SELECT_ADD:
-               conchan->flag |= CONSTRAINT_CHANNEL_SELECT;
-               break;
-       case SELECT_SUBTRACT:
-               conchan->flag &= ~CONSTRAINT_CHANNEL_SELECT;
-               break;
-       case SELECT_INVERT:
-               conchan->flag ^= CONSTRAINT_CHANNEL_SELECT;
-               break;
+/* 'Clean' IPO curves - remove any unnecessary keyframes */
+void clean_action (void)
+{      
+       ListBase act_data = {NULL, NULL};
+       bActListElem *ale;
+       int filter;
+       void *data;
+       short datatype, ok;
+       
+       /* don't proceed any further if nothing to work on or user refuses */
+       data= get_action_context(&datatype);
+       ok= fbutton(&G.scene->toolsettings->clean_thresh, 
+                               0.0000001f, 1.0, 0.001, 0.1,
+                               "Clean Threshold");
+       if (!ok) return;
+       
+       /* filter data */
+       filter= (ACTFILTER_VISIBLE | ACTFILTER_FOREDIT | ACTFILTER_SEL | ACTFILTER_ONLYICU);
+       actdata_filter(&act_data, filter, data, datatype);
+       
+       /* loop through filtered data and clean curves */
+       for (ale= act_data.first; ale; ale= ale->next) {
+               clean_ipo_curve((IpoCurve *)ale->key_data);
        }
-       flag = (conchan->flag & CONSTRAINT_CHANNEL_SELECT) ? 1 : 0;
-
-       return flag;
+       
+       /* admin and redraws */
+       BLI_freelistN(&act_data);
+       
+       BIF_undo_push("Clean Action");
+       allqueue(REMAKEIPO, 0);
+       allqueue(REDRAWIPO, 0);
+       allqueue(REDRAWACTION, 0);
+       allqueue(REDRAWNLA, 0);
 }
 
-/* lefthand side */
-static void mouse_actionchannels(bAction *act, short *mval,
-                                 short *mvalo, int selectmode) {
-       /* Select action channels, based on mouse values.
-        * If mvalo is NULL we assume it is a one click
-        * action, other wise we treat it like it is a
-        * border select with mval[0],mval[1] and
-        * mvalo[0], mvalo[1] forming the corners of
-        * a rectangle.
-        */
-       bActionChannel *chan;
-       float   click, x,y;
-       int   clickmin, clickmax;
-       int             wsize, sel;
-       bConstraintChannel *conchan;
-
-       if (!act)
-               return;
-  
-       if (selectmode == SELECT_REPLACE) {
-               deselect_actionchannels (act, 0);
-               selectmode = SELECT_ADD;
-       }
+/* **************************************************** */
+/* 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.
+ */
 
-       /* wsize is the greatest possible height (in pixels) that would be
-        * needed to draw all of the action channels and constraint
-        * channels.
-        */
-       wsize =  count_action_levels(act)*(CHANNELHEIGHT+CHANNELSKIP);
-       wsize += CHANNELHEIGHT/2;
+/* globals for copy/paste data (like for other copy/paste buffers) */
+ListBase actcopybuf = {NULL, NULL};
 
-    areamouseco_to_ipoco(G.v2d, mval, &x, &y);
-    clickmin = (int) ((wsize - y) / (CHANNELHEIGHT+CHANNELSKIP));
+/* This function frees any MEM_calloc'ed copy/paste buffer data */
+void free_actcopybuf ()
+{
+       IpoCurve *icu;
        
-       /* Only one click */
-       if (mvalo == NULL) {
-               clickmax = clickmin;
+       while( (icu= actcopybuf.first) ) {
+               BLI_remlink(&actcopybuf, icu);
+               free_ipo_curve(icu);
        }
-       /* Two click values (i.e., border select */
-       else {
-               areamouseco_to_ipoco(G.v2d, mvalo, &x, &y);
-               click = ((wsize - y) / (CHANNELHEIGHT+CHANNELSKIP));
+}
 
-               if ( ((int) click) < clickmin) {
-                       clickmax = clickmin;
-                       clickmin = (int) click;
+/* 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;
+                       }
                }
-               else {
-                       clickmax = (int) click;
+               /* 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);
+}
 
-       if (clickmax < 0) {
+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;
        }
-
-       /* clickmin and clickmax now coorespond to indices into
-        * the collection of channels and constraint channels.
-        * What we need to do is apply the selection mode on all
-        * channels and constraint channels between these indices.
-        * This is done by traversing the channels and constraint
-        * channels, for each item decrementing clickmin and clickmax.
-        * When clickmin is less than zero we start selecting stuff,
-        * until clickmax is less than zero or we run out of channels
-        * and constraint channels.
-        */
-
-       for (chan = act->chanbase.first; chan; chan=chan->next){
-               if((chan->flag & ACHAN_HIDDEN)==0) {
-                       if (clickmax < 0) break;
-
-                       if ( clickmin <= 0) {
-                               /* Select the channel with the given mode. If the
-                                * channel is freshly selected then set it to the
-                                * active channel for the action
-                                */
-                               sel = (chan->flag & ACHAN_SELECTED);
-                               select_channel(act, chan, selectmode);
-                               /* messy... */
-                               select_poseelement_by_name(chan->name, 2);
-                               
-                       }
-                       --clickmin;
-                       --clickmax;
-
-                       /* Check for click in a constraint */
-                       for (conchan=chan->constraintChannels.first; 
-                                conchan; conchan=conchan->next){
-                               if (clickmax < 0) break;
-                               if ( clickmin <= 0) {
-                                       select_constraint_channel(act, conchan, selectmode);
+       
+       /* 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;
                                }
-                               --clickmin;
-                               --clickmax;
+                               
+                               /* recalculate channel's handles? */
+                               calchandles_ipocurve(icu);
+                               
+                               /* done for this channel */
+                               break;
                        }
                }
        }
-
-       allqueue (REDRAWIPO, 0);
-       allqueue (REDRAWVIEW3D, 0);
-       allqueue (REDRAWACTION, 0);
-       allqueue (REDRAWNLA, 0);
-       allqueue (REDRAWOOPS, 0);
-       allqueue (REDRAWBUTSALL, 0);
+       
+       /* 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");
 }
 
-/* turn on/off protect option for action channel */
-static void mouse_actionchannels_protect (bAction *act, short *mval)
+/* **************************************************** */
+/* VARIOUS SETTINGS */
+
+/* This function combines several features related to setting 
+ * various ipo extrapolation/interpolation
+ */
+void action_set_ipo_flags (short mode, short event)
 {
-       bActionChannel *achan;
-       bConstraintChannel *conchan;
+       ListBase act_data = {NULL, NULL};
+       bActListElem *ale;
+       void *data;
+       short datatype;
+       int filter;
        
-       float x,y;
-       int clickmin, clickmax;
-       int wsize, lock;
+       /* determine what type of data we are operating on */
+       data = get_action_context(&datatype);
+       if (data == NULL) return;
        
-       /* wsize is the greatest possible height (in pixels) that would be
-        * needed to draw all of the action channels and constraint
-        * channels.
-        */
-       wsize =  count_action_levels(act)*(CHANNELHEIGHT+CHANNELSKIP);
-       wsize += CHANNELHEIGHT/2;
+       /* determine which set of processing we are doing */
+       switch (mode) {
+               case SET_EXTEND_POPUP:
+               {
+                       /* present popup menu for ipo extrapolation type */
+                       event
+                               =  pupmenu("Channel Extending Type %t|"
+                                                  "Constant %x11|"
+                                                  "Extrapolation %x12|"
+                                                  "Cyclic %x13|"
+                                                  "Cyclic extrapolation %x14");
+                       if (event < 1) return;
+               }
+                       break;
+               case SET_IPO_POPUP:
+               {
+                       /* present popup menu for ipo interpolation type */
+                       event
+                               =  pupmenu("Channel Ipo Type %t|"
+                                                  "Constant %x1|"
+                                                  "Linear %x2|"
+                                                  "Bezier %x3");
+                       if (event < 1) return;
+               }
+                       break;
+                       
+               case SET_IPO_MENU:      /* called from menus */
+               case SET_EXTEND_MENU:
+                       break;
+                       
+               default: /* weird, unhandled case */
+                       return;
+       }
        
-       areamouseco_to_ipoco(G.v2d, mval, &x, &y);
-    clickmin = (int) ((wsize - y) / (CHANNELHEIGHT+CHANNELSKIP));
-       clickmax = clickmin;
+       /* filter data */
+       filter= (ACTFILTER_VISIBLE | ACTFILTER_SEL | ACTFILTER_FOREDIT | ACTFILTER_IPOKEYS);
+       actdata_filter(&act_data, filter, data, datatype);
        
-       if (clickmax < 0)
-               return;
+       /* loop through setting flags */
+       for (ale= act_data.first; ale; ale= ale->next) {
+               Ipo *ipo= (Ipo *)ale->key_data; 
        
-       /* clickmin and clickmax now coorespond to indices into
-        * the collection of channels and constraint channels.
-        * What we need to do is turn locks on/off for all action
-        * channels and constraint channels between these indices.
-        * This is done by traversing the channels and constraint
-        * channels, for each item decrementing clickmin and clickmax.
-        * When clickmin is less than zero we start locking stuff,
-        * until clickmax is less than zero or we run out of channels
-        * and constraint channels.
-        */
-
-       for (achan = act->chanbase.first; achan; achan= achan->next){
-               if((achan->flag & ACHAN_HIDDEN)==0) {
-                       if (clickmax < 0) break;
-
-                       /* assume locking this action channel */
-                       if ( clickmin <= 0) {
-                               /* invert the channel's protect property */
-                               lock = (achan->flag & ACHAN_PROTECTED);
-                               if (lock) {
-                                       achan->flag &= ~ACHAN_PROTECTED;
-                               }
-                               else {
-                                       achan->flag |= ACHAN_PROTECTED;
+               /* depending on the mode */
+               switch (mode) {
+                       case SET_EXTEND_POPUP: /* extrapolation */
+                       case SET_EXTEND_MENU:
+                       {
+                               switch (event) {
+                                       case SET_EXTEND_CONSTANT:
+                                               setexprap_ipoloop(ipo, IPO_HORIZ);
+                                               break;
+                                       case SET_EXTEND_EXTRAPOLATION:
+                                               setexprap_ipoloop(ipo, IPO_DIR);
+                                               break;
+                                       case SET_EXTEND_CYCLIC:
+                                               setexprap_ipoloop(ipo, IPO_CYCL);
+                                               break;
+                                       case SET_EXTEND_CYCLICEXTRAPOLATION:
+                                               setexprap_ipoloop(ipo, IPO_CYCLX);
+                                               break;
                                }
                        }
-                       --clickmin;
-                       --clickmax;
-
-                       /* Check for click in a constraint channel */
-                       for (conchan=achan->constraintChannels.first; conchan; conchan=conchan->next){
-                               if (clickmax < 0) break;
-                               if ( clickmin <= 0) {
-                                       /* invert the channel's protect property */
-                                       lock = (conchan->flag & CONSTRAINT_CHANNEL_PROTECTED);
-                                       if (lock)
-                                               conchan->flag &= ~CONSTRAINT_CHANNEL_PROTECTED;
-                                       else
-                                               conchan->flag |= CONSTRAINT_CHANNEL_PROTECTED;
-                               }
-                               --clickmin;
-                               --clickmax;
+                               break;
+                       case SET_IPO_POPUP: /* interpolation */
+                       case SET_IPO_MENU:
+                       {
+                               setipotype_ipo(ipo, event);
                        }
+                               break;
                }
        }
        
-       allqueue (REDRAWACTION, 0);
-}
-
-void delete_meshchannel_keys(Key *key)
-{
-       delete_ipo_keys(key->ipo);
+       /* cleanup */
+       BLI_freelistN(&act_data);
        
-       BIF_undo_push("Delete Action Keys");
-       meshkey_do_redraw(key);
+       if (datatype == ACTCONT_ACTION)
+               remake_action_ipos(data);
+       
+       BIF_undo_push("Set Ipo Type");
        allspace(REMAKEIPO, 0);
        allqueue(REDRAWACTION, 0);
        allqueue(REDRAWIPO, 0);
        allqueue(REDRAWNLA, 0);
 }
 
-void delete_actionchannel_keys(void)
+/* this function sets the handles on keyframes */
+void sethandles_action_keys (int code)
 {
-       bAction *act;
-       bActionChannel *achan;
-       bConstraintChannel *conchan;
-
-       act = G.saction->action;
-       if (!act)
-               return;
-
-       for (achan = act->chanbase.first; achan; achan= achan->next){
-               if(EDITABLE_ACHAN(achan)) {
-                       /* Check action channel keys*/
-                       delete_ipo_keys(achan->ipo);
-               }
-               
-               if (VISIBLE_ACHAN(achan)) {
-                       /* Delete constraint channel keys */
-                       for (conchan=achan->constraintChannels.first; conchan; conchan=conchan->next) {
-                               if (EDITABLE_CONCHAN(conchan)) 
-                                       delete_ipo_keys(conchan->ipo);
-                       }
-               }
+       ListBase act_data = {NULL, NULL};
+       bActListElem *ale;
+       void *data;
+       short datatype;
+       int filter;
+       
+       /* determine what type of data we are operating on */
+       data = get_action_context(&datatype);
+       if (data == NULL) return;
+       
+       /* filter data */
+       filter= (ACTFILTER_VISIBLE | ACTFILTER_FOREDIT | ACTFILTER_IPOKEYS);
+       actdata_filter(&act_data, filter, data, datatype);
+       
+       /* loop through setting flags */
+       for (ale= act_data.first; ale; ale= ale->next) {
+               sethandles_ipo_keys((Ipo *)ale->key_data, code);
        }
-
-       remake_action_ipos(act);
-       BIF_undo_push("Delete Action Keys");
+       
+       /* cleanup */
+       BLI_freelistN(&act_data);
+       if (datatype == ACTCONT_ACTION)
+               remake_action_ipos(data);
+       
+       BIF_undo_push("Set Handle Type");
        allspace(REMAKEIPO, 0);
        allqueue(REDRAWACTION, 0);
        allqueue(REDRAWIPO, 0);
        allqueue(REDRAWNLA, 0);
-
 }
 
-static void delete_actionchannels (void)
-{
-       bConstraintChannel *conchan=NULL, *nextconchan;
-       bActionChannel *chan, *next;
-       bAction *act;
-       int freechan;
-
-       act=G.saction->action;
+/* ----------------------------------------- */
 
-       if (!act)
+static void clever_keyblock_names (Key *key, short *mval)
+{
+    KeyBlock   *kb;
+       int        but=0, keynum;
+    char       str[64];
+       float      x;
+       
+       /* get the keynum cooresponding to the y value
+        * of the mouse pointer, return if this is
+        * 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_get_keyblock(key, keynum);
+       if (kb == NULL)
                return;
+       
+       if (kb->name[0] == '\0')
+               sprintf(str, "Key %d", keynum);
+       else
+               strcpy(str, kb->name);
 
-       for (chan=act->chanbase.first; chan; chan=chan->next){
-               if((chan->flag & ACHAN_HIDDEN)==0) {
-                       if (chan->flag & ACHAN_SELECTED)
-                               break;
-                       for (conchan=chan->constraintChannels.first; conchan; conchan=conchan->next)
-                       {
-                               if (conchan->flag & CONSTRAINT_CHANNEL_SELECT){
-                                       chan=act->chanbase.last;
-                                       break;
-                               }
-                       }
-               }
+       if ( (kb->slidermin >= kb->slidermax) ) {
+               kb->slidermin = 0.0;
+               kb->slidermax = 1.0;
        }
 
-       if (!chan && !conchan)
-               return;
-
-       if (!okee("Erase selected channels"))
-               return;
+    add_numbut(but++, TEX, "KB: ", 0, 24, str, 
+               "Does this really need a tool tip?");
+       add_numbut(but++, NUM|FLO, "Slider Min:", 
+                          -10000, kb->slidermax, &kb->slidermin, 0);
+       add_numbut(but++, NUM|FLO, "Slider Max:", 
+                          kb->slidermin, 10000, &kb->slidermax, 0);
 
-       for (chan=act->chanbase.first; chan; chan=next){
-               freechan = 0;
-               next=chan->next;
-               if((chan->flag & ACHAN_HIDDEN)==0) {
-                       
-                       /* Remove action channels */
-                       if (chan->flag & ACHAN_SELECTED){
-                               if (chan->ipo)
-                                       chan->ipo->id.us--;     /* Release the ipo */
-                               freechan = 1;
-                       }
-                       
-                       /* Remove constraint channels */
-                       for (conchan=chan->constraintChannels.first; conchan; conchan=nextconchan){
-                               nextconchan=conchan->next;
-                               if (freechan || conchan->flag & CONSTRAINT_CHANNEL_SELECT){
-                                       if (conchan->ipo)
-                                               conchan->ipo->id.us--;
-                                       BLI_freelinkN(&chan->constraintChannels, conchan);
-                               }
-                       }
-                       
-                       if (freechan)
-                               BLI_freelinkN (&act->chanbase, chan);
-               }
+    if (do_clever_numbuts(str, but, REDRAW)) {
+               strcpy(kb->name, str);
+        allqueue (REDRAWACTION, 0);
+               allspace(REMAKEIPO, 0);
+        allqueue (REDRAWIPO, 0);
        }
-
-       BIF_undo_push("Delete Action channels");
-       allqueue (REDRAWACTION, 0);
-       allqueue(REDRAWNLA, 0);
-
 }
 
-void clean_shapekeys(Key *key)
+static void clever_achannel_names (short *mval)
 {
-       int ok;
-
-       /* don't proceed if user refuses */
-       if (!key) return;
-       if (G.scene->toolsettings->clean_thresh==0) 
-               G.scene->toolsettings->clean_thresh= 0.1f;
-       ok= fbutton(&G.scene->toolsettings->clean_thresh, 
-                               0.0000001f, 1.0, 0.001, 0.1,
-                               "Clean Threshold");
-       if (!ok) return;
-       
-       /* viable option? */
-       if (key->ipo) {
-               IpoCurve *icu;
+       void *act_channel;
+       bActionChannel *achan= NULL;
+       bConstraintChannel *conchan= NULL;
+       IpoCurve *icu= NULL;
+       
+       int but=0;
+    char str[64];
+       short chantype;
+       short expand, protect, mute;
+       float slidermin, slidermax;
+       
+       /* figure out what is under cursor */
+       act_channel= get_nearest_act_channel(mval, &chantype);
+       
+       /* create items for clever-numbut */
+       if (chantype == ACTTYPE_ACHAN) {
+               achan= (bActionChannel *)act_channel;
                
-               for (icu= key->ipo->curve.first; icu; icu=icu->next)
-                       clean_ipo_curve(icu);
+               strcpy(str, achan->name);
+               protect= (achan->flag & ACHAN_PROTECTED);
+               expand = (achan->flag & ACHAN_EXPANDED);
+               mute = (achan->ipo)? (achan->ipo->muteipo): 0;
+
+               add_numbut(but++, TEX, "ActChan: ", 0, 31, str, "Name of Action Channel");
+               add_numbut(but++, TOG|SHO, "Expanded", 0, 24, &expand, "Action Channel is Expanded");
+               add_numbut(but++, TOG|SHO, "Muted", 0, 24, &mute, "Channel is Muted");
+               add_numbut(but++, TOG|SHO, "Protected", 0, 24, &protect, "Channel is Protected");
+       }
+       else if (chantype == ACTTYPE_CONCHAN) {
+               conchan= (bConstraintChannel *)act_channel;
+               
+               strcpy(str, conchan->name);
+               protect= (conchan->flag & CONSTRAINT_CHANNEL_PROTECTED);
+               mute = (conchan->ipo)? (conchan->ipo->muteipo): 0;
+               
+               add_numbut(but++, TEX, "ConChan: ", 0, 29, str, "Name of Constraint Channel");
+               add_numbut(but++, TOG|SHO, "Muted", 0, 24, &mute, "Channel is Muted");
+               add_numbut(but++, TOG|SHO, "Protected", 0, 24, &protect, "Channel is Protected");
+       }
+       else if (chantype == ACTTYPE_ICU) {
+               icu= (IpoCurve *)act_channel;
+               
+               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)) {
+                               icu->slide_min= -100.0;
+                               icu->slide_max= 100.0;
+                       }
+                       else {
+                               icu->slide_min= icu->ymin;
+                               icu->slide_max= icu->ymax;
+                       }
+               }
+               slidermin= icu->slide_min;
+               slidermax= icu->slide_max;
+               
+               //protect= (icu->flag & IPO_PROTECT);
+               mute = (icu->flag & IPO_MUTE);
+               
+               add_numbut(but++, NUM|FLO, "Slider Min:", -10000, slidermax, &slidermin, 0);
+               add_numbut(but++, NUM|FLO, "Slider Max:", slidermin, 10000, &slidermax, 0);
+               add_numbut(but++, TOG|SHO, "Muted", 0, 24, &mute, "Channel is Muted");
+               //add_numbut(but++, TOG|SHO, "Protected", 0, 24, &protect, "Channel is Protected");
+       }
+       else {
+               /* nothing under-cursor */
+               return;
+       }
+       
+       /* draw clever-numbut */
+    if (do_clever_numbuts(str, but, REDRAW)) {
+               /* restore settings based on type */
+               if (icu) {
+                       icu->slide_min= slidermin;
+                       icu->slide_max= slidermax;
                        
-               /* admin and redraw stuff */
-               BIF_undo_push("Clean Action");
-               allqueue(REMAKEIPO, 0);
-               allqueue(REDRAWIPO, 0);
-               allqueue(REDRAWACTION, 0);
-               allqueue(REDRAWNLA, 0);
+                       //if (protect) icu->flag |= IPO_PROTECT;
+                       //else icu->flag &= ~IPO_PROTECT;
+                       if (mute) icu->flag |= IPO_MUTE;
+                       else icu->flag &= ~IPO_MUTE;
+               }
+               else if (conchan) {
+                       strcpy(conchan->name, str);
+                       
+                       if (protect) conchan->flag |= CONSTRAINT_CHANNEL_PROTECTED;
+                       else conchan->flag &= ~CONSTRAINT_CHANNEL_PROTECTED;
+                       
+                       if (conchan->ipo)
+                               conchan->ipo->muteipo = mute;
+               }
+               else if (achan) {
+                       strcpy(achan->name, str);
+                       
+                       if (expand) achan->flag |= ACHAN_EXPANDED;
+                       else achan->flag &= ~ACHAN_EXPANDED;
+                       
+                       if (protect) achan->flag |= ACHAN_PROTECTED;
+                       else achan->flag &= ~ACHAN_PROTECTED;
+                       
+                       if (achan->ipo)
+                               achan->ipo->muteipo = mute;
+               }
+               
+        allqueue (REDRAWACTION, 0);
+               allqueue (REDRAWVIEW3D, 0);
        }
 }
 
-void clean_actionchannels(bAction *act) 
+/* this gets called when nkey is pressed (no Transform Properties panel yet) */
+static void numbuts_action (void)
 {
-       bActionChannel *achan;
-       bConstraintChannel *conchan;
-       
-       Ipo *ipo;
-       IpoCurve *icu;
-       
-       int ok;
+       /* now called from action window event loop, plus reacts on mouseclick */
+       /* removed Hos grunts for that reason! :) (ton) */
+       void *data;
+       short datatype;
+    short mval[2];
        
-       /* don't proceed any further if no action or user refuses */
-       if (!act) return;
-       if (G.scene->toolsettings->clean_thresh==0) 
-               G.scene->toolsettings->clean_thresh= 0.1f;
-       ok= fbutton(&G.scene->toolsettings->clean_thresh, 
-                               0.0000001f, 1.0, 0.001, 0.1,
-                               "Clean Threshold");
-       if (!ok) return;
+       /* determine what type of data we are operating on */
+       data = get_action_context(&datatype);
+       getmouseco_areawin(mval);
        
-       /* clean selected channels only */
-       for (achan= act->chanbase.first; achan; achan= achan->next) {
-               if(EDITABLE_ACHAN(achan)) {
-                       /* clean if action channel if selected */
-                       if (achan->flag & ACHAN_SELECTED) {
-                               ipo= achan->ipo;
-                               if (ipo) {
-                                       for (icu= ipo->curve.first; icu; icu= icu->next) 
-                                               clean_ipo_curve(icu);
-                               }
-                       }
-               }
-
-               if (VISIBLE_ACHAN(achan)) {
-                       /* clean action channel's constraint channels */
-                       for (conchan= achan->constraintChannels.first; conchan; conchan=conchan->next) {
-                               if (EDITABLE_CONCHAN(conchan)) {
-                                       ipo= conchan->ipo;
-                                       if (ipo) {
-                                               for (icu= ipo->curve.first; icu; icu= icu->next) 
-                                                       clean_ipo_curve(icu);
-                                       }
-                               }
-                       }
+       if (mval[0] < NAMEWIDTH) {
+               switch (datatype) {
+                       case ACTCONT_ACTION:
+                               clever_achannel_names(mval);
+                               break;
+                       case ACTCONT_SHAPEKEY:
+                               clever_keyblock_names(data, mval);
+                               break;
                }
        }
-       
-       /* admin and redraws */
-       BIF_undo_push("Clean Action");
-       allqueue(REMAKEIPO, 0);
-       allqueue(REDRAWIPO, 0);
-       allqueue(REDRAWACTION, 0);
-       allqueue(REDRAWNLA, 0);
 }
 
-void sethandles_meshchannel_keys(int code, Key *key)
-{
-       
-    sethandles_ipo_keys(key->ipo, code);
 
-       BIF_undo_push("Set handles Action keys");
-       meshkey_do_redraw(key);
-}
+/* **************************************************** */
+/* CHANNEL SELECTION */
 
-void sethandles_actionchannel_keys(int code)
+static void hilight_channel(bAction *act, bActionChannel *achan, short select)
 {
-       bAction *act;
-       bActionChannel *achan;
-       bConstraintChannel *conchan;
+       bActionChannel *curchan;
 
-       /* Get the selected action, exit if none are selected 
-        */
-       act = G.saction->action;
        if (!act)
                return;
 
-       /* Loop through the channels and set the beziers
-        * of the selected keys based on the integer code
-        */
-       for (achan = act->chanbase.first; achan; achan= achan->next){
-               if(EDITABLE_ACHAN(achan))
-                       sethandles_ipo_keys(achan->ipo, code);
-                       
-               if (VISIBLE_ACHAN(achan)) {
-                       for (conchan= achan->constraintChannels.first; conchan; conchan= conchan->next) {
-                               if (EDITABLE_CONCHAN(conchan))
-                                       sethandles_ipo_keys(conchan->ipo, code);
-                       }
-               }
+       for (curchan=act->chanbase.first; curchan; curchan=curchan->next) {
+               if (curchan==achan && select)
+                       curchan->flag |= ACHAN_HILIGHTED;
+               else
+                       curchan->flag &= ~ACHAN_HILIGHTED;
        }
-
-       /* Clean up and redraw stuff
-        */
-       remake_action_ipos(act);
-       BIF_undo_push("Set handles Action channel");
-       allspace(REMAKEIPO, 0);
-       allqueue(REDRAWACTION, 0);
-       allqueue(REDRAWIPO, 0);
-       allqueue(REDRAWNLA, 0);
 }
 
-void set_ipotype_actionchannels(int ipotype) 
+/* messy call... */
+static void select_poseelement_by_name(char *name, int select)
 {
+       /* Syncs selection of channels with selection of object elements in posemode */
+       Object *ob= OBACT;
+       bPoseChannel *pchan;
+       
+       if (!ob || ob->type!=OB_ARMATURE)
+               return;
+       
+       if(select==2) {
+               for(pchan= ob->pose->chanbase.first; pchan; pchan= pchan->next)
+                       pchan->bone->flag &= ~(BONE_ACTIVE);
+       }
+       
+       pchan= get_pose_channel(ob->pose, name);
+       if(pchan) {
+               if(select)
+                       pchan->bone->flag |= (BONE_SELECTED);
+               else 
+                       pchan->bone->flag &= ~(BONE_SELECTED);
+               if(select==2)
+                       pchan->bone->flag |= (BONE_ACTIVE);
+       }
+}
 
-       bAction *act; 
+/* apparently within active object context */
+/* called extern, like on bone selection */
+void select_actionchannel_by_name (bAction *act, char *name, int select)
+{
        bActionChannel *achan;
-       bConstraintChannel *conchan;
-       short event;
 
-       /* Get the selected action, exit if none are selected 
-        */
-       act = G.saction->action;
        if (!act)
                return;
 
-       if (ipotype == SET_IPO_POPUP) {
-               /* Present a popup menu asking the user what type
-                * of IPO curve he/she/GreenBTH wants. ;)
-                */
-               event
-                       =  pupmenu("Channel Ipo Type %t|"
-                                          "Constant %x1|"
-                                          "Linear %x2|"
-                                          "Bezier %x3");
-               if(event < 1) return;
-               ipotype = event;
-       }
-       
-       /* Loop through the channels and for the selected ones set
-        * the type for each Ipo curve in the channel Ipo (based on
-        * the value from the popup).
-        */
-       for (achan = act->chanbase.first; achan; achan= achan->next){
-               if(EDITABLE_ACHAN(achan)) {
-                       if (achan->flag & ACHAN_SELECTED){
-                               if (achan->ipo)
-                                       setipotype_ipo(achan->ipo, ipotype);
+       for (achan = act->chanbase.first; achan; achan= achan->next) {
+               if (!strcmp(achan->name, name)) {
+                       if (select) {
+                               achan->flag |= ACHAN_SELECTED;
+                               hilight_channel(act, achan, 1);
                        }
-               }
-               
-               if (VISIBLE_ACHAN(achan)) {
-                       /* constraint channels */
-                       for (conchan=achan->constraintChannels.first; conchan; conchan= conchan->next) {
-                               if (EDITABLE_CONCHAN(conchan)) {
-                                       if (conchan->flag & CONSTRAINT_CHANNEL_SELECT) {
-                                               if (conchan->ipo)
-                                                       setipotype_ipo(conchan->ipo, ipotype);
-                                       }
-                               }
+                       else {
+                               achan->flag &= ~ACHAN_SELECTED;
+                               hilight_channel(act, achan, 0);
                        }
+                       return;
                }
        }
-
-       /* Clean up and redraw stuff
-        */
-       remake_action_ipos(act);
-       BIF_undo_push("Set Ipo type Action channel");
-       allspace(REMAKEIPO, 0);
-       allqueue(REDRAWACTION, 0);
-       allqueue(REDRAWIPO, 0);
-       allqueue(REDRAWNLA, 0);
 }
 
-void set_extendtype_actionchannels(int extendtype)
-{
-       bAction *act; 
-       bActionChannel *achan;
-       bConstraintChannel *conchan;
-       short event;
+/* select_mode = SELECT_REPLACE
+ *             = SELECT_ADD
+ *             = SELECT_SUBTRACT
+ *             = SELECT_INVERT
+ */
 
-       /* Get the selected action, exit if none are selected 
-        */
-       act = G.saction->action;
-       if (!act)
-               return;
+/* exported for outliner (ton) */
+/* apparently within active object context */
+int select_channel(bAction *act, bActionChannel *achan, int selectmode) 
+{
+       /* Select the channel based on the selection mode */
+       int flag;
 
-       if (extendtype == SET_EXTEND_POPUP) {
-               /* Present a popup menu asking the user what type
-                * of IPO curve he/she/GreenBTH wants. ;)
-                */
-               event
-                       =  pupmenu("Channel Extending Type %t|"
-                                          "Constant %x1|"
-                                          "Extrapolation %x2|"
-                                          "Cyclic %x3|"
-                                          "Cyclic extrapolation %x4");
-               if(event < 1) return;
-               extendtype = event;
-       }
-       
-       /* Loop through the channels and for the selected ones set
-        * the type for each Ipo curve in the channel Ipo (based on
-        * the value from the popup).
-        */
-       for (achan = act->chanbase.first; achan; achan= achan->next){
-               if (EDITABLE_ACHAN(achan)) {
-                       if (achan->flag & ACHAN_SELECTED) {
-                               if (achan->ipo) {
-                                       switch (extendtype) {
-                                       case SET_EXTEND_CONSTANT:
-                                               setexprap_ipoloop(achan->ipo, IPO_HORIZ);
-                                               break;
-                                       case SET_EXTEND_EXTRAPOLATION:
-                                               setexprap_ipoloop(achan->ipo, IPO_DIR);
-                                               break;
-                                       case SET_EXTEND_CYCLIC:
-                                               setexprap_ipoloop(achan->ipo, IPO_CYCL);
-                                               break;
-                                       case SET_EXTEND_CYCLICEXTRAPOLATION:
-                                               setexprap_ipoloop(achan->ipo, IPO_CYCLX);
-                                               break;
-                                       }
-                               }
-                       }
-               }
-               
-               if (VISIBLE_ACHAN(achan)) {
-                       /* constraint channels */
-                       for (conchan=achan->constraintChannels.first; conchan; conchan= conchan->next) {
-                               if (EDITABLE_CONCHAN(conchan)) {
-                                       if (conchan->flag & CONSTRAINT_CHANNEL_SELECT) {
-                                               if (conchan->ipo) {
-                                                       switch (extendtype) {
-                                                               case SET_EXTEND_CONSTANT:
-                                                                       setexprap_ipoloop(conchan->ipo, IPO_HORIZ);
-                                                                       break;
-                                                               case SET_EXTEND_EXTRAPOLATION:
-                                                                       setexprap_ipoloop(conchan->ipo, IPO_DIR);
-                                                                       break;
-                                                               case SET_EXTEND_CYCLIC:
-                                                                       setexprap_ipoloop(conchan->ipo, IPO_CYCL);
-                                                                       break;
-                                                               case SET_EXTEND_CYCLICEXTRAPOLATION:
-                                                                       setexprap_ipoloop(conchan->ipo, IPO_CYCLX);
-                                                                       break;
-                                                       }
-                                               }
-                                       }
-                               }
-                       }
-               }
+       switch (selectmode) {
+       case SELECT_ADD:
+               achan->flag |= ACHAN_SELECTED;
+               break;
+       case SELECT_SUBTRACT:
+               achan->flag &= ~ACHAN_SELECTED;
+               break;
+       case SELECT_INVERT:
+               achan->flag ^= ACHAN_SELECTED;
+               break;
        }
+       flag = (achan->flag & ACHAN_SELECTED) ? 1 : 0;
 
-       /* Clean up and redraw stuff
-        */
-       remake_action_ipos(act);
-       BIF_undo_push("Set Ipo type Action channel");
-       allspace(REMAKEIPO, 0);
-       allqueue(REDRAWACTION, 0);
-       allqueue(REDRAWIPO, 0);
-       allqueue(REDRAWNLA, 0);
+       hilight_channel(act, achan, flag);
+       select_poseelement_by_name(achan->name, flag);
+
+       return flag;
 }
 
-static void set_snap_actionchannels(bAction *act, short snaptype) 
+static int select_constraint_channel(bAction *act, 
+                                     bConstraintChannel *conchan, 
+                                     int selectmode) 
 {
-       /* snapping function for action channels*/
-       bActionChannel *achan;
-       bConstraintChannel *conchan;
-       
-       /* Loop through the channels */
-       for (achan = act->chanbase.first; achan; achan= achan->next){
-               if(EDITABLE_ACHAN(achan)) {
-                       if (achan->ipo) {
-                               if(G.saction->pin==0 && OBACT) {
-                                       actstrip_map_ipo_keys(OBACT, achan->ipo, 0);
-                                       snap_ipo_keys(achan->ipo, snaptype);
-                                       actstrip_map_ipo_keys(OBACT, achan->ipo, 1);
-                               }
-                               else {
-                                       snap_ipo_keys(achan->ipo, snaptype);
-                               }
-                       }
-               }
-               
-               if (VISIBLE_ACHAN(achan)) {
-                       /* constraint channels */
-                       for (conchan=achan->constraintChannels.first; conchan; conchan= conchan->next) {
-                               if (EDITABLE_CONCHAN(conchan)) {
-                                       if (conchan->ipo) {
-                                               if(G.saction->pin==0 && OBACT) {
-                                                       actstrip_map_ipo_keys(OBACT, conchan->ipo, 0);
-                                                       snap_ipo_keys(conchan->ipo, snaptype);
-                                                       actstrip_map_ipo_keys(OBACT, conchan->ipo, 1);
-                                               }
-                                               else {
-                                                       snap_ipo_keys(conchan->ipo, snaptype);
-                                               }
-                                       }
-                               }
-                       }                                               
-               }
+       /* Select the constraint channel based on the selection mode */
+       int flag;
+
+       switch (selectmode) {
+       case SELECT_ADD:
+               conchan->flag |= CONSTRAINT_CHANNEL_SELECT;
+               break;
+       case SELECT_SUBTRACT:
+               conchan->flag &= ~CONSTRAINT_CHANNEL_SELECT;
+               break;
+       case SELECT_INVERT:
+               conchan->flag ^= CONSTRAINT_CHANNEL_SELECT;
+               break;
        }
+       flag = (conchan->flag & CONSTRAINT_CHANNEL_SELECT) ? 1 : 0;
+
+       return flag;
 }
 
-static void set_snap_meshchannels(Key *key, short snaptype) 
+int select_icu_channel(bAction *act, IpoCurve *icu, int selectmode) 
 {
-       /* snapping function for mesh channels */
-       if(key->ipo) {
-               snap_ipo_keys(key->ipo, snaptype);
+       /* Select the channel based on the selection mode */
+       int flag;
+
+       switch (selectmode) {
+       case SELECT_ADD:
+               icu->flag |= IPO_SELECT;
+               break;
+       case SELECT_SUBTRACT:
+               icu->flag &= ~IPO_SELECT;
+               break;
+       case SELECT_INVERT:
+               icu->flag ^= IPO_SELECT;
+               break;
        }
+       flag = (icu->flag & IPO_SELECT) ? 1 : 0;
+       return flag;
 }
 
+/* ----------------------------------------- */
 
-void snap_keys_to_frame(int snap_mode) 
+/* deselects action channels in given action */
+void deselect_actionchannels (bAction *act, short test)
 {
-       /* This function is the generic entry-point for snapping keyframes
-        * to a frame(s). It passes the work off to sub-functions for the 
-        * different types in the action editor.
-        */
-        
-       SpaceAction *saction;
-       bAction *act;
-       Key *key;
-       char str[32];
-
-       /* get data */
-       saction= curarea->spacedata.first;
-       if (!saction) return;
-       act = saction->action;
-       key = get_action_mesh_key();
+       ListBase act_data = {NULL, NULL};
+       bActListElem *ale;
+       int filter, sel=1;
        
-       /* determine mode */
-       switch (snap_mode) {
-               case 1:
-                       strcpy(str, "Snap Keys To Nearest Frame");
-                       break;
-               case 2:
-                       strcpy(str, "Snap Keys To Current Frame");
-                       break;
-               default:
-                       return;
-       }
+       /* filter data */
+       filter= ACTFILTER_VISIBLE;
+       actdata_filter(&act_data, filter, act, ACTCONT_ACTION);
        
-       /* snap to frame */
-       if (key) {
-               set_snap_meshchannels(key, snap_mode);
-       }
-       else {
-               set_snap_actionchannels(act, snap_mode);
-               remake_action_ipos (act);
+       /* See if we should be selecting or deselecting */
+       if (test) {
+               for (ale= act_data.first; ale; ale= ale->next) {
+                       if (sel == 0) 
+                               break;
+                       
+                       switch (ale->type) {
+                               case ACTTYPE_ACHAN:
+                                       if (ale->flag & ACHAN_SELECTED) 
+                                               sel= 0;
+                                       break;
+                               case ACTTYPE_CONCHAN:
+                                       if (ale->flag & CONSTRAINT_CHANNEL_SELECT) 
+                                               sel=0;
+                                       break;
+                               case ACTTYPE_ICU:
+                                       if (ale->flag & IPO_SELECT)
+                                               sel=0;
+                                       break;
+                       }
+               }
        }
-       
-       BIF_undo_push(str);
-       allspace(REMAKEIPO, 0);
-       allqueue(REDRAWACTION, 0);
-       allqueue(REDRAWIPO, 0);
-       allqueue(REDRAWNLA, 0);
-}
-
-static void mirror_actionchannels(bAction *act, short mirror_mode) 
-{
-       /* mirror function for action channels */
-       bActionChannel *achan;
-       bConstraintChannel *conchan;
-       
-       /* Loop through the channels */
-       for (achan = act->chanbase.first; achan; achan= achan->next){
-               if(EDITABLE_ACHAN(achan)) {
-                       if (achan->ipo) {
-                               if(G.saction->pin==0 && OBACT) {
-                                       actstrip_map_ipo_keys(OBACT, achan->ipo, 0);
-                                       mirror_ipo_keys(achan->ipo, mirror_mode);
-                                       actstrip_map_ipo_keys(OBACT, achan->ipo, 1);
-                               }
-                               else {
-                                       mirror_ipo_keys(achan->ipo, mirror_mode);
-                               }
-                       }
-               }
+       else
+               sel= 0;
                
-               if (VISIBLE_ACHAN(achan)) {
-                       /* constraint channels */
-                       for (conchan=achan->constraintChannels.first; conchan; conchan= conchan->next) {
-                               if (EDITABLE_CONCHAN(conchan)) {        
-                                       if (conchan->ipo) {
-                                               if(G.saction->pin==0 && OBACT) {
-                                                       actstrip_map_ipo_keys(OBACT, conchan->ipo, 0);
-                                                       mirror_ipo_keys(conchan->ipo, mirror_mode);
-                                                       actstrip_map_ipo_keys(OBACT, conchan->ipo, 1);
-                                               }
-                                               else {
-                                                       mirror_ipo_keys(conchan->ipo, mirror_mode);
-                                               }
-                                       }
-                               }
-                       }                                               
+       /* Now set the flags */
+       for (ale= act_data.first; ale; ale= ale->next) {
+               switch (ale->type) {
+                       case ACTTYPE_ACHAN:
+                       {
+                               bActionChannel *achan= (bActionChannel *)ale->data;
+                               
+                               if (sel)
+                                       achan->flag |= ACHAN_SELECTED;
+                               else
+                                       achan->flag &= ~ACHAN_SELECTED;
+                               select_poseelement_by_name(achan->name, sel);
+                       }
+                               break;
+                       case ACTTYPE_CONCHAN:
+                       {
+                               bConstraintChannel *conchan= (bConstraintChannel *)ale->data;
+                               
+                               if (sel)
+                                       conchan->flag |= CONSTRAINT_CHANNEL_SELECT;
+                               else
+                                       conchan->flag &= ~CONSTRAINT_CHANNEL_SELECT;
+                       }
+                               break;
+                       case ACTTYPE_ICU:
+                       {
+                               IpoCurve *icu= (IpoCurve *)ale->data;
+                               
+                               if (sel)
+                                       icu->flag |= IPO_SELECT;
+                               else
+                                       icu->flag &= ~IPO_SELECT;
+                       }
+                               break;
                }
        }
+       
+       /* Cleanup */
+       BLI_freelistN(&act_data);
 }
 
-static void mirror_meshchannels(Key *key, short mirror_mode) 
+/* deselects channels in the action editor */
+void deselect_action_channels (short test)
 {
-       /* mirror function for mesh channels */
-       if(key->ipo) {
-               mirror_ipo_keys(key->ipo, mirror_mode);
-       }
+       void *data;
+       short datatype;
+       
+       /* determine what type of data we are operating on */
+       data = get_action_context(&datatype);
+       if (data == NULL) return;
+       
+       /* based on type */
+       if (datatype == ACTCONT_ACTION)
+               deselect_actionchannels(data, test);
+       // should shapekey channels be allowed to do this? 
 }
 
-void mirror_action_keys(short mirror_mode)
+/* deselects keyframes in the action editor */
+void deselect_action_keys (short test, short sel)
 {
-       /* This function is the generic entry-point for mirroring keyframes
-        * to over a frame. It passes the work off to sub-functions for the 
-        * different types in the action editor.
-        */
-        
-       SpaceAction *saction;
-       bAction *act;
-       Key *key;
-       char str[32];
-
-       /* get data */
-       saction= curarea->spacedata.first;
-       if (!saction) return;
-       act = saction->action;
-       key = get_action_mesh_key();
-       
-       /* determine mode */
-       switch (mirror_mode) {
-               case 1:
-                       strcpy(str, "Mirror Keys Over Current Frame");
-                       break;
-               case 2:
-                       strcpy(str, "Mirror Keys Over Y-Axis");
-                       break;
-               default:
-                       return;
-       }
+       ListBase act_data = {NULL, NULL};
+       bActListElem *ale;
+       int filter;
+       void *data;
+       short datatype;
+       
+       /* determine what type of data we are operating on */
+       data = get_action_context(&datatype);
+       if (data == NULL) return;
+               
+       /* filter data */
+       filter= (ACTFILTER_VISIBLE | ACTFILTER_IPOKEYS);
+       actdata_filter(&act_data, filter, data, datatype);
        
-       /* mirror */
-       if (key) {
-               mirror_meshchannels(key, mirror_mode);
+       /* See if we should be selecting or deselecting */
+       if (test) {
+               for (ale= act_data.first; ale; ale= ale->next) {
+                       if (is_ipo_key_selected(ale->key_data)) {
+                               sel= 0;
+                               break;
+                       }
+               }
        }
-       else {
-               mirror_actionchannels(act, mirror_mode);
-               remake_action_ipos (act);
+               
+       /* Now set the flags */
+       for (ale= act_data.first; ale; ale= ale->next) {
+               set_ipo_key_selection(ale->key_data, sel);
        }
        
-       BIF_undo_push(str);
-       allspace(REMAKEIPO, 0);
-       allqueue(REDRAWACTION, 0);
-       allqueue(REDRAWIPO, 0);
-       allqueue(REDRAWNLA, 0);
+       /* Cleanup */
+       BLI_freelistN(&act_data);
 }
 
-static void select_all_keys_frames(bAction *act, short *mval, 
-                                                       short *mvalo, int selectmode) {
-       
-       /* This function tries to select all action keys in
-        * every channel for a given range of keyframes that
-        * are within the mouse values mval and mvalo (usually
-        * the result of a border select). If mvalo is passed as
-        * NULL then the selection is treated as a one-click and
-        * the function tries to select all keys within half a
-        * frame of the click point.
-        */
+/* selects all keyframes in the action editor - per channel or time 
+ *     mode = 0: all in channel; mode = 1: all in frame
+ */
+void selectall_action_keys (short mval[], short mode, short select_mode)
+{
+       void *data;
+       short datatype;
        
-       rcti rect;
-       rctf rectf;
-       bActionChannel *chan;
-       bConstraintChannel *conchan;
-
-       if (!act)
-               return;
-
-       if (selectmode == SELECT_REPLACE) {
-               deselect_actionchannel_keys(act, 0, 1);
-               selectmode = SELECT_ADD;
-       }
-
-       if (mvalo == NULL) {
-               rect.xmin = rect.xmax = mval[0];
-               rect.ymin = rect.ymax = mval[1];
+       /* determine what type of data we are operating on */
+       data = get_action_context(&datatype);
+       if (data == NULL) return;
+               
+       if (select_mode == SELECT_REPLACE) {
+               deselect_action_keys(0, 0);
+               select_mode = SELECT_ADD;
        }
-       else {
-               if (mval[0] < mvalo[0] ) {
-                       rect.xmin = mval[0];
-                       rect.xmax = mvalo[0];
-               }
-               else {
-                       rect.xmin = mvalo[0];
-                       rect.xmax = mval[0];
-               }
-               if (mval[1] < mvalo[1] ) {
-                       rect.ymin = mval[1];
-                       rect.ymax = mvalo[1];
+               
+       /* depending on mode */
+       switch (mode) {
+               case 0: /* all in channel*/
+               {
+                       void *act_channel;
+                       short chantype;
+                       
+                       /* get channel, and act according to type */
+                       act_channel= get_nearest_act_channel(mval, &chantype);
+                       switch (chantype) {
+                               case ACTTYPE_ACHAN:
+                               {
+                                       bActionChannel *achan= (bActionChannel *)act_channel;
+                                       select_ipo_bezier_keys(achan->ipo, select_mode);
+                               }
+                                       break;
+                               case ACTTYPE_CONCHAN:
+                               {
+                                       bConstraintChannel *conchan= (bConstraintChannel *)act_channel;
+                                       select_ipo_bezier_keys(conchan->ipo, select_mode);
+                               }
+                                       break;
+                               case ACTTYPE_ICU:
+                               {
+                                       IpoCurve *icu= (IpoCurve *)act_channel;
+                                       select_icu_bezier_keys(icu, select_mode);
+                               }
+                                       break;
+                       }
                }
-               else {
-                       rect.ymin = mvalo[1];
-                       rect.ymax = mval[1];
+                       break;
+               case 1: /* all in frame */
+               {
+                       ListBase act_data = {NULL, NULL};
+                       bActListElem *ale;
+                       int filter;
+                       rcti rect;
+                       rctf rectf;
+                       
+                       /* use bounding box to find kframe */
+                       rect.xmin = rect.xmax = mval[0];
+                       rect.ymin = rect.ymax = mval[1];
+                       
+                       mval[0]= rect.xmin;
+                       mval[1]= rect.ymin+2;
+                       areamouseco_to_ipoco(G.v2d, mval, &rectf.xmin, &rectf.ymin);
+                       rectf.xmax= rectf.xmin;
+                       rectf.ymax= rectf.ymin;
+                       
+                       rectf.xmin = rectf.xmin - 0.5;
+                       rectf.xmax = rectf.xmax + 0.5;
+                       
+                       /* filter data */
+                       filter= (ACTFILTER_VISIBLE | ACTFILTER_IPOKEYS);
+                       actdata_filter(&act_data, filter, data, datatype);
+                               
+                       /* Now set the flags */
+                       for (ale= act_data.first; ale; ale= ale->next)
+                               borderselect_ipo_key(ale->key_data, rectf.xmin, rectf.xmax, select_mode);
+                       
+                       /* Cleanup */
+                       BLI_freelistN(&act_data);
                }
+               break;
        }
-
-       mval[0]= rect.xmin;
-       mval[1]= rect.ymin+2;
-       areamouseco_to_ipoco(G.v2d, mval, &rectf.xmin, &rectf.ymin);
-       mval[0]= rect.xmax;
-       mval[1]= rect.ymax-2;
-       areamouseco_to_ipoco(G.v2d, mval, &rectf.xmax, &rectf.ymax);
-
-       if (mvalo == NULL) {
-               rectf.xmin = rectf.xmin - 0.5;
-               rectf.xmax = rectf.xmax + 0.5;
-       }
-    
-       for (chan=act->chanbase.first; chan; chan=chan->next){
-               if((chan->flag & ACHAN_HIDDEN)==0) {
-                       borderselect_ipo_key(chan->ipo, rectf.xmin, rectf.xmax,
-                                                                selectmode);
-                       for (conchan=chan->constraintChannels.first; conchan; 
-                                conchan=conchan->next){
-                               borderselect_ipo_key(conchan->ipo, rectf.xmin, rectf.xmax,
-                                                                        selectmode);
-                       }
-               }
-       }       
-
+       
        allqueue(REDRAWNLA, 0);
        allqueue(REDRAWACTION, 0);
        allqueue(REDRAWIPO, 0);
 }
 
-
-static void select_all_keys_channels(bAction *act, short *mval, 
-                              short *mvalo, int selectmode) {
-       bActionChannel    *chan;
-       float              click, x,y;
-       int                clickmin, clickmax;
-       int                wsize;
-       bConstraintChannel *conchan;
-
-       /* This function selects all the action keys that
-        * are in the mouse selection range defined by
-        * the ordered pairs mval and mvalo (usually
-        * these 2 are obtained from a border select).
-        * If mvalo is NULL, then the selection is
-        * treated like a one-click action, and at most
-        * one channel is selected.
-        */
-
-       /* If the action is null then abort
-        */
-       if (!act)
-               return;
-
-       if (selectmode == SELECT_REPLACE) {
-               deselect_actionchannel_keys(act, 0, 1);
-               selectmode = SELECT_ADD;
-       }
-
-       /* wsize is the greatest possible height (in pixels) that would be
-        * needed to draw all of the action channels and constraint
-        * channels.
-        */
-
-       wsize =  count_action_levels(act)*(CHANNELHEIGHT+CHANNELSKIP);
-       wsize += CHANNELHEIGHT/2;
+void markers_selectkeys_between (void)
+{
+       ListBase act_data = {NULL, NULL};
+       bActListElem *ale;
+       int filter;
+       void *data;
+       short datatype;
+       float min, max;
        
-    areamouseco_to_ipoco(G.v2d, mval, &x, &y);
-    clickmin = (int) ((wsize - y) / (CHANNELHEIGHT+CHANNELSKIP));
+       /* determine what type of data we are operating on */
+       data = get_action_context(&datatype);
+       if (data == NULL) return;
        
-       /* Only one click */
-       if (mvalo == NULL) {
-               clickmax = clickmin;
-       }
-       /* Two click values (i.e., border select) */
-       else {
-
-               areamouseco_to_ipoco(G.v2d, mvalo, &x, &y);
-               click = ((wsize - y) / (CHANNELHEIGHT+CHANNELSKIP));
+       /* get extreme markers */
+       get_minmax_markers(1, &min, &max);
+       if (min==max) return;
+       min -= 0.5f;
+       max += 0.5f;
+       
+       /* filter data */
+       filter= (ACTFILTER_VISIBLE | ACTFILTER_IPOKEYS);
+       actdata_filter(&act_data, filter, data, datatype);
                
-               if ( ((int) click) < clickmin) {
-                       clickmax = clickmin;
-                       clickmin = (int) click;
+       /* select keys in-between */
+       for (ale= act_data.first; ale; ale= ale->next) {
+               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);
                }
                else {
-                       clickmax = (int) click;
+                       borderselect_ipo_key(ale->key_data, min, max, SELECT_ADD);
                }
        }
+       
+       /* Cleanup */
+       BLI_freelistN(&act_data);
+}
 
-       if (clickmax < 0) {
-               return;
-       }
+/* ----------------------------------------- */
 
-       for (chan = act->chanbase.first; chan; chan=chan->next){
-               if((chan->flag & ACHAN_HIDDEN)==0) {
-                       if (clickmax < 0) break;
+/* This function makes a list of the selected keyframes
+ * in the ipo curves it has been passed
+ */
+static void make_sel_cfra_list(Ipo *ipo, ListBase *elems)
+{
+       IpoCurve *icu;
+       
+       if (ipo == NULL) return;
+       
+       for(icu= ipo->curve.first; icu; icu= icu->next) {
+               BezTriple *bezt;
+               int a= 0;
+               
+               for (bezt=icu->bezt; a<icu->totvert; a++, bezt++) {
+                       if (bezt && BEZSELECTED(bezt))
+                               add_to_cfra_elem(elems, bezt);
+               }
+       }
+}
 
-                       if ( clickmin <= 0) {
-                               /* Select the channel with the given mode. If the
-                                * channel is freshly selected then set it to the
-                                * active channel for the action
-                                */
-                               select_ipo_bezier_keys(chan->ipo, selectmode);
+/* This function selects all key frames in the same column(s) as a already selected key(s)
+ * or marker(s), or all the keyframes on a particular frame (triggered by a RMB on x-scrollbar)
+ */
+void column_select_action_keys(int mode)
+{
+       ListBase elems= {NULL, NULL};
+       CfraElem *ce;
+       IpoCurve *icu;
+       ListBase act_data = {NULL, NULL};
+       bActListElem *ale;
+       int filter;
+       void *data;
+       short datatype;
+       
+       /* determine what type of data we are operating on */
+       data = get_action_context(&datatype);
+       if (data == NULL) return;
+       
+       /* build list of columns */
+       switch (mode) {
+               case 1: /* list of selected keys */
+                       filter= (ACTFILTER_VISIBLE | ACTFILTER_IPOKEYS);
+                       actdata_filter(&act_data, filter, data, datatype);
+                       
+                       for (ale= act_data.first; ale; ale= ale->next)
+                               make_sel_cfra_list(ale->key_data, &elems);
+                       
+                       BLI_freelistN(&act_data);
+                       break;
+               case 2: /* list of selected markers */
+                       make_marker_cfra_list(&elems, 1);
+                       
+                       /* apply scaled action correction if needed */
+                       if (NLA_ACTION_SCALED && datatype==ACTCONT_ACTION) {
+                               for (ce= elems.first; ce; ce= ce->next) 
+                                       ce->cfra= get_action_frame(OBACT, ce->cfra);
                        }
-                       --clickmin;
-                       --clickmax;
-
-                       /* Check for click in a constraint */
-                       for (conchan=chan->constraintChannels.first; 
-                                conchan; conchan=conchan->next){
-                               if (clickmax < 0) break;
-                               if ( clickmin <= 0) {
-                                       select_ipo_bezier_keys(chan->ipo, selectmode);
+                       break;
+       }
+       
+       /* loop through all of the keys and select additional keyframes
+        * based on the keys found to be selected above
+        */
+       filter= (ACTFILTER_VISIBLE | ACTFILTER_ONLYICU);
+       actdata_filter(&act_data, filter, data, datatype);
+       
+       for (ale= act_data.first; ale; ale= ale->next) {
+               for(ce= elems.first; ce; ce= ce->next) {
+                       for (icu= ale->key_data; icu; icu= icu->next) {
+                               BezTriple *bezt;
+                               int verts = 0;
+                               
+                               for (bezt=icu->bezt; verts<icu->totvert; bezt++, verts++) {
+                                       if (bezt) {
+                                               if( (int)(ce->cfra) == (int)(bezt->vec[1][0]) )
+                                                       bezt->f2 |= 1;
+                                       }
                                }
-                               --clickmin;
-                               --clickmax;
                        }
                }
        }
-  
-       allqueue (REDRAWIPO, 0);
-       allqueue (REDRAWVIEW3D, 0);
-       allqueue (REDRAWACTION, 0);
-       allqueue (REDRAWNLA, 0);
-  
+       
+       BLI_freelistN(&act_data);
+       BLI_freelistN(&elems);
 }
 
-static void borderselect_function(void (*select_func)(bAction *act, 
-                                                     short *mval, 
-                                                     short *mvalo, 
-                                                     int selectmode)) {
-       /* This function executes an arbitrary selection
-        * function as part of a border select. This
-        * way the same function that is used for
-        * right click selection points can generally
-        * be used as the argument to this function
-        */
-       rcti rect;
-       short   mval[2], mvalo[2];
-       bAction *act;
-       int val;                
-
-       /* Get the selected action, exit if none are selected 
-        */
-       act=G.saction->action;
-       if (!act)
-               return;
+/* some quick defines for borderselect modes */
+#define ACTEDIT_BORDERSEL_ALL 0
+#define ACTEDIT_BORDERSEL_FRA 1
+#define ACTEDIT_BORDERSEL_CHA 2
 
-       /* Let the user draw a border (or abort)
-        */
-       if ( (val=get_border (&rect, 3)) ) {
+/* borderselect: for keyframes only */
+void borderselect_action (void)
+{
+       ListBase act_data = {NULL, NULL};
+       bActListElem *ale;
+       int filter;
+       void *data;
+       short datatype;
+       
+       rcti rect;
+       rctf rectf;
+       int val, selectmode, mode;
+       int (*select_function)(BezTriple *);
+       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) {
+                       selectmode = SELECT_ADD;
+                       select_function = select_bezier_add;
+               }
+               else {
+                       selectmode = SELECT_SUBTRACT;
+                       select_function = select_bezier_subtract;
+               }
+               
                mval[0]= rect.xmin;
                mval[1]= rect.ymin+2;
-               mvalo[0]= rect.xmax;
-               mvalo[1]= rect.ymax-2;
-
-               /* if the left mouse was used, do an additive
-                * selection with the user defined selection
-                * function.
-                */
-               if (val == LEFTMOUSE)
-                       select_func(act, mval, mvalo, SELECT_ADD);
+               areamouseco_to_ipoco(G.v2d, mval, &rectf.xmin, &rectf.ymin);
+               mval[0]= rect.xmax;
+               mval[1]= rect.ymax-2;
+               areamouseco_to_ipoco(G.v2d, mval, &rectf.xmax, &rectf.ymax);
                
-               /* if the right mouse was used, do a subtractive
-                * selection with the user defined selection
-                * function.
-                */
-               else if (val == RIGHTMOUSE)
-                       select_func(act, mval, mvalo, SELECT_SUBTRACT);
-       }
-       BIF_undo_push("Border select Action");
-       
-}
-
-static void clever_keyblock_names(Key *key, short* mval){
-    int        but=0, i, keynum;
-    char       str[64];
-       float      x;
-       KeyBlock   *kb;
-       /* get the keynum cooresponding to the y value
-        * of the mouse pointer, return if this is
-        * 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') {
-               sprintf(str, "Key %d", keynum);
-       }
-       else {
-               strcpy(str, kb->name);
-       }
-
-       if ( (kb->slidermin >= kb->slidermax) ) {
-               kb->slidermin = 0.0;
-               kb->slidermax = 1.0;
-       }
-
-    add_numbut(but++, TEX, "KB: ", 0, 24, str, 
-               "Does this really need a tool tip?");
-       add_numbut(but++, NUM|FLO, "Slider Min:", 
-                          -10000, kb->slidermax, &kb->slidermin, 0);
-       add_numbut(but++, NUM|FLO, "Slider Max:", 
-                          kb->slidermin, 10000, &kb->slidermax, 0);
-
-    if (do_clever_numbuts(str, but, REDRAW)) {
-               strcpy(kb->name, str);
-        allqueue (REDRAWACTION, 0);
-               allspace(REMAKEIPO, 0);
-        allqueue (REDRAWIPO, 0);
+               /* if action is mapped in NLA, it returns a correction */
+               if (NLA_ACTION_SCALED && datatype==ACTCONT_ACTION) {
+                       rectf.xmin= get_action_frame(OBACT, rectf.xmin);
+                       rectf.xmax= get_action_frame(OBACT, rectf.xmax);
+               }
+               
+               ymax = CHANNELHEIGHT/2;
+               
+               /* filter data */
+               filter= (ACTFILTER_VISIBLE | ACTFILTER_CHANNELS);
+               actdata_filter(&act_data, filter, data, datatype);
+               
+               /* loop over data, doing border select */
+               for (ale= act_data.first; ale; ale= ale->next) {
+                       ymin=ymax-(CHANNELHEIGHT+CHANNELSKIP);
+                       
+                       /* 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;
+               }
+               
+               /* cleanup */
+               BLI_freelistN(&act_data);
+               
+               BIF_undo_push("Border Select Action");
+               allqueue(REDRAWIPO, 0);
+               allqueue(REDRAWACTION, 0);
+               allqueue(REDRAWNLA, 0);
        }
-
-       
 }
 
-static void numbuts_action(void)
-{
-       /* now called from action window event loop, plus reacts on mouseclick */
-       /* removed Hos grunts for that reason! :) (ton) */
-    Key *key;
-    short mval[2];
-
-    if ( (key = get_action_mesh_key()) ) {
-        getmouseco_areawin (mval);
-               if (mval[0]<NAMEWIDTH) {
-                       clever_keyblock_names(key, mval);
-               }
-    }
-}
+/* **************************************************** */
+/* MOUSE-HANDLING */
 
-void winqreadactionspace(ScrArea *sa, void *spacedata, BWinEvent *evt)
+/* right-hand side - mouse click */
+static void mouse_action (int selectmode)
 {
-       extern void do_actionbuts(unsigned short event); // drawaction.c
-       SpaceAction *saction;
-       bAction *act;
-       Key *key;
-       float dx,dy;
-       int doredraw= 0;
-       int     cfra;
-       short   mval[2];
-       short nr;
-       unsigned short event= evt->event;
-       short val= evt->val;
-       short mousebut = L_MOUSE;
-
-       if(curarea->win==0) return;
-
-       saction= curarea->spacedata.first;
-       if (!saction)
-               return;
+       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;
+       
+       /* determine what type of data we are operating on */
+       data = get_action_context(&datatype);
+       if (data == NULL) return;
+       if (datatype == ACTCONT_ACTION) act= (bAction *)data;
 
-       act=saction->action;
-       if(val) {
+       act_channel= get_nearest_action_key(&selx, &sel, &act_type, &achan);
+       marker=find_nearest_marker(1);
                
-               if( uiDoBlocks(&curarea->uiblocks, event)!=UI_NOTHING ) event= 0;
+       if (act_channel) {
+               switch (act_type) {
+                       case ACTTYPE_ICU:
+                               icu= (IpoCurve *)act_channel;
+                               break;
+                       case ACTTYPE_CONCHAN:
+                               conchan= (bConstraintChannel *)act_channel;
+                               break;
+                       case ACTTYPE_ACHAN:
+                               achan= (bActionChannel *)act_channel;
+                               break;
+                       default:
+                               return;
+               }
                
-               /* swap mouse buttons based on user preference */
-               if (U.flag & USER_LMOUSESELECT) {
-                       if (event == LEFTMOUSE) {
-                               event = RIGHTMOUSE;
-                               mousebut = L_MOUSE;
-                       } else if (event == RIGHTMOUSE) {
-                               event = LEFTMOUSE;
-                               mousebut = R_MOUSE;
+               if (selectmode == SELECT_REPLACE) {
+                       selectmode = SELECT_ADD;
+                       
+                       deselect_action_keys(0, 0);
+                       
+                       if (datatype == ACTCONT_ACTION) {
+                               deselect_action_channels(0);
+                               
+                               achan->flag |= ACHAN_SELECTED;
+                               hilight_channel(act, achan, 1);
+                               select_poseelement_by_name(achan->name, 2);     /* 2 is activate */
                        }
                }
                
-               getmouseco_areawin(mval);
-
-               key = get_action_mesh_key();
-
-               switch(event) {
-               case UI_BUT_EVENT:
-                       do_actionbuts(val);     // window itself
-                       break;
+               if (icu)
+                       select_icu_key(icu, selx, selectmode);
+               else if (conchan)
+                       select_ipo_key(conchan->ipo, selx, selectmode);
+               else
+                       select_ipo_key(achan->ipo, selx, selectmode);
                
-               case HOMEKEY:
-                       do_action_buttons(B_ACTHOME);   // header
-                       break;
+               std_rmouse_transform(transform_action_keys);
+               
+               allqueue(REDRAWIPO, 0);
+               allqueue(REDRAWVIEW3D, 0);
+               allqueue(REDRAWACTION, 0);
+               allqueue(REDRAWNLA, 0);
+               allqueue(REDRAWOOPS, 0);
+               allqueue(REDRAWBUTSALL, 0);
+       }
+       else if (marker) {
+               /* not channel, so maybe 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(REDRAWTIME, 0);
+               allqueue(REDRAWIPO, 0);
+               allqueue(REDRAWACTION, 0);
+               allqueue(REDRAWNLA, 0);
+               allqueue(REDRAWSOUND, 0);
+       }
+}
 
-               case AKEY:
-                       if (key) {
-                               if (mval[0]<ACTWIDTH){
-                                       /* to do ??? */
-                               }
+/* lefthand side - mouse-click  */
+static void mouse_actionchannels (short mval[])
+{
+       bAction *act= G.saction->action;
+       void *data, *act_channel;
+       short datatype, chantype;
+       
+       /* determine what type of data we are operating on */
+       data = get_action_context(&datatype);
+       if (data == NULL) return;
+       
+       /* get channel to work on */
+       act_channel= get_nearest_act_channel(mval, &chantype);
+       
+       /* action to take depends on what channel we've got */
+       switch (chantype) {
+               case ACTTYPE_ACHAN:
+                       {
+                               bActionChannel *achan= (bActionChannel *)act_channel;
+                               
+                               if (mval[0] >= (NAMEWIDTH-16)) {
+                                       /* toggle protect */
+                                       achan->flag ^= ACHAN_PROTECTED;
+                               }
+                               else if ((mval[0] >= (NAMEWIDTH-32)) && (achan->ipo)) {
+                                       /* toggle mute */
+                                       achan->ipo->muteipo = (achan->ipo->muteipo)? 0: 1;
+                               }
+                               else if (mval[0] <= 17) {
+                                       /* toggle expand */
+                                       achan->flag ^= ACHAN_EXPANDED;
+                               }                               
                                else {
-                                       if (G.qual == LR_CTRLKEY) {
-                                               deselect_markers(1, 0);
-                                               allqueue(REDRAWTIME, 0);
-                                               allqueue(REDRAWIPO, 0);
-                                               allqueue(REDRAWACTION, 0);
-                                               allqueue(REDRAWNLA, 0);
-                                               allqueue(REDRAWSOUND, 0);
+                                       /* select/deselect achan */             
+                                       if (G.qual & LR_SHIFTKEY) {
+                                               select_channel(act, achan, SELECT_INVERT);
                                        }
                                        else {
-                                               deselect_meshchannel_keys(key, 1, 1);
-                                               allqueue (REDRAWACTION, 0);
-                                               allqueue(REDRAWNLA, 0);
-                                               allqueue (REDRAWIPO, 0);
+                                               deselect_actionchannels(act, 0);
+                                               select_channel(act, achan, SELECT_ADD);
                                        }
+                                       
+                                       /* messy... set active bone */
+                                       select_poseelement_by_name(achan->name, 2);
                                }
                        }
-                       else {
-                               if (mval[0]<NAMEWIDTH) {
-                                       deselect_actionchannels (act, 1);
-                                       allqueue (REDRAWVIEW3D, 0);
-                                       allqueue (REDRAWACTION, 0);
-                                       allqueue(REDRAWNLA, 0);
-                                       allqueue (REDRAWIPO, 0);
-                               }
-                               else if (mval[0]>ACTWIDTH) {
-                                       if (G.qual == LR_CTRLKEY) {
-                                               deselect_markers (1, 0);
-                                               allqueue(REDRAWTIME, 0);
-                                               allqueue(REDRAWIPO, 0);
-                                               allqueue(REDRAWACTION, 0);
-                                               allqueue(REDRAWNLA, 0);
-                                               allqueue(REDRAWSOUND, 0);
-                                       }
-                                       else {
-                                               deselect_actionchannel_keys (act, 1, 1);
-                                               allqueue (REDRAWACTION, 0);
-                                               allqueue(REDRAWNLA, 0);
-                                               allqueue (REDRAWIPO, 0);
-                                       }
-                               }
+                               break;
+               case ACTTYPE_FILLIPO:
+                       {
+                               bActionChannel *achan= (bActionChannel *)act_channel;
+                               
+                               achan->flag ^= ACHAN_SHOWIPO;
+                               
+                               if ((mval[0] > 24) && (achan->flag & ACHAN_SHOWIPO)) {
+                                       /* select+make active achan */          
+                                       deselect_actionchannels(act, 0);
+                                       select_channel(act, achan, SELECT_ADD);
+                                       
+                                       /* messy... set active bone */
+                                       select_poseelement_by_name(achan->name, 2);
+                               }       
                        }
                        break;
-
-               case BKEY:
-                       if (G.qual & LR_CTRLKEY)
-                               borderselect_markers();
-                       else if (key) {
-                               if (mval[0]<ACTWIDTH){
-                                       /* to do?? */
-                               }
-                               else {
-                                       borderselect_mesh(key);
-                               }
+               case ACTTYPE_FILLCON:
+                       {
+                               bActionChannel *achan= (bActionChannel *)act_channel;
+                               
+                               achan->flag ^= ACHAN_SHOWCONS;
+                               
+                               if ((mval[0] > 24) && (achan->flag & ACHAN_SHOWCONS)) {
+                                       /* select+make active achan */          
+                                       deselect_actionchannels(act, 0);
+                                       select_channel(act, achan, SELECT_ADD);
+                                       
+                                       /* messy... set active bone */
+                                       select_poseelement_by_name(achan->name, 2);
+                               }       
                        }
-                       else {
-
-                               /* If the border select is initiated in the
-                                * part of the action window where the channel
-                                * names reside, then select the channels
-                                */
-                               if (mval[0]<NAMEWIDTH){
-                                       borderselect_function(mouse_actionchannels);
-                                       BIF_undo_push("Select Action");
+                       break;
+               case ACTTYPE_ICU: 
+                       {
+                               IpoCurve *icu= (IpoCurve *)act_channel;
+                               
+#if 0 /* disabled until all ipo tools support this ------->  */
+                               if (mval[0] >= (NAMEWIDTH-16)) {
+                                       /* toggle protection */
+                                       icu->flag ^= IPO_PROTECT;
                                }
-                               else if (mval[0]>ACTWIDTH){
-
-                                       /* If the border select is initiated in the
-                                        * vertical scrollbar, then (de)select all keys
-                                        * for the channels in the selection region
-                                        */
-                                       if (IN_2D_VERT_SCROLL(mval)) {
-                                               borderselect_function(select_all_keys_channels);
-                                       }
-
-                                       /* If the border select is initiated in the
-                                        * horizontal scrollbar, then (de)select all keys
-                                        * for the keyframes in the selection region
-                                        */
-                                       else if (IN_2D_HORIZ_SCROLL(mval)) {
-                                               borderselect_function(select_all_keys_frames);
-                                       }
-
-                                       /* Other wise, select the action keys
-                                        */
-                                       else {
-                                               borderselect_action();
-                                       }
+#endif /* <------- end of disabled code */
+                               if (mval[0] >= (NAMEWIDTH-16)) {
+                                       /* toggle mute */
+                                       icu->flag ^= IPO_MUTE;
+                               }
+                               else {
+                                       /* select/deselect */
+                                       select_icu_channel(act, icu, SELECT_INVERT);
                                }
                        }
                        break;
-
-               case CKEY:
-                       /* scroll the window so the current
-                        * frame is in the center.
-                        */
-                       center_currframe();
-                       break;
-
-               case DKEY:
-                       if (mval[0]>ACTWIDTH) {
-                               if (G.qual == (LR_CTRLKEY|LR_SHIFTKEY)) {
-                                       duplicate_marker();
+               case ACTTYPE_CONCHAN:
+                       {
+                               bConstraintChannel *conchan= (bConstraintChannel *)act_channel;
+                               
+                               if (mval[0] >= (NAMEWIDTH-16)) {
+                                       /* toggle protection */
+                                       conchan->flag ^= CONSTRAINT_CHANNEL_PROTECTED;
                                }
-                               else if (G.qual == LR_SHIFTKEY) {
-                                       if (key) {
-                                               duplicate_meshchannel_keys(key);
-                                       }
-                                       else if (act) {
-                                               duplicate_actionchannel_keys();
-                                               remake_action_ipos(act);
-                                       }
+                               else if ((mval[0] >= (NAMEWIDTH-32)) && (conchan->ipo)) {
+                                       /* toggle mute */
+                                       conchan->ipo->muteipo = (conchan->ipo->muteipo)? 0: 1;
                                }
-                       }
-                       break;
-
-               case GKEY:
-                       if (G.qual & LR_CTRLKEY) {
-                               transform_markers('g', 0);
-                       }
-                       else {
-                               if (mval[0]>=ACTWIDTH) {
-                                       if (key) {
-                                               transform_meshchannel_keys('g', key);
-                                       }
-                                       else if (act) {
-                                               transform_actionchannel_keys ('g', 0);
-                                       }
+                               else {
+                                       /* select/deselect */
+                                       select_constraint_channel(act, conchan, SELECT_INVERT);
                                }
                        }
-                       break;
+                               break;
+               default:
+                       return;
+       }
+       
+       allqueue (REDRAWIPO, 0);
+       allqueue (REDRAWVIEW3D, 0);
+       allqueue (REDRAWACTION, 0);
+       allqueue (REDRAWNLA, 0);
+       allqueue (REDRAWOOPS, 0);
+       allqueue (REDRAWBUTSALL, 0);
+}
 
-               case HKEY:
-                       if(G.qual & LR_SHIFTKEY) {
-                               if(okee("Set Keys to Auto Handle")) {
-                                       if (key)
-                                               sethandles_meshchannel_keys(HD_AUTO, key);
-                                       else
-                                               sethandles_actionchannel_keys(HD_AUTO);
-                               }
-                       }
-                       else {
-                               if(okee("Toggle Keys Aligned Handle")) {
-                                       if (key)
-                                               sethandles_meshchannel_keys(HD_ALIGN, key);
-                                       else
-                                               sethandles_actionchannel_keys(HD_ALIGN);
-                               }
+/* **************************************************** */
+/* ACTION CHANNEL RE-ORDERING */
+
+void top_sel_action ()
+{
+       bAction *act;
+       bActionChannel *achan;
+       
+       /* Get the selected action, exit if none are selected */
+       act = G.saction->action;
+       if (!act) return;
+       
+       for (achan= act->chanbase.first; achan; achan= achan->next){
+               if (VISIBLE_ACHAN(achan)) {
+                       if (SEL_ACHAN(achan) && !(achan->flag & ACHAN_MOVED)){
+                               /* take it out off the chain keep data */
+                               BLI_remlink (&act->chanbase, achan);
+                               /* make it first element */
+                               BLI_insertlinkbefore(&act->chanbase, act->chanbase.first, achan);
+                               achan->flag |= ACHAN_MOVED;
+                               /* restart with rest of list */
+                               achan= achan->next;
                        }
-                       break;
-                       
+               }
+       }
+    /* clear temp flags */
+       for (achan= act->chanbase.first; achan; achan= achan->next){
+               achan->flag = achan->flag & ~ACHAN_MOVED;
+       }
+       
+       /* Clean up and redraw stuff */
+       remake_action_ipos (act);
+       BIF_undo_push("Top Action channel");
+       allspace(REMAKEIPO, 0);
+       allqueue(REDRAWACTION, 0);
+       allqueue(REDRAWIPO, 0);
+       allqueue(REDRAWNLA, 0);
+}
+
+void up_sel_action ()
+{
+       bAction *act;
+       bActionChannel *achan, *prev;
+       
+       /* Get the selected action, exit if none are selected */
+       act = G.saction->action;
+       if (!act) return;
+       
+       for (achan=act->chanbase.first; achan; achan= achan->next) {
+               if (VISIBLE_ACHAN(achan)) {
+                       if (SEL_ACHAN(achan) && !(achan->flag & ACHAN_MOVED)){
+                               prev = achan->prev;
+                               if (prev) {
+                                       /* take it out off the chain keep data */
+                                       BLI_remlink (&act->chanbase, achan);
+                                       /* push it up */
+                                       BLI_insertlinkbefore(&act->chanbase, prev, achan);
+                                       achan->flag |= ACHAN_MOVED;
+                                       /* restart with rest of list */
+                                       achan= achan->next;
+                               }
+                       }
+               }
+       }
+       /* clear temp flags */
+       for (achan=act->chanbase.first; achan; achan= achan->next){
+               achan->flag = achan->flag & ~ACHAN_MOVED;
+       }
+       
+       /* Clean up and redraw stuff */
+       remake_action_ipos (act);
+       BIF_undo_push("Up Action channel");
+       allspace(REMAKEIPO, 0);
+       allqueue(REDRAWACTION, 0);
+       allqueue(REDRAWIPO, 0);
+       allqueue(REDRAWNLA, 0);
+}
+
+void down_sel_action ()
+{
+       bAction *act;
+       bActionChannel *achan, *next;
+       
+       /* Get the selected action, exit if none are selected */
+       act = G.saction->action;
+       if (!act) return;
+       
+       for (achan= act->chanbase.last; achan; achan= achan->prev) {
+               if (VISIBLE_ACHAN(achan)) {
+                       if (SEL_ACHAN(achan) && !(achan->flag & ACHAN_MOVED)){
+                               next = achan->next;
+                               if (next) next = next->next;
+                               if (next) {
+                                       /* take it out off the chain keep data */
+                                       BLI_remlink (&act->chanbase, achan);
+                                       /* move it down */
+                                       BLI_insertlinkbefore(&act->chanbase, next, achan);
+                                       achan->flag |= ACHAN_MOVED;
+                               }
+                               else {
+                                       /* take it out off the chain keep data */
+                                       BLI_remlink (&act->chanbase, achan);
+                                       /* add at end */
+                                       BLI_addtail(&act->chanbase, achan);
+                                       achan->flag |= ACHAN_MOVED;
+                               }
+                       }
+               }
+       }
+       /* clear temp flags */
+       for (achan= act->chanbase.first; achan; achan= achan->next){
+               achan->flag = achan->flag & ~ACHAN_MOVED;
+       }
+       
+       /* Clean up and redraw stuff */
+       remake_action_ipos (act);
+       BIF_undo_push("Down Action channel");
+       allspace(REMAKEIPO, 0);
+       allqueue(REDRAWACTION, 0);
+       allqueue(REDRAWIPO, 0);
+       allqueue(REDRAWNLA, 0);
+}
+
+void bottom_sel_action ()
+{
+       bAction *act;
+       bActionChannel *achan;
+       
+       /* Get the selected action, exit if none are selected */
+       act = G.saction->action;
+       if (!act) return;
+       
+       for (achan=act->chanbase.last; achan; achan= achan->prev) {
+               if (VISIBLE_ACHAN(achan)) {
+                       if (SEL_ACHAN(achan) && !(achan->flag & ACHAN_MOVED)) {
+                               /* take it out off the chain keep data */
+                               BLI_remlink (&act->chanbase, achan);
+                               /* add at end */
+                               BLI_addtail(&act->chanbase, achan);
+                               achan->flag |= ACHAN_MOVED;
+                       }
+               }               
+       }
+       /* clear temp flags */
+       for (achan=act->chanbase.first; achan; achan= achan->next) {
+               achan->flag = achan->flag & ~ACHAN_MOVED;
+       }
+       
+       /* Clean up and redraw stuff */
+       remake_action_ipos (act);
+       BIF_undo_push("Bottom Action channel");
+       allspace(REMAKEIPO, 0);
+       allqueue(REDRAWACTION, 0);
+       allqueue(REDRAWIPO, 0);
+       allqueue(REDRAWNLA, 0);
+}
+
+/* **************************************************** */
+/* EVENT HANDLING */
+
+void winqreadactionspace(ScrArea *sa, void *spacedata, BWinEvent *evt)
+{
+       extern void do_actionbuts(unsigned short event); // drawaction.c
+       SpaceAction *saction;
+       void *data;
+       short datatype;
+       float dx, dy;
+       int doredraw= 0;
+       int     cfra;
+       short mval[2];
+       unsigned short event= evt->event;
+       short val= evt->val;
+       short mousebut = L_MOUSE;
+
+       if (curarea->win==0) return;
+
+       saction= curarea->spacedata.first;
+       if (!saction)
+               return;
+
+       data= get_action_context(&datatype);
+       
+       if (val) {
+               if ( uiDoBlocks(&curarea->uiblocks, event)!=UI_NOTHING ) event= 0;
+               
+               /* swap mouse buttons based on user preference */
+               if (U.flag & USER_LMOUSESELECT) {
+                       if (event == LEFTMOUSE) {
+                               event = RIGHTMOUSE;
+                               mousebut = L_MOUSE;
+                       } else if (event == RIGHTMOUSE) {
+                               event = LEFTMOUSE;
+                               mousebut = R_MOUSE;
+                       }
+               }
+               
+               getmouseco_areawin(mval);
+               
+               switch(event) {
+               case UI_BUT_EVENT:
+                       do_actionbuts(val);     // window itself
+                       break;
+               
+               case HOMEKEY:
+                       do_action_buttons(B_ACTHOME);   // header
+                       break;
+
+               case AKEY:
+                       if (mval[0]<NAMEWIDTH) {
+                               deselect_action_channels (1);
+                               allqueue (REDRAWVIEW3D, 0);
+                               allqueue (REDRAWACTION, 0);
+                               allqueue(REDRAWNLA, 0);
+                               allqueue (REDRAWIPO, 0);
+                       }
+                       else if (mval[0]>ACTWIDTH) {
+                               if (G.qual == LR_CTRLKEY) {
+                                       deselect_markers (1, 0);
+                                       allqueue(REDRAWTIME, 0);
+                                       allqueue(REDRAWIPO, 0);
+                                       allqueue(REDRAWACTION, 0);
+                                       allqueue(REDRAWNLA, 0);
+                                       allqueue(REDRAWSOUND, 0);
+                               }
+                               else {
+                                       deselect_action_keys (1, 1);
+                                       allqueue (REDRAWACTION, 0);
+                                       allqueue(REDRAWNLA, 0);
+                                       allqueue (REDRAWIPO, 0);
+                               }
+                       }
+                       break;
+
+               case BKEY:
+                       if (G.qual & LR_CTRLKEY) {
+                               borderselect_markers();
+                       }
+                       else {
+                               if (mval[0]>ACTWIDTH)
+                                       borderselect_action();
+                       }
+                       break;
+
+               case CKEY:
+                       /* scroll the window so the current
+                        * frame is in the center.
+                        */
+                       center_currframe();
+                       break;
+
+               case DKEY:
+                       if (mval[0]>ACTWIDTH) {
+                               if (G.qual == (LR_CTRLKEY|LR_SHIFTKEY))
+                                       duplicate_marker();
+                               else if (G.qual == LR_SHIFTKEY)
+                                       duplicate_action_keys();
+                       }
+                       break;
+
+               case GKEY:
+                       if (G.qual & LR_CTRLKEY) {
+                               transform_markers('g', 0);
+                       }
+                       else {
+                               if (mval[0]>=ACTWIDTH)
+                                       transform_action_keys('g', 0);
+                       }
+                       break;
+               
+               case HKEY:
+                       if (G.qual & LR_SHIFTKEY) {
+                               if (okee("Set Keys to Auto Handle"))
+                                       sethandles_action_keys(HD_AUTO);
+                       }
+                       else {
+                               if (okee("Toggle Keys Aligned Handle"))
+                                       sethandles_action_keys(HD_ALIGN);
+                       }
+                       break;
+               
                case KKEY:
                        if (G.qual & LR_CTRLKEY) {
                                markers_selectkeys_between();
                        }
                        else {
                                val= (G.qual & LR_SHIFTKEY) ? 2 : 1;
-                               
-                               if(key)
-                                       column_select_shapekeys(key, val);
-                               else if(act)
-                                       column_select_actionkeys(act, val);
+                               column_select_action_keys(val);
                        }
                        
                        allqueue(REDRAWTIME, 0);
                        allqueue(REDRAWIPO, 0);
                        allqueue(REDRAWACTION, 0);
                        allqueue(REDRAWNLA, 0);
-                       allqueue(REDRAWSOUND, 0);
                        break;
                        
                case MKEY:
                        if (G.qual & LR_SHIFTKEY) {
                                /* mirror keyframes */
-                               val = pupmenu("Mirror Keys Over%t|Current Frame%x1|Vertical Axis%x2");
-                               mirror_action_keys(val);
+                               if (data) {
+                                       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);
+                               }
                        }
                        else {
                                /* marker operations */
@@ -2892,55 +3145,64 @@ void winqreadactionspace(ScrArea *sa, void *spacedata, BWinEvent *evt)
                        break;
                        
                case OKEY:
-                       if(key)
-                               clean_shapekeys(key);
-                       else if(act)
-                               clean_actionchannels(act);
+                       clean_action();
+                       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(REDRAWTIME, 0);
+                       allqueue(REDRAWBUTSALL, 0);
+                       allqueue(REDRAWACTION, 0);
+                       allqueue(REDRAWNLA, 0);
+                       allqueue(REDRAWIPO, 0);
                        break;
                        
                case SKEY: 
                        if (mval[0]>=ACTWIDTH) {
-                               if(G.qual & LR_SHIFTKEY) {
-                                       val = pupmenu("Snap Keys To%t|Nearest Frame%x1|Current Frame%x2");
-                                       snap_keys_to_frame(val);
+                               if (G.qual & LR_SHIFTKEY) {
+                                       if (data) {
+                                               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 {
-                                       if (key)
-                                               transform_meshchannel_keys('s', key);
-                                       else if (act)
-                                               transform_actionchannel_keys ('s', 0);
+                                       transform_action_keys('s', 0);  
                                }
                        }
                        break;
-
-                       /*** set the Ipo type  ***/
+               
                case TKEY:
-                       if (key) {
-                               /* to do */
-                       }
-                       else {
-                               if(G.qual & LR_SHIFTKEY)
-                                       set_ipotype_actionchannels(SET_IPO_POPUP);
-                               else
-                                       transform_actionchannel_keys ('t', 0);
-                       }
+                       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 (key)
-                                       sethandles_meshchannel_keys(HD_VECT, key);
-                               else
-                                       sethandles_actionchannel_keys(HD_VECT);
-                       }
+                       if (okee("Set Keys to Vector Handle"))
+                               sethandles_action_keys(HD_VECT);
                        break;
-
+               
                case PAGEUPKEY:
-                       if (key) {
-                               /* only jump to markers possible (key channels can't be moved yet) */
-                               nextprev_marker(1);
-                       }
-                       else {
+                       if (datatype == ACTCONT_ACTION) {
                                if(G.qual & LR_SHIFTKEY)
                                        top_sel_action();
                                else if (G.qual & LR_CTRLKEY)
@@ -2948,13 +3210,13 @@ void winqreadactionspace(ScrArea *sa, void *spacedata, BWinEvent *evt)
                                else
                                        nextprev_marker(1);
                        }
-                       break;
-               case PAGEDOWNKEY:
-                       if (key) {
+                       else if (datatype == ACTCONT_SHAPEKEY) {
                                /* only jump to markers possible (key channels can't be moved yet) */
-                               nextprev_marker(-1);
+                               nextprev_marker(1);
                        }
-                       else {
+                       break;
+               case PAGEDOWNKEY:
+                       if (datatype == ACTCONT_ACTION) {
                                if(G.qual & LR_SHIFTKEY)
                                        bottom_sel_action();
                                else if (G.qual & LR_CTRLKEY) 
@@ -2962,40 +3224,38 @@ void winqreadactionspace(ScrArea *sa, void *spacedata, BWinEvent *evt)
                                else
                                        nextprev_marker(-1);
                        }
+                       else if (datatype == ACTCONT_SHAPEKEY) {
+                               /* only jump to markers possible (key channels can't be moved yet) */
+                               nextprev_marker(-1);
+                       }
                        break;
+                       
                case DELKEY:
                case XKEY:
-                       nr= pupmenu("Erase selected%t|Keys %x1|Markers %x2|Both %x3");
-                       if (nr>0) {
-                               if (nr==1||nr==3) {
-                                       if (key) {
-                                               delete_meshchannel_keys(key);
-                                       }
-                                       else {
-                                               if (mval[0]<NAMEWIDTH)
-                                                       delete_actionchannels();
-                                               else
-                                                       delete_actionchannel_keys();
-                                       }
-                               }
-                               if (nr==2) {
+                       if (okee("Erase selected")) {
+                               if (mval[0]<NAMEWIDTH)
+                                       delete_action_channels();
+                               else
+                                       delete_action_keys();
+                               
+                               if (mval[0] >= NAMEWIDTH)
                                        remove_marker();
-                                       
-                                       allqueue(REDRAWTIME, 0);
-                                       allqueue(REDRAWIPO, 0);
-                                       allqueue(REDRAWACTION, 0);
-                                       allqueue(REDRAWNLA, 0);
-                                       allqueue(REDRAWSOUND, 0);
-                               }
+                               
+                               allqueue(REDRAWTIME, 0);
+                               allqueue(REDRAWIPO, 0);
+                               allqueue(REDRAWACTION, 0);
+                               allqueue(REDRAWNLA, 0);
+                               allqueue(REDRAWSOUND, 0);
                        }
                        break;
+               
                /* LEFTMOUSE and RIGHTMOUSE event codes can be swapped above,
                 * based on user preference USER_LMOUSESELECT
                 */
                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);
                                        
@@ -3007,7 +3267,8 @@ void winqreadactionspace(ScrArea *sa, void *spacedata, BWinEvent *evt)
                                        if( cfra!=CFRA ) {
                                                CFRA= cfra;
                                                update_for_newframe();
-                                               force_draw_all(0);                                      }
+                                               force_draw_all(0);                                      
+                                       }
                                        else PIL_sleep_ms(30);
                                        
                                } while(get_mbut() & mousebut);
@@ -3015,81 +3276,45 @@ void winqreadactionspace(ScrArea *sa, void *spacedata, BWinEvent *evt)
                        }
                        /* passed on as selection */
                case RIGHTMOUSE:
-                       /* Clicking in the channel area selects the
-                        * channel or constraint channel
-                        */
-                       if (mval[0]<NAMEWIDTH) {
-                               if(act) {
-                                       if (mval[0] < (NAMEWIDTH-16)) {
-                                               /* mouse is over action channels */
-                                               if(G.qual & LR_SHIFTKEY)
-                                                       mouse_actionchannels(act, mval, NULL,  SELECT_INVERT);
-                                               else
-                                                       mouse_actionchannels(act, mval, NULL,  SELECT_REPLACE);
-                                               
-                                               BIF_undo_push("Select Action");
-                                       }
-                                       else {
-                                               /* mouse is over channel locks */
-                                               mouse_actionchannels_protect(act, mval);
-                                               
-                                               BIF_undo_push("Protect Channel");
-                                       }
+                       /* Clicking in the channel area */
+                       if ((G.v2d->mask.xmin) && (mval[0]<NAMEWIDTH)) {
+                               if (datatype == ACTCONT_ACTION) {
+                                       /* mouse is over action channels */
+                                       if (G.qual & LR_CTRLKEY)
+                                               clever_achannel_names(mval);
+                                       else 
+                                               mouse_actionchannels(mval);
                                }
                                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
                                 * all of the keys for that channel at that height
                                 */
-                               if (IN_2D_VERT_SCROLL(mval)) {
-                                       if(G.qual & LR_SHIFTKEY)
-                                               select_all_keys_channels(act, mval, NULL, 
-                                                                                                SELECT_INVERT);
-                                       else
-                                               select_all_keys_channels(act, mval, NULL, 
-                                                                                                SELECT_REPLACE);
-                               }
+                               if (IN_2D_VERT_SCROLL(mval))
+                                       selectall_action_keys(mval, 0, select_mode);
                
                                /* Clicking in the horizontal scrollbar selects
                                 * all of the keys within 0.5 of the nearest integer
                                 * frame
                                 */
-                               else if (IN_2D_HORIZ_SCROLL(mval)) {
-                                       if(G.qual & LR_SHIFTKEY)
-                                               select_all_keys_frames(act, mval, NULL, 
-                                                                                          SELECT_INVERT);
-                                       else
-                                               select_all_keys_frames(act, mval, NULL, 
-                                                                                          SELECT_REPLACE);
-                                       BIF_undo_push("Select all Action");
-                               }
+                               else if (IN_2D_HORIZ_SCROLL(mval))
+                                       selectall_action_keys(mval, 1, select_mode);
                                
                                /* Clicking in the main area of the action window
                                 * selects keys and markers
                                 */
-                               else {
-                                       if (key) {
-                                               if(G.qual & LR_SHIFTKEY)
-                                                       mouse_mesh_action(SELECT_INVERT, key);
-                                               else
-                                                       mouse_mesh_action(SELECT_REPLACE, key);
-                                       }
-                                       else {
-                                               if(G.qual & LR_SHIFTKEY)
-                                                       mouse_action(SELECT_INVERT);
-                                               else
-                                                       mouse_action(SELECT_REPLACE);
-                                       }
-                               }
+                               else
+                                       mouse_action(select_mode);
                        }
                        break;
                case PADPLUSKEY:
                        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:
@@ -3111,523 +3336,4 @@ void winqreadactionspace(ScrArea *sa, void *spacedata, BWinEvent *evt)
        
 }
 
-Key *get_action_mesh_key(void) 
-{
-       /* gets the key data from the currently selected
-        * mesh/lattice. If a mesh is not selected, or does not have
-        * key data, then we return NULL (currently only
-        * returns key data for RVK type meshes). If there
-        * is an action that is pinned, return null
-        */
-    Object *ob;
-    Key    *key;
-
-    ob = OBACT;
-    if (!ob) return NULL;
-
-       if (G.saction->pin) return NULL;
-
-    if (ob->type==OB_MESH ) {
-               key = ((Mesh *)ob->data)->key;
-    }
-       else if (ob->type==OB_LATTICE ) {
-               key = ((Lattice *)ob->data)->key;
-       }
-       else return NULL;
-
-       if (key) {
-               if (key->type == KEY_RELATIVE)
-                       return key;
-       }
-
-    return NULL;
-}
-
-int get_nearest_key_num(Key *key, short *mval, float *x) {
-       /* returns the key num that cooresponds to the
-        * y value of the mouse click. Does not check
-        * if this is a valid keynum. Also gives the Ipo
-        * x coordinate.
-        */
-    int num;
-    float ybase, y;
-
-    areamouseco_to_ipoco(G.v2d, mval, x, &y);
-
-    ybase = key->totkey * (CHANNELHEIGHT + CHANNELSKIP);
-    num = (int) ((ybase - y + CHANNELHEIGHT/2) / (CHANNELHEIGHT+CHANNELSKIP));
-
-    return (num + 1);
-}
-
-/* ************************************* Action Editor Markers ************************************* */
-
-void markers_selectkeys_between(void)
-{
-       bAction *act;
-       Key *key;
-       float min, max;
-       
-       /* get extreme markers */
-       get_minmax_markers(1, &min, &max);
-       if (min==max) return;
-       min -= 0.5f;
-       max += 0.5f;
-       
-       /* get keyframe data */
-       act = G.saction->action;
-       key = get_action_mesh_key();
-       
-       /* select keys in-between */
-       if (key) {
-               if (key->ipo) 
-                       borderselect_ipo_key(key->ipo, min, max, SELECT_ADD);
-       }
-       else {
-               bActionChannel *achan;
-               bConstraintChannel *conchan;
-               
-               for (achan= act->chanbase.first; achan; achan= achan->next) {
-                       if (achan->ipo) {
-                               if(G.saction->pin==0 && OBACT) {
-                                       actstrip_map_ipo_keys(OBACT, achan->ipo, 0);
-                                       borderselect_ipo_key(achan->ipo, min, max, SELECT_ADD);
-                                       actstrip_map_ipo_keys(OBACT, achan->ipo, 1);
-                               }
-                               else {
-                                       borderselect_ipo_key(achan->ipo, min, max, SELECT_ADD);
-                               }
-                       }
-                               
-                       for (conchan= achan->constraintChannels.first; conchan; conchan= conchan->next) {
-                               if (conchan->ipo) {
-                                       if(G.saction->pin==0 && OBACT) {
-                                               actstrip_map_ipo_keys(OBACT, conchan->ipo, 0);
-                                               borderselect_ipo_key(conchan->ipo, min, max, SELECT_ADD);
-                                               actstrip_map_ipo_keys(OBACT, conchan->ipo, 1);
-                                       }
-                                       else {
-                                               borderselect_ipo_key(conchan->ipo, min, max, SELECT_ADD);
-                                       }
-                               }
-                       }
-               }
-       }
-}
-
-/* ************************************* Action Channel Ordering *********************************** */
-
-void top_sel_action()
-{
-       bAction *act;
-       bActionChannel *chan;
-       
-       /* Get the selected action, exit if none are selected */
-       act = G.saction->action;
-       if (!act) return;
-       
-       for (chan=act->chanbase.first; chan; chan=chan->next){
-               if((chan->flag & ACHAN_HIDDEN)==0) {
-                       if ((chan->flag & ACHAN_SELECTED) && !(chan->flag & ACHAN_MOVED)){
-                               /* take it out off the chain keep data */
-                               BLI_remlink (&act->chanbase, chan);
-                               /* make it first element */
-                               BLI_insertlinkbefore(&act->chanbase,act->chanbase.first, chan);
-                               chan->flag |= ACHAN_MOVED;
-                               /* restart with rest of list */
-                               chan=chan->next;
-                       }
-               }
-       }
-    /* clear temp flags */
-       for (chan=act->chanbase.first; chan; chan=chan->next){
-               chan->flag = chan->flag & ~ACHAN_MOVED;
-       }
-       
-       /* Clean up and redraw stuff */
-       remake_action_ipos (act);
-       BIF_undo_push("Top Action channel");
-       allspace(REMAKEIPO, 0);
-       allqueue(REDRAWACTION, 0);
-       allqueue(REDRAWIPO, 0);
-       allqueue(REDRAWNLA, 0);
-}
-
-void up_sel_action()
-{
-       bAction *act;
-       bActionChannel *chan;
-       bActionChannel *prev;
-       
-       /* Get the selected action, exit if none are selected */
-       act = G.saction->action;
-       if (!act) return;
-       
-       for (chan=act->chanbase.first; chan; chan=chan->next){
-               if((chan->flag & ACHAN_HIDDEN)==0) {
-                       if ((chan->flag & ACHAN_SELECTED) && !(chan->flag & ACHAN_MOVED)){
-                               prev = chan->prev;
-                               if (prev){
-                                       /* take it out off the chain keep data */
-                                       BLI_remlink (&act->chanbase, chan);
-                                       /* push it up */
-                                       BLI_insertlinkbefore(&act->chanbase,prev, chan);
-                                       chan->flag |= ACHAN_MOVED;
-                                       /* restart with rest of list */
-                                       chan=chan->next;
-                               }
-                       }
-               }
-       }
-       /* clear temp flags */
-       for (chan=act->chanbase.first; chan; chan=chan->next){
-               chan->flag = chan->flag & ~ACHAN_MOVED;
-       }
-       
-       /* Clean up and redraw stuff
-       */
-       remake_action_ipos (act);
-       BIF_undo_push("Up Action channel");
-       allspace(REMAKEIPO, 0);
-       allqueue(REDRAWACTION, 0);
-       allqueue(REDRAWIPO, 0);
-       allqueue(REDRAWNLA, 0);
-}
-
-void down_sel_action()
-{
-       bAction *act;
-       bActionChannel *chan;
-       bActionChannel *next;
-       
-       /* Get the selected action, exit if none are selected */
-       act = G.saction->action;
-       if (!act) return;
-       
-       for (chan=act->chanbase.last; chan; chan=chan->prev){
-               if((chan->flag & ACHAN_HIDDEN)==0) {
-                       if ((chan->flag & ACHAN_SELECTED) && !(chan->flag & ACHAN_MOVED)){
-                               next = chan->next;
-                               if (next) next = next->next;
-                               if (next){
-                                       /* take it out off the chain keep data */
-                                       BLI_remlink (&act->chanbase, chan);
-                                       /* move it down */
-                                       BLI_insertlinkbefore(&act->chanbase,next, chan);
-                                       chan->flag |= ACHAN_MOVED;
-                               }
-                               else {
-                                       /* take it out off the chain keep data */
-                                       BLI_remlink (&act->chanbase, chan);
-                                       /* add at end */
-                                       BLI_addtail(&act->chanbase,chan);
-                                       chan->flag |= ACHAN_MOVED;
-                               }
-                       }
-               }
-       }
-       /* clear temp flags */
-       for (chan=act->chanbase.first; chan; chan=chan->next){
-               chan->flag = chan->flag & ~ACHAN_MOVED;
-       }
-       
-       /* Clean up and redraw stuff
-       */
-       remake_action_ipos (act);
-       BIF_undo_push("Down Action channel");
-       allspace(REMAKEIPO, 0);
-       allqueue(REDRAWACTION, 0);
-       allqueue(REDRAWIPO, 0);
-       allqueue(REDRAWNLA, 0);
-}
-
-void bottom_sel_action()
-{
-       bAction *act;
-       bActionChannel *chan;
-       
-       /* Get the selected action, exit if none are selected */
-       act = G.saction->action;
-       if (!act) return;
-       
-       for (chan=act->chanbase.last; chan; chan=chan->prev){
-               if((chan->flag & ACHAN_HIDDEN)==0) {
-                       if ((chan->flag & ACHAN_SELECTED) && !(chan->flag & ACHAN_MOVED)) {
-                               /* take it out off the chain keep data */
-                               BLI_remlink (&act->chanbase, chan);
-                               /* add at end */
-                               BLI_addtail(&act->chanbase,chan);
-                               chan->flag |= ACHAN_MOVED;
-                       }
-               }               
-       }
-       /* clear temp flags */
-       for (chan=act->chanbase.first; chan; chan=chan->next){
-               chan->flag = chan->flag & ~ACHAN_MOVED;
-       }
-       
-       /* Clean up and redraw stuff
-       */
-       remake_action_ipos (act);
-       BIF_undo_push("Bottom Action channel");
-       allspace(REMAKEIPO, 0);
-       allqueue(REDRAWACTION, 0);
-       allqueue(REDRAWIPO, 0);
-       allqueue(REDRAWNLA, 0);
-}
-
-/* ********************************* BAKING STUFF ********************************** */
-/* NOTE: these functions should probably be moved to their own file sometime - Aligorith */
-
-void world2bonespace(float boneSpaceMat[][4], float worldSpace[][4], float restPos[][4], float armPos[][4])
-{
-       float imatarm[4][4], imatbone[4][4], tmat[4][4], t2mat[4][4];
-       
-       Mat4Invert(imatarm, armPos);
-       Mat4Invert(imatbone, restPos);
-       Mat4MulMat4(tmat, imatarm, worldSpace);
-       Mat4MulMat4(t2mat, tmat, imatbone);
-       Mat4MulMat4(boneSpaceMat, restPos, t2mat);
-}
-
-bAction* bake_action_with_client (bAction *act, Object *armob, float tolerance)
-{
-       bArmature               *arm;
-       bAction                 *result=NULL;
-       bActionChannel *achan;
-       bAction                 *temp;
-       bPoseChannel    *pchan;
-       ID                              *id;
-       float                   actstart, actend;
-       int                             oldframe;
-       int                             curframe;
-       char                    newname[64];
-
-       if (!act)
-               return NULL;
-       
-       arm = get_armature(armob);
-
-       if (G.obedit){
-               error ("Actions can't be baked in Edit Mode");
-               return NULL;
-       }
-
-       if (!arm || armob->pose==NULL){
-               error ("Select an armature before baking");
-               return NULL;
-       }
-       
-       /* Get a new action */
-       result = add_empty_action(ID_PO);
-       id= (ID *)armob;
-
-       /* Assign the new action a unique name */
-       sprintf (newname, "%s.BAKED", act->id.name+2);
-       rename_id(&result->id, newname);
-
-       calc_action_range(act, &actstart, &actend, 1);
-
-       oldframe = G.scene->r.cfra;
-
-       temp = armob->action;
-       armob->action = result;
-       
-       for (curframe=1; curframe<ceil(actend+1.0f); curframe++){
-
-               /* Apply the old action */
-               
-               G.scene->r.cfra = curframe;
-
-               /* Apply the object ipo */
-               extract_pose_from_action(armob->pose, act, curframe);
-
-               where_is_pose(armob);
-               
-               /* For each channel: set quats and locs if channel is a bone */
-               for (pchan=armob->pose->chanbase.first; pchan; pchan=pchan->next){
-
-                       /* Apply to keys */
-                       insertkey(id, ID_PO, pchan->name, NULL, AC_LOC_X);
-                       insertkey(id, ID_PO, pchan->name, NULL, AC_LOC_Y);
-                       insertkey(id, ID_PO, pchan->name, NULL, AC_LOC_Z);
-                       insertkey(id, ID_PO, pchan->name, NULL, AC_QUAT_X);
-                       insertkey(id, ID_PO, pchan->name, NULL, AC_QUAT_Y);
-                       insertkey(id, ID_PO, pchan->name, NULL, AC_QUAT_Z);
-                       insertkey(id, ID_PO, pchan->name, NULL, AC_QUAT_W);
-                       insertkey(id, ID_PO, pchan->name, NULL, AC_SIZE_X);
-                       insertkey(id, ID_PO, pchan->name, NULL, AC_SIZE_Y);
-                       insertkey(id, ID_PO, pchan->name, NULL, AC_SIZE_Z);
-               }
-       }
-
-
-       /* Make another pass to ensure all keyframes are set to linear interpolation mode */
-       for (achan = result->chanbase.first; achan; achan=achan->next){
-               IpoCurve* icu;
-               if(achan->ipo) {
-                       for (icu = achan->ipo->curve.first; icu; icu=icu->next){
-                               icu->ipo= IPO_LIN;
-                       }
-               }
-       }
-
-       notice ("Made a new action named \"%s\"", newname);
-       G.scene->r.cfra = oldframe;
-       armob->action = temp;
-               
-       /* restore */
-       extract_pose_from_action(armob->pose, act, G.scene->r.cfra);
-       where_is_pose(armob);
-       
-       allqueue(REDRAWACTION, 1);
-       
-       return result;
-}
-
-
-bAction* bake_obIPO_to_action (Object *ob)
-{
-       bArmature               *arm;
-       bAction                 *result=NULL;
-       bAction                 *temp;
-       Bone                    *bone;
-       ID                              *id;
-       ListBase                elems;
-       int                     oldframe,testframe;
-       char                    newname[64];
-       float                   quat[4],tmat[4][4],startpos[4][4];
-       CfraElem                *firstcfra, *lastcfra;
-       
-       arm = get_armature(ob);
-       
-       if (arm) {      
-       
-               oldframe = CFRA;
-               result = add_empty_action(ID_PO);
-               id = (ID *)ob;
-               
-               sprintf (newname, "TESTOBBAKE");
-               rename_id(&result->id, newname);
-               
-               if(ob!=G.obedit) { // make sure object is not in edit mode
-                       if(ob->ipo) {
-                               /* convert the ipo to a list of 'current frame elements' */
-                               
-                               temp = ob->action;
-                               ob->action = result;
-                               
-                               elems.first= elems.last= NULL;
-                               make_cfra_list(ob->ipo, &elems);
-                               /* set the beginning armature location */
-                               firstcfra=elems.first;
-                               lastcfra=elems.last;
-                               CFRA=firstcfra->cfra;
-                               
-                               where_is_object(ob);
-                               Mat4CpyMat4(startpos,ob->obmat);
-                               
-                               /* loop from first key to last, sampling every 10 */
-                               for (testframe = firstcfra->cfra; testframe<=lastcfra->cfra; testframe=testframe+10) { 
-                                       CFRA=testframe;
-                                       where_is_object(ob);
-
-                                       for (bone = arm->bonebase.first; bone; bone=bone->next) {
-                                               if (!bone->parent) { /* this is a root bone, so give it a key! */
-                                                       world2bonespace(tmat,ob->obmat,bone->arm_mat,startpos);
-                                                       Mat4ToQuat(tmat,quat);
-                                                       printf("Frame: %i %f, %f, %f, %f\n",CFRA,quat[0],quat[1],quat[2],quat[3]);
-                                                       insertmatrixkey(id, ID_PO, bone->name, NULL, AC_LOC_X,tmat[3][0]);
-                                                       insertmatrixkey(id, ID_PO, bone->name, NULL, AC_LOC_Y,tmat[3][1]);
-                                                       insertmatrixkey(id, ID_PO, bone->name, NULL, AC_LOC_Z,tmat[3][2]);
-                                                       insertmatrixkey(id, ID_PO, bone->name, NULL, AC_QUAT_X,quat[1]);
-                                                       insertmatrixkey(id, ID_PO, bone->name, NULL, AC_QUAT_Y,quat[2]);
-                                                       insertmatrixkey(id, ID_PO, bone->name, NULL, AC_QUAT_Z,quat[3]);
-                                                       insertmatrixkey(id, ID_PO, bone->name, NULL, AC_QUAT_W,quat[0]);
-                                                       //insertmatrixkey(id, ID_PO, bone->name, NULL, AC_SIZE_X,size[0]);
-                                                       //insertmatrixkey(id, ID_PO, bone->name, NULL, AC_SIZE_Y,size[1]);
-                                                       //insertmatrixkey(id, ID_PO, bone->name, NULL, AC_SIZE_Z,size[2]);
-                                               }
-                                       }
-                               }
-                               BLI_freelistN(&elems);  
-                       }
-               }
-               CFRA = oldframe;
-       }
-       return result;  
-}
-
-bAction* bake_everything_to_action (Object *ob)
-{
-       bArmature               *arm;
-       bAction                 *result=NULL;
-       bAction                 *temp;
-       Bone                    *bone;
-       ID                              *id;
-       ListBase                elems;
-       int                     oldframe,testframe;
-       char                    newname[64];
-       float                   quat[4],tmat[4][4],startpos[4][4];
-       CfraElem                *firstcfra, *lastcfra;
-       
-       arm = get_armature(ob);
-       
-       if (arm) {      
-       
-               oldframe = CFRA;
-               result = add_empty_action(ID_PO);
-               id = (ID *)ob;
-               
-               sprintf (newname, "TESTOBBAKE");
-               rename_id(&result->id, newname);
-               
-               if(ob!=G.obedit) { // make sure object is not in edit mode
-                &