Action Editor: Stash Action Operator
authorJoshua Leung <aligorith@gmail.com>
Fri, 27 Feb 2015 14:32:13 +0000 (03:32 +1300)
committerJoshua Leung <aligorith@gmail.com>
Sat, 28 Feb 2015 13:34:44 +0000 (02:34 +1300)
This operator (the snowflake icon, beside the pushdown button on the Action Editor
header) adds the currently active action to the NLA stack in a muted track, then
creates + loads a new action ready to be populated with new keyframes.
Since the NLA is being used to hang on to all the actions here, no actions are
getting lost.

Usage Notes (there will be some additional tweaks to make this nicer):
* To preview different actions that have been "stashed", simply click the "Solo"
  toggle for the track containing the action in question. Playing back the NLA will
  now show the stashed track
* To edit a previously stashed action - simply enter tweakmode on it in the NLA
  while the "Solo" toggle is enabled.

Todo:
* Add some more operators here to polish up the Action <-> NLA bridge to make the
  layered and stash workflows smoother. Examples include some tools to easily
  switch between the different actions layers in the stack, as well as making it
  easier to get out of tweakmode (and sync up the action lengths)

* Review and cleanup the behaviour of the "new" operator here to avoid the old
  problems that users were running into

* After the next release - Implement the full Action Libraries functionality, with
  ways to bridge the stashed strips over to a full-blown library.

release/scripts/startup/bl_ui/space_dopesheet.py
source/blender/editors/space_action/CMakeLists.txt
source/blender/editors/space_action/SConscript
source/blender/editors/space_action/action_edit.c
source/blender/editors/space_action/action_intern.h
source/blender/editors/space_action/action_ops.c

index 16dfb1d75cc12dc6eea9927e55e967ff2cf89869..f69ab3aaa4e86c7ab4323a913e40759f5b62e799 100644 (file)
@@ -126,7 +126,7 @@ class DOPESHEET_HT_header(Header):
 
             row = layout.row(align=True)
             row.operator("action.push_down", text="", icon='NLA_PUSHDOWN')
-            row.operator("action.push_down", text="", icon='FREEZE') # XXX: "stash"
+            row.operator("action.stash", text="", icon='FREEZE')
 
         # Grease Pencil mode doesn't need snapping, as it's frame-aligned only
         if st.mode != 'GPENCIL':
index 96a1e74c882b9ae9bf4bfcb5cda5e22096e15330..50cf84079d28e1df7d5a5f2ba2eeeef394570cb8 100644 (file)
@@ -22,6 +22,7 @@ set(INC
        ../include
        ../../blenkernel
        ../../blenlib
+       ../../blenfont
        ../../gpu
        ../../makesdna
        ../../makesrna
index 2e2081b3c6ecb76845d10cc39031867c27ea0ba1..20f31dd639952479466a1538ed64ed46e54501d7 100644 (file)
@@ -38,6 +38,7 @@ incs = [
     '../include',
     '../../blenkernel',
     '../../blenlib',
+    '../../blenfont',
     '../../gpu',
     '../../makesdna',
     '../../makesrna',
index fe29fed761d152c3b3de81281d7da46f0ce24247..156209709aae91d22b5924e39840ce82dbd11f13 100644 (file)
@@ -40,6 +40,8 @@
 #include "BLI_math.h"
 #include "BLI_utildefines.h"
 
+#include "BLF_translation.h"
+
 #include "DNA_anim_types.h"
 #include "DNA_gpencil_types.h"
 #include "DNA_key_types.h"
@@ -54,6 +56,7 @@
 #include "BKE_action.h"
 #include "BKE_fcurve.h"
 #include "BKE_global.h"
+#include "BKE_library.h"
 #include "BKE_key.h"
 #include "BKE_main.h"
 #include "BKE_nla.h"
@@ -107,7 +110,7 @@ static int act_new_exec(bContext *C, wmOperator *UNUSED(op))
                }
                else {
                        Main *bmain = CTX_data_main(C);
-
+                       
                        /* just make a new (empty) action */
                        action = add_empty_action(bmain, "Action");
                }
@@ -237,6 +240,121 @@ void ACTION_OT_push_down(wmOperatorType *ot)
        ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
 }
 
