Bugfix [#25960] Action/NLA Editor issues with animdata context
authorJoshua Leung <aligorith@gmail.com>
Tue, 19 Apr 2011 13:01:50 +0000 (13:01 +0000)
committerJoshua Leung <aligorith@gmail.com>
Tue, 19 Apr 2011 13:01:50 +0000 (13:01 +0000)
Actions now get tagged with an ID-code, which is used to determine
what ID-blocks they can be assigned to. This ensures that material
actions cannot be assigned to the object-level for example.

* Action lists in general will now show only the actions that can be
set for that particular slot. This prevents selection of invalid
actions, and helps cut down the list of actions.
** An exception here is the Add Action Clip in NLA Editor, which will
show all actions but will only add where appropriate. This is because
it's not easy/possible to tell in advance which blocktypes to filter
for when building this list. (TODO?)

* The "Action Editor" is now strictly for object-level action
editing+setting now. This avoids repeateded confusion by people who
try using this to view their shapekey actions, which should go to the
Shape Key Editor instead!
** A context switcher for the legitimate times where this capability
might come in handy is still being investigated.

* "Floating" actions (i.e. actions in some action_library.blend) are
NOT able to be automatically tagged until they are assigned to some
datablocks (i.e. loaded onto the rig + played back once). It is
possible to write scripts that check for certain RNA-paths and "guess"
what datablocks they work on, but it is recommended that you load up
the Datablocks Viewer, and go through such actions by hand, setting
the "ID Root Type" property as appropriate per action.

source/blender/blenkernel/intern/anim_sys.c
source/blender/blenkernel/intern/ipo.c
source/blender/blenloader/intern/readfile.c
source/blender/editors/space_nla/nla_edit.c
source/blender/makesdna/DNA_action_types.h
source/blender/makesrna/intern/rna_action.c
source/blender/makesrna/intern/rna_animation.c
source/blender/makesrna/intern/rna_internal.h
source/blender/makesrna/intern/rna_nla.c
source/blender/makesrna/intern/rna_space.c

index 2708239a4c37ef307dfcda0da43631004c7ecac9..f7eaff475fae328ea8f1ef0bddb8fc3f8b94f1bb 100644 (file)
@@ -1199,6 +1199,39 @@ static void animsys_evaluate_drivers (PointerRNA *ptr, AnimData *adt, float ctim
 /* ***************************************** */
 /* Actions Evaluation */
 
+/* strictly not necessary for actual "evaluation", but it is a useful safety check
+ * to reduce the amount of times that users end up having to "revive" wrongly-assigned
+ * actions
+ */
+static void action_idcode_patch_check (ID *id, bAction *act)
+{
+       int idcode = 0;
+       
+       /* just in case */
+       if (ELEM(NULL, id, act))
+               return;
+       else
+               idcode = GS(id->name);
+       
+       /* the actual checks... hopefully not too much of a performance hit in the long run... */
+       if (act->idroot == 0) {
+               /* use the current root if not set already (i.e. newly created actions and actions from 2.50-2.57 builds)
+                *      - this has problems if there are 2 users, and the first one encountered is the invalid one
+                *        in which case, the user will need to manually fix this (?)
+                */
+               act->idroot = idcode;
+       }
+       else if (act->idroot != idcode) {
+               /* only report this error if debug mode is enabled (to save performance everywhere else) */
+               if (G.f & G_DEBUG) {
+                       printf("AnimSys Safety Check Failed: Action '%s' is not meant to be used from ID-Blocks of type %d such as '%s'\n",
+                               act->id.name+2, idcode, id->name);
+               }
+       }
+}
+
+/* ----------------------------------------- */
+
 /* Evaluate Action Group */
 void animsys_evaluate_action_group (PointerRNA *ptr, bAction *act, bActionGroup *agrp, AnimMapper *remap, float ctime)
 {
@@ -1208,6 +1241,8 @@ void animsys_evaluate_action_group (PointerRNA *ptr, bAction *act, bActionGroup
        if ELEM(NULL, act, agrp) return;
        if ((remap) && (remap->target != act)) remap= NULL;
        
+       action_idcode_patch_check(ptr->id.data, act);
+       
        /* if group is muted, don't evaluated any of the F-Curve */
        if (agrp->flag & AGRP_MUTED)
                return;
@@ -1231,6 +1266,8 @@ void animsys_evaluate_action (PointerRNA *ptr, bAction *act, AnimMapper *remap,
        if (act == NULL) return;
        if ((remap) && (remap->target != act)) remap= NULL;
        
+       action_idcode_patch_check(ptr->id.data, act);
+       
        /* calculate then execute each curve */
        animsys_evaluate_fcurves(ptr, &act->curves, remap, ctime);
 }
@@ -1630,6 +1667,17 @@ static void nlastrip_evaluate_actionclip (PointerRNA *ptr, ListBase *channels, L
        FCurve *fcu;
        float evaltime;
        
+       /* sanity checks for action */
+       if (strip == NULL)
+               return;
+               
+       if (strip->act == NULL) {
+               printf("NLA-Strip Eval Error: Strip '%s' has no Action\n", strip->name);
+               return;
+       }
+       
+       action_idcode_patch_check(ptr->id.data, strip->act);
+       
        /* join this strip's modifiers to the parent's modifiers (own modifiers first) */
        nlaeval_fmodifiers_join_stacks(&tmp_modifiers, &strip->modifiers, modifiers);
        
index 08e0ad4f3ff8038a0a6e6509a8022be71f7c65f3..689e17958fe8d0e2f0624a4a334ec2d8e0611b3a 100644 (file)
@@ -1372,7 +1372,7 @@ static void icu_to_fcurves (ID *id, ListBase *groups, ListBase *list, IpoCurve *
  * This does not assume that any ID or AnimData uses it, but does assume that
  * it is given two lists, which it will perform driver/animation-data separation.
  */
-static void ipo_to_animato (ID *id, Ipo *ipo, char actname[], char constname[], Sequence * seq, ListBase *animgroups, ListBase *anim, ListBase *drivers)
+static void ipo_to_animato (ID *id, Ipo *ipo, char actname[], char constname[], Sequence *seq, ListBase *animgroups, ListBase *anim, ListBase *drivers)
 {
        IpoCurve *icu;
        
@@ -1804,6 +1804,10 @@ void do_versions_ipos_to_animato(Main *main)
                                BLI_freelinkN(&ob->constraintChannels, conchan);
                        }
                }
+               
+               /* object's action will always be object-rooted */
+               if (adt->action)
+                       adt->action->idroot = ID_OB;
        }
        
        /* shapekeys */
@@ -1822,6 +1826,10 @@ void do_versions_ipos_to_animato(Main *main)
                        
                        /* Convert Shapekey data... */
                        ipo_to_animdata(id, key->ipo, NULL, NULL, NULL);
+                       
+                       if (adt->action)
+                               adt->action->idroot = key->ipo->blocktype;
+                       
                        key->ipo->id.us--;
                        key->ipo= NULL;
                }
@@ -1840,6 +1848,10 @@ void do_versions_ipos_to_animato(Main *main)
                        
                        /* Convert Material data... */
                        ipo_to_animdata(id, ma->ipo, NULL, NULL, NULL);
+                       
+                       if (adt->action)
+                               adt->action->idroot = ma->ipo->blocktype;
+                       
                        ma->ipo->id.us--;
                        ma->ipo= NULL;
                }
@@ -1858,6 +1870,10 @@ void do_versions_ipos_to_animato(Main *main)
                        
                        /* Convert World data... */
                        ipo_to_animdata(id, wo->ipo, NULL, NULL, NULL);
+                       
+                       if (adt->action)
+                               adt->action->idroot = wo->ipo->blocktype;
+                       
                        wo->ipo->id.us--;
                        wo->ipo= NULL;
                }
@@ -1904,6 +1920,10 @@ void do_versions_ipos_to_animato(Main *main)
                                
                                /* convert IPO */
                                ipo_to_animdata((ID *)scene, seq->ipo, NULL, NULL, seq);
+                               
+                               if (adt->action)
+                                       adt->action->idroot = ID_SCE; /* scene-rooted */
+                               
                                seq->ipo->id.us--;
                                seq->ipo = NULL;
                        }
