Merge branch 'master' into blender2.8
[blender.git] / source / blender / editors / animation / anim_channels_edit.c
index a8ee2f98b5b07ea3acab2c7302fc87da18c67a16..20e5bec44a37b188e19d4c9aed57ee5b60b59849 100644 (file)
@@ -34,6 +34,7 @@
 
 #include "MEM_guardedalloc.h"
 
+#include "BKE_depsgraph.h"
 #include "BLI_blenlib.h"
 #include "BLI_utildefines.h"
 #include "BLI_listbase.h"
@@ -49,6 +50,7 @@
 #include "RNA_access.h"
 #include "RNA_define.h"
 
+#include "BKE_animsys.h"
 #include "BKE_action.h"
 #include "BKE_fcurve.h"
 #include "BKE_gpencil.h"
@@ -74,7 +76,7 @@
 
 /* Set the given animation-channel as the active one for the active context */
 // TODO: extend for animdata types...
-void ANIM_set_active_channel(bAnimContext *ac, void *data, short datatype, int filter, void *channel_data, short channel_type)
+void ANIM_set_active_channel(bAnimContext *ac, void *data, eAnimCont_Types datatype, eAnimFilter_Flags filter, void *channel_data, eAnim_ChannelType channel_type)
 {
        ListBase anim_data = {NULL, NULL};
        bAnimListElem *ale;
@@ -100,6 +102,7 @@ void ANIM_set_active_channel(bAnimContext *ac, void *data, short datatype, int f
                                break;
                        }
                        case ANIMTYPE_FCURVE:
+                       case ANIMTYPE_NLACURVE:
                        {
                                FCurve *fcu = (FCurve *)ale->data;
                                
@@ -117,10 +120,10 @@ void ANIM_set_active_channel(bAnimContext *ac, void *data, short datatype, int f
                        case ANIMTYPE_DSMAT:    /* Datablock AnimData Expanders */
                        case ANIMTYPE_DSLAM:
                        case ANIMTYPE_DSCAM:
+                       case ANIMTYPE_DSCACHEFILE:
                        case ANIMTYPE_DSCUR:
                        case ANIMTYPE_DSSKEY:
                        case ANIMTYPE_DSWOR:
-                       case ANIMTYPE_DSPART:
                        case ANIMTYPE_DSMBALL:
                        case ANIMTYPE_DSARM:
                        case ANIMTYPE_DSMESH:
@@ -128,6 +131,7 @@ void ANIM_set_active_channel(bAnimContext *ac, void *data, short datatype, int f
                        case ANIMTYPE_DSLAT:
                        case ANIMTYPE_DSLINESTYLE:
                        case ANIMTYPE_DSSPK:
+                       case ANIMTYPE_DSGPENCIL:
                        {
                                /* need to verify that this data is valid for now */
                                if (ale->adt) {
@@ -135,6 +139,13 @@ void ANIM_set_active_channel(bAnimContext *ac, void *data, short datatype, int f
                                }
                                break;
                        }
+                       case ANIMTYPE_GPLAYER:
+                       {
+                               bGPDlayer *gpl = (bGPDlayer *)ale->data;
+                               
+                               ACHANNEL_SET_FLAG(gpl, ACHANNEL_SETFLAG_CLEAR, GP_LAYER_ACTIVE);
+                               break;
+                       }
                }
        }
        
@@ -148,6 +159,7 @@ void ANIM_set_active_channel(bAnimContext *ac, void *data, short datatype, int f
                                break;
                        }
                        case ANIMTYPE_FCURVE:
+                       case ANIMTYPE_NLACURVE:
                        {
                                FCurve *fcu = (FCurve *)channel_data;
                                fcu->flag |= FCURVE_ACTIVE;
@@ -163,16 +175,19 @@ void ANIM_set_active_channel(bAnimContext *ac, void *data, short datatype, int f
                        case ANIMTYPE_DSMAT:    /* Datablock AnimData Expanders */
                        case ANIMTYPE_DSLAM:
                        case ANIMTYPE_DSCAM:
+                       case ANIMTYPE_DSCACHEFILE:
                        case ANIMTYPE_DSCUR:
                        case ANIMTYPE_DSSKEY:
                        case ANIMTYPE_DSWOR:
-                       case ANIMTYPE_DSPART:
                        case ANIMTYPE_DSMBALL:
                        case ANIMTYPE_DSARM:
                        case ANIMTYPE_DSMESH:
                        case ANIMTYPE_DSLAT:
                        case ANIMTYPE_DSLINESTYLE:
                        case ANIMTYPE_DSSPK:
+                       case ANIMTYPE_DSNTREE:
+                       case ANIMTYPE_DSTEX:
+                       case ANIMTYPE_DSGPENCIL:
                        {
                                /* need to verify that this data is valid for now */
                                if (ale && ale->adt) {
@@ -180,6 +195,23 @@ void ANIM_set_active_channel(bAnimContext *ac, void *data, short datatype, int f
                                }
                                break;
                        }
+                       
+                       case ANIMTYPE_GPLAYER:
+                       {
+                               bGPDlayer *gpl = (bGPDlayer *)channel_data;
+                               gpl->flag |= GP_LAYER_ACTIVE;
+                               break;
+                       }
+                       
+                       /* unhandled currently, but may be interesting */
+                       case ANIMTYPE_MASKLAYER:
+                       case ANIMTYPE_SHAPEKEY:
+                       case ANIMTYPE_NLAACTION:
+                               break;
+                       
+                       /* other types */
+                       default:
+                               break;
                }
        }
        
@@ -193,7 +225,7 @@ void ANIM_set_active_channel(bAnimContext *ac, void *data, short datatype, int f
  *     - test: check if deselecting instead of selecting
  *     - sel: eAnimChannels_SetFlag;
  */
-void ANIM_deselect_anim_channels(bAnimContext *ac, void *data, short datatype, short test, short sel)
+void ANIM_deselect_anim_channels(bAnimContext *ac, void *data, eAnimCont_Types datatype, bool test, eAnimChannels_SetFlag sel)
 {
        ListBase anim_data = {NULL, NULL};
        bAnimListElem *ale;
@@ -226,6 +258,7 @@ void ANIM_deselect_anim_channels(bAnimContext *ac, void *data, short datatype, s
                                                sel = ACHANNEL_SETFLAG_CLEAR;
                                        break;
                                case ANIMTYPE_FCURVE:
+                               case ANIMTYPE_NLACURVE:
                                        if (ale->flag & FCURVE_SELECTED)
                                                sel = ACHANNEL_SETFLAG_CLEAR;
                                        break;
@@ -242,10 +275,10 @@ void ANIM_deselect_anim_channels(bAnimContext *ac, void *data, short datatype, s
                                case ANIMTYPE_DSMAT:    /* Datablock AnimData Expanders */
                                case ANIMTYPE_DSLAM:
                                case ANIMTYPE_DSCAM:
+                               case ANIMTYPE_DSCACHEFILE:
                                case ANIMTYPE_DSCUR:
                                case ANIMTYPE_DSSKEY:
                                case ANIMTYPE_DSWOR:
-                               case ANIMTYPE_DSPART:
                                case ANIMTYPE_DSMBALL:
                                case ANIMTYPE_DSARM:
                                case ANIMTYPE_DSMESH:
@@ -254,6 +287,7 @@ void ANIM_deselect_anim_channels(bAnimContext *ac, void *data, short datatype, s
                                case ANIMTYPE_DSLAT:
                                case ANIMTYPE_DSLINESTYLE:
                                case ANIMTYPE_DSSPK:
+                               case ANIMTYPE_DSGPENCIL:
                                {
                                        if ((ale->adt) && (ale->adt->flag & ADT_UI_SELECTED))
                                                sel = ACHANNEL_SETFLAG_CLEAR;
@@ -309,6 +343,7 @@ void ANIM_deselect_anim_channels(bAnimContext *ac, void *data, short datatype, s
                                break;
                        }
                        case ANIMTYPE_FCURVE:
+                       case ANIMTYPE_NLACURVE:
                        {
                                FCurve *fcu = (FCurve *)ale->data;
                                
@@ -335,10 +370,10 @@ void ANIM_deselect_anim_channels(bAnimContext *ac, void *data, short datatype, s
                        case ANIMTYPE_DSMAT:    /* Datablock AnimData Expanders */
                        case ANIMTYPE_DSLAM:
                        case ANIMTYPE_DSCAM:
+                       case ANIMTYPE_DSCACHEFILE:
                        case ANIMTYPE_DSCUR:
                        case ANIMTYPE_DSSKEY:
                        case ANIMTYPE_DSWOR:
-                       case ANIMTYPE_DSPART:
                        case ANIMTYPE_DSMBALL:
                        case ANIMTYPE_DSARM:
                        case ANIMTYPE_DSMESH:
@@ -347,6 +382,7 @@ void ANIM_deselect_anim_channels(bAnimContext *ac, void *data, short datatype, s
                        case ANIMTYPE_DSLAT:
                        case ANIMTYPE_DSLINESTYLE:
                        case ANIMTYPE_DSSPK:
+                       case ANIMTYPE_DSGPENCIL:
                        {
                                /* need to verify that this data is valid for now */
                                if (ale->adt) {
@@ -387,7 +423,7 @@ void ANIM_deselect_anim_channels(bAnimContext *ac, void *data, short datatype, s
  *     - setting: type of setting to set
  *     - on: whether the visibility setting has been enabled or disabled 
  */
-void ANIM_flush_setting_anim_channels(bAnimContext *ac, ListBase *anim_data, bAnimListElem *ale_setting, int setting, short mode)
+void ANIM_flush_setting_anim_channels(bAnimContext *ac, ListBase *anim_data, bAnimListElem *ale_setting, eAnimChannel_Settings setting, eAnimChannels_SetFlag mode)
 {
        bAnimListElem *ale, *match = NULL;
        int prevLevel = 0, matchLevel = 0;
@@ -413,7 +449,7 @@ void ANIM_flush_setting_anim_channels(bAnimContext *ac, ListBase *anim_data, bAn
                return;
        }
        else {
-               bAnimChannelType *acf = ANIM_channel_get_typeinfo(ale_setting);
+               const bAnimChannelType *acf = ANIM_channel_get_typeinfo(ale_setting);
                
                if (acf == NULL) {
                        printf("ERROR: no channel info for the changed channel\n");
@@ -442,7 +478,7 @@ void ANIM_flush_setting_anim_channels(bAnimContext *ac, ListBase *anim_data, bAn
        {
                /* go backwards in the list, until the highest-ranking element (by indention has been covered) */
                for (ale = match->prev; ale; ale = ale->prev) {
-                       bAnimChannelType *acf = ANIM_channel_get_typeinfo(ale);
+                       const bAnimChannelType *acf = ANIM_channel_get_typeinfo(ale);
                        int level;
                        
                        /* if no channel info was found, skip, since this type might not have any useful info */
@@ -486,7 +522,7 @@ void ANIM_flush_setting_anim_channels(bAnimContext *ac, ListBase *anim_data, bAn
        {
                /* go forwards in the list, until the lowest-ranking element (by indention has been covered) */
                for (ale = match->next; ale; ale = ale->next) {
-                       bAnimChannelType *acf = ANIM_channel_get_typeinfo(ale);
+                       const bAnimChannelType *acf = ANIM_channel_get_typeinfo(ale);
                        int level;
                        
                        /* if no channel info was found, skip, since this type might not have any useful info */
@@ -591,7 +627,7 @@ static int animedit_poll_channels_active(bContext *C)
        if (ELEM(NULL, sa, CTX_wm_region(C)))
                return 0;
        /* animation editor test */
-       if (ELEM3(sa->spacetype, SPACE_ACTION, SPACE_IPO, SPACE_NLA) == 0)
+       if (ELEM(sa->spacetype, SPACE_ACTION, SPACE_IPO, SPACE_NLA) == 0)
                return 0;
 
        return 1;
@@ -608,7 +644,7 @@ static int animedit_poll_channels_nla_tweakmode_off(bContext *C)
        if (ELEM(NULL, sa, CTX_wm_region(C)))
                return 0;
        /* animation editor test */
-       if (ELEM3(sa->spacetype, SPACE_ACTION, SPACE_IPO, SPACE_NLA) == 0)
+       if (ELEM(sa->spacetype, SPACE_ACTION, SPACE_IPO, SPACE_NLA) == 0)
                return 0;
        
        /* NLA TweakMode test */
@@ -816,6 +852,7 @@ static void rearrange_animchannel_add_to_islands(ListBase *islands, ListBase *sr
                        break;
                }
                case ANIMTYPE_FCURVE:
+               case ANIMTYPE_NLACURVE:
                {
                        FCurve *fcu = (FCurve *)channel;
                        
@@ -829,8 +866,15 @@ static void rearrange_animchannel_add_to_islands(ListBase *islands, ListBase *sr
                        is_sel = SEL_NLT(nlt);
                        break;
                }
+               case ANIMTYPE_GPLAYER:
+               {
+                       bGPDlayer *gpl = (bGPDlayer *)channel;
+                       
+                       is_sel = SEL_GPL(gpl);
+                       break;
+               }
                default:
-                       printf("rearrange_animchannel_add_to_islands(): don't know how to handle channels of type %d\n", type);
+                       printf("rearrange_animchannel_add_to_islands(): don't know how to handle channels of type %u\n", type);
                        return;
        }
        
@@ -887,10 +931,10 @@ static void rearrange_animchannels_filter_visible(ListBase *anim_data_visible, b
 {
        ListBase anim_data = {NULL, NULL};
        bAnimListElem *ale, *ale_next;
-    int filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS);
+       int filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS);
        
        /* get all visible channels */
-    ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
+       ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
        
        /* now, only keep the ones that are of the types we are interested in */
        for (ale = anim_data.first; ale; ale = ale_next) {
@@ -1153,10 +1197,85 @@ static void rearrange_action_channels(bAnimContext *ac, bAction *act, eRearrange
 
 /* ------------------- */
 
+static void rearrange_nla_control_channels(bAnimContext *ac, AnimData *adt, eRearrangeAnimChan_Mode mode)
+{
+       ListBase anim_data_visible = {NULL, NULL};
+       
+       NlaTrack *nlt;
+       NlaStrip *strip;
+       
+       /* get rearranging function */
+       AnimChanRearrangeFp rearrange_func = rearrange_get_mode_func(mode);
+       
+       if (rearrange_func == NULL)
+               return;
+               
+       /* skip if these curves aren't being shown */
+       if (adt->flag & ADT_NLA_SKEYS_COLLAPSED)
+               return;
+       
+       /* Filter visible data. */
+       rearrange_animchannels_filter_visible(&anim_data_visible, ac, ANIMTYPE_NLACURVE);
+       
+       /* we cannot rearrange between strips, but within each strip, we can rearrange those curves */
+       for (nlt = adt->nla_tracks.first; nlt; nlt = nlt->next) {
+               for (strip = nlt->strips.first; strip; strip = strip->next) {
+                       rearrange_animchannel_islands(&strip->fcurves, rearrange_func, mode, ANIMTYPE_NLACURVE,
+                                                         &anim_data_visible);
+               }
+       }
+       
+       /* free temp data */
+       BLI_freelistN(&anim_data_visible);
+}
+
+/* ------------------- */
+
+static void rearrange_gpencil_channels(bAnimContext *ac, eRearrangeAnimChan_Mode mode)
+{
+       ListBase anim_data = {NULL, NULL};
+       bAnimListElem *ale;
+       int filter;
+       
+       /* get rearranging function */
+       AnimChanRearrangeFp rearrange_func = rearrange_get_mode_func(mode);
+       
+       if (rearrange_func == NULL)
+               return;
+       
+       /* get Grease Pencil datablocks */
+       filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_ANIMDATA);
+       ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
+       
+       for (ale = anim_data.first; ale; ale = ale->next) {
+               ListBase anim_data_visible = {NULL, NULL};
+               bGPdata *gpd = ale->data;
+               
+               /* only consider layers if this datablock is open */
+               BLI_assert(ale->type == ANIMTYPE_GPDATABLOCK);
+               if ((gpd->flag & GP_DATA_EXPAND) == 0)
+                       continue;
+               
+               /* Filter visible data. */
+               rearrange_animchannels_filter_visible(&anim_data_visible, ac, ANIMTYPE_GPLAYER);
+               
+               /* rearrange datablock's layers */
+               rearrange_animchannel_islands(&gpd->layers, rearrange_func, mode, ANIMTYPE_GPLAYER, &anim_data_visible);
+               
+               /* free visible layers data */
+               BLI_freelistN(&anim_data_visible);
+       }
+       
+       /* free GPD channel data */
+       ANIM_animdata_freelist(&anim_data);
+}
+
+/* ------------------- */
+
 static int animchannels_rearrange_exec(bContext *C, wmOperator *op)
 {
        bAnimContext ac;
-       short mode;
+       eRearrangeAnimChan_Mode mode;
        
        /* get editor data */
        if (ANIM_animdata_get_context(C, &ac) == 0)
@@ -1168,7 +1287,7 @@ static int animchannels_rearrange_exec(bContext *C, wmOperator *op)
        /* method to move channels depends on the editor */
        if (ac.datatype == ANIMCONT_GPENCIL) {
                /* Grease Pencil channels */
-               printf("Grease Pencil not supported for moving yet\n");
+               rearrange_gpencil_channels(&ac, mode);
        }
        else if (ac.datatype == ANIMCONT_MASK) {
                /* Grease Pencil channels */
@@ -1199,13 +1318,29 @@ static int animchannels_rearrange_exec(bContext *C, wmOperator *op)
                                        rearrange_driver_channels(&ac, adt, mode);
                                        break;
                                
+                               case ANIMCONT_ACTION: /* Single Action only... */
                                case ANIMCONT_SHAPEKEY: // DOUBLE CHECK ME...
-                               default: /* some collection of actions */
+                               {
+                                       if (adt->action)
+                                               rearrange_action_channels(&ac, adt->action, mode);
+                                       else if (G.debug & G_DEBUG)
+                                               printf("Animdata has no action\n");
+                                       break;
+                               }
+                               
+                               default: /* DopeSheet/Graph Editor - Some Actions + NLA Control Curves */
+                               {
+                                       /* NLA Control Curves */
+                                       if (adt->nla_tracks.first)
+                                               rearrange_nla_control_channels(&ac, adt, mode);
+                                       
+                                       /* Action */
                                        if (adt->action)
                                                rearrange_action_channels(&ac, adt->action, mode);
                                        else if (G.debug & G_DEBUG)
                                                printf("Animdata has no action\n");
                                        break;
+                               }
                        }
                }
                
@@ -1261,9 +1396,9 @@ static int animchannels_grouping_poll(bContext *C)
                        /* dopesheet and action only - all others are for other datatypes or have no groups */
                        if (ELEM(saction->mode, SACTCONT_ACTION, SACTCONT_DOPESHEET) == 0)
                                return 0;
-               }
+
                        break;
-                       
+               }
                case SPACE_IPO:
                {
                        SpaceIpo *sipo = (SpaceIpo *)sl;
@@ -1271,9 +1406,9 @@ static int animchannels_grouping_poll(bContext *C)
                        /* drivers can't have groups... */
                        if (sipo->mode != SIPO_MODE_ANIMATION)
                                return 0;
-               }
+
                        break;
-                       
+               }
                /* unsupported... */
                default:
                        return 0;
@@ -1523,6 +1658,27 @@ static int animchannels_delete_exec(bContext *C, wmOperator *UNUSED(op))
                                ANIM_fcurve_delete_from_animdata(&ac, adt, fcu);
                                break;
                        }
+                       case ANIMTYPE_NLACURVE:
+                       {
+                               /* NLA Control Curve - Deleting it should disable the corresponding setting... */
+                               NlaStrip *strip = (NlaStrip *)ale->owner;
+                               FCurve *fcu = (FCurve *)ale->data;
+                               
+                               if (STREQ(fcu->rna_path, "strip_time")) {
+                                       strip->flag &= ~NLASTRIP_FLAG_USR_TIME;
+                               }
+                               else if (STREQ(fcu->rna_path, "influence")) {
+                                       strip->flag &= ~NLASTRIP_FLAG_USR_INFLUENCE;
+                               }
+                               else {
+                                       printf("ERROR: Trying to delete NLA Control Curve for unknown property '%s'\n", fcu->rna_path);
+                               }
+                               
+                               /* unlink and free the F-Curve */
+                               BLI_remlink(&strip->fcurves, fcu);
+                               free_fcurve(fcu);
+                               break;
+                       }
                        case ANIMTYPE_GPLAYER:
                        {
                                /* Grease Pencil layer */
@@ -1530,7 +1686,7 @@ static int animchannels_delete_exec(bContext *C, wmOperator *UNUSED(op))
                                bGPDlayer *gpl = (bGPDlayer *)ale->data;
                                
                                /* try to delete the layer's data and the layer itself */
-                               free_gpencil_frames(gpl);
+                               BKE_gpencil_free_frames(gpl);
                                BLI_freelinkN(&gpd->layers, gpl);
                                break;
                        }
@@ -1552,7 +1708,8 @@ static int animchannels_delete_exec(bContext *C, wmOperator *UNUSED(op))
        
        /* send notifier that things have changed */
        WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL);
-       
+       DAG_relations_tag_update(CTX_data_main(C));
+
        return OPERATOR_FINISHED;
 }
  
@@ -1571,175 +1728,6 @@ static void ANIM_OT_channels_delete(wmOperatorType *ot)
        ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
 }
 
-/* ******************** Set Channel Visibility Operator *********************** */
-/* NOTE: this operator is only valid in the Graph Editor channels region */
-
-static int animchannels_visibility_set_exec(bContext *C, wmOperator *UNUSED(op))
-{
-       bAnimContext ac;
-       ListBase anim_data = {NULL, NULL};
-       ListBase all_data = {NULL, NULL};
-       bAnimListElem *ale;
-       int filter;
-       
-       /* get editor data */
-       if (ANIM_animdata_get_context(C, &ac) == 0)
-               return OPERATOR_CANCELLED;
-       
-       /* get list of all channels that selection may need to be flushed to 
-        * - hierarchy mustn't affect what we have access to here...
-        */
-       filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_CHANNELS | ANIMFILTER_NODUPLIS);
-       ANIM_animdata_filter(&ac, &all_data, filter, ac.data, ac.datatype);
-               
-       /* hide all channels not selected
-        * - hierarchy matters if we're doing this from the channels region
-        *   since we only want to apply this to channels we can "see", 
-        *   and have these affect their relatives
-        * - but for Graph Editor, this gets used also from main region
-        *   where hierarchy doesn't apply, as for [#21276]
-        */
-       if ((ac.spacetype == SPACE_IPO) && (ac.regiontype != RGN_TYPE_CHANNELS)) {
-               /* graph editor (case 2) */
-               filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_UNSEL | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_NODUPLIS);
-       }
-       else {
-               /* standard case */
-               filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_UNSEL | ANIMFILTER_NODUPLIS);
-       }
-       ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
-       
-       for (ale = anim_data.first; ale; ale = ale->next) {
-               /* clear setting first */
-               ANIM_channel_setting_set(&ac, ale, ACHANNEL_SETTING_VISIBLE, ACHANNEL_SETFLAG_CLEAR);
-               
-               /* now also flush selection status as appropriate 
-                * NOTE: in some cases, this may result in repeat flushing being performed
-                */
-               ANIM_flush_setting_anim_channels(&ac, &all_data, ale, ACHANNEL_SETTING_VISIBLE, 0);
-       }
-       
-       ANIM_animdata_freelist(&anim_data);
-       
-       /* make all the selected channels visible */
-       filter = (ANIMFILTER_SEL | ANIMFILTER_NODUPLIS);
-       ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
-
-       for (ale = anim_data.first; ale; ale = ale->next) {
-               /* hack: skip object channels for now, since flushing those will always flush everything, but they are always included */
-               /* TODO: find out why this is the case, and fix that */
-               if (ale->type == ANIMTYPE_OBJECT)
-                       continue;
-               
-               /* enable the setting */
-               ANIM_channel_setting_set(&ac, ale, ACHANNEL_SETTING_VISIBLE, ACHANNEL_SETFLAG_ADD);
-               
-               /* now, also flush selection status up/down as appropriate */
-               ANIM_flush_setting_anim_channels(&ac, &all_data, ale, ACHANNEL_SETTING_VISIBLE, 1);
-       }
-       
-       ANIM_animdata_freelist(&anim_data);
-       BLI_freelistN(&all_data);
-       
-       
-       /* send notifier that things have changed */
-       WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL);
-       
-       return OPERATOR_FINISHED;
-}
-
-static void ANIM_OT_channels_visibility_set(wmOperatorType *ot)
-{
-       /* identifiers */
-       ot->name = "Set Visibility";
-       ot->idname = "ANIM_OT_channels_visibility_set";
-       ot->description = "Make only the selected animation channels visible in the Graph Editor";
-       
-       /* api callbacks */
-       ot->exec = animchannels_visibility_set_exec;
-       ot->poll = ED_operator_graphedit_active;
-       
-       /* flags */
-       ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
-}
-
-
-/* ******************** Toggle Channel Visibility Operator *********************** */
-/* NOTE: this operator is only valid in the Graph Editor channels region */
-
-static int animchannels_visibility_toggle_exec(bContext *C, wmOperator *UNUSED(op))
-{
-       bAnimContext ac;
-       ListBase anim_data = {NULL, NULL};
-       ListBase all_data = {NULL, NULL};
-       bAnimListElem *ale;
-       int filter;
-       short vis = ACHANNEL_SETFLAG_ADD;
-       
-       /* get editor data */
-       if (ANIM_animdata_get_context(C, &ac) == 0)
-               return OPERATOR_CANCELLED;
-               
-       /* get list of all channels that selection may need to be flushed to 
-        * - hierarchy mustn't affect what we have access to here...
-        */
-       filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_CHANNELS | ANIMFILTER_NODUPLIS);
-       ANIM_animdata_filter(&ac, &all_data, filter, ac.data, ac.datatype);
-               
-       /* filter data
-        * - restrict this to only applying on settings we can get to in the list
-        */
-       filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_SEL | ANIMFILTER_NODUPLIS);
-       ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
-       
-       /* See if we should be making showing all selected or hiding */
-       for (ale = anim_data.first; ale; ale = ale->next) {
-               /* set the setting in the appropriate way (if available) */
-               if (ANIM_channel_setting_get(&ac, ale, ACHANNEL_SETTING_VISIBLE)) {
-                       vis = ACHANNEL_SETFLAG_CLEAR;
-                       break;
-               }
-       }
-
-       /* Now set the flags */
-       for (ale = anim_data.first; ale; ale = ale->next) {
-               /* hack: skip object channels for now, since flushing those will always flush everything, but they are always included */
-               /* TODO: find out why this is the case, and fix that */
-               if (ale->type == ANIMTYPE_OBJECT)
-                       continue;
-               
-               /* change the setting */
-               ANIM_channel_setting_set(&ac, ale, ACHANNEL_SETTING_VISIBLE, vis);
-               
-               /* now, also flush selection status up/down as appropriate */
-               ANIM_flush_setting_anim_channels(&ac, &all_data, ale, ACHANNEL_SETTING_VISIBLE, (vis == ACHANNEL_SETFLAG_ADD));
-       }
-       
-       /* cleanup */
-       ANIM_animdata_freelist(&anim_data);
-       BLI_freelistN(&all_data);
-       
-       /* send notifier that things have changed */
-       WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL);
-       
-       return OPERATOR_FINISHED;
-}
-
-static void ANIM_OT_channels_visibility_toggle(wmOperatorType *ot)
-{
-       /* identifiers */
-       ot->name = "Toggle Visibility";
-       ot->idname = "ANIM_OT_channels_visibility_toggle";
-       ot->description = "Toggle visibility in Graph Editor of all selected animation channels";
-       
-       /* api callbacks */
-       ot->exec = animchannels_visibility_toggle_exec;
-       ot->poll = ED_operator_graphedit_active;
-       
-       /* flags */
-       ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
-}
-
 /* ********************** Set Flags Operator *********************** */
 
 /* defines for setting animation-channel flags */
@@ -2052,6 +2040,113 @@ static void ANIM_OT_channels_collapse(wmOperatorType *ot)
        ot->prop = RNA_def_boolean(ot->srna, "all", true, "All", "Collapse all channels (not just selected ones)");
 }
 
+/* ************ Remove All "Empty" AnimData Blocks Operator ********* */
+/* We define "empty" AnimData blocks here as those which have all 3 of criteria:
+ *  1) No active action OR that active actions are empty
+ *     Assuming that all legitimate entries will have an action,
+ *     and that empty actions
+ *  2) No NLA Tracks + NLA Strips
+ *     Assuming that users haven't set up any of these as "placeholders"
+ *     for convenience sake, and that most that exist were either unintentional
+ *     or are no longer wanted
+ *  3) No drivers
+ */
+static int animchannels_clean_empty_exec(bContext *C, wmOperator *UNUSED(op))
+{
+       bAnimContext ac;
+       
+       ListBase anim_data = {NULL, NULL};
+       bAnimListElem *ale;
+       int filter;
+       
+       /* get editor data */
+       if (ANIM_animdata_get_context(C, &ac) == 0)
+               return OPERATOR_CANCELLED;
+       
+       /* get animdata blocks */
+       filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_ANIMDATA);
+       ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
+       
+       for (ale = anim_data.first; ale; ale = ale->next) {
+               ID *id = ale->id;
+               AnimData *adt = ale->data;
+               
+               bool action_empty  = false;
+               bool nla_empty     = false;
+               bool drivers_empty = false;
+               
+               /* sanity checks */
+               BLI_assert((id != NULL) && (adt != NULL));
+               
+               /* check if this is "empty" and can be deleted */
+               /* (For now, there are only these 3 criteria) */
+               
+               /* 1) Active Action is missing or empty */
+               if (ELEM(NULL, adt->action, adt->action->curves.first)) {
+                       action_empty = true;
+               }
+               else {
+                       /* TODO: check for keyframe + fmodifier data on these too */
+               }
+               
+               /* 2) No NLA Tracks and/or NLA Strips */
+               if (adt->nla_tracks.first == NULL) {
+                       nla_empty = true;
+               }
+               else {
+                       NlaTrack *nlt;
+                       
+                       /* empty tracks? */
+                       for (nlt = adt->nla_tracks.first; nlt; nlt = nlt->next) {
+                               if (nlt->strips.first) {
+                                       /* stop searching, as we found one that actually had stuff we don't want lost 
+                                        * NOTE: nla_empty gets reset to false, as a previous track may have been empty
+                                        */
+                                       nla_empty = false;
+                                       break;
+                               }
+                               else if (nlt->strips.first == NULL) {
+                                       /* this track is empty, but another one may still have stuff in it, so can't break yet */
+                                       nla_empty = true;
+                               }
+                       }
+               }
+               
+               /* 3) Drivers */
+               drivers_empty = (adt->drivers.first == NULL);
+               
+               
+               /* remove AnimData? */
+               if (action_empty && nla_empty && drivers_empty) {
+                       BKE_animdata_free(id, true);
+               }
+       }
+       
+       /* free temp data */
+       ANIM_animdata_freelist(&anim_data);
+       
+       /* send notifier that things have changed */
+       WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL);
+       
+       return OPERATOR_FINISHED;
+}
+
+static void ANIM_OT_channels_clean_empty(wmOperatorType *ot)
+{
+       /* identifiers */
+       ot->name = "Remove Empty Animation Data";
+       ot->idname = "ANIM_OT_channels_clean_empty";
+       ot->description = "Delete all empty animation data containers from visible datablocks";
+       
+       /* api callbacks */
+       ot->exec = animchannels_clean_empty_exec;
+       ot->poll = animedit_poll_channels_nla_tweakmode_off;
+       
+       /* flags */
+       ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
+
 /* ******************* Reenable Disabled Operator ******************* */
 
 static int animchannels_enable_poll(bContext *C)
@@ -2136,7 +2231,7 @@ static int animchannels_find_poll(bContext *C)
                return 0;
        
        /* animation editor with dopesheet */
-       return ELEM3(sa->spacetype, SPACE_ACTION, SPACE_IPO, SPACE_NLA);
+       return ELEM(sa->spacetype, SPACE_ACTION, SPACE_IPO, SPACE_NLA);
 }
 
 /* find_invoke() - Get initial channels */
@@ -2165,7 +2260,7 @@ static int animchannels_find_exec(bContext *C, wmOperator *op)
                return OPERATOR_CANCELLED;
        
        /* update filter text, and ensure that filter is enabled if there's something there
-        * NOTE: we turn the filter off if there's nothing (this is a quicky shortcut for dismissing)
+        * NOTE: we turn the filter off if there's nothing (this is a quick shortcut for dismissing)
         */
        RNA_string_get(op->ptr, "query", ac.ads->searchstr);
        
@@ -2213,9 +2308,9 @@ static int animchannels_deselectall_exec(bContext *C, wmOperator *op)
                
        /* 'standard' behavior - check if selected, then apply relevant selection */
        if (RNA_boolean_get(op->ptr, "invert"))
-               ANIM_deselect_anim_channels(&ac, ac.data, ac.datatype, 0, ACHANNEL_SETFLAG_TOGGLE);
+               ANIM_deselect_anim_channels(&ac, ac.data, ac.datatype, false, ACHANNEL_SETFLAG_INVERT);
        else
-               ANIM_deselect_anim_channels(&ac, ac.data, ac.datatype, 1, ACHANNEL_SETFLAG_ADD);
+               ANIM_deselect_anim_channels(&ac, ac.data, ac.datatype, true, ACHANNEL_SETFLAG_ADD);
        
        /* send notifier that things have changed */
        WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_SELECTED, NULL);
@@ -2261,7 +2356,7 @@ static void borderselect_anim_channels(bAnimContext *ac, rcti *rect, short selec
        }
        else {
                ymin = 0.0f;
-               ymax = (float)(-ACHANNEL_HEIGHT);
+               ymax = (float)(-ACHANNEL_HEIGHT(ac));
        }
        
        /* convert border-region to view coordinates */
@@ -2277,7 +2372,7 @@ static void borderselect_anim_channels(bAnimContext *ac, rcti *rect, short selec
                if (ac->datatype == ANIMCONT_NLA)
                        ymin = ymax - NLACHANNEL_STEP(snla);
                else
-                       ymin = ymax - ACHANNEL_STEP;
+                       ymin = ymax - ACHANNEL_STEP(ac);
                
                /* if channel is within border-select region, alter it */
                if (!((ymax < rectf.ymin) || (ymin > rectf.ymax))) {
@@ -2362,7 +2457,7 @@ static int animchannels_borderselect_exec(bContext *C, wmOperator *op)
        extend = RNA_boolean_get(op->ptr, "extend");
 
        if (!extend)
-               ANIM_deselect_anim_channels(&ac, ac.data, ac.datatype, 1, ACHANNEL_SETFLAG_CLEAR);
+               ANIM_deselect_anim_channels(&ac, ac.data, ac.datatype, true, ACHANNEL_SETFLAG_CLEAR);
 
        if (gesture_mode == GESTURE_MODAL_SELECT)
                selectmode = ACHANNEL_SETFLAG_ADD;
@@ -2403,12 +2498,13 @@ static void ANIM_OT_channels_select_border(wmOperatorType *ot)
 /* ******************* Rename Operator ***************************** */
 /* Allow renaming some channels by clicking on them */
 
-static void rename_anim_channels(bAnimContext *ac, int channel_index)
+static bool rename_anim_channels(bAnimContext *ac, int channel_index)
 {
        ListBase anim_data = {NULL, NULL};
-       bAnimChannelType *acf;
+       const bAnimChannelType *acf;
        bAnimListElem *ale;
        int filter;
+       bool success = false;
        
        /* get the channel that was clicked on */
        /* filter channels */
@@ -2423,7 +2519,7 @@ static void rename_anim_channels(bAnimContext *ac, int channel_index)
                        printf("Error: animation channel (index = %d) not found in rename_anim_channels()\n", channel_index);
                
                ANIM_animdata_freelist(&anim_data);
-               return;
+               return false;
        }
        
        /* check that channel can be renamed */
@@ -2443,6 +2539,7 @@ static void rename_anim_channels(bAnimContext *ac, int channel_index)
                         */
                        if (ac->ads) {
                                ac->ads->renameIndex = channel_index + 1;
+                               success = true;
                        }
                }
        }
@@ -2450,22 +2547,18 @@ static void rename_anim_channels(bAnimContext *ac, int channel_index)
        /* free temp data and tag for refresh */
        ANIM_animdata_freelist(&anim_data);
        ED_region_tag_redraw(ac->ar);
+       return success;
 }
 
-static int animchannels_rename_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *event)
+static int animchannels_channel_get(bAnimContext *ac, const int mval[2])
 {
-       bAnimContext ac;
        ARegion *ar;
        View2D *v2d;
        int channel_index;
        float x, y;
        
-       /* get editor data */
-       if (ANIM_animdata_get_context(C, &ac) == 0)
-               return OPERATOR_CANCELLED;
-               
        /* get useful pointers from animation context data */
-       ar = ac.ar;
+       ar = ac->ar;
        v2d = &ar->v2d;
        
        /* figure out which channel user clicked in 
@@ -2473,20 +2566,36 @@ static int animchannels_rename_invoke(bContext *C, wmOperator *UNUSED(op), const
         *              so that the tops of channels get caught ok. Since ACHANNEL_FIRST is really ACHANNEL_HEIGHT, we simply use
         *              ACHANNEL_HEIGHT_HALF.
         */
-       UI_view2d_region_to_view(v2d, event->mval[0], event->mval[1], &x, &y);
+       UI_view2d_region_to_view(v2d, mval[0], mval[1], &x, &y);
        
-       if (ac.datatype == ANIMCONT_NLA) {
-               SpaceNla *snla = (SpaceNla *)ac.sl;
+       if (ac->datatype == ANIMCONT_NLA) {
+               SpaceNla *snla = (SpaceNla *)ac->sl;
                UI_view2d_listview_view_to_cell(v2d, NLACHANNEL_NAMEWIDTH, NLACHANNEL_STEP(snla), 0, (float)NLACHANNEL_HEIGHT_HALF(snla), x, y, NULL, &channel_index);
        }
        else {
-               UI_view2d_listview_view_to_cell(v2d, ACHANNEL_NAMEWIDTH, ACHANNEL_STEP, 0, (float)ACHANNEL_HEIGHT_HALF, x, y, NULL, &channel_index);
+               UI_view2d_listview_view_to_cell(v2d, ACHANNEL_NAMEWIDTH, ACHANNEL_STEP(ac), 0, (float)ACHANNEL_HEIGHT_HALF(ac), x, y, NULL, &channel_index);
        }
-       
+
+       return channel_index;
+}
+
+static int animchannels_rename_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *event)
+{
+       bAnimContext ac;
+       int channel_index;
+
+       /* get editor data */
+       if (ANIM_animdata_get_context(C, &ac) == 0)
+               return OPERATOR_CANCELLED;
+
+       channel_index = animchannels_channel_get(&ac, event->mval);
+
        /* handle click */
-       rename_anim_channels(&ac, channel_index);
-       
-       return OPERATOR_FINISHED;
+       if (rename_anim_channels(&ac, channel_index))
+               return OPERATOR_FINISHED;
+       else
+               /* allow event to be handled by selectall operator */
+               return OPERATOR_PASS_THROUGH;
 }
 
 static void ANIM_OT_channels_rename(wmOperatorType *ot)
@@ -2596,6 +2705,10 @@ static int mouse_anim_channels(bContext *C, bAnimContext *ac, int channel_index,
                        if ((adt) && (adt->flag & ADT_UI_SELECTED))
                                adt->flag |= ADT_UI_ACTIVE;
                        
+                       /* ensure we exit editmode on whatever object was active before to avoid getting stuck there - T48747 */
+                       if (ob != sce->obedit)
+                               ED_object_editmode_exit(C, EM_FREEDATA | EM_FREEUNDO | EM_WAITCURSOR | EM_DO_UNDO);
+                       
                        notifierFlags |= (ND_ANIMCHAN | NA_SELECTED);
                        break;
                }
@@ -2603,10 +2716,10 @@ static int mouse_anim_channels(bContext *C, bAnimContext *ac, int channel_index,
                case ANIMTYPE_DSMAT:    /* Datablock AnimData Expanders */
                case ANIMTYPE_DSLAM:
                case ANIMTYPE_DSCAM:
+               case ANIMTYPE_DSCACHEFILE:
                case ANIMTYPE_DSCUR:
                case ANIMTYPE_DSSKEY:
                case ANIMTYPE_DSWOR:
-               case ANIMTYPE_DSPART:
                case ANIMTYPE_DSMBALL:
                case ANIMTYPE_DSARM:
                case ANIMTYPE_DSMESH:
@@ -2615,6 +2728,7 @@ static int mouse_anim_channels(bContext *C, bAnimContext *ac, int channel_index,
                case ANIMTYPE_DSLAT:
                case ANIMTYPE_DSLINESTYLE:
                case ANIMTYPE_DSSPK:
+               case ANIMTYPE_DSGPENCIL:
                {
                        /* sanity checking... */
                        if (ale->adt) {
@@ -2625,7 +2739,7 @@ static int mouse_anim_channels(bContext *C, bAnimContext *ac, int channel_index,
                                }
                                else {
                                        /* select AnimData block by itself */
-                                       ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, 0, ACHANNEL_SETFLAG_CLEAR);
+                                       ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, false, ACHANNEL_SETFLAG_CLEAR);
                                        ale->adt->flag |= ADT_UI_SELECTED;
                                }
                                
@@ -2680,8 +2794,8 @@ static int mouse_anim_channels(bContext *C, bAnimContext *ac, int channel_index,
                                FCurve *fcu;
                                
                                /* deselect all other channels */
-                               ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, 0, ACHANNEL_SETFLAG_CLEAR);
-                               if (pchan) ED_pose_deselectall(ob, 0);
+                               ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, false, ACHANNEL_SETFLAG_CLEAR);
+                               if (pchan) ED_pose_de_selectall(ob, SEL_DESELECT, false);
                                
                                /* only select channels in group and group itself */
                                for (fcu = agrp->channels.first; fcu && fcu->grp == agrp; fcu = fcu->next)
@@ -2690,8 +2804,8 @@ static int mouse_anim_channels(bContext *C, bAnimContext *ac, int channel_index,
                        }
                        else {
                                /* select group by itself */
-                               ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, 0, ACHANNEL_SETFLAG_CLEAR);
-                               if (pchan) ED_pose_deselectall(ob, 0);
+                               ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, false, ACHANNEL_SETFLAG_CLEAR);
+                               if (pchan) ED_pose_de_selectall(ob, SEL_DESELECT, false);
                                
                                agrp->flag |= AGRP_SELECTED;
                        }
@@ -2709,7 +2823,8 @@ static int mouse_anim_channels(bContext *C, bAnimContext *ac, int channel_index,
                        notifierFlags |= (ND_ANIMCHAN | NA_SELECTED);
                        break;
                }
-               case ANIMTYPE_FCURVE: 
+               case ANIMTYPE_FCURVE:
+               case ANIMTYPE_NLACURVE:
                {
                        FCurve *fcu = (FCurve *)ale->data;
                        
@@ -2720,13 +2835,13 @@ static int mouse_anim_channels(bContext *C, bAnimContext *ac, int channel_index,
                        }
                        else {
                                /* select F-Curve by itself */
-                               ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, 0, ACHANNEL_SETFLAG_CLEAR);
+                               ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, false, ACHANNEL_SETFLAG_CLEAR);
                                fcu->flag |= FCURVE_SELECTED;
                        }
                        
                        /* if F-Curve is selected now, make F-Curve the 'active' one in the visible list */
                        if (fcu->flag & FCURVE_SELECTED)
-                               ANIM_set_active_channel(ac, ac->data, ac->datatype, filter, fcu, ANIMTYPE_FCURVE);
+                               ANIM_set_active_channel(ac, ac->data, ac->datatype, filter, fcu, ale->type);
                                
                        notifierFlags |= (ND_ANIMCHAN | NA_SELECTED);
                        break;
@@ -2742,13 +2857,26 @@ static int mouse_anim_channels(bContext *C, bAnimContext *ac, int channel_index,
                        }
                        else {
                                /* select ShapeKey by itself */
-                               ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, 0, ACHANNEL_SETFLAG_CLEAR);
+                               ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, false, ACHANNEL_SETFLAG_CLEAR);
                                kb->flag |= KEYBLOCK_SEL;
                        }
                                
                        notifierFlags |= (ND_ANIMCHAN | NA_SELECTED);
                        break;
                }
+               case ANIMTYPE_NLACONTROLS:
+               {
+                       AnimData *adt = (AnimData *)ale->data;
+                       
+                       /* toggle expand
+                        *   - Although the triangle widget already allows this, since there's nothing else that can be done here now,
+                        *     let's just use it for easier expand/collapse for now
+                        */
+                       adt->flag ^= ADT_NLA_SKEYS_COLLAPSED;
+                       
+                       notifierFlags |= (ND_ANIMCHAN | NA_EDITED);
+                       break;
+               }
                case ANIMTYPE_GPDATABLOCK:
                {
                        bGPdata *gpd = (bGPdata *)ale->data;
@@ -2772,11 +2900,17 @@ static int mouse_anim_channels(bContext *C, bAnimContext *ac, int channel_index,
                        }
                        else {
                                /* select layer by itself */
-                               ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, 0, ACHANNEL_SETFLAG_CLEAR);
+                               ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, false, ACHANNEL_SETFLAG_CLEAR);
                                gpl->flag |= GP_LAYER_SELECT;
                        }
                        
