RNA
[blender.git] / source / blender / editors / animation / anim_channels.c
index c0025a1b0f541437e457e6448912438c9df934de..c52ade1bba855db71d14228fbce0fa1575b9414c 100644 (file)
@@ -58,6 +58,7 @@
 #include "DNA_userdef_types.h"
 #include "DNA_gpencil_types.h"
 #include "DNA_windowmanager_types.h"
+#include "DNA_world_types.h"
 
 #include "RNA_access.h"
 #include "RNA_define.h"
                else if (smode == ACHANNEL_SETFLAG_ADD) (channel)->flag |= (sflag); \
                else                                                                    (channel)->flag &= ~(sflag); \
        }
-
-/* -------------------------- Internal Tools -------------------------------- */
-
-/* set the given Action Group to be the 'active' one in its Action */
-static void action_set_active_agrp (bAction *act, bActionGroup *agrp)
-{
-       bActionGroup *grp;
        
-       /* sanity check */
-       if (act == NULL)
-               return;
-               
-       /* clear active flag on all others */
-       for (grp= act->groups.first; grp; grp= grp->next)
-               grp->flag &= ~AGRP_ACTIVE;
-               
-       /* set the given group to be the active one */
-       if (agrp)
-               agrp->flag |= AGRP_ACTIVE;
-}
+/* set/clear/toggle macro, where the flag is negative 
+ *     - channel - channel with a 'flag' member that we're setting
+ *     - smode - 0=clear, 1=set, 2=toggle
+ *     - sflag - bitflag to set
+ */
+#define ACHANNEL_SET_FLAG_NEG(channel, smode, sflag) \
+       { \
+               if (smode == ACHANNEL_SETFLAG_TOGGLE)   (channel)->flag ^= (sflag); \
+               else if (smode == ACHANNEL_SETFLAG_ADD) (channel)->flag &= ~(sflag); \
+               else                                                                    (channel)->flag |= (sflag); \
+       }
 
 /* -------------------------- Exposed API ----------------------------------- */
 
