WIP: Added dedicated operator for unlinking actions from the Action Editor (NLA butto...
authorJoshua Leung <aligorith@gmail.com>
Fri, 3 Apr 2015 22:36:46 +0000 (11:36 +1300)
committerJoshua Leung <aligorith@gmail.com>
Fri, 3 Apr 2015 22:37:06 +0000 (11:37 +1300)
After looking into this more carefully, I've found that we do in fact need a dedicate
operator to add some custom logic when trying to unlink an action from the editor/datablocks.

Specifically, this new operator does the following:
1) When in Tweak Mode, it shouldn't be possible to unlink the active action,
   or else, everything turns to custard.
2) If the Action doesn't have any other users, the user should at least get
   a warning that it is going to get lost.
3) We need a convenient way to exit Tweak Mode from the Action Editor
4) If none of the above apply, we can just unlink normally

This commit implements this for the Action Editor, with stubs for the NLA Editor too.
Those will be fixed next.

release/scripts/startup/bl_ui/space_dopesheet.py
source/blender/editors/include/ED_anim_api.h
source/blender/editors/space_action/action_data.c
source/blender/editors/space_action/action_intern.h
source/blender/editors/space_action/action_ops.c
source/blender/editors/space_nla/nla_buttons.c
source/blender/editors/space_nla/nla_channels.c
source/blender/editors/space_nla/nla_intern.h
source/blender/editors/space_nla/nla_ops.c

index c4452ee61f4afc5ebd4f0eb3fe81ecdc4a7e3014..bb08a7c392e1d1362f4b5b9ab085a71f1a3a834e 100644 (file)
@@ -118,7 +118,7 @@ class DOPESHEET_HT_header(Header):
             row.operator("action.layer_prev", text="", icon='TRIA_DOWN')
             row.operator("action.layer_next", text="", icon='TRIA_UP')
 
-            layout.template_ID(st, "action", new="action.new")
+            layout.template_ID(st, "action", new="action.new", unlink="action.unlink")
 
             row = layout.row(align=True)
             row.operator("action.push_down", text="Push Down", icon='NLA_PUSHDOWN')
index 4c7c78df34923be574440526e21fe600a6a7e424..9dea66adc7c49ab1e9032f992f45a11dd736874c 100644 (file)
@@ -675,6 +675,14 @@ void ED_operatormacros_graph(void);
 /* space_action */
 void ED_operatormacros_action(void);
 
+/* ************************************************ */
+/* Animation Editor Exports */
+/* XXX: Should we be doing these here, or at all? */
+
+/* Action Editor - Action Management */
+struct AnimData *ED_actedit_animdata_from_context(struct bContext *C);
+void ED_animedit_unlink_action(struct bContext *C, struct ID *id, struct AnimData *adt, struct bAction *act, struct ReportList *reports);
+
 /* ************************************************ */
 
 #endif /* __ED_ANIM_API_H__ */
index a4eadd8a63b394ae8e5cc425adab9d422566bc33..e55da7dd4b3a5a527986c80bdc61064b86aa10b2 100644 (file)
 #include "action_intern.h"
 
 /* ************************************************************************** */
-/* ACTION MANAGEMENT */
+/* ACTION CREATION */
 
 /* Helper function to find the active AnimData block from the Action Editor context */
-static AnimData *actedit_animdata_from_context(bContext *C)
+AnimData *ED_actedit_animdata_from_context(bContext *C)
 {
        SpaceAction *saction = (SpaceAction *)CTX_wm_space_data(C);
        Object *ob = CTX_data_active_object(C);
@@ -234,7 +234,7 @@ static int action_new_exec(bContext *C, wmOperator *UNUSED(op))
                        adt = ptr.data;
                }
                else if (ptr.type == &RNA_SpaceDopeSheetEditor) {
-                       adt = actedit_animdata_from_context(C);
+                       adt = ED_actedit_animdata_from_context(C);
                }
                
                /* Perform stashing operation - But only if there is an action */