-                       notifierFlags |= (ND_ANIMCHAN | NA_EDITED);
+                       /* change active layer, if this is selected (since we must always have an active layer) */
+                       if (gpl->flag & GP_LAYER_SELECT) {
+                               ANIM_set_active_channel(ac, ac->data, ac->datatype, filter, gpl, ANIMTYPE_GPLAYER);
+                       }
+                       
+                       WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL); /* Grease Pencil updates */
+                       notifierFlags |= (ND_ANIMCHAN | NA_EDITED); /* Animation Ediotrs updates */
                        break;
                }
                case ANIMTYPE_MASKDATABLOCK:
@@ -2802,7 +2936,7 @@ static int mouse_anim_channels(bContext *C, bAnimContext *ac, int channel_index,
                        }
                        else {
                                /* select layer by itself */
-                               ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, 0, ACHANNEL_SETFLAG_CLEAR);
+                               ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, false, ACHANNEL_SETFLAG_CLEAR);
                                masklay->flag |= MASK_LAYERFLAG_SELECT;
                        }
                        
@@ -2858,7 +2992,7 @@ static int animchannels_mouseclick_invoke(bContext *C, wmOperator *op, const wmE
         *              ACHANNEL_HEIGHT_HALF.
         */
        UI_view2d_region_to_view(v2d, event->mval[0], event->mval[1], &x, &y);
-       UI_view2d_listview_view_to_cell(v2d, ACHANNEL_NAMEWIDTH, ACHANNEL_STEP, 0, (float)ACHANNEL_HEIGHT_HALF, x, y, NULL, &channel_index);
+       UI_view2d_listview_view_to_cell(v2d, ACHANNEL_NAMEWIDTH, ACHANNEL_STEP(&ac), 0, (float)ACHANNEL_HEIGHT_HALF(&ac), x, y, NULL, &channel_index);
        
        /* handle mouse-click in the relevant channel then */
        notifierFlags = mouse_anim_channels(C, &ac, channel_index, selectmode);
@@ -2894,6 +3028,105 @@ static void ANIM_OT_channels_click(wmOperatorType *ot)
        RNA_def_property_flag(prop, PROP_SKIP_SAVE);
 }
 