@@ -1925,6 +1945,10 @@ void do_versions_ipos_to_animato(Main *main)
                        
                        /* Convert Texture data... */
                        ipo_to_animdata(id, te->ipo, NULL, NULL, NULL);
+                       
+                       if (adt->action)
+                               adt->action->idroot = te->ipo->blocktype;
+                       
                        te->ipo->id.us--;
                        te->ipo= NULL;
                }
@@ -1943,6 +1967,10 @@ void do_versions_ipos_to_animato(Main *main)
                        
                        /* Convert Camera data... */
                        ipo_to_animdata(id, ca->ipo, NULL, NULL, NULL);
+                       
+                       if (adt->action)
+                               adt->action->idroot = ca->ipo->blocktype;
+                       
                        ca->ipo->id.us--;
                        ca->ipo= NULL;
                }
@@ -1961,6 +1989,10 @@ void do_versions_ipos_to_animato(Main *main)
                        
                        /* Convert Lamp data... */
                        ipo_to_animdata(id, la->ipo, NULL, NULL, NULL);
+                       
+                       if (adt->action)
+                               adt->action->idroot = la->ipo->blocktype;
+                       
                        la->ipo->id.us--;
                        la->ipo= NULL;
                }
@@ -1979,6 +2011,10 @@ void do_versions_ipos_to_animato(Main *main)
                        
                        /* Convert Curve data... */
                        ipo_to_animdata(id, cu->ipo, NULL, NULL, NULL);
