code cleanup: use const events for modal and invoke operators.
[blender.git] / source / blender / editors / animation / anim_channels_edit.c
index df8c1d2907920ca9bef57e2a7ecef58be94677fa..96dc1010e2cdd696364af046c3962383bcee944d 100644 (file)
@@ -43,6 +43,7 @@
 #include "DNA_scene_types.h"
 #include "DNA_key_types.h"
 #include "DNA_gpencil_types.h"
+#include "DNA_mask_types.h"
 
 #include "RNA_access.h"
 #include "RNA_define.h"
@@ -51,6 +52,7 @@
 #include "BKE_fcurve.h"
 #include "BKE_gpencil.h"
 #include "BKE_context.h"
+#include "BKE_mask.h"
 #include "BKE_global.h"
 
 #include "UI_view2d.h"
@@ -258,6 +260,10 @@ void ANIM_deselect_anim_channels(bAnimContext *ac, void *data, short datatype, s
                                        if (ale->flag & GP_LAYER_SELECT)
                                                sel = ACHANNEL_SETFLAG_CLEAR;
                                        break;
+                               case ANIMTYPE_MASKLAYER:
+                                       if (ale->flag & MASK_LAYERFLAG_SELECT)
+                                               sel = ACHANNEL_SETFLAG_CLEAR;
+                                       break;
                        }
                }
        }
@@ -281,10 +287,10 @@ void ANIM_deselect_anim_channels(bAnimContext *ac, void *data, short datatype, s
                                {
                                        Base *base = (Base *)ale->data;
                                        Object *ob = base->object;
-                               
+                                       
                                        ACHANNEL_SET_FLAG(base, sel, SELECT);
                                        ACHANNEL_SET_FLAG(ob, sel, SELECT);
-                               
+                                       
                                        if (ob->adt) {
                                                ACHANNEL_SET_FLAG(ob, sel, ADT_UI_SELECTED);
                                        }
@@ -354,6 +360,14 @@ void ANIM_deselect_anim_channels(bAnimContext *ac, void *data, short datatype, s
                                ACHANNEL_SET_FLAG(gpl, sel, GP_LAYER_SELECT);
                        }
                        break;
+                       
+                       case ANIMTYPE_MASKLAYER:
+                       {
+                               MaskLayer *masklay = (MaskLayer *)ale->data;
+                               
+                               ACHANNEL_SET_FLAG(masklay, sel, MASK_LAYERFLAG_SELECT);
+                       }
+                       break;
                }
        }
        
@@ -386,7 +400,7 @@ void ANIM_flush_setting_anim_channels(bAnimContext *ac, ListBase *anim_data, bAn
                /* compare data, and type as main way of identifying the channel */
                if ((ale->data == ale_setting->data) && (ale->type == ale_setting->type)) {
                        /* we also have to check the ID, this is assigned to, since a block may have multiple users */
-                       // TODO: is the owner-data more revealing?
+                       /* TODO: is the owner-data more revealing? */
                        if (ale->id == ale_setting->id) {
                                match = ale;
                                break;
@@ -450,7 +464,7 @@ void ANIM_flush_setting_anim_channels(bAnimContext *ac, ListBase *anim_data, bAn
                                
                                /* store this level as the 'old' level now */
                                prevLevel = level;
-                       }       
+                       }
                        /* if the level is 'greater than' (i.e. less important) than the previous level... */
                        else if (level > prevLevel) {
                                /* if previous level was a base-level (i.e. 0 offset / root of one hierarchy),
@@ -496,7 +510,7 @@ void ANIM_flush_setting_anim_channels(bAnimContext *ac, ListBase *anim_data, bAn
                                break;
                        
                        /* store this level as the 'old' level now */
-                       // prevLevel= level; // XXX: prevLevel is unused
+                       // prevLevel = level; // XXX: prevLevel is unused
                }
        }
 }