+static bool select_anim_channel_keys(bAnimContext *ac, int channel_index, bool extend)
+{
+       ListBase anim_data = {NULL, NULL};
+       bAnimListElem *ale;
+       int filter;
+       bool success = false;
+       FCurve *fcu;
+       int i;
+
+       /* get the channel that was clicked on */
+       /* filter channels */
+       filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS);
+       ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
+
+       /* get channel from index */
+       ale = BLI_findlink(&anim_data, channel_index);
+       if (ale == NULL) {
+               /* channel not found */
+               if (G.debug & G_DEBUG)
+                       printf("Error: animation channel (index = %d) not found in rename_anim_channels()\n", channel_index);
+
+               ANIM_animdata_freelist(&anim_data);
+               return false;
+       }
+
+       fcu = (FCurve *)ale->key_data;
+       success = (fcu != NULL);
+
+       ANIM_animdata_freelist(&anim_data);
+
+       /* F-Curve may not have any keyframes */
+       if (fcu && fcu->bezt) {
+               BezTriple *bezt;
+
+               if (!extend) {
+                       filter = (ANIMFILTER_DATA_VISIBLE);
+                       ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
+                       for (ale = anim_data.first; ale; ale = ale->next) {
+                               FCurve *fcu_inner = (FCurve *)ale->key_data;
+
+                               if (fcu_inner) {
+                                       for (i = 0, bezt = fcu_inner->bezt; i < fcu_inner->totvert; i++, bezt++) {
+                                               bezt->f2 = bezt->f1 = bezt->f3 = 0;
+                                       }
+                               }
+                       }
+
+                       ANIM_animdata_freelist(&anim_data);
+               }
+
+               for (i = 0, bezt = fcu->bezt; i < fcu->totvert; i++, bezt++) {
+                       bezt->f2 = bezt->f1 = bezt->f3 = SELECT;
+               }
+       }
+
+       /* free temp data and tag for refresh */
+       ED_region_tag_redraw(ac->ar);
+       return success;
+}
+
+static int animchannels_channel_select_keys_invoke(bContext *C, wmOperator *op, const wmEvent *event)
+{
+       bAnimContext ac;
+       int channel_index;
+       bool extend = RNA_boolean_get(op->ptr, "extend");
+
+       /* get editor data */
+       if (ANIM_animdata_get_context(C, &ac) == 0)
+               return OPERATOR_CANCELLED;
+
+       channel_index = animchannels_channel_get(&ac, event->mval);
+
+       /* handle click */
+       if (select_anim_channel_keys(&ac, channel_index, extend)) {
+               WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_SELECTED, NULL);
+               return OPERATOR_FINISHED;
+       }
+       else
+               /* allow event to be handled by selectall operator */
+               return OPERATOR_PASS_THROUGH;
+}
+
+static void ANIM_OT_channel_select_keys(wmOperatorType *ot)
+{
+       PropertyRNA *prop;
+
+       /* identifiers */
+       ot->name = "Select Channel keyframes";
+       ot->idname = "ANIM_OT_channel_select_keys";
+       ot->description = "Select all keyframes of channel under mouse";
+
+       /* api callbacks */
+       ot->invoke = animchannels_channel_select_keys_invoke;
+       ot->poll = animedit_poll_channels_active;
+
+       prop = RNA_def_boolean(ot->srna, "extend", false, "Extend", "Extend selection");
+       RNA_def_property_flag(prop, PROP_SKIP_SAVE);
+}
+
 /* ************************************************************************** */
 /* Operator Registration */
 