+                       
+                       if (adt->action)
+                               adt->action->idroot = cu->ipo->blocktype;
+                       
                        cu->ipo->id.us--;
                        cu->ipo= NULL;
                }
@@ -2001,6 +2037,10 @@ void do_versions_ipos_to_animato(Main *main)
                
                if (G.f & G_DEBUG) printf("\tconverting action %s \n", id->name+2);
                
+               /* if old action, it will be object-only... */
+               if (act->chanbase.first)
+                       act->idroot = ID_OB;
+               
                /* be careful! some of the actions we encounter will be converted ones... */
                action_to_animato(NULL, act, &act->groups, &act->curves, &drivers);
        }
@@ -2018,6 +2058,7 @@ void do_versions_ipos_to_animato(Main *main)
                        /* add a new action for this, and convert all data into that action */
                        new_act= add_empty_action("ConvIPO_Action"); // XXX need a better name...
                        ipo_to_animato(NULL, ipo, NULL, NULL, NULL, NULL, &new_act->curves, &drivers);
+                       new_act->idroot = ipo->blocktype;
                }
                
                /* clear fake-users, and set user-count to zero to make sure it is cleared on file-save */
index 4ff65aeb213be6b31fb9a7e6bc98e7d17f5f889b..a87d993bc15b370597901f325334fe0dc11be8aa 100644 (file)
@@ -1863,6 +1863,10 @@ static void lib_link_nladata_strips(FileData *fd, ID *id, ListBase *list)
                
                /* reassign the counted-reference to action */
                strip->act = newlibadr_us(fd, id->lib, strip->act);
+               
+               /* fix action id-root (i.e. if it comes from a pre 2.57 .blend file) */
+               if ((strip->act) && (strip->act->idroot == 0))
+                       strip->act->idroot = GS(id->name);
        }
 }
 
@@ -1956,6 +1960,12 @@ static void lib_link_animdata(FileData *fd, ID *id, AnimData *adt)
        adt->action= newlibadr_us(fd, id->lib, adt->action);
        adt->tmpact= newlibadr_us(fd, id->lib, adt->tmpact);
        
+       /* fix action id-roots (i.e. if they come from a pre 2.57 .blend file) */
+       if ((adt->action) && (adt->action->idroot == 0))
+               adt->action->idroot = GS(id->name);
+       if ((adt->tmpact) && (adt->tmpact->idroot == 0))
+               adt->tmpact->idroot = GS(id->name);
+       
        /* link drivers */
        lib_link_fcurves(fd, id, &adt->drivers);
        
index 0bcf84c18704ed15c701b3fb6918b7f7b6000434..77c91b28a634215718df8bed19cd293598b453d1 100644 (file)
@@ -271,6 +271,12 @@ static int nlaedit_add_actionclip_exec (bContext *C, wmOperator *op)
                //printf("Add strip - actname = '%s' \n", actname);
                return OPERATOR_CANCELLED;
        }