-/* Set the given ActionChannel or ActionGroup as the active one in the given action
- *     - data: should be bAction...
- *     - datatype: should be ANIMCONT_ACTION 
- *     - channel_data: bActionChannel or bActionGroup
- *     - channel_type: eAnim_ChannelType
- */
-void ANIM_action_set_active_channel (void *data, short datatype, void *channel_data, short channel_type)
+/* Set the given animation-channel as the active one for the active context */
+void ANIM_set_active_channel (void *data, short datatype, int filter, void *channel_data, short channel_type)
 {
-       /* sanity checks */
-       if ((data == NULL) || (datatype != ANIMCONT_ACTION))
+       ListBase anim_data = {NULL, NULL};
+       bAnimListElem *ale;
+       
+       /* try to build list of filtered items */
+       // XXX we don't need/supply animcontext for now, since in this case, there's nothing really essential there that isn't already covered
+       ANIM_animdata_filter(NULL, &anim_data, filter, data, datatype);
+       if (anim_data.first == NULL)
                return;
                
-       switch (channel_type) {
-               case ANIMTYPE_GROUP:
-                       action_set_active_agrp((bAction *)data, (bActionGroup *)channel_data);
-                       break;
+       /* only clear the 'active' flag for the channels of the same type */
+       for (ale= anim_data.first; ale; ale= ale->next) {
+               /* skip if types don't match */
+               if (channel_type != ale->type)
+                       continue;
+               
+               /* flag to set depends on type */
+               switch (ale->type) {
+                       case ANIMTYPE_GROUP:
+                       {
+                               bActionGroup *agrp= (bActionGroup *)ale->data;
+                               
+                               ACHANNEL_SET_FLAG(agrp, ACHANNEL_SETFLAG_CLEAR, AGRP_ACTIVE);
+                       }
+                               break;
+                       case ANIMTYPE_FCURVE:
+                       {
+                               FCurve *fcu= (FCurve *)ale->data;
+                               
+                               ACHANNEL_SET_FLAG(fcu, ACHANNEL_SETFLAG_CLEAR, FCURVE_ACTIVE);
+                       }
+                               break;
+               }
        }
+       
+       /* set active flag */
+       if (channel_data) {
+               switch (channel_type) {
+                       case ANIMTYPE_GROUP:
+                       {
+                               bActionGroup *agrp= (bActionGroup *)channel_data;
+                               agrp->flag |= AGRP_ACTIVE;
+                       }
+                               break;
+                       case ANIMTYPE_FCURVE:
+                       {
+                               FCurve *fcu= (FCurve *)channel_data;
+                               fcu->flag |= FCURVE_ACTIVE;
+                       }
+                               break;
+               }
+       }
+       
+       /* clean up */
+       BLI_freelistN(&anim_data);
 }
 
 /* Deselect all animation channels 
@@ -164,6 +197,10 @@ void ANIM_deselect_anim_channels (void *data, short datatype, short test, short
                                break;
                        
                        switch (ale->type) {
+                               case ANIMTYPE_SCENE:
+                                       if (ale->flag & SCE_DS_SELECTED)
+                                               sel= ACHANNEL_SETFLAG_CLEAR;
+                                       break;
                                case ANIMTYPE_OBJECT:
                                        if (ale->flag & SELECT)
                                                sel= ACHANNEL_SETFLAG_CLEAR;
@@ -187,6 +224,13 @@ void ANIM_deselect_anim_channels (void *data, short datatype, short test, short
        /* Now set the flags */
        for (ale= anim_data.first; ale; ale= ale->next) {
                switch (ale->type) {
+                       case ANIMTYPE_SCENE:
+                       {
+                               Scene *scene= (Scene *)ale->data;
+                               
+                               ACHANNEL_SET_FLAG(scene, sel, SCE_DS_SELECTED);
+                       }
+                               break;
                        case ANIMTYPE_OBJECT:
                        {
                                Base *base= (Base *)ale->data;
@@ -672,50 +716,179 @@ enum {
        ACHANNEL_SETTING_PROTECT = 1,
        ACHANNEL_SETTING_MUTE,
        ACHANNEL_SETTING_VISIBLE,
+       ACHANNEL_SETTING_EXPAND,
 } eAnimChannel_Settings;
 
 /* defines for setting animation-channel flags */
 EnumPropertyItem prop_animchannel_setflag_types[] = {
-       {ACHANNEL_SETFLAG_CLEAR, "DISABLE", "Disable", ""},
-       {ACHANNEL_SETFLAG_ADD, "ENABLE", "Enable", ""},
-       {ACHANNEL_SETFLAG_TOGGLE, "TOGGLE", "Toggle", ""},
-       {0, NULL, NULL, NULL}
+       {ACHANNEL_SETFLAG_CLEAR, "DISABLE", 0, "Disable", ""},
+       {ACHANNEL_SETFLAG_ADD, "ENABLE", 0, "Enable", ""},
+       {ACHANNEL_SETFLAG_TOGGLE, "TOGGLE", 0, "Toggle", ""},
+       {0, NULL, 0, NULL, NULL}
 };
 
 /* defines for set animation-channel settings */
 EnumPropertyItem prop_animchannel_settings_types[] = {
-       {ACHANNEL_SETTING_PROTECT, "PROTECT", "Protect", ""},
-       {ACHANNEL_SETTING_MUTE, "MUTE", "Mute", ""},
-       {0, NULL, NULL, NULL}
+       {ACHANNEL_SETTING_PROTECT, "PROTECT", 0, "Protect", ""},
+       {ACHANNEL_SETTING_MUTE, "MUTE", 0, "Mute", ""},
+       {0, NULL, 0, NULL, NULL}
 };
 
 
 /* ------------------- */
 
+/* macro to be used in setflag_anim_channels */
+#define ASUBCHANNEL_SEL_OK(ale) ( (onlysel == 0) || \
+               ((ale->id) && (GS(ale->id->name)==ID_OB) && (((Object *)ale->id)->flag & SELECT)) ) 
+
 /* Set/clear a particular flag (setting) for all selected + visible channels 
  *     setting: the setting to modify
  *     mode: eAnimChannels_SetFlag
+ *     onlysel: only selected channels get the flag set
  */
-static void setflag_anim_channels (bAnimContext *ac, short setting, short mode)
+static void setflag_anim_channels (bAnimContext *ac, short setting, short mode, short onlysel)
 {
        ListBase anim_data = {NULL, NULL};
        bAnimListElem *ale;
        int filter;
        
        /* filter data */
-       filter= (ANIMFILTER_VISIBLE | ANIMFILTER_CHANNELS | ANIMFILTER_SEL);
+       filter= (ANIMFILTER_VISIBLE | ANIMFILTER_CHANNELS);
+       if (onlysel) filter |= ANIMFILTER_SEL;
        ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
        
        /* affect selected channels */
        for (ale= anim_data.first; ale; ale= ale->next) {
                switch (ale->type) {
+                       case ANIMTYPE_OBJECT:
+                       {
+                               Base *base= (Base *)ale->data;
+                               Object *ob= base->object;
+                               
+                               if (setting == ACHANNEL_SETTING_EXPAND) {
+                                       // XXX - settings should really be moved out of ob->nlaflag
+                                       if (mode == ACHANNEL_SETFLAG_TOGGLE)    ob->nlaflag ^= OB_ADS_COLLAPSED;
+                                       else if (mode == ACHANNEL_SETFLAG_ADD)  ob->nlaflag &= ~OB_ADS_COLLAPSED;
+                                       else                                                                    ob->nlaflag |= OB_ADS_COLLAPSED;
+                               }
+                       }
+                               break;
+                       
+                       case ANIMTYPE_FILLACTD:
+                       {
+                               bAction *act= (bAction *)ale->data;
+                               
+                               if (ASUBCHANNEL_SEL_OK(ale)) {
+                                       if (setting == ACHANNEL_SETTING_EXPAND) {
+                                               ACHANNEL_SET_FLAG_NEG(act, mode, ACT_COLLAPSED);
+                                       }
+                               }
+                       }
+                               break;
+                       case ANIMTYPE_FILLDRIVERS:
+                       {
+                               AnimData *adt= (AnimData *)ale->data;
+                               
+                               if (ASUBCHANNEL_SEL_OK(ale)) {
+                                       if (setting == ACHANNEL_SETTING_EXPAND) {
+                                               ACHANNEL_SET_FLAG_NEG(adt, mode, ADT_DRIVERS_COLLAPSED);
+                                       }
+                               }
+                       }
+                               break;
+                       case ANIMTYPE_FILLMATD:
+                       {
+                               Object *ob= (Object *)ale->data;
+                               
+                               // XXX - settings should really be moved out of ob->nlaflag
+                               if ((onlysel == 0) || (ob->flag & SELECT)) {
+                                       if (setting == ACHANNEL_SETTING_EXPAND) {
+                                               if (mode == ACHANNEL_SETFLAG_TOGGLE)    ob->nlaflag ^= OB_ADS_SHOWMATS;
+                                               else if (mode == ACHANNEL_SETFLAG_ADD)  ob->nlaflag |= OB_ADS_SHOWMATS;
+                                               else                                                                    ob->nlaflag &= ~OB_ADS_SHOWMATS;
+                                       }
+                               }
+                       }
+                               break;
+                                       
+                       case ANIMTYPE_DSMAT:
+                       {
+                               Material *ma= (Material *)ale->data;
+                               
+                               if (ASUBCHANNEL_SEL_OK(ale)) {
+                                       if (setting == ACHANNEL_SETTING_EXPAND) {
+                                               ACHANNEL_SET_FLAG(ma, mode, MA_DS_EXPAND);
+                                       }
+                               }
+                       }
+                               break;
+                       case ANIMTYPE_DSLAM:
+                       {
+                               Lamp *la= (Lamp *)ale->data;
+                               
+                               if (ASUBCHANNEL_SEL_OK(ale)) {
+                                       if (setting == ACHANNEL_SETTING_EXPAND) {
+                                               ACHANNEL_SET_FLAG(la, mode, LA_DS_EXPAND);
+                                       }
+                               }
+                       }
+                               break;
+                       case ANIMTYPE_DSCAM:
+                       {
+                               Camera *ca= (Camera *)ale->data;
+                               
+                               if (ASUBCHANNEL_SEL_OK(ale)) {
+                                       if (setting == ACHANNEL_SETTING_EXPAND) {
+                                               ACHANNEL_SET_FLAG(ca, mode, CAM_DS_EXPAND);
+                                       }
+                               }
+                       }
+                               break;
+                       case ANIMTYPE_DSCUR:
+                       {
+                               Curve *cu= (Curve *)ale->data;
+                               
+                               if (ASUBCHANNEL_SEL_OK(ale)) {
+                                       if (setting == ACHANNEL_SETTING_EXPAND) {
+                                               ACHANNEL_SET_FLAG(cu, mode, CU_DS_EXPAND);
+                                       }
+                               }
+                       }
+                               break;
+                       case ANIMTYPE_DSSKEY:
+                       {
+                               Key *key= (Key *)ale->data;
+                               
+                               if (ASUBCHANNEL_SEL_OK(ale)) {
+                                       if (setting == ACHANNEL_SETTING_EXPAND) {
+                                               ACHANNEL_SET_FLAG(key, mode, KEYBLOCK_DS_EXPAND);
+                                       }
+                               }
+                       }
+                               break;
+                       case ANIMTYPE_DSWOR:
+                       {
+                               World *wo= (World *)ale->data;
+                               
+                               if (ASUBCHANNEL_SEL_OK(ale)) {
+                                       if (setting == ACHANNEL_SETTING_EXPAND) {
+                                               ACHANNEL_SET_FLAG(wo, mode, WO_DS_EXPAND);
+                                       }
+                               }
+                       }
+                               break;
+                               
                        case ANIMTYPE_GROUP:
                        {
                                bActionGroup *agrp= (bActionGroup *)ale->data;
                                
-                               /* only 'protect' is available */
-                               if (setting == ACHANNEL_SETTING_PROTECT) {
-                                       ACHANNEL_SET_FLAG(agrp, mode, AGRP_PROTECTED);
+                               switch (setting) {
+                                       case ACHANNEL_SETTING_PROTECT:
+                                               ACHANNEL_SET_FLAG(agrp, mode, AGRP_PROTECTED);
+                                               break;
+                                       case ACHANNEL_SETTING_EXPAND:
+                                               ACHANNEL_SET_FLAG(agrp, mode, AGRP_EXPANDED);
+                                               break;
                                }
                        }
                                break;
@@ -723,15 +896,16 @@ static void setflag_anim_channels (bAnimContext *ac, short setting, short mode)
                        {
                                FCurve *fcu= (FCurve *)ale->data;
                                
-                               /* mute */
-                               if (setting == ACHANNEL_SETTING_MUTE) {
-                                       ACHANNEL_SET_FLAG(fcu, mode, FCURVE_MUTED);
-                               }
-                               else if (setting == ACHANNEL_SETTING_PROTECT) {
-                                       ACHANNEL_SET_FLAG(fcu, mode, FCURVE_PROTECTED);
-                               }
-                               else if (setting == ACHANNEL_SETTING_VISIBLE) {
-                                       ACHANNEL_SET_FLAG(fcu, mode, FCURVE_VISIBLE);
+                               switch (setting) {
+                                       case ACHANNEL_SETTING_MUTE:
+                                               ACHANNEL_SET_FLAG(fcu, mode, FCURVE_MUTED);
+                                               break;
+                                       case ACHANNEL_SETTING_PROTECT:
+                                               ACHANNEL_SET_FLAG(fcu, mode, FCURVE_PROTECTED);
+                                               break;
+                                       case ACHANNEL_SETTING_VISIBLE:
+                                               ACHANNEL_SET_FLAG(fcu, mode, FCURVE_VISIBLE);
+                                               break;
                                }
                        }
                                break;
@@ -739,14 +913,13 @@ static void setflag_anim_channels (bAnimContext *ac, short setting, short mode)
                        {
                                bGPDlayer *gpl= (bGPDlayer *)ale->data;
                                
-                               /* 'protect' and 'mute' */
-                               if (setting == ACHANNEL_SETTING_MUTE) {
-                                       /* mute */
-                                       ACHANNEL_SET_FLAG(gpl, mode, GP_LAYER_HIDE);
-                               }
-                               else if (setting == ACHANNEL_SETTING_PROTECT) {
-                                       /* protected */
-                                       ACHANNEL_SET_FLAG(gpl, mode, GP_LAYER_LOCKED);
+                               switch (setting) {
+                                       case ACHANNEL_SETTING_MUTE:
+                                               ACHANNEL_SET_FLAG(gpl, mode, GP_LAYER_HIDE);
+                                               break;
+                                       case ACHANNEL_SETTING_PROTECT:
+                                               ACHANNEL_SET_FLAG(gpl, mode, GP_LAYER_LOCKED);
+                                               break;
                                }
                        }
                                break;
@@ -772,7 +945,7 @@ static int animchannels_setflag_exec(bContext *C, wmOperator *op)
        setting= RNA_enum_get(op->ptr, "type");
        
        /* modify setting */
-       setflag_anim_channels(&ac, setting, mode);
+       setflag_anim_channels(&ac, setting, mode, 1);
        
        /* set notifier tha things have changed */
        ANIM_animdata_send_notifiers(C, &ac, ANIM_CHANGED_CHANNELS);
@@ -781,11 +954,11 @@ static int animchannels_setflag_exec(bContext *C, wmOperator *op)
 }
 
 
-void ANIM_OT_channels_enable_setting (wmOperatorType *ot)
+void ANIM_OT_channels_setting_enable (wmOperatorType *ot)
 {
        /* identifiers */
        ot->name= "Enable Channel Setting";
-       ot->idname= "ANIM_OT_channels_enable_setting";
+       ot->idname= "ANIM_OT_channels_setting_enable";
        
        /* api callbacks */
        ot->invoke= WM_menu_invoke;
@@ -802,11 +975,11 @@ void ANIM_OT_channels_enable_setting (wmOperatorType *ot)
        RNA_def_enum(ot->srna, "type", prop_animchannel_settings_types, 0, "Type", "");
 }
 
-void ANIM_OT_channels_disable_setting (wmOperatorType *ot)
+void ANIM_OT_channels_setting_disable (wmOperatorType *ot)
 {
        /* identifiers */
        ot->name= "Disable Channel Setting";
-       ot->idname= "ANIM_OT_channels_disable_setting";
+       ot->idname= "ANIM_OT_channels_setting_disable";
        
        /* api callbacks */
        ot->invoke= WM_menu_invoke;
@@ -823,11 +996,11 @@ void ANIM_OT_channels_disable_setting (wmOperatorType *ot)
        RNA_def_enum(ot->srna, "type", prop_animchannel_settings_types, 0, "Type", "");
 }
 
-void ANIM_OT_channels_toggle_setting (wmOperatorType *ot)
+void ANIM_OT_channels_setting_toggle (wmOperatorType *ot)
 {
        /* identifiers */
        ot->name= "Toggle Channel Setting";
-       ot->idname= "ANIM_OT_channels_toggle_setting";
+       ot->idname= "ANIM_OT_channels_setting_toggle";
        
        /* api callbacks */
        ot->invoke= WM_menu_invoke;
@@ -844,6 +1017,109 @@ void ANIM_OT_channels_toggle_setting (wmOperatorType *ot)
        RNA_def_enum(ot->srna, "type", prop_animchannel_settings_types, 0, "Type", "");
 }
 
+// XXX currently, this is a separate operator, but perhaps we could in future specify in keymaps whether to call invoke or exec?
+void ANIM_OT_channels_editable_toggle (wmOperatorType *ot)
+{
+       /* identifiers */
+       ot->name= "Toggle Channel Editability";
+       ot->idname= "ANIM_OT_channels_editable_toggle";
+       
+       /* api callbacks */
+       ot->exec= animchannels_setflag_exec;
+       ot->poll= ED_operator_areaactive;
+       
+       /* flags */
+       ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+       
+       /* props */
+               /* 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", "");
+}
+
+/* ********************** Expand Channels Operator *********************** */
+
+static int animchannels_expand_exec (bContext *C, wmOperator *op)
+{
+       bAnimContext ac;
+       short onlysel= 1;
+       
+       /* get editor data */
+       if (ANIM_animdata_get_context(C, &ac) == 0)
+               return OPERATOR_CANCELLED;
+               
+       /* only affect selected channels? */
+       if (RNA_boolean_get(op->ptr, "all"))
+               onlysel= 0;
+       
+       /* modify setting */
+       setflag_anim_channels(&ac, ACHANNEL_SETTING_EXPAND, ACHANNEL_SETFLAG_ADD, onlysel);
+       
+       /* set notifier that things have changed */
+       ANIM_animdata_send_notifiers(C, &ac, ANIM_CHANGED_CHANNELS);
+       
+       return OPERATOR_FINISHED;
+}
+
+void ANIM_OT_channels_expand (wmOperatorType *ot)
+{
+       /* identifiers */
+       ot->name= "Expand Channels";
+       ot->idname= "ANIM_OT_channels_expand";
+       
+       /* api callbacks */
+       ot->exec= animchannels_expand_exec;
+       ot->poll= ED_operator_areaactive;
+       
+       /* flags */
+       ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+       
+       /* props */
+       RNA_def_boolean(ot->srna, "all", 0, "All", "Expand all channels (not just selected ones)");
+}
+
+/* ********************** Collapse Channels Operator *********************** */
+
+static int animchannels_collapse_exec (bContext *C, wmOperator *op)
+{
+       bAnimContext ac;
+       short onlysel= 1;
+       
+       /* get editor data */
+       if (ANIM_animdata_get_context(C, &ac) == 0)
+               return OPERATOR_CANCELLED;
+               
+       /* only affect selected channels? */
+       if (RNA_boolean_get(op->ptr, "all"))
+               onlysel= 0;
+       
+       /* modify setting */
+       setflag_anim_channels(&ac, ACHANNEL_SETTING_EXPAND, ACHANNEL_SETFLAG_CLEAR, onlysel);
+       
+       /* set notifier that things have changed */
+       ANIM_animdata_send_notifiers(C, &ac, ANIM_CHANGED_CHANNELS);
+       
+       return OPERATOR_FINISHED;
+}
+
+void ANIM_OT_channels_collapse (wmOperatorType *ot)
+{
+       /* identifiers */
+       ot->name= "Collapse Channels";
+       ot->idname= "ANIM_OT_channels_collapse";
+       
+       /* api callbacks */
+       ot->exec= animchannels_collapse_exec;
+       ot->poll= ED_operator_areaactive;
+       
+       /* flags */
+       ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+       
+       /* props */
+       RNA_def_boolean(ot->srna, "all", 0, "All", "Collapse all channels (not just selected ones)");
+}
+
 /* ********************** Select All Operator *********************** */
 
 static int animchannels_deselectall_exec(bContext *C, wmOperator *op)
@@ -866,11 +1142,11 @@ static int animchannels_deselectall_exec(bContext *C, wmOperator *op)
        return OPERATOR_FINISHED;
 }
  
-void ANIM_OT_channels_deselectall (wmOperatorType *ot)
+void ANIM_OT_channels_select_all_toggle (wmOperatorType *ot)
 {
        /* identifiers */
        ot->name= "Select All";
-       ot->idname= "ANIM_OT_channels_deselectall";
+       ot->idname= "ANIM_OT_channels_select_all_toggle";
        
        /* api callbacks */
        ot->exec= animchannels_deselectall_exec;
@@ -984,11 +1260,11 @@ static int animchannels_borderselect_exec(bContext *C, wmOperator *op)
        return OPERATOR_FINISHED;
 } 
 
-void ANIM_OT_channels_borderselect(wmOperatorType *ot)
+void ANIM_OT_channels_select_border(wmOperatorType *ot)
 {
        /* identifiers */
        ot->name= "Border Select";
-       ot->idname= "ANIM_OT_channels_borderselect";
+       ot->idname= "ANIM_OT_channels_select_border";
        
        /* api callbacks */
        ot->invoke= WM_border_select_invoke;
@@ -1046,6 +1322,26 @@ static void mouse_anim_channels (bAnimContext *ac, float x, int channel_index, s
        
        /* action to take depends on what channel we've got */
        switch (ale->type) {
+               case ANIMTYPE_SCENE:
+               {
+                       Scene *sce= (Scene *)ale->data;
+                       
+                       if (x < 16) {
+                               /* toggle expand */
+                               sce->flag ^= SCE_DS_COLLAPSED;
+                       }
+                       else {
+                               /* set selection status */
+                               if (selectmode == SELECT_INVERT) {
+                                       /* swap select */
+                                       sce->flag ^= SCE_DS_SELECTED;
+                               }
+                               else {
+                                       sce->flag |= SCE_DS_SELECTED;
+                               }
+                       }
+               }
+                       break;
                case ANIMTYPE_OBJECT:
                {
                        bDopeSheet *ads= (bDopeSheet *)ac->data;
@@ -1132,11 +1428,17 @@ static void mouse_anim_channels (bAnimContext *ac, float x, int channel_index, s
                        key->flag ^= KEYBLOCK_DS_EXPAND;
                }
                        break;
+               case ANIMTYPE_DSWOR:
+               {
+                       World *wo= (World *)ale->data;
+                       wo->flag ^= WO_DS_EXPAND;
+               }
+                       break;
                        
                case ANIMTYPE_GROUP: 
                {
                        bActionGroup *agrp= (bActionGroup *)ale->data;
-                       short offset= (ac->datatype == ANIMCONT_DOPESHEET)? 18 : 0;
+                       short offset= (ELEM3(ac->datatype, ANIMCONT_DOPESHEET, ANIMCONT_FCURVES, ANIMCONT_DRIVERS))? 18 : 0;
                        
                        if ((x < (offset+17)) && (agrp->channels.first)) {
                                /* toggle expand */
@@ -1154,14 +1456,14 @@ static void mouse_anim_channels (bAnimContext *ac, float x, int channel_index, s
                                }
                                else if (selectmode == -1) {
                                        /* select all in group (and deselect everthing else) */ 
-                                       bActionChannel *achan;
+                                       FCurve *fcu;
                                        
                                        /* deselect all other channels */
                                        ANIM_deselect_anim_channels(ac->data, ac->datatype, 0, ACHANNEL_SETFLAG_CLEAR);
                                        
                                        /* only select channels in group and group itself */
-                                       for (achan= agrp->channels.first; achan && achan->grp==agrp; achan= achan->next)
-                                               achan->flag |= ACHAN_SELECTED;
+                                       for (fcu= agrp->channels.first; fcu && fcu->grp==agrp; fcu= fcu->next)
+                                               fcu->flag |= FCURVE_SELECTED;
                                        agrp->flag |= AGRP_SELECTED;                                    
                                }
                                else {
@@ -1170,22 +1472,31 @@ static void mouse_anim_channels (bAnimContext *ac, float x, int channel_index, s
                                        agrp->flag |= AGRP_SELECTED;
                                }
                                
-                               /* if group is selected now, and we're in Action Editor mode (so that we have pointer to active action),
-                                * we can make this group the 'active' one in that action
-                                */
-                               if ((agrp->flag & AGRP_SELECTED) && (ac->datatype == ANIMCONT_ACTION))
-                                       action_set_active_agrp((bAction *)ac->data, agrp);
+                               /* if group is selected now, make group the 'active' one in the visible list */
+                               if (agrp->flag & AGRP_SELECTED)
+                                       ANIM_set_active_channel(ac->data, ac->datatype, filter, agrp, ANIMTYPE_GROUP);
                        }
                }
                        break;
                case ANIMTYPE_FCURVE: 
                {
                        FCurve *fcu= (FCurve *)ale->data;
-                       short offset= (ac->datatype != ANIMCONT_ACTION)? 18 : 0;
+                       short offset;
+                       
+                       if (ac->datatype != ANIMCONT_ACTION) {
+                               /* for now, special case for materials */
+                               if (ale->ownertype == ANIMTYPE_DSMAT)
+                                       offset= 21;
+                               else
+                                       offset= 18;
+                       }
+                       else
+                               offset = 0;
                        
                        if (x >= (ACHANNEL_NAMEWIDTH-ACHANNEL_BUTTON_WIDTH)) {
-                               /* toggle protection */
-                               fcu->flag ^= FCURVE_PROTECTED;
+                               /* toggle protection (only if there's a toggle there) */
+                               if (fcu->bezt)
+                                       fcu->flag ^= FCURVE_PROTECTED;
                        }
                        else if (x >= (ACHANNEL_NAMEWIDTH-2*ACHANNEL_BUTTON_WIDTH)) {
                                /* toggle mute */
@@ -1193,17 +1504,23 @@ static void mouse_anim_channels (bAnimContext *ac, float x, int channel_index, s
                        }
                        else if ((x < (offset+17)) && (ac->spacetype==SPACE_IPO)) {
                                /* toggle visibility */
-                               // XXX this is supposed to be button before name, though this sometimes fails
                                fcu->flag ^= FCURVE_VISIBLE;
                        }
                        else {
                                /* select/deselect */
-                               fcu->flag ^= FCURVE_SELECTED;
+                               if (selectmode == SELECT_INVERT) {
+                                       /* inverse selection status of this F-Curve only */
+                                       fcu->flag ^= FCURVE_SELECTED;
+                               }
+                               else {
+                                       /* select F-Curve by itself */
+                                       ANIM_deselect_anim_channels(ac->data, ac->datatype, 0, 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)
-                                       fcu->flag |= FCURVE_ACTIVE;
-                               else
-                                       fcu->flag &= ~FCURVE_ACTIVE;
+                                       ANIM_set_active_channel(ac->data, ac->datatype, filter, fcu, ANIMTYPE_FCURVE);
                        }
                }
                        break;
@@ -1281,9 +1598,9 @@ static int animchannels_mouseclick_invoke(bContext *C, wmOperator *op, wmEvent *
        mval[1]= (event->y - ar->winrct.ymin);
        
        /* select mode is either replace (deselect all, then add) or add/extend */
-       if (RNA_boolean_get(op->ptr, "extend_select"))
+       if (RNA_boolean_get(op->ptr, "extend"))
                selectmode= SELECT_INVERT;
-       else if (RNA_boolean_get(op->ptr, "select_children_only"))
+       else if (RNA_boolean_get(op->ptr, "children_only"))
                selectmode= -1; /* this is a bit of a special case for ActionGroups only... should it be removed or extended to all instead? */
        else
                selectmode= SELECT_REPLACE;
@@ -1305,11 +1622,11 @@ static int animchannels_mouseclick_invoke(bContext *C, wmOperator *op, wmEvent *
        return OPERATOR_FINISHED;
 }
  
-void ANIM_OT_channels_mouseclick (wmOperatorType *ot)
+void ANIM_OT_channels_click (wmOperatorType *ot)
 {
        /* identifiers */
        ot->name= "Mouse Click on Channels";
-       ot->idname= "ANIM_OT_channels_mouseclick";
+       ot->idname= "ANIM_OT_channels_click";
        
        /* api callbacks */
        ot->invoke= animchannels_mouseclick_invoke;
@@ -1319,8 +1636,8 @@ void ANIM_OT_channels_mouseclick (wmOperatorType *ot)
        ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
        
        /* id-props */
-       RNA_def_boolean(ot->srna, "extend_select", 0, "Extend Select", ""); // SHIFTKEY
-       RNA_def_boolean(ot->srna, "select_children_only", 0, "Select Children Only", ""); // CTRLKEY|SHIFTKEY
+       RNA_def_boolean(ot->srna, "extend", 0, "Extend Select", ""); // SHIFTKEY
+       RNA_def_boolean(ot->srna, "children_only", 0, "Select Children Only", ""); // CTRLKEY|SHIFTKEY
 }
 
 /* ************************************************************************** */
@@ -1328,13 +1645,16 @@ void ANIM_OT_channels_mouseclick (wmOperatorType *ot)
 
 void ED_operatortypes_animchannels(void)
 {
-       WM_operatortype_append(ANIM_OT_channels_deselectall);
-       WM_operatortype_append(ANIM_OT_channels_borderselect);
-       WM_operatortype_append(ANIM_OT_channels_mouseclick);
+       WM_operatortype_append(ANIM_OT_channels_select_all_toggle);
+       WM_operatortype_append(ANIM_OT_channels_select_border);
+       WM_operatortype_append(ANIM_OT_channels_click);
        
-       WM_operatortype_append(ANIM_OT_channels_enable_setting);
-       WM_operatortype_append(ANIM_OT_channels_disable_setting);
-       WM_operatortype_append(ANIM_OT_channels_toggle_setting);
+       WM_operatortype_append(ANIM_OT_channels_setting_enable);
+       WM_operatortype_append(ANIM_OT_channels_setting_disable);
+       WM_operatortype_append(ANIM_OT_channels_setting_toggle);
+       
+               // XXX does this need to be a separate operator?
+       WM_operatortype_append(ANIM_OT_channels_editable_toggle);
        
                // XXX these need to be updated for new system... todo...
        //WM_operatortype_append(ANIM_OT_channels_move_up);
@@ -1342,6 +1662,9 @@ void ED_operatortypes_animchannels(void)
        //WM_operatortype_append(ANIM_OT_channels_move_top);
        //WM_operatortype_append(ANIM_OT_channels_move_bottom);
        
+       WM_operatortype_append(ANIM_OT_channels_expand);
+       WM_operatortype_append(ANIM_OT_channels_collapse);
+       
        WM_operatortype_append(ANIM_OT_channels_visibility_toggle);
 }
 
@@ -1352,21 +1675,31 @@ void ED_keymap_animchannels(wmWindowManager *wm)
        /* selection */
                /* click-select */
                // XXX for now, only leftmouse.... 
-       WM_keymap_add_item(keymap, "ANIM_OT_channels_mouseclick", LEFTMOUSE, KM_PRESS, 0, 0);
-       RNA_boolean_set(WM_keymap_add_item(keymap, "ANIM_OT_channels_mouseclick", LEFTMOUSE, KM_PRESS, KM_SHIFT, 0)->ptr, "extend_select", 1);
-       RNA_boolean_set(WM_keymap_add_item(keymap, "ANIM_OT_channels_mouseclick", LEFTMOUSE, KM_PRESS, KM_CTRL|KM_SHIFT, 0)->ptr, "select_children_only", 1);
+       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", 1);
+       RNA_boolean_set(WM_keymap_add_item(keymap, "ANIM_OT_channels_click", LEFTMOUSE, KM_PRESS, KM_CTRL|KM_SHIFT, 0)->ptr, "children_only", 1);
        
                /* deselect all */
-       WM_keymap_add_item(keymap, "ANIM_OT_channels_deselectall", AKEY, KM_PRESS, 0, 0);
-       RNA_boolean_set(WM_keymap_add_item(keymap, "ANIM_OT_channels_deselectall", IKEY, KM_PRESS, KM_CTRL, 0)->ptr, "invert", 1);
+       WM_keymap_add_item(keymap, "ANIM_OT_channels_select_all_toggle", AKEY, KM_PRESS, 0, 0);
+       RNA_boolean_set(WM_keymap_add_item(keymap, "ANIM_OT_channels_select_all_toggle", IKEY, KM_PRESS, KM_CTRL, 0)->ptr, "invert", 1);
        
                /* borderselect */
-       WM_keymap_add_item(keymap, "ANIM_OT_channels_borderselect", BKEY, KM_PRESS, 0, 0);
+       WM_keymap_add_item(keymap, "ANIM_OT_channels_select_border", BKEY, KM_PRESS, 0, 0);
        
        /* settings */
-       WM_keymap_add_item(keymap, "ANIM_OT_channels_toggle_setting", WKEY, KM_PRESS, KM_SHIFT, 0);
-       WM_keymap_add_item(keymap, "ANIM_OT_channels_enable_setting", WKEY, KM_PRESS, KM_CTRL|KM_SHIFT, 0);
-       WM_keymap_add_item(keymap, "ANIM_OT_channels_disable_setting", WKEY, KM_PRESS, KM_ALT, 0);
+       WM_keymap_add_item(keymap, "ANIM_OT_channels_setting_toggle", WKEY, KM_PRESS, KM_SHIFT, 0);
+       WM_keymap_add_item(keymap, "ANIM_OT_channels_setting_enable", WKEY, KM_PRESS, KM_CTRL|KM_SHIFT, 0);
+       WM_keymap_add_item(keymap, "ANIM_OT_channels_setting_disable", WKEY, KM_PRESS, KM_ALT, 0);
+       
+       /* settings - specialised hotkeys */
+       WM_keymap_add_item(keymap, "ANIM_OT_channels_editable_toggle", TABKEY, KM_PRESS, 0, 0);
+       
+       /* expand/collapse */
+       WM_keymap_add_item(keymap, "ANIM_OT_channels_expand", PADPLUSKEY, KM_PRESS, 0, 0);
+       WM_keymap_add_item(keymap, "ANIM_OT_channels_collapse", PADMINUS, KM_PRESS, 0, 0);
+       
+       RNA_boolean_set(WM_keymap_add_item(keymap, "ANIM_OT_channels_expand", PADPLUSKEY, KM_PRESS, KM_CTRL, 0)->ptr, "all", 1);
+       RNA_boolean_set(WM_keymap_add_item(keymap, "ANIM_OT_channels_collapse", PADMINUS, KM_PRESS, KM_CTRL, 0)->ptr, "all", 1);
        
        /* rearranging - actions only */
        //WM_keymap_add_item(keymap, "ANIM_OT_channels_move_up", PAGEUPKEY, KM_PRESS, KM_SHIFT, 0);