@@ -524,12 +538,26 @@ void ANIM_fcurve_delete_from_animdata(bAnimContext *ac, AnimData *adt, FCurve *f
                BLI_remlink(&adt->drivers, fcu);
        }
        else if (adt->action) {
+               bAction *act = adt->action;
+               
                /* remove from group or action, whichever one "owns" the F-Curve */
-               if (fcu->grp)
-                       action_groups_remove_channel(adt->action, fcu);
-               else
-                       BLI_remlink(&adt->action->curves, fcu);
+               if (fcu->grp) {
+                       bActionGroup *agrp = fcu->grp;
+                       
+                       /* remove F-Curve from group+action */
+                       action_groups_remove_channel(act, fcu);
                        
+                       /* if group has no more channels, remove it too, 
+                        * otherwise can have many dangling groups [#33541]
+                        */
+                       if (agrp->channels.first == NULL) {
+                               BLI_freelinkN(&act->groups, agrp);
+                       }
+               }
+               else {
+                       BLI_remlink(&act->curves, fcu);
+               }
+               
                /* if action has no more F-Curves as a result of this, unlink it from
                 * AnimData if it did not come from a NLA Strip being tweaked.
                 *
@@ -537,12 +565,8 @@ void ANIM_fcurve_delete_from_animdata(bAnimContext *ac, AnimData *adt, FCurve *f
                 * channel list that are empty, and linger around long after the data they
                 * are for has disappeared (and probably won't come back).
                 */
-               // XXX: does everybody always want this?
-               /* XXX: there's a problem where many actions could build up in the file if multiple
-                * full add/delete cycles are performed on the same objects, but assume that this is rare
-                */
-               if ((adt->action->curves.first == NULL) && (adt->flag & ADT_NLA_EDIT_ON) == 0) {
-                       id_us_min(&adt->action->id);
+               if ((act->curves.first == NULL) && (adt->flag & ADT_NLA_EDIT_ON) == 0) {
+                       id_us_min(&act->id);
                        adt->action = NULL;
                }
        }
@@ -562,13 +586,13 @@ static int animedit_poll_channels_active(bContext *C)
        ScrArea *sa = CTX_wm_area(C);
        
        /* channels region test */
-       // TODO: could enhance with actually testing if channels region?
+       /* TODO: could enhance with actually testing if channels region? */
        if (ELEM(NULL, sa, CTX_wm_region(C)))
                return 0;
        /* animation editor test */
        if (ELEM3(sa->spacetype, SPACE_ACTION, SPACE_IPO, SPACE_NLA) == 0)
                return 0;
-               
+
        return 1;
 }
 