@@ -2903,8 +3136,9 @@ void ED_operatortypes_animchannels(void)
        WM_operatortype_append(ANIM_OT_channels_select_border);
        
        WM_operatortype_append(ANIM_OT_channels_click);
+       WM_operatortype_append(ANIM_OT_channel_select_keys);
        WM_operatortype_append(ANIM_OT_channels_rename);
-       
+
        WM_operatortype_append(ANIM_OT_channels_find);
        
        WM_operatortype_append(ANIM_OT_channels_setting_enable);
@@ -2921,11 +3155,10 @@ void ED_operatortypes_animchannels(void)
        WM_operatortype_append(ANIM_OT_channels_expand);
        WM_operatortype_append(ANIM_OT_channels_collapse);
        
-       WM_operatortype_append(ANIM_OT_channels_visibility_toggle);
-       WM_operatortype_append(ANIM_OT_channels_visibility_set);
-       
        WM_operatortype_append(ANIM_OT_channels_fcurves_enable);
        
+       WM_operatortype_append(ANIM_OT_channels_clean_empty);
+       
        WM_operatortype_append(ANIM_OT_channels_group);
        WM_operatortype_append(ANIM_OT_channels_ungroup);
 }
@@ -2945,7 +3178,9 @@ void ED_keymap_animchannels(wmKeyConfig *keyconf)
        /* rename */
        WM_keymap_add_item(keymap, "ANIM_OT_channels_rename", LEFTMOUSE, KM_PRESS, KM_CTRL, 0);
        WM_keymap_add_item(keymap, "ANIM_OT_channels_rename", LEFTMOUSE, KM_DBL_CLICK, 0, 0);