+       else if (act->idroot == 0) {
+               /* hopefully in this case (i.e. library of userless actions), the user knows what they're doing... */
+               BKE_reportf(op->reports, RPT_WARNING,
+                       "Action '%s' does not specify what datablocks it can be used on. Try setting the 'ID Root Type' setting from the Datablocks Editor for this Action to avoid future problems",
+                       act->id.name+2);
+       }
        
        /* get a list of the editable tracks being shown in the NLA
         *      - this is limited to active ones for now, but could be expanded to 
@@ -289,6 +295,16 @@ static int nlaedit_add_actionclip_exec (bContext *C, wmOperator *op)
                AnimData *adt= ale->adt;
                NlaStrip *strip= NULL;
                
+               /* sanity check: only apply actions of the right type for this ID 
+                * NOTE: in the case that this hasn't been set, we've already warned the user about this already
+                */
+               if ((act->idroot) && (act->idroot != GS(ale->id->name))) {
+                       BKE_reportf(op->reports, RPT_ERROR, 
+                               "Couldn't add action '%s' as it cannot be used relative to ID-blocks of type '%s'",
+                               act->id.name+2, ale->id->name);
+                       continue;
+               }
+               
                /* create a new strip, and offset it to start on the current frame */
                strip= add_nlastrip(act);
                
index 412b9bc56f60da123aac1eb01dd273dc792ddac2..0716d1ddbf21ca66d8076e4dacd0623ab18aca60 100644 (file)
@@ -487,6 +487,9 @@ typedef struct bAction {
        
        int flag;                       /* settings for this action */
        int active_marker;      /* index of the active marker */
+       
+       int idroot;                     /* type of ID-blocks that action can be assigned to (if 0, will be set to whatever ID first evaluates it) */
+       int pad;
 } bAction;
 
 
index 418451801ca8e956d6cbf5c025205bb0e1ff8519..7fdb96fda6e7f7eed05b58068af999158b9b49cd 100644 (file)
@@ -30,6 +30,7 @@
 #include <stdlib.h>
 
 #include "RNA_define.h"
+#include "RNA_enum_types.h"
 
 #include "rna_internal.h"
 
@@ -194,6 +195,56 @@ static void rna_Action_frame_range_get(PointerRNA *ptr,float *values)
        calc_action_range(ptr->id.data, values, values+1, 1);
 }
 
+
+/* used to check if an action (value pointer) is suitable to be assigned to the ID-block that is ptr */
+int rna_Action_id_poll(PointerRNA *ptr, PointerRNA value)
+{
+       ID *srcId = (ID *)ptr->id.data;
+       bAction *act = (bAction *)value.id.data;
+       
+       if (act) {
+               /* there can still be actions that will have undefined id-root 
+                * (i.e. floating "action-library" members) which we will not
+                * be able to resolve an idroot for automatically, so let these through
+                */
+               if (act->idroot == 0)
+                       return 1;
+               else if (srcId)
+                       return GS(srcId->name) == act->idroot;
+       }
+       
+       return 0;
+}
+
+/* used to check if an action (value pointer) can be assigned to Action Editor given current mode */
+int rna_Action_actedit_assign_poll(PointerRNA *ptr, PointerRNA value)
+{
+       SpaceAction *saction = (SpaceAction *)ptr->data;
+       bAction *act = (bAction *)value.id.data;
+       
+       if (act) {
+               /* there can still be actions that will have undefined id-root 
+                * (i.e. floating "action-library" members) which we will not
+                * be able to resolve an idroot for automatically, so let these through
+                */
+               if (act->idroot == 0)
+                       return 1;
+               
+               if (saction) {
+                       if (saction->mode == SACTCONT_ACTION) {
+                               /* this is only Object-level for now... */
+                               return act->idroot == ID_OB;
+                       }
+                       else if (saction->mode == SACTCONT_SHAPEKEY) {
+                               /* obviously shapekeys only */
+                               return act->idroot == ID_KE;
+                       }
+               }
+       }
+       
+       return 0;
+}
+
 #else
 
 static void rna_def_dopesheet(BlenderRNA *brna)