@@ -577,16 +601,16 @@ static int animedit_poll_channels_nla_tweakmode_off(bContext *C)
 {
        ScrArea *sa = CTX_wm_area(C);
        Scene *scene = CTX_data_scene(C);
-       
+
        /* channels region test */
-       // TODO: could enhance with actually testing if channels region?
+       /* TODO: could enhance with actually testing if channels region? */
        if (ELEM(NULL, sa, CTX_wm_region(C)))
                return 0;
        /* animation editor test */
        if (ELEM3(sa->spacetype, SPACE_ACTION, SPACE_IPO, SPACE_NLA) == 0)
                return 0;
        
-       /* NLA TweakMode test */        
+       /* NLA TweakMode test */
        if (sa->spacetype == SPACE_NLA) {
                if ((scene == NULL) || (scene->flag & SCE_NLA_EDIT_ON))
                        return 0;
@@ -780,7 +804,7 @@ static void rearrange_animchannel_add_to_islands(ListBase *islands, ListBase *sr
                        FCurve *fcu = (FCurve *)channel;
                        
                        is_sel = SEL_FCU(fcu);
-               }       
+               }
                break;
                case ANIMTYPE_NLATRACK:
                {
@@ -840,7 +864,7 @@ static short rearrange_animchannel_islands(ListBase *list, AnimChanRearrangeFp r
 {
        ListBase islands = {NULL, NULL};
        Link *channel, *chanNext = NULL;
-       short done = 0;
+       short done = FALSE;
        
        /* don't waste effort on an empty list */
        if (list->first == NULL)
@@ -866,7 +890,7 @@ static short rearrange_animchannel_islands(ListBase *list, AnimChanRearrangeFp r
                        /* perform rearranging */
                        if (rearrange_func(&islands, island)) {
                                island->flag |= REORDER_ISLAND_MOVED;
-                               done = 1;
+                               done = TRUE;
                        }
                }
        }
@@ -896,7 +920,7 @@ static void rearrange_nla_channels(bAnimContext *UNUSED(ac), AnimData *adt, shor
        if (rearrange_func == NULL)
                return;
        
-       /* only consider NLA data if it's accessible */ 
+       /* only consider NLA data if it's accessible */
        //if (EXPANDED_DRVD(adt) == 0)
        //      return;
        
@@ -917,7 +941,7 @@ static void rearrange_driver_channels(bAnimContext *UNUSED(ac), AnimData *adt, s
        if (rearrange_func == NULL)
                return;
        
-       /* only consider drivers if they're accessible */       
+       /* only consider drivers if they're accessible */
        if (EXPANDED_DRVD(adt) == 0)
                return;
        
@@ -1055,6 +1079,10 @@ static int animchannels_rearrange_exec(bContext *C, wmOperator *op)
                /* Grease Pencil channels */
                printf("Grease Pencil not supported for moving yet\n");
        }
+       else if (ac.datatype == ANIMCONT_MASK) {
+               /* Grease Pencil channels */
+               printf("Mask does not supported for moving yet\n");
+       }
        else if (ac.datatype == ANIMCONT_ACTION) {
                /* Directly rearrange action's channels */
                rearrange_action_channels(&ac, ac.data, mode);
@@ -1119,6 +1147,218 @@ static void ANIM_OT_channels_move(wmOperatorType *ot)
        ot->prop = RNA_def_enum(ot->srna, "direction", prop_animchannel_rearrange_types, REARRANGE_ANIMCHAN_DOWN, "Direction", "");
 }
 
+/* ******************** Group Channel Operator ************************ */
+
+static int animchannels_grouping_poll(bContext *C)
+{
+       ScrArea *sa = CTX_wm_area(C);
+       SpaceLink *sl;
+
+       /* channels region test */
+       /* TODO: could enhance with actually testing if channels region? */
+       if (ELEM(NULL, sa, CTX_wm_region(C)))
+               return 0;
+               
+       /* animation editor test - must be suitable modes only */
+       sl = CTX_wm_space_data(C);
+       
+       switch (sa->spacetype) {
+               /* supported... */
+               case SPACE_ACTION:
+               {
+                       SpaceAction *saction = (SpaceAction *)sl;
+                       
+                       /* 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;
+                       
+                       /* drivers can't have groups... */
+                       if (sipo->mode != SIPO_MODE_ANIMATION)
+                               return 0;
+               }
+                       break;
+                       
+               /* unsupported... */
+               default:
+                       return 0;
+       }
+       
+       return 1;
+}
+
+/* ----------------------------------------------------------- */
+
+static void animchannels_group_channels(bAnimContext *ac, bAnimListElem *adt_ref, const char name[])
+{      
+       AnimData *adt = adt_ref->adt;
+       bAction *act = adt->action;
+       
+       if (act) {
+               ListBase anim_data = {NULL, NULL};
+               int filter;
+               
+               /* find selected F-Curves to re-group */
+               filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_SEL);
+               ANIM_animdata_filter(ac, &anim_data, filter, adt_ref, ANIMCONT_CHANNEL);
+               
+               if (anim_data.first) {
+                       bActionGroup *agrp;
+                       bAnimListElem *ale;
+                       
+                       /* create new group, which should now be part of the action */
+                       agrp = action_groups_add_new(act, name);
+                       BLI_assert(agrp != NULL);
+                       
+                       /* transfer selected F-Curves across to new group  */
+                       for (ale = anim_data.first; ale; ale = ale->next) {
+                               FCurve *fcu = (FCurve *)ale->data;
+                               bActionGroup *grp = fcu->grp;
+                               
+                               /* remove F-Curve from group, then group too if it is now empty */
+                               action_groups_remove_channel(act, fcu);
+                               
+                               if ((grp) && (grp->channels.first == NULL)) {
+                                       BLI_freelinkN(&act->groups, grp);
+                               }
+                               
+                               /* add F-Curve to group */
+                               action_groups_add_channel(act, agrp, fcu);
+                       }
+               }
+               
+               /* cleanup */
+               BLI_freelistN(&anim_data);
+       }
+}
+
+static int animchannels_group_exec(bContext *C, wmOperator *op)
+{
+       bAnimContext ac;
+       char name[MAX_NAME];
+       
+       /* get editor data */
+       if (ANIM_animdata_get_context(C, &ac) == 0)
+               return OPERATOR_CANCELLED;
+       
+       /* get name for new group */
+       RNA_string_get(op->ptr, "name", name);
+       
+       /* XXX: name for group should never be empty... */
+       if (name[0]) {
+               ListBase anim_data = {NULL, NULL};
+               bAnimListElem *ale;
+               int filter;
+               
+               /* handle each animdata block separately, so that the regrouping doesn't flow into blocks  */
+               filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_ANIMDATA | ANIMFILTER_NODUPLIS);
+               ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
+               
+               for (ale = anim_data.first; ale; ale = ale->next) {
+                       animchannels_group_channels(&ac, ale, name);
+               }
+               
+               /* free temp data */
+               BLI_freelistN(&anim_data);
+               
+               /* updatss */
+               WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL);
+       }
+       
+       return OPERATOR_FINISHED;
+}
+
+static void ANIM_OT_channels_group(wmOperatorType *ot)
+{
+       /* identifiers */
+       ot->name = "Group Channels";
+       ot->idname = "ANIM_OT_channels_group";
+       ot->description = "Add selected F-Curves to a new group";
+       
+       /* callbacks */
+       ot->invoke = WM_operator_props_popup;
+       ot->exec = animchannels_group_exec;
+       ot->poll = animchannels_grouping_poll;
+       
+       /* flags */
+       ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+       
+       /* props */
+       ot->prop = RNA_def_string(ot->srna, "name", "New Group", 
+                                 sizeof(((bActionGroup *)NULL)->name), 
+                                 "Name", "Name of newly created group");
+       /* RNA_def_property_flag(ot->prop, PROP_SKIP_SAVE); */ /* XXX: still not too sure about this - keeping same text is confusing... */
+}
+
+/* ----------------------------------------------------------- */
+
+static int animchannels_ungroup_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;
+               
+       /* just selected F-Curves... */
+       filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_SEL | ANIMFILTER_NODUPLIS);
+       ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
+       
+       for (ale = anim_data.first; ale; ale = ale->next) {
+               /* find action for this F-Curve... */
+               if (ale->adt && ale->adt->action) {
+                       FCurve  *fcu = (FCurve *)ale->data;
+                       bAction *act = ale->adt->action;
+                       
+                       /* only proceed to remove if F-Curve is in a group... */
+                       if (fcu->grp) { 
+                               bActionGroup *agrp = fcu->grp;
+                               
+                               /* remove F-Curve from group and add at tail (ungrouped) */
+                               action_groups_remove_channel(act, fcu);
+                               BLI_addtail(&act->curves, fcu);
+                               
+                               /* delete group if it is now empty */
+                               if (agrp->channels.first == NULL) {
+                                       BLI_freelinkN(&act->groups, agrp);
+                               }
+                       }
+               }
+       }
+       
+       /* cleanup */
+       BLI_freelistN(&anim_data);
+       
+       /* updates */
+       WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL);
+       
+       return OPERATOR_FINISHED;
+}
+
+static void ANIM_OT_channels_ungroup(wmOperatorType *ot)
+{
+       /* identifiers */
+       ot->name = "Ungroup Channels";
+       ot->idname = "ANIM_OT_channels_ungroup";
+       ot->description = "Remove selected F-Curves from their current groups";
+       
+       /* callbacks */
+       ot->exec = animchannels_ungroup_exec;
+       ot->poll = animchannels_grouping_poll;
+       
+       /* flags */
+       ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
+
 /* ******************** Delete Channel Operator *********************** */
 
 static int animchannels_delete_exec(bContext *C, wmOperator *UNUSED(op))
@@ -1205,6 +1445,17 @@ static int animchannels_delete_exec(bContext *C, wmOperator *UNUSED(op))
                                BLI_freelinkN(&gpd->layers, gpl);
                        }
                        break;