-       
+       WM_keymap_add_item(keymap, "ANIM_OT_channel_select_keys", LEFTMOUSE, KM_DBL_CLICK, 0, 0);
+       RNA_boolean_set(WM_keymap_add_item(keymap, "ANIM_OT_channel_select_keys", LEFTMOUSE, KM_DBL_CLICK, KM_SHIFT, 0)->ptr, "extend", true);
+
        /* find (i.e. a shortcut for setting the name filter) */
        WM_keymap_add_item(keymap, "ANIM_OT_channels_find", FKEY, KM_PRESS, KM_CTRL, 0);
        
@@ -2987,10 +3222,6 @@ void ED_keymap_animchannels(wmKeyConfig *keyconf)
        /* grouping */
        WM_keymap_add_item(keymap, "ANIM_OT_channels_group", GKEY, KM_PRESS, KM_CTRL, 0);
        WM_keymap_add_item(keymap, "ANIM_OT_channels_ungroup", GKEY, KM_PRESS, KM_ALT, 0);
-       
-       /* Graph Editor only */
-       WM_keymap_add_item(keymap, "ANIM_OT_channels_visibility_set", VKEY, KM_PRESS, 0, 0);
-       WM_keymap_add_item(keymap, "ANIM_OT_channels_visibility_toggle", VKEY, KM_PRESS, KM_SHIFT, 0);
 }
 
 /* ************************************************************************** */