+/* ******************* Action Stash Operator ******************** */
+
+static int action_stash_exec(bContext *C, wmOperator *op)
+{
+       SpaceAction *saction = (SpaceAction *)CTX_wm_space_data(C);
+       Object *ob = CTX_data_active_object(C);
+       AnimData *adt = NULL;
+       
+       /* Get AnimData block to use */
+       if (saction->mode == SACTCONT_ACTION) {
+               /* Currently, "Action Editor" means object-level only... */
+               adt = ob->adt;
+       }
+       else if (saction->mode == SACTCONT_SHAPEKEY) {
+               Key *key = BKE_key_from_object(ob);
+               adt = key->adt;
+       }
+       
+       /* Perform stashing operation */
+       // TODO: abstract this out to an API call
+       if (adt) {
+               /* don't do anything if this action is empty... */
+               if (action_has_motion(adt->action) == 0) {
+                       /* action may not be suitable... */
+                       BKE_report(op->reports, RPT_WARNING, "Action needs have at least a keyframe or some FModifiers");
+                       return OPERATOR_CANCELLED;
+               }
+               else {
+                       const char *STASH_TRACK_NAME = DATA_("[Action Stash]");
+                       
+                       NlaTrack *prev_track = NULL;
+                       NlaTrack *nlt;
+                       NlaStrip *strip;
+                       
+                       bAction *new_action = NULL;
+                       
+                       PointerRNA ptr, idptr;
+                       PropertyRNA *prop;
+                       
+                       /* create a new track, and add this immediately above the previous stashing track */
+                       for (prev_track = adt->nla_tracks.last; prev_track; prev_track = prev_track->prev) {
+                               if (strstr(prev_track->name, STASH_TRACK_NAME)) {
+                                       break;
+                               }
+                       }
+                       
+                       nlt = add_nlatrack(adt, prev_track);
+                       nlt->flag |= NLATRACK_MUTED; /* so that stash track doesn't disturb the stack animation */
+                       
+                       BLI_strncpy(nlt->name, STASH_TRACK_NAME, sizeof(nlt->name));
+                       BLI_uniquename(&adt->nla_tracks, nlt, STASH_TRACK_NAME, '.', offsetof(NlaTrack, name), sizeof(nlt->name));
+                       
+                       /* add the action as a strip in this new track
+                        * NOTE: a new user is created here
+                        */
+                       strip = add_nlastrip(adt->action);
+                       
+                       BKE_nlatrack_add_strip(nlt, strip);
+                       BKE_nlastrip_validate_name(adt, strip);
+                       BKE_nlastrip_set_active(adt, strip);
+                       
+                       /* create new action (if required) */
+                       if (RNA_boolean_get(op->ptr, "create_new")) {
+                               new_action = add_empty_action(CTX_data_main(C), "New Action");
+                               
+                               /* when creating new ID blocks, there is already 1 user (as for all new datablocks), 
+                                * but the RNA pointer code will assign all the proper users instead, so we compensate
+                                * for that here
+                                */
+                               BLI_assert(new_action->id.us == 1);
+                               new_action->id.us--;
+                               
+                               /* set ID-Root type */
+                               if (saction->mode == SACTCONT_SHAPEKEY)
+                                       new_action->idroot = ID_KE;
+                               else
+                                       new_action->idroot = ID_OB;
+                       }
+                       
+                       /* update pointers
+                        * NOTE: this will clear the user from whatever it is using now
+                        */
+                       RNA_pointer_create((ID *)CTX_wm_screen(C), &RNA_SpaceDopeSheetEditor, saction, &ptr);
+                       prop = RNA_struct_find_property(&ptr, "action");
+                       RNA_id_pointer_create((ID *)new_action, &idptr);
+                       
+                       RNA_property_pointer_set(&ptr, prop, idptr);
+                       RNA_property_update(C, &ptr, prop);
+               }
+       }
+       
+       /* Send notifiers that stuff has changed */
+       WM_event_add_notifier(C, NC_ANIMATION | ND_NLA_ACTCHANGE, NULL);
+       return OPERATOR_FINISHED;
+}
+
+void ACTION_OT_stash(wmOperatorType *ot)
+{
+       /* identifiers */
+       ot->name = "Stash Action";
+       ot->idname = "ACTION_OT_stash";
+       ot->description = "Store this action in the NLA stack as a non-contributing strip for later use";
+       
+       /* callbacks */
+       ot->exec = action_stash_exec;
+       ot->poll = action_pushdown_poll;
+       
+       /* flags */
+       ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+       
+       /* properties */
+       ot->prop = RNA_def_boolean(ot->srna, "create_new", true, "Create New Action", 
+                                  "Create a new action once the existing one has been safely stored");
+}
+
 /* ************************************************************************** */
 /* POSE MARKERS STUFF */
 
index 8bade7a011f30d823a7cb1a02382c26a33e25d30..f0f651dd07a690a0c50970044a04fe746711c37a 100644 (file)
@@ -102,6 +102,7 @@ void ACTION_OT_mirror(struct wmOperatorType *ot);
 
 void ACTION_OT_new(struct wmOperatorType *ot);
 void ACTION_OT_push_down(struct wmOperatorType *ot);
+void ACTION_OT_stash(struct wmOperatorType *ot);
 
 void ACTION_OT_markers_make_local(struct wmOperatorType *ot);
 
index 76b08012c5619400d6cf33c71840c25e7f24f31a..0bf31d2dfb32551d8fcc9cfb414f8529fca79450 100644 (file)
@@ -80,6 +80,7 @@ void action_operatortypes(void)
        
        WM_operatortype_append(ACTION_OT_new);
        WM_operatortype_append(ACTION_OT_push_down);
+       WM_operatortype_append(ACTION_OT_stash);
        
        WM_operatortype_append(ACTION_OT_previewrange_set);
        WM_operatortype_append(ACTION_OT_view_all);