+                       
+                       case ANIMTYPE_MASKLAYER:
+                       {
+                               /* Mask layer */
+                               Mask *mask = (Mask *)ale->id;
+                               MaskLayer *masklay = (MaskLayer *)ale->data;
+                               
+                               /* try to delete the layer's data and the layer itself */
+                               BKE_mask_layer_remove(mask, masklay);
+                       }
+                       break;
                }
        }
        
@@ -1285,10 +1536,10 @@ static int animchannels_visibility_set_exec(bContext *C, wmOperator *UNUSED(op))
        /* 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
+               /* TODO: find out why this is the case, and fix that */
                if (ale->type == ANIMTYPE_OBJECT)
                        continue;
                
@@ -1365,7 +1616,7 @@ static int animchannels_visibility_toggle_exec(bContext *C, wmOperator *UNUSED(o
        /* 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
+               /* TODO: find out why this is the case, and fix that */
                if (ale->type == ANIMTYPE_OBJECT)
                        continue;
                
@@ -1531,6 +1782,8 @@ static int animchannels_setflag_exec(bContext *C, wmOperator *op)
 /* duplicate of 'ANIM_OT_channels_setting_toggle' for menu title only, weak! */
 static void ANIM_OT_channels_setting_enable(wmOperatorType *ot)
 {
+       PropertyRNA *prop;
+       
        /* identifiers */
        ot->name = "Enable Channel Setting";
        ot->idname = "ANIM_OT_channels_setting_enable";
@@ -1546,13 +1799,16 @@ static void ANIM_OT_channels_setting_enable(wmOperatorType *ot)
        
        /* props */
        /* flag-setting mode */
-       RNA_def_enum(ot->srna, "mode", prop_animchannel_setflag_types, ACHANNEL_SETFLAG_ADD, "Mode", "");
+       prop = RNA_def_enum(ot->srna, "mode", prop_animchannel_setflag_types, ACHANNEL_SETFLAG_ADD, "Mode", "");
+       RNA_def_property_flag(prop, PROP_HIDDEN);
        /* setting to set */
        ot->prop = RNA_def_enum(ot->srna, "type", prop_animchannel_settings_types, 0, "Type", "");
 }
 /* duplicate of 'ANIM_OT_channels_setting_toggle' for menu title only, weak! */
 static void ANIM_OT_channels_setting_disable(wmOperatorType *ot)
 {
+       PropertyRNA *prop;
+       
        /* identifiers */
        ot->name = "Disable Channel Setting";
        ot->idname = "ANIM_OT_channels_setting_disable";
@@ -1568,13 +1824,16 @@ static void ANIM_OT_channels_setting_disable(wmOperatorType *ot)
        
        /* props */
        /* flag-setting mode */
-       RNA_def_enum(ot->srna, "mode", prop_animchannel_setflag_types, ACHANNEL_SETFLAG_CLEAR, "Mode", "");
+       prop = RNA_def_enum(ot->srna, "mode", prop_animchannel_setflag_types, ACHANNEL_SETFLAG_CLEAR, "Mode", "");
+       RNA_def_property_flag(prop, PROP_HIDDEN); /* internal hack - don't expose */
        /* setting to set */
        ot->prop = RNA_def_enum(ot->srna, "type", prop_animchannel_settings_types, 0, "Type", "");
 }
 
 static void ANIM_OT_channels_setting_toggle(wmOperatorType *ot)
 {
+       PropertyRNA *prop;
+       
        /* identifiers */
        ot->name = "Toggle Channel Setting";
        ot->idname = "ANIM_OT_channels_setting_toggle";
@@ -1590,13 +1849,16 @@ static void ANIM_OT_channels_setting_toggle(wmOperatorType *ot)
        
        /* props */
        /* flag-setting mode */
-       RNA_def_enum(ot->srna, "mode", prop_animchannel_setflag_types, ACHANNEL_SETFLAG_TOGGLE, "Mode", "");
+       prop = RNA_def_enum(ot->srna, "mode", prop_animchannel_setflag_types, ACHANNEL_SETFLAG_TOGGLE, "Mode", "");
+       RNA_def_property_flag(prop, PROP_HIDDEN); /* internal hack - don't expose */
        /* setting to set */
        ot->prop = RNA_def_enum(ot->srna, "type", prop_animchannel_settings_types, 0, "Type", "");
 }
 
 static void ANIM_OT_channels_editable_toggle(wmOperatorType *ot)
 {
+       PropertyRNA *prop;
+       
        /* identifiers */
        ot->name = "Toggle Channel Editability";
        ot->idname = "ANIM_OT_channels_editable_toggle";
@@ -1613,7 +1875,8 @@ static void ANIM_OT_channels_editable_toggle(wmOperatorType *ot)
        /* flag-setting mode */
        RNA_def_enum(ot->srna, "mode", prop_animchannel_setflag_types, ACHANNEL_SETFLAG_TOGGLE, "Mode", "");
        /* setting to set */
-       RNA_def_enum(ot->srna, "type", prop_animchannel_settings_types, ACHANNEL_SETTING_PROTECT, "Type", "");
+       prop = RNA_def_enum(ot->srna, "type", prop_animchannel_settings_types, ACHANNEL_SETTING_PROTECT, "Type", "");
+       RNA_def_property_flag(prop, PROP_HIDDEN); /* internal hack - don't expose */
 }
 
 /* ********************** Expand Channels Operator *********************** */
@@ -1705,9 +1968,9 @@ static void ANIM_OT_channels_collapse(wmOperatorType *ot)
 static int animchannels_enable_poll(bContext *C)
 {
        ScrArea *sa = CTX_wm_area(C);
-       
+
        /* channels region test */
-       // TODO: could enhance with actually testing if channels region?
+       /* TODO: could enhance with actually testing if channels region? */
        if (ELEM(NULL, sa, CTX_wm_region(C)))
                return 0;
                
@@ -1901,10 +2164,7 @@ static int animchannels_borderselect_exec(bContext *C, wmOperator *op)
                return OPERATOR_CANCELLED;
        
        /* get settings from operator */
-       rect.xmin = RNA_int_get(op->ptr, "xmin");
-       rect.ymin = RNA_int_get(op->ptr, "ymin");
-       rect.xmax = RNA_int_get(op->ptr, "xmax");
-       rect.ymax = RNA_int_get(op->ptr, "ymax");
+       WM_operator_properties_border_to_rcti(op, &rect);
        
        gesture_mode = RNA_int_get(op->ptr, "gesture_mode");
        extend = RNA_boolean_get(op->ptr, "extend");
@@ -2000,7 +2260,7 @@ static void rename_anim_channels(bAnimContext *ac, int channel_index)
        ED_region_tag_redraw(ac->ar);
 }
 
-static int animchannels_rename_invoke(bContext *C, wmOperator *UNUSED(op), wmEvent *evt)
+static int animchannels_rename_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *event)
 {
        bAnimContext ac;
        ARegion *ar;
@@ -2017,11 +2277,11 @@ static int animchannels_rename_invoke(bContext *C, wmOperator *UNUSED(op), wmEve
        v2d = &ar->v2d;
        
        /* figure out which channel user clicked in 
-        * Note: although channels technically start at y= ACHANNEL_FIRST, we need to adjust by half a channel's height
+        * Note: although channels technically start at (y = ACHANNEL_FIRST), we need to adjust by half a channel's height
         *              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, evt->mval[0], evt->mval[1], &x, &y);
+       UI_view2d_region_to_view(v2d, event->mval[0], event->mval[1], &x, &y);
        
        if (ac.datatype == ANIMCONT_NLA) {
                SpaceNla *snla = (SpaceNla *)ac.sl;
@@ -2074,17 +2334,17 @@ static int mouse_anim_channels(bAnimContext *ac, float UNUSED(x), int channel_in
                BLI_freelistN(&anim_data);
                return 0;
        }
-       
+
        /* selectmode -1 is a special case for ActionGroups only, which selects all of the channels underneath it only... */
-       // TODO: should this feature be extended to work with other channel types too?
+       /* TODO: should this feature be extended to work with other channel types too? */
        if ((selectmode == -1) && (ale->type != ANIMTYPE_GROUP)) {
                /* normal channels should not behave normally in this case */
                BLI_freelistN(&anim_data);
                return 0;
        }
-       
+
        /* action to take depends on what channel we've got */
-       // WARNING: must keep this in sync with the equivalent function in nla_channels.c
+       /* WARNING: must keep this in sync with the equivalent function in nla_channels.c */
        switch (ale->type) {
                case ANIMTYPE_SCENE:
                {
@@ -2125,7 +2385,7 @@ static int mouse_anim_channels(bAnimContext *ac, float UNUSED(x), int channel_in
                                Base *b;
                                
                                /* deselect all */
-                               // TODO: should this deselect all other types of channels too?
+                               /* TODO: should this deselect all other types of channels too? */
                                for (b = sce->base.first; b; b = b->next) {
                                        b->flag &= ~SELECT;
                                        b->object->flag = b->flag;
@@ -2180,7 +2440,7 @@ static int mouse_anim_channels(bAnimContext *ac, float UNUSED(x), int channel_in
                        }
                        
                        notifierFlags |= (ND_ANIMCHAN | NA_SELECTED);
-               }       
+               }
                break;
                
                case ANIMTYPE_GROUP: 
@@ -2193,7 +2453,7 @@ static int mouse_anim_channels(bAnimContext *ac, float UNUSED(x), int channel_in
                                agrp->flag ^= AGRP_SELECTED;
                        }
                        else if (selectmode == -1) {
-                               /* select all in group (and deselect everthing else) */ 
+                               /* select all in group (and deselect everthing else) */
                                FCurve *fcu;
                                
                                /* deselect all other channels */
@@ -2202,7 +2462,7 @@ static int mouse_anim_channels(bAnimContext *ac, float UNUSED(x), int channel_in
                                /* only select channels in group and group itself */
                                for (fcu = agrp->channels.first; fcu && fcu->grp == agrp; fcu = fcu->next)
                                        fcu->flag |= FCURVE_SELECTED;
-                               agrp->flag |= AGRP_SELECTED;                                    
+                               agrp->flag |= AGRP_SELECTED;
                        }
                        else {
                                /* select group by itself */
@@ -2213,6 +2473,8 @@ static int mouse_anim_channels(bAnimContext *ac, float UNUSED(x), int channel_in
                        /* if group is selected now, make group the 'active' one in the visible list */
                        if (agrp->flag & AGRP_SELECTED)
                                ANIM_set_active_channel(ac, ac->data, ac->datatype, filter, agrp, ANIMTYPE_GROUP);
+                       else
+                               ANIM_set_active_channel(ac, ac->data, ac->datatype, filter, NULL, ANIMTYPE_GROUP);
                                
                        notifierFlags |= (ND_ANIMCHAN | NA_SELECTED);
                }
@@ -2278,7 +2540,7 @@ static int mouse_anim_channels(bAnimContext *ac, float UNUSED(x), int channel_in
                                /* invert selection status of this layer only */
                                gpl->flag ^= GP_LAYER_SELECT;
                        }
-                       else {  
+                       else {
                                /* select layer by itself */
                                ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, 0, ACHANNEL_SETFLAG_CLEAR);
                                gpl->flag |= GP_LAYER_SELECT;
@@ -2287,6 +2549,36 @@ static int mouse_anim_channels(bAnimContext *ac, float UNUSED(x), int channel_in
                        notifierFlags |= (ND_ANIMCHAN | NA_EDITED);
                }
                break;
+               case ANIMTYPE_MASKDATABLOCK:
+               {
+                       Mask *mask = (Mask *)ale->data;
+                       
+                       /* toggle expand
+                        *      - although the triangle widget already allows this, the whole channel can also be used for this purpose
+                        */
+                       mask->flag ^= MASK_ANIMF_EXPAND;
+                       
+                       notifierFlags |= (ND_ANIMCHAN | NA_EDITED);
+               }
+               break;
+               case ANIMTYPE_MASKLAYER:
+               {
+                       MaskLayer *masklay = (MaskLayer *)ale->data;
+                       
+                       /* select/deselect */
+                       if (selectmode == SELECT_INVERT) {
+                               /* invert selection status of this layer only */
+                               masklay->flag ^= MASK_LAYERFLAG_SELECT;
+                       }
+                       else {
+                               /* select layer by itself */
+                               ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, 0, ACHANNEL_SETFLAG_CLEAR);
+                               masklay->flag |= MASK_LAYERFLAG_SELECT;
+                       }
+                       
+                       notifierFlags |= (ND_ANIMCHAN | NA_EDITED);
+               }
+               break;
                default:
                        if (G.debug & G_DEBUG)
                                printf("Error: Invalid channel type in mouse_anim_channels()\n");
@@ -2302,7 +2594,7 @@ static int mouse_anim_channels(bAnimContext *ac, float UNUSED(x), int channel_in
 /* ------------------- */
 
 /* handle clicking */
-static int animchannels_mouseclick_invoke(bContext *C, wmOperator *op, wmEvent *event)
+static int animchannels_mouseclick_invoke(bContext *C, wmOperator *op, const wmEvent *event)
 {
        bAnimContext ac;
        ARegion *ar;
@@ -2330,7 +2622,7 @@ static int animchannels_mouseclick_invoke(bContext *C, wmOperator *op, wmEvent *
                selectmode = SELECT_REPLACE;
        
        /* figure out which channel user clicked in 
-        * Note: although channels technically start at y= ACHANNEL_FIRST, we need to adjust by half a channel's height
+        * Note: although channels technically start at (y = ACHANNEL_FIRST), we need to adjust by half a channel's height
         *              so that the tops of channels get caught ok. Since ACHANNEL_FIRST is really ACHANNEL_HEIGHT, we simply use
         *              ACHANNEL_HEIGHT_HALF.
         */
@@ -2348,6 +2640,8 @@ static int animchannels_mouseclick_invoke(bContext *C, wmOperator *op, wmEvent *
  
 static void ANIM_OT_channels_click(wmOperatorType *ot)
 {
+       PropertyRNA *prop;
+       
        /* identifiers */
        ot->name = "Mouse Click on Channels";
        ot->idname = "ANIM_OT_channels_click";
@@ -2360,9 +2654,13 @@ static void ANIM_OT_channels_click(wmOperatorType *ot)
        /* flags */
        ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
        
-       /* id-props */
-       RNA_def_boolean(ot->srna, "extend", 0, "Extend Select", ""); // SHIFTKEY
-       RNA_def_boolean(ot->srna, "children_only", 0, "Select Children Only", ""); // CTRLKEY|SHIFTKEY
+       /* properties */
+       /* NOTE: don't save settings, otherwise, can end up with some weird behaviour (sticky extend) */
+       prop = RNA_def_boolean(ot->srna, "extend", 0, "Extend Select", ""); // SHIFTKEY
+       RNA_def_property_flag(prop, PROP_SKIP_SAVE);
+       
+       prop = RNA_def_boolean(ot->srna, "children_only", 0, "Select Children Only", ""); // CTRLKEY|SHIFTKEY
+       RNA_def_property_flag(prop, PROP_SKIP_SAVE);
 }
 
 /* ************************************************************************** */
@@ -2379,10 +2677,10 @@ void ED_operatortypes_animchannels(void)
        WM_operatortype_append(ANIM_OT_channels_setting_enable);
        WM_operatortype_append(ANIM_OT_channels_setting_disable);
        WM_operatortype_append(ANIM_OT_channels_setting_toggle);
-       
+
        WM_operatortype_append(ANIM_OT_channels_delete);
-       
-       // XXX does this need to be a separate operator?
+
+       /* XXX does this need to be a separate operator? */
        WM_operatortype_append(ANIM_OT_channels_editable_toggle);
        
        WM_operatortype_append(ANIM_OT_channels_move);
@@ -2394,6 +2692,9 @@ void ED_operatortypes_animchannels(void)
        WM_operatortype_append(ANIM_OT_channels_visibility_set);
        
        WM_operatortype_append(ANIM_OT_channels_fcurves_enable);
+       
+       WM_operatortype_append(ANIM_OT_channels_group);
+       WM_operatortype_append(ANIM_OT_channels_ungroup);
 }
 
 // TODO: check on a poll callback for this, to get hotkeys into menus
@@ -2404,11 +2705,11 @@ void ED_keymap_animchannels(wmKeyConfig *keyconf)
 
        /* selection */
        /* click-select */
-       // XXX for now, only leftmouse....
+       /* XXX for now, only leftmouse.... */
        WM_keymap_add_item(keymap, "ANIM_OT_channels_click", LEFTMOUSE, KM_PRESS, 0, 0);
        RNA_boolean_set(WM_keymap_add_item(keymap, "ANIM_OT_channels_click", LEFTMOUSE, KM_PRESS, KM_SHIFT, 0)->ptr, "extend", TRUE);
        RNA_boolean_set(WM_keymap_add_item(keymap, "ANIM_OT_channels_click", LEFTMOUSE, KM_PRESS, KM_CTRL | KM_SHIFT, 0)->ptr, "children_only", TRUE);
-       
+
        /* rename */
        WM_keymap_add_item(keymap, "ANIM_OT_channels_rename", LEFTMOUSE, KM_PRESS, KM_CTRL, 0);
        
@@ -2447,6 +2748,10 @@ void ED_keymap_animchannels(wmKeyConfig *keyconf)
        RNA_enum_set(WM_keymap_add_item(keymap, "ANIM_OT_channels_move", PAGEUPKEY, KM_PRESS, KM_SHIFT, 0)->ptr, "direction", REARRANGE_ANIMCHAN_TOP);
        RNA_enum_set(WM_keymap_add_item(keymap, "ANIM_OT_channels_move", PAGEDOWNKEY, KM_PRESS, KM_SHIFT, 0)->ptr, "direction", REARRANGE_ANIMCHAN_BOTTOM);
        
+       /* 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);