@@ -515,34 +566,43 @@ static void rna_def_action(BlenderRNA *brna)
 {
        StructRNA *srna;
        PropertyRNA *prop;
-
+       
        srna= RNA_def_struct(brna, "Action", "ID");
        RNA_def_struct_sdna(srna, "bAction");
        RNA_def_struct_ui_text(srna, "Action", "A collection of F-Curves for animation");
        RNA_def_struct_ui_icon(srna, ICON_ACTION);
-
+       
+       /* collections */
        prop= RNA_def_property(srna, "fcurves", PROP_COLLECTION, PROP_NONE);
        RNA_def_property_collection_sdna(prop, NULL, "curves", NULL);
        RNA_def_property_struct_type(prop, "FCurve");
        RNA_def_property_ui_text(prop, "F-Curves", "The individual F-Curves that make up the Action");
        rna_def_action_fcurves(brna, prop);
-
+       
        prop= RNA_def_property(srna, "groups", PROP_COLLECTION, PROP_NONE);
        RNA_def_property_collection_sdna(prop, NULL, "groups", NULL);
        RNA_def_property_struct_type(prop, "ActionGroup");
        RNA_def_property_ui_text(prop, "Groups", "Convenient groupings of F-Curves");
        rna_def_action_groups(brna, prop);
-
+       
        prop= RNA_def_property(srna, "pose_markers", PROP_COLLECTION, PROP_NONE);
        RNA_def_property_collection_sdna(prop, NULL, "markers", NULL);
        RNA_def_property_struct_type(prop, "TimelineMarker");
        RNA_def_property_ui_text(prop, "Pose Markers", "Markers specific to this Action, for labeling poses");
        rna_def_action_pose_markers(brna, prop);
-
+       
+       /* properties */
        prop= RNA_def_float_vector(srna, "frame_range" , 2 , NULL , 0, 0, "Frame Range" , "The final frame range of all fcurves within this action" , 0 , 0);
        RNA_def_property_float_funcs(prop, "rna_Action_frame_range_get" , NULL, NULL);
        RNA_def_property_clear_flag(prop, PROP_EDITABLE);
-
+       
+       /* special "type" limiter - should not really be edited in general, but is still available/editable in 'emergencies' */
+       prop= RNA_def_property(srna, "id_root", PROP_ENUM, PROP_NONE);
+       RNA_def_property_enum_sdna(prop, NULL, "idroot");
+       RNA_def_property_enum_items(prop, id_type_items);
+       RNA_def_property_ui_text(prop, "ID Root Type", "Type of ID-block that action can be used on. DO NOT CHANGE UNLESS YOU KNOW WHAT YOU'RE DOING");
+       
+       /* API calls */
        RNA_api_action(srna);
 }
 
index 17d2e27c462ffae1c8a6d586bc8610d66a65e765..8e210a7a4cb6a6d2899c301dcfa606994d603ac5 100644 (file)
@@ -70,6 +70,41 @@ static int rna_AnimData_action_editable(PointerRNA *ptr)
                return 1;
 }
 
+static void rna_AnimData_action_set(PointerRNA *ptr, PointerRNA value)
+{
+       ID *ownerId = (ID *)ptr->id.data;
+       AnimData *adt = (AnimData *)ptr->data;
+       
+       /* assume that AnimData's action can in fact be edited... */
+       if ((value.data) && (ownerId)) {
+               bAction *act = (bAction *)value.data;
+               
+               /* action must have same type as owner */
+               if (ownerId) {
+                       if (ELEM(act->idroot, 0, GS(ownerId->name))) {
+                               /* can set */
+                               adt->action = act;
+                       }
+                       else {
+                               /* cannot set */
+                               printf("ERROR: Couldn't set Action '%s' onto ID '%s', as it doesn't have suitably rooted paths for this purpose\n", 
+                                               act->id.name+2, ownerId->name);
+                       }
+               }
+               else {
+                       /* cannot tell if we can set, so let's just be generous... */
+                       printf("Warning: Set Action '%s' onto AnimData block with an unknown ID-owner. May have attached invalid data\n",
+                                       act->id.name+2);
+                               
+                       adt->action = act;
+               }
+       }
+       else {
+               /* just clearing the action... */
+               adt->action = NULL;
+       }
+}
+
 /* ****************************** */
 
 /* wrapper for poll callback */
@@ -739,6 +774,7 @@ void rna_def_animdata(BlenderRNA *brna)
        /* Active Action */
        prop= RNA_def_property(srna, "action", PROP_POINTER, PROP_NONE);
        RNA_def_property_flag(prop, PROP_EDITABLE); /* this flag as well as the dynamic test must be defined for this to be editable... */
+       RNA_def_property_pointer_funcs(prop, NULL, NULL, NULL, "rna_Action_id_poll");
        RNA_def_property_editable_func(prop, "rna_AnimData_action_editable");
        RNA_def_property_ui_text(prop, "Action", "Active Action for this datablock");
        RNA_def_property_update(prop, NC_ANIMATION, NULL); /* this will do? */