@@ -301,7 +301,7 @@ static int action_pushdown_poll(bContext *C)
 {
        if (ED_operator_action_active(C)) {
                SpaceAction *saction = (SpaceAction *)CTX_wm_space_data(C);
-               AnimData *adt = actedit_animdata_from_context(C);
+               AnimData *adt = ED_actedit_animdata_from_context(C);
                
                /* Check for AnimData, Actions, and that tweakmode is off */
                if (adt && saction->action) {
@@ -320,7 +320,7 @@ static int action_pushdown_poll(bContext *C)
 static int action_pushdown_exec(bContext *C, wmOperator *op)
 {
        SpaceAction *saction = (SpaceAction *)CTX_wm_space_data(C);
-       AnimData *adt = actedit_animdata_from_context(C);
+       AnimData *adt = ED_actedit_animdata_from_context(C);
        
        /* Do the deed... */
        if (adt) {
@@ -368,7 +368,7 @@ void ACTION_OT_push_down(wmOperatorType *ot)
 static int action_stash_exec(bContext *C, wmOperator *op)
 {
        SpaceAction *saction = (SpaceAction *)CTX_wm_space_data(C);
-       AnimData *adt = actedit_animdata_from_context(C);
+       AnimData *adt = ED_actedit_animdata_from_context(C);
        
        /* Perform stashing operation */
        if (adt) {
@@ -431,7 +431,7 @@ void ACTION_OT_stash(wmOperatorType *ot)
 static int action_stash_create_poll(bContext *C)
 {
        if (ED_operator_action_active(C)) {
-               AnimData *adt = actedit_animdata_from_context(C);
+               AnimData *adt = ED_actedit_animdata_from_context(C);
                
                /* Check tweakmode is off (as you don't want to be tampering with the action in that case) */
                /* NOTE: unlike for pushdown, this operator needs to be run when creating an action from nothing... */
@@ -461,7 +461,7 @@ static int action_stash_create_poll(bContext *C)
 static int action_stash_create_exec(bContext *C, wmOperator *op)
 {
        SpaceAction *saction = (SpaceAction *)CTX_wm_space_data(C);
-       AnimData *adt = actedit_animdata_from_context(C);
+       AnimData *adt = ED_actedit_animdata_from_context(C);
        
        /* Check for no action... */
        if (saction->action == NULL) {
@@ -520,6 +520,108 @@ void ACTION_OT_stash_and_create(wmOperatorType *ot)
        ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
 }
 
+/* ************************************************************************** */
+/* ACTION UNLINK */
+
+/* ******************* Action Unlink Operator ******************** */
+/* We use a custom unlink operator here, as there are some technicalities which need special care:
+ * 1) When in Tweak Mode, it shouldn't be possible to unlink the active action,
+ *    or else, everything turns to custard.
+ * 2) If the Action doesn't have any other users, the user should at least get
+ *    a warning that it is going to get lost.
+ * 3) We need a convenient way to exit Tweak Mode from the Action Editor
+ */
+
+void ED_animedit_unlink_action(bContext *C, ID *id, AnimData *adt, bAction *act, ReportList *reports)
+{
+       ScrArea *sa = CTX_wm_area(C);
+       
+       /* If the old action only has a single user (that it's about to lose),
+        * warn user about it
+        *
+        * TODO: Maybe we should just save it for them? But then, there's the problem of
+        *       trying to get rid of stuff that's actually unwanted!
+        */
+       if (act->id.us == 1) {
+               BKE_reportf(reports, RPT_ERROR,
+                                       "Action '%s' will not be saved, create Fake User or Stash in NLA Stack to retain",
+                                       act->id.name + 2);
+       }
+       
+       /* If in Tweak Mode, don't unlink. Instead, this 
+        * becomes a shortcut to exit Tweak Mode instead
+        */
+       if ((adt) && (adt->flag & ADT_NLA_EDIT_ON)) {
+               /* Exit Tweak Mode */
+               BKE_nla_tweakmode_exit(adt);
+               
+               /* Flush this to the Action Editor (if that's where this change was initiated) */
+               if (sa->spacetype == SPACE_ACTION) {
+                       actedit_change_action(C, NULL);
+               }
+       }
+       else {
+               /* Unlink normally - Setting it to NULL should be enough to get the old one unlinked */
+               if (sa->spacetype == SPACE_ACTION) {
+                       /* clear action editor -> action */
+                       actedit_change_action(C, NULL);
+               }
+               else {
+                       /* clear AnimData -> action */
+                       PointerRNA ptr;
+                       PropertyRNA *prop;
+                       
+                       /* create AnimData RNA pointers */
+                       RNA_pointer_create(id, &RNA_AnimData, adt, &ptr);
+                       prop = RNA_struct_find_property(&ptr, "action");
+                       
+                       /* clear... */
+                       RNA_property_pointer_set(&ptr, prop, PointerRNA_NULL);
+                       RNA_property_update(C, &ptr, prop);
+               }
+       }
+}
+
+/* -------------------------- */
+
+static int action_unlink_poll(bContext *C)
+{
+       if (ED_operator_action_active(C)) {
+               SpaceAction *saction = (SpaceAction *)CTX_wm_space_data(C);
+               AnimData *adt = ED_actedit_animdata_from_context(C);
+               
+               /* Only when there's an active action, in the right modes... */
+               if (saction->action && adt)
+                       return true;
+       }
+       
+       /* something failed... */
+       return false;
+}
+
+static int action_unlink_exec(bContext *C, wmOperator *op)
+{
+       AnimData *adt = ED_actedit_animdata_from_context(C);
+       
+       if (adt && adt->action) {
+               ED_animedit_unlink_action(C, NULL, adt, adt->action, op->reports);
+       }
+       
+       return OPERATOR_FINISHED;
+}
+
+void ACTION_OT_unlink(wmOperatorType *ot)
+{
+       /* identifiers */
+       ot->name = "Unlink Action";
+       ot->idname = "ACTION_OT_unlink";
+       ot->description = "Unlink this action from the active action slot (and/or exit Tweak Mode)";
+       
+       /* callbacks */
+       ot->exec = action_unlink_exec;
+       ot->poll = action_unlink_poll;
+}
+
 /* ************************************************************************** */
 /* ACTION BROWSING */
 
@@ -631,7 +733,7 @@ static int action_layer_next_poll(bContext *C)
 {
        /* Action Editor's action editing modes only */
        if (ED_operator_action_active(C)) {
-               AnimData *adt = actedit_animdata_from_context(C);
+               AnimData *adt = ED_actedit_animdata_from_context(C);
                if (adt) {
                        /* only allow if we're in tweakmode, and there's something above us... */
                        if (adt->flag & ADT_NLA_EDIT_ON) {
@@ -665,7 +767,7 @@ static int action_layer_next_poll(bContext *C)
 
 static int action_layer_next_exec(bContext *C, wmOperator *op)
 {
-       AnimData *adt = actedit_animdata_from_context(C);
+       AnimData *adt = ED_actedit_animdata_from_context(C);
        NlaTrack *act_track;
        
        Scene *scene = CTX_data_scene(C);
@@ -742,7 +844,7 @@ static int action_layer_prev_poll(bContext *C)
 {
        /* Action Editor's action editing modes only */
        if (ED_operator_action_active(C)) {
-               AnimData *adt = actedit_animdata_from_context(C);
+               AnimData *adt = ED_actedit_animdata_from_context(C);
                if (adt) {
                        if (adt->flag & ADT_NLA_EDIT_ON) {
                                /* Tweak Mode: We need to check if there are any tracks below the active one that we can move to */
@@ -775,7 +877,7 @@ static int action_layer_prev_poll(bContext *C)
 
 static int action_layer_prev_exec(bContext *C, wmOperator *op)
 {
-       AnimData *adt = actedit_animdata_from_context(C);
+       AnimData *adt = ED_actedit_animdata_from_context(C);
        NlaTrack *act_track;
        NlaTrack *nlt;
        
index 3b0da089b9b323aea493984dfbea4ea12deaf675..9f9b15c3c26b6cbd7ed9b2624ec04cc212bc05b1 100644 (file)
@@ -98,6 +98,8 @@ void ACTION_OT_snap(struct wmOperatorType *ot);
 void ACTION_OT_mirror(struct wmOperatorType *ot);
 
 void ACTION_OT_new(struct wmOperatorType *ot);
+void ACTION_OT_unlink(struct wmOperatorType *ot);
+
 void ACTION_OT_push_down(struct wmOperatorType *ot);
 void ACTION_OT_stash(struct wmOperatorType *ot);
 void ACTION_OT_stash_and_create(struct wmOperatorType *ot);
index 431dd27d06428f9de71c6f85f038f6248fdae43b..9ad0931a957c4ccbb8e1ccbf5d4b95d194c6f3fc 100644 (file)
@@ -79,6 +79,8 @@ void action_operatortypes(void)
        WM_operatortype_append(ACTION_OT_paste);
        
        WM_operatortype_append(ACTION_OT_new);
+       WM_operatortype_append(ACTION_OT_unlink);
+       
        WM_operatortype_append(ACTION_OT_push_down);
        WM_operatortype_append(ACTION_OT_stash);
        WM_operatortype_append(ACTION_OT_stash_and_create);
index 66023ce124357862a6872cd705b22ac7039ec655..1fb7228dd984ff2a162def166fb73e0459603c4e 100644 (file)
@@ -159,7 +159,9 @@ bool nla_panel_context(const bContext *C, PointerRNA *adt_ptr, PointerRNA *nlt_p
                                        }
                                        
                                        /* AnimData pointer */
-                                       RNA_pointer_create(id, &RNA_AnimData, ale->adt, adt_ptr);
+                                       if (adt_ptr) {
+                                               RNA_pointer_create(id, &RNA_AnimData, ale->adt, adt_ptr);
+                                       }
                                        
                                        /* set found status to -1, since setting to 1 would break the loop 
                                         * and potentially skip an active NLA-Track in some cases...
@@ -282,7 +284,7 @@ static void nla_panel_animdata(const bContext *C, Panel *pa)
        /* Active Action Properties ------------------------------------- */
        /* action */
        row = uiLayoutRow(layout, true);
-       uiTemplateID(row, (bContext *)C, &adt_ptr, "action", "ACTION_OT_new", NULL, NULL /*"ACTION_OT_unlink"*/);     // XXX: need to make these operators
+       uiTemplateID(row, (bContext *)C, &adt_ptr, "action", "ACTION_OT_new", NULL, "NLA_OT_action_unlink");
        
        /* extrapolation */
        row = uiLayoutRow(layout, true);
index 5476d1c268355e970af7ee3b76eb2a29165547de..70d9bfe99fe5e5ec56b6595b46b4aa7b08fb2235 100644 (file)
@@ -504,6 +504,52 @@ void NLA_OT_action_pushdown(wmOperatorType *ot)
        RNA_def_property_flag(ot->prop, PROP_SKIP_SAVE);
 }
 
+/* ******************** Action Unlink ******************************** */
+
+static int nla_action_unlink_poll(bContext *C)
+{
+       if (ED_operator_nla_active(C)) {
+               return nla_panel_context(C, NULL, NULL, NULL);
+       }
+       
+       /* something failed... */
+       return false;
+}
+
+static int nla_action_unlink_exec(bContext *C, wmOperator *op)
+{
+       PointerRNA adt_ptr;
+       AnimData *adt;
+       
+       /* check context and also validity of pointer */
+       if (!nla_panel_context(C, &adt_ptr, NULL, NULL))
+               return OPERATOR_CANCELLED;
+       
+       /* get animdata */
+       adt = adt_ptr.data;
+       if (adt == NULL)
+               return OPERATOR_CANCELLED;
+       
+       /* do unlinking */
+       if (adt && adt->action) {
+               ED_animedit_unlink_action(C, adt_ptr.id.data, adt, adt->action, op->reports);
+       }
+       
+       return OPERATOR_FINISHED;
+}
+
+void NLA_OT_action_unlink(wmOperatorType *ot)
+{
+       /* identifiers */
+       ot->name = "Unlink Action";
+       ot->idname = "NLA_OT_action_unlink";
+       ot->description = "Unlink this action from the active action slot (and/or exit Tweak Mode)";
+       
+       /* callbacks */
+       ot->exec = nla_action_unlink_exec;
+       ot->poll = nla_action_unlink_poll;
+}
+
 /* ******************** Add Tracks Operator ***************************** */
 /* Add NLA Tracks to the same AnimData block as a selected track, or above the selected tracks */
 
index bd96b5a4de5263054493d1382a3c61735a9cc0d6..70fdb2d478d381d1c598a14636a413070cb26443 100644 (file)
@@ -137,6 +137,7 @@ bool nlaedit_add_tracks_empty(bAnimContext *ac);
 void NLA_OT_channels_click(wmOperatorType *ot);
 
 void NLA_OT_action_pushdown(wmOperatorType *ot);
+void NLA_OT_action_unlink(wmOperatorType *ot);
 
 void NLA_OT_tracks_add(wmOperatorType *ot);
 void NLA_OT_tracks_delete(wmOperatorType *ot);
index b3a875047dba451fc0a682ad4d73435b88928cfa..43bc4b9df0f970383d256616b48aadde3033b25c 100644 (file)
@@ -114,6 +114,7 @@ void nla_operatortypes(void)
        WM_operatortype_append(NLA_OT_channels_click);
        
        WM_operatortype_append(NLA_OT_action_pushdown);
+       WM_operatortype_append(NLA_OT_action_unlink);
        
        WM_operatortype_append(NLA_OT_tracks_add);
        WM_operatortype_append(NLA_OT_tracks_delete);