Bugfix #20664: NLA Editor - Hiding stuff causes instant crash ..
[blender.git] / source / blender / editors / animation / anim_channels_edit.c
index e4ce26b615600cf2f917e9cc8bcb3c8a5246eb71..f756c5b3158640025e37b1ed4f0a92271c975af8 100644 (file)
@@ -86,9 +86,9 @@
 #include "WM_types.h"
 
 /* ************************************************************************** */
-/* CHANNELS API */
+/* CHANNELS API - Exposed API */
 
-/* -------------------------- Exposed API ----------------------------------- */
+/* -------------------------- Selection ------------------------------------- */
 
 /* Set the given animation-channel as the active one for the active context */
 // TODO: extend for animdata types...
@@ -142,6 +142,7 @@ void ANIM_set_active_channel (bAnimContext *ac, void *data, short datatype, int
                        case ANIMTYPE_DSPART:
                        case ANIMTYPE_DSMBALL:
                        case ANIMTYPE_DSARM:
+                       case ANIMTYPE_DSMESH:
                        {
                                /* need to verify that this data is valid for now */
                                if (ale->adt) {
@@ -184,6 +185,7 @@ void ANIM_set_active_channel (bAnimContext *ac, void *data, short datatype, int
                        case ANIMTYPE_DSPART:
                        case ANIMTYPE_DSMBALL:
                        case ANIMTYPE_DSARM:
+                       case ANIMTYPE_DSMESH:
                        {
                                /* need to verify that this data is valid for now */
                                if (ale->adt)
@@ -203,7 +205,7 @@ void ANIM_set_active_channel (bAnimContext *ac, void *data, short datatype, int
  *     - test: check if deselecting instead of selecting
  *     - sel: eAnimChannels_SetFlag;
  */
-void ANIM_deselect_anim_channels (void *data, short datatype, short test, short sel)
+void ANIM_deselect_anim_channels (bAnimContext *ac, void *data, short datatype, short test, short sel)
 {
        ListBase anim_data = {NULL, NULL};
        bAnimListElem *ale;
@@ -211,7 +213,7 @@ void ANIM_deselect_anim_channels (void *data, short datatype, short test, short
        
        /* filter data */
        filter= ANIMFILTER_VISIBLE|ANIMFILTER_CHANNELS;
-       ANIM_animdata_filter(NULL, &anim_data, filter, data, datatype);
+       ANIM_animdata_filter(ac, &anim_data, filter, data, datatype);
        
        /* See if we should be selecting or deselecting */
        if (test) {
@@ -257,6 +259,7 @@ void ANIM_deselect_anim_channels (void *data, short datatype, short test, short
                                case ANIMTYPE_DSPART:
                                case ANIMTYPE_DSMBALL:
                                case ANIMTYPE_DSARM:
+                               case ANIMTYPE_DSMESH:
                                {
                                        if ((ale->adt) && (ale->adt->flag & ADT_UI_SELECTED))
                                                sel= ACHANNEL_SETFLAG_CLEAR;
@@ -337,6 +340,7 @@ void ANIM_deselect_anim_channels (void *data, short datatype, short test, short
                        case ANIMTYPE_DSPART:
                        case ANIMTYPE_DSMBALL:
                        case ANIMTYPE_DSARM:
+                       case ANIMTYPE_DSMESH:
                        {
                                /* need to verify that this data is valid for now */
                                if (ale->adt) {
@@ -352,19 +356,26 @@ void ANIM_deselect_anim_channels (void *data, short datatype, short test, short
        BLI_freelistN(&anim_data);
 }
 
+/* ---------------------------- Graph Editor ------------------------------------- */
+
 /* Flush visibility (for Graph Editor) changes up/down hierarchy for changes in the given setting 
  *     - anim_data: list of the all the anim channels that can be chosen
  *             -> filtered using ANIMFILTER_CHANNELS only, since if we took VISIBLE too,
  *               then the channels under closed expanders get ignored...
  *     - ale_setting: the anim channel (not in the anim_data list directly, though occuring there)
  *             with the new state of the setting that we want flushed up/down the hierarchy 
- *     - vizOn: whether the visibility setting has been enabled or disabled 
+ *     - setting: type of setting to set
+ *     - on: whether the visibility setting has been enabled or disabled 
  */
-void ANIM_visibility_flush_anim_channels (bAnimContext *ac, ListBase *anim_data, bAnimListElem *ale_setting, short vizOn)
+void ANIM_flush_setting_anim_channels (bAnimContext *ac, ListBase *anim_data, bAnimListElem *ale_setting, int setting, short on)
 {
        bAnimListElem *ale, *match=NULL;
        int prevLevel=0, matchLevel=0;
        
+       /* sanity check */
+       if (ELEM(NULL, anim_data, anim_data->first))
+               return;
+       
        /* find the channel that got changed */
        for (ale= anim_data->first; ale; ale= ale->next) {
                /* compare data, and type as main way of identifying the channel */
@@ -384,22 +395,40 @@ void ANIM_visibility_flush_anim_channels (bAnimContext *ac, ListBase *anim_data,
        else {
                bAnimChannelType *acf= ANIM_channel_get_typeinfo(ale_setting);
                
+               if (acf == NULL) {
+                       printf("ERROR: no channel info for the changed channel \n");
+                       return;
+               }
+               
                /* get the level of the channel that was affected
                 *       - we define the level as simply being the offset for the start of the channel
                 */
                matchLevel= (acf->get_offset)? acf->get_offset(ac, ale_setting) : 0;
+               prevLevel= matchLevel;
        }
        
        /* flush up? 
-        *      - only flush up if the current state is now enabled 
+        *
+        * For Visibility:
+        *      - only flush up if the current state is now enabled (positive 'on' state is default) 
         *        (otherwise, it's too much work to force the parents to be inactive too)
+        *
+        * For everything else:
+        *      - only flush up if the current state is now disabled (negative 'off' state is default)
+        *        (otherwise, it's too much work to force the parents to be active too)
         */
-       if (vizOn) {
+       if ( ((setting == ACHANNEL_SETTING_VISIBLE) && on) ||
+                ((setting != ACHANNEL_SETTING_VISIBLE) && on==0) )
+       {
                /* 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);
                        int level;
                        
+                       /* if no channel info was found, skip, since this type might not have any useful info */
+                       if (acf == NULL)
+                               continue;
+                       
                        /* get the level of the current channel traversed 
                         *       - we define the level as simply being the offset for the start of the channel
                         */
@@ -410,7 +439,7 @@ void ANIM_visibility_flush_anim_channels (bAnimContext *ac, ListBase *anim_data,
                         * when toggling visibility of F-Curves, gets flushed), flush the new status...
                         */
                        if (level < prevLevel)
-                               ANIM_channel_setting_set(ac, ale, ACHANNEL_SETTING_VISIBLE, vizOn);
+                               ANIM_channel_setting_set(ac, ale, setting, on);
                        /* however, if the level is 'greater than' (i.e. less important than the previous channel,
                         * stop searching, since we've already reached the bottom of another hierarchy
                         */
@@ -429,6 +458,10 @@ void ANIM_visibility_flush_anim_channels (bAnimContext *ac, ListBase *anim_data,
                        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 */
+                       if (acf == NULL)
+                               continue;
+                       
                        /* get the level of the current channel traversed 
                         *       - we define the level as simply being the offset for the start of the channel
                         */
@@ -438,7 +471,7 @@ void ANIM_visibility_flush_anim_channels (bAnimContext *ac, ListBase *anim_data,
                         * flush the new status...
                         */
                        if (level > matchLevel)
-                               ANIM_channel_setting_set(ac, ale, ACHANNEL_SETTING_VISIBLE, vizOn);
+                               ANIM_channel_setting_set(ac, ale, setting, on);
                        /* however, if the level is 'less than or equal to' the channel that was changed,
                         * (i.e. the current channel is as important if not more important than the changed channel)
                         * then we should stop, since we've found the last one of the children we should flush
@@ -452,6 +485,35 @@ void ANIM_visibility_flush_anim_channels (bAnimContext *ac, ListBase *anim_data,
        }
 }
 
+/* -------------------------- F-Curves ------------------------------------- */
+
+/* Delete the given F-Curve from its AnimData block */
+void ANIM_fcurve_delete_from_animdata (bAnimContext *ac, AnimData *adt, FCurve *fcu)
+{
+       /* - if no AnimData, we've got nowhere to remove the F-Curve from 
+        *      (this doesn't guarantee that the F-Curve is in there, but at least we tried
+        * - if no F-Curve, there is nothing to remove
+        */
+       if (ELEM(NULL, adt, fcu))
+               return;
+               
+       /* remove from whatever list it came from
+        *      - Action Group
+        *      - Action
+        *      - Drivers
+        *      - TODO... some others?
+        */
+       if (fcu->grp)
+               action_groups_remove_channel(adt->action, fcu);
+       else if ((ac) && (ac->datatype == ANIMCONT_DRIVERS))
+               BLI_remlink(&adt->drivers, fcu);
+       else if (adt->action)
+               BLI_remlink(&adt->action->curves, fcu);
+               
+       /* free the F-Curve itself */
+       free_fcurve(fcu);
+}
+
 /* ************************************************************************** */
 /* OPERATORS */
 
@@ -945,28 +1007,8 @@ static int animchannels_delete_exec(bContext *C, wmOperator *op)
                                AnimData *adt= ale->adt;
                                FCurve *fcu= (FCurve *)ale->data;
                                
-                               /* if no AnimData, we've got nowhere to remove the F-Curve from */
-                               if (adt == NULL)
-                                       continue;
-                                       
-                               /* remove from whatever list it came from
-                                *      - Action Group
-                                *      - Action
-                                *      - Drivers
-                                *      - TODO... some others?
-                                *
-                                *      note: this isn't well tested, we could also try remove
-                                *            from all lists just to be safe - campbell
-                                */
-                               if (fcu->grp)
-                                       action_groups_remove_channel(adt->action, fcu);
-                               else if (ac.datatype == ANIMCONT_DRIVERS)
-                                       BLI_remlink(&adt->drivers, fcu);
-                               else if (adt->action)
-                                       BLI_remlink(&adt->action->curves, fcu);
-                                       
-                               /* free the F-Curve itself */
-                               free_fcurve(fcu);
+                               /* try to free F-Curve */
+                               ANIM_fcurve_delete_from_animdata(&ac, adt, fcu);
                        }
                }
                
@@ -1039,7 +1081,7 @@ static int animchannels_visibility_set_exec(bContext *C, wmOperator *op)
                ANIM_channel_setting_set(&ac, ale, ACHANNEL_SETTING_VISIBLE, ACHANNEL_SETFLAG_ADD);
                
                /* now, also flush selection status up/down as appropriate */
-               ANIM_visibility_flush_anim_channels(&ac, &all_data, ale, 1);
+               ANIM_flush_setting_anim_channels(&ac, &all_data, ale, ACHANNEL_SETTING_VISIBLE, 1);
        }
        
        BLI_freelistN(&anim_data);
@@ -1104,11 +1146,16 @@ static int animchannels_visibility_toggle_exec(bContext *C, wmOperator *op)
 
        /* 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_visibility_flush_anim_channels(&ac, &all_data, ale, (vis == ACHANNEL_SETFLAG_ADD));
+               ANIM_flush_setting_anim_channels(&ac, &all_data, ale, ACHANNEL_SETTING_VISIBLE, (vis == ACHANNEL_SETFLAG_ADD));
        }
        
        /* cleanup */
@@ -1230,7 +1277,7 @@ void ANIM_OT_channels_setting_enable (wmOperatorType *ot)
                /* flag-setting mode */
        RNA_def_enum(ot->srna, "mode", prop_animchannel_setflag_types, ACHANNEL_SETFLAG_ADD, "Mode", "");
                /* setting to set */
-       RNA_def_enum(ot->srna, "type", prop_animchannel_settings_types, 0, "Type", "");
+       ot->prop= RNA_def_enum(ot->srna, "type", prop_animchannel_settings_types, 0, "Type", "");
 }
 
 void ANIM_OT_channels_setting_disable (wmOperatorType *ot)
@@ -1252,7 +1299,7 @@ void ANIM_OT_channels_setting_disable (wmOperatorType *ot)
                /* flag-setting mode */
        RNA_def_enum(ot->srna, "mode", prop_animchannel_setflag_types, ACHANNEL_SETFLAG_CLEAR, "Mode", "");
                /* setting to set */
-       RNA_def_enum(ot->srna, "type", prop_animchannel_settings_types, 0, "Type", "");
+       ot->prop= RNA_def_enum(ot->srna, "type", prop_animchannel_settings_types, 0, "Type", "");
 }
 
 void ANIM_OT_channels_setting_toggle (wmOperatorType *ot)
@@ -1274,7 +1321,7 @@ void ANIM_OT_channels_setting_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, 0, "Type", "");
+       ot->prop= 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?
@@ -1338,7 +1385,7 @@ void ANIM_OT_channels_expand (wmOperatorType *ot)
        ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
        
        /* props */
-       RNA_def_boolean(ot->srna, "all", 0, "All", "Expand all channels (not just selected ones)");
+       RNA_def_boolean(ot->srna, "all", 1, "All", "Expand all channels (not just selected ones)");
 }
 
 /* ********************** Collapse Channels Operator *********************** */
@@ -1380,7 +1427,7 @@ void ANIM_OT_channels_collapse (wmOperatorType *ot)
        ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
        
        /* props */
-       RNA_def_boolean(ot->srna, "all", 0, "All", "Collapse all channels (not just selected ones)");
+       RNA_def_boolean(ot->srna, "all", 1, "All", "Collapse all channels (not just selected ones)");
 }
 
 /* ********************** Select All Operator *********************** */
@@ -1395,9 +1442,9 @@ static int animchannels_deselectall_exec(bContext *C, wmOperator *op)
                
        /* 'standard' behaviour - check if selected, then apply relevant selection */
        if (RNA_boolean_get(op->ptr, "invert"))
-               ANIM_deselect_anim_channels(ac.data, ac.datatype, 0, ACHANNEL_SETFLAG_TOGGLE);
+               ANIM_deselect_anim_channels(&ac, ac.data, ac.datatype, 0, ACHANNEL_SETFLAG_TOGGLE);
        else
-               ANIM_deselect_anim_channels(ac.data, ac.datatype, 1, ACHANNEL_SETFLAG_ADD);
+               ANIM_deselect_anim_channels(&ac, ac.data, ac.datatype, 1, ACHANNEL_SETFLAG_ADD);
        
        /* send notifier that things have changed */
        WM_event_add_notifier(C, NC_ANIMATION|ND_ANIMCHAN_SELECT, NULL);
@@ -1635,6 +1682,7 @@ static int mouse_anim_channels (bAnimContext *ac, float x, int channel_index, sh
                case ANIMTYPE_DSPART:
                case ANIMTYPE_DSMBALL:
                case ANIMTYPE_DSARM:
+               case ANIMTYPE_DSMESH:
                {
                        /* sanity checking... */
                        if (ale->adt) {
@@ -1645,7 +1693,7 @@ static int mouse_anim_channels (bAnimContext *ac, float x, int channel_index, sh
                                }
                                else {
                                        /* select AnimData block by itself */
-                                       ANIM_deselect_anim_channels(ac->data, ac->datatype, 0, ACHANNEL_SETFLAG_CLEAR);
+                                       ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, 0, ACHANNEL_SETFLAG_CLEAR);
                                        ale->adt->flag |= ADT_UI_SELECTED;
                                }
                                
@@ -1672,7 +1720,7 @@ static int mouse_anim_channels (bAnimContext *ac, float x, int channel_index, sh
                                FCurve *fcu;
                                
                                /* deselect all other channels */
-                               ANIM_deselect_anim_channels(ac->data, ac->datatype, 0, ACHANNEL_SETFLAG_CLEAR);
+                               ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, 0, ACHANNEL_SETFLAG_CLEAR);
                                
                                /* only select channels in group and group itself */
                                for (fcu= agrp->channels.first; fcu && fcu->grp==agrp; fcu= fcu->next)
@@ -1681,7 +1729,7 @@ static int mouse_anim_channels (bAnimContext *ac, float x, int channel_index, sh
                        }
                        else {
                                /* select group by itself */
-                               ANIM_deselect_anim_channels(ac->data, ac->datatype, 0, ACHANNEL_SETFLAG_CLEAR);
+                               ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, 0, ACHANNEL_SETFLAG_CLEAR);
                                agrp->flag |= AGRP_SELECTED;
                        }
                        
@@ -1703,7 +1751,7 @@ static int mouse_anim_channels (bAnimContext *ac, float x, int channel_index, sh
                        }
                        else {
                                /* select F-Curve by itself */
-                               ANIM_deselect_anim_channels(ac->data, ac->datatype, 0, ACHANNEL_SETFLAG_CLEAR);
+                               ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, 0, ACHANNEL_SETFLAG_CLEAR);
                                fcu->flag |= FCURVE_SELECTED;
                        }
                        
@@ -1725,7 +1773,7 @@ static int mouse_anim_channels (bAnimContext *ac, float x, int channel_index, sh
                        }
                        else {
                                /* select ShapeKey by itself */
-                               ANIM_deselect_anim_channels(ac->data, ac->datatype, 0, ACHANNEL_SETFLAG_CLEAR);
+                               ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, 0, ACHANNEL_SETFLAG_CLEAR);
                                kb->flag |= KEYBLOCK_SEL;
                        }
                                
@@ -1883,9 +1931,10 @@ void ED_operatortypes_animchannels(void)
        WM_operatortype_append(ANIM_OT_channels_visibility_set);
 }
 
+// TODO: check on a poll callback for this, to get hotkeys into menus
 void ED_keymap_animchannels(wmKeyConfig *keyconf)
 {
-       wmKeyMap *keymap = WM_keymap_find(keyconf, "Animation_Channels", 0, 0);
+       wmKeyMap *keymap = WM_keymap_find(keyconf, "Animation Channels", 0, 0);
        
        /* selection */
                /* click-select */
@@ -1900,6 +1949,7 @@ void ED_keymap_animchannels(wmKeyConfig *keyconf)
        
                /* borderselect */
        WM_keymap_add_item(keymap, "ANIM_OT_channels_select_border", BKEY, KM_PRESS, 0, 0);
+       WM_keymap_add_item(keymap, "ANIM_OT_channels_select_border", EVT_TWEAK_L, KM_ANY, 0, 0);
        
        /* delete */
        WM_keymap_add_item(keymap, "ANIM_OT_channels_delete", XKEY, KM_PRESS, 0, 0);
@@ -1917,8 +1967,8 @@ void ED_keymap_animchannels(wmKeyConfig *keyconf)
        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);
+       RNA_boolean_set(WM_keymap_add_item(keymap, "ANIM_OT_channels_expand", PADPLUSKEY, KM_PRESS, KM_CTRL, 0)->ptr, "all", 0);
+       RNA_boolean_set(WM_keymap_add_item(keymap, "ANIM_OT_channels_collapse", PADMINUS, KM_PRESS, KM_CTRL, 0)->ptr, "all", 0);
        
        /* rearranging - actions only */
        //WM_keymap_add_item(keymap, "ANIM_OT_channels_move_up", PAGEUPKEY, KM_PRESS, KM_SHIFT, 0);