index 4dc98ceb0a431c44e232276caa185cffa20050e8..1d060c8a3624378e19156fa9c9838fe3bace0528 100644 (file)
@@ -222,6 +222,9 @@ int rna_Curve_object_poll(struct PointerRNA *ptr, struct PointerRNA value);
 int rna_Lattice_object_poll(struct PointerRNA *ptr, struct PointerRNA value);
 int rna_Mesh_object_poll(struct PointerRNA *ptr, struct PointerRNA value);
 
+/* basic poll functions for actions (to prevent actions getting set in wrong places) */
+int rna_Action_id_poll(struct PointerRNA *ptr, struct PointerRNA value);
+int rna_Action_actedit_assign_poll(struct PointerRNA *ptr, struct PointerRNA value);
 
 char *rna_TextureSlot_path(struct PointerRNA *ptr);
 
index 098375799492a1bb7f5b6b7989618336a3f2ec6f..9bf5f53f8b78c7c9e29bbf6bfb5b80a768c34274 100644 (file)
@@ -426,6 +426,7 @@ static void rna_def_nlastrip(BlenderRNA *brna)
        /* Action */
        prop= RNA_def_property(srna, "action", PROP_POINTER, PROP_NONE);
        RNA_def_property_pointer_sdna(prop, NULL, "act");
+       RNA_def_property_pointer_funcs(prop, NULL, NULL, NULL, "rna_Action_id_poll");
        RNA_def_property_flag(prop, PROP_EDITABLE); 
        RNA_def_property_ui_text(prop, "Action", "Action referenced by this strip");
        RNA_def_property_update(prop, NC_ANIMATION|ND_NLA, NULL); /* this will do? */
index 3d9d8a8e9f4024aa1eeff2dc374d473a5f35ee6d..52fae14a0239949874da43cfc4aa08ac715b779e 100644 (file)
@@ -644,7 +644,32 @@ static void rna_ConsoleLine_cursor_index_range(PointerRNA *ptr, int *min, int *m
 static void rna_SpaceDopeSheetEditor_action_set(PointerRNA *ptr, PointerRNA value)
 {
        SpaceAction *saction= (SpaceAction*)(ptr->data);
-       saction->action= value.data;
+       bAction *act = (bAction*)value.data;
+       
+       if ((act == NULL) || (act->idroot == 0)) {
+               /* just set if we're clearing the action or if the action is "amorphous" still */
+               saction->action= act;
+       }
+       else {
+               /* action to set must strictly meet the mode criteria... */
+               if (saction->mode == SACTCONT_ACTION) {
+                       /* currently, this is "object-level" only, until we have some way of specifying this */
+                       if (act->idroot == ID_OB)
+                               saction->action = act;
+                       else
+                               printf("ERROR: cannot assign Action '%s' to Action Editor, as action is not object-level animation\n", act->id.name+2);
+               }
+               else if (saction->mode == SACTCONT_SHAPEKEY) {
+                       /* as the name says, "shapekey-level" only... */
+                       if (act->idroot == ID_KE)
+                               saction->action = act;
+                       else
+                               printf("ERROR: cannot assign Action '%s' to Shape Key Editor, as action doesn't animate Shape Keys\n", act->id.name+2);
+               }
+               else {
+                       printf("ACK: who's trying to set an action while not in a mode displaying a single Action only?\n");
+               }
+       }
 }
 
 static void rna_SpaceDopeSheetEditor_action_update(Main *bmain, Scene *scene, PointerRNA *ptr)
@@ -1771,7 +1796,7 @@ static void rna_def_space_dopesheet(BlenderRNA *brna)
        /* data */
        prop= RNA_def_property(srna, "action", PROP_POINTER, PROP_NONE);
        RNA_def_property_flag(prop, PROP_EDITABLE);
-       RNA_def_property_pointer_funcs(prop, NULL, "rna_SpaceDopeSheetEditor_action_set", NULL, NULL);
+       RNA_def_property_pointer_funcs(prop, NULL, "rna_SpaceDopeSheetEditor_action_set", NULL, "rna_Action_actedit_assign_poll");
        RNA_def_property_ui_text(prop, "Action", "Action displayed and edited in this space");
        RNA_def_property_update(prop, NC_ANIMATION|ND_KEYFRAME|NA_EDITED, "rna_SpaceDopeSheetEditor_action_update");