== PyConstraints ==
authorJoshua Leung <aligorith@gmail.com>
Mon, 18 Jun 2007 07:41:21 +0000 (07:41 +0000)
committerJoshua Leung <aligorith@gmail.com>
Mon, 18 Jun 2007 07:41:21 +0000 (07:41 +0000)
At last! The ability to code constraints in Python. This opens up many interesting rigging possibilities, as well as making prototyping constraints easier.

* A PyConstraint script must begin with the line
#BPYCONSTRAINT
* It must also define a doConstraint function, which performs the core actions of the constraint.
* PyConstraints use IDProperties to store custom properties for each PyConstraint instance. The scripter can choose which of these ID-Properties to expose to a user to control the behaviour of the constraint. This must be done using the Draw.PupBlock method.

Credits to Joe Eager (joeedh) for coding the original patch on which this is based. I've made heavy revisions to large parts of the patch.

For more detailed information, and some demo scripts, see the following page:
http://aligorith.googlepages.com/pyconstraints2

13 files changed:
source/blender/blenkernel/bad_level_call_stubs/stubs.c
source/blender/blenkernel/intern/constraint.c
source/blender/blenkernel/intern/idprop.c
source/blender/blenloader/intern/readfile.c
source/blender/blenloader/intern/writefile.c
source/blender/include/BIF_editconstraint.h
source/blender/makesdna/DNA_constraint_types.h
source/blender/python/BPY_extern.h
source/blender/python/BPY_interface.c
source/blender/python/api2_2x/Constraint.c
source/blender/src/buttons_object.c
source/blender/src/editconstraint.c
source/blender/src/header_text.c

index 344f763608c23b25196b953a1f5195a29e242e63..e76c53fc72cd5295da0abaf97b719737e762bdaf 100644 (file)
@@ -54,6 +54,7 @@ struct IpoCurve;
 struct FluidsimSettings;
 struct Render;
 struct RenderResult;
+struct bPythonConstraint;
 
 char *getIpoCurveName( struct IpoCurve * icu );
 void insert_vert_ipo(struct IpoCurve *icu, float x, float y);
@@ -124,6 +125,16 @@ int BPY_button_eval(char *expr, double *value)
        return 0;
 }
 
+/* constraint.c */
+void BPY_pyconstraint_eval(struct bPythonConstraint *con, float obmat[][4], short ownertype, void *ownerdata, float targetmat[][4])
+{
+}
+int BPY_pyconstraint_targets(struct bPythonConstraint *con, float targetmat[][4])
+{
+       return 0;
+}
+
+
 /* writefile.c */
        /* struct Oops; */
 void free_oops(struct Oops *oops){}
index 97f5ed18cbed2444e22ddbcbfbb2f9892cda502a..9c03db824af47f51fccc5dd3453904f66bdbf0fc 100644 (file)
@@ -58,6 +58,9 @@
 #include "BKE_ipo.h"
 #include "BKE_global.h"
 #include "BKE_library.h"
+#include "BKE_idprop.h"
+
+#include "BPY_extern.h"
 
 #include "blendef.h"
 
@@ -80,6 +83,15 @@ void free_constraint_data (bConstraint *con)
 {
        if (con->data) {
                /* any constraint-type specific stuff here */
+               switch (con->type) {
+                       case CONSTRAINT_TYPE_PYTHON:
+                       {
+                               bPythonConstraint *data= con->data;
+                               IDP_FreeProperty(data->prop);
+                               MEM_freeN(data->prop);
+                       }
+                               break;
+               }
                
                MEM_freeN(con->data);
        }
@@ -117,6 +129,14 @@ void relink_constraints (struct ListBase *list)
        
        for (con = list->first; con; con=con->next) {
                switch (con->type) {
+                       case CONSTRAINT_TYPE_PYTHON:
+                       {
+                               bPythonConstraint *data;
+                               data = con->data;
+                               
+                               ID_NEW(data->tar);
+                       }
+                               break;
                        case CONSTRAINT_TYPE_KINEMATIC:
                        {
                                bKinematicConstraint *data;
@@ -260,6 +280,13 @@ void copy_constraints (ListBase *dst, ListBase *src)
 char constraint_has_target (bConstraint *con) 
 {
        switch (con->type) {
+       case CONSTRAINT_TYPE_PYTHON:
+               {
+                       bPythonConstraint *data = con->data;
+                       if (data->tar)
+                               return 1;
+               }
+               break;
        case CONSTRAINT_TYPE_TRACKTO:
                {
                        bTrackToConstraint *data = con->data;
@@ -354,6 +381,13 @@ Object *get_constraint_target(bConstraint *con, char **subtarget)
          * to the name for this constraints subtarget ... NULL otherwise
          */
        switch (con->type) {
+       case CONSTRAINT_TYPE_PYTHON:
+               {
+                       bPythonConstraint *data=con->data;
+                       *subtarget = data->subtarget;
+                       return data->tar;
+               }
+               break;
        case CONSTRAINT_TYPE_ACTION:
                {
                        bActionConstraint *data = con->data;
@@ -450,6 +484,14 @@ void set_constraint_target(bConstraint *con, Object *ob, char *subtarget)
 {
        /* Set the target for this constraint  */
        switch (con->type) {
+               
+               case CONSTRAINT_TYPE_PYTHON:
+               {
+                       bPythonConstraint *data = con->data;
+                       data->tar= ob;
+                       if(subtarget) BLI_strncpy(data->subtarget, subtarget, 32);
+               }
+                       break;          
                case CONSTRAINT_TYPE_ACTION:
                {
                        bActionConstraint *data = con->data;
@@ -590,6 +632,18 @@ void *new_constraint_data (short type)
        void *result;
        
        switch (type) {
+       case CONSTRAINT_TYPE_PYTHON:
+               {
+                       bPythonConstraint *data;
+                       data = MEM_callocN(sizeof(bPythonConstraint), "pythonConstraint");
+                       
+                       /* everything should be set correctly by calloc, except for the prop->type constant.*/
+                       data->prop = MEM_callocN(sizeof(IDProperty), "PyConstraintProps");
+                       data->prop->type = IDP_GROUP;
+                       
+                       result = data;
+               }
+               break;          
        case CONSTRAINT_TYPE_KINEMATIC:
                {
                        bKinematicConstraint *data;
@@ -1272,6 +1326,31 @@ short get_constraint_target_matrix (bConstraint *con, short ownertype, void* own
                                Mat4One (mat);
                }
                break;
+       case CONSTRAINT_TYPE_PYTHON:
+               {
+                       bPythonConstraint *data;
+                       data = (bPythonConstraint*)con->data;
+                       
+                       /* special exception for curves - depsgraph issues */
+                       if (data->tar && data->tar->type == OB_CURVE) {
+                               Curve *cu= data->tar->data;
+                               
+                               /* this check is to make sure curve objects get updated on file load correctly.*/
+                               if(cu->path==NULL || cu->path->data==NULL) /* only happens on reload file, but violates depsgraph still... fix! */
+                                       makeDispListCurveTypes(data->tar, 0);                           
+                       }
+                       
+                       /* if the script doesn't set the target matrix for any reason, fall back to standard methods */
+                       if (BPY_pyconstraint_targets(data, mat) < 1) {
+                               if (data->tar) {
+                                       constraint_target_to_mat4(data->tar, data->subtarget, mat, size);
+                                       valid = 1;
+                               }
+                               else
+                                       Mat4One (mat);
+                       }
+               }
+               break;
        case CONSTRAINT_TYPE_CLAMPTO:
                {
                        bClampToConstraint *data;
@@ -1316,7 +1395,14 @@ void evaluate_constraint (bConstraint *constraint, Object *ob, short ownertype,
        case CONSTRAINT_TYPE_NULL:
        case CONSTRAINT_TYPE_KINEMATIC: /* removed */
                break;
-       
+       case CONSTRAINT_TYPE_PYTHON:
+               {
+                       bPythonConstraint *data;
+                       
+                       data= constraint->data;
+                       BPY_pyconstraint_eval(data, ob->obmat, ownertype, ownerdata, targetmat);
+               } 
+               break;
        case CONSTRAINT_TYPE_ACTION:
                {
                        bActionConstraint *data;
index 4ff4073fdbe02e8b74c315e15c4363eb4c95e432..bb4c66da0b16f7feeb1c32b8b61d97dc0889a11e 100644 (file)
@@ -274,6 +274,7 @@ void IDP_FreeGroup(IDProperty *prop)
        for (loop=prop->data.group.first; loop; loop=next)
        {
                next = loop->next;
+               BLI_remlink(&prop->data.group, loop);
                IDP_FreeProperty(loop);
                MEM_freeN(loop);
        }
index 10f9be743eab81ba1b51626ae1770c3ffaef42f2..7cfef325f6340658b5cf39ceab2465054ead958b 100644 (file)
@@ -1611,6 +1611,15 @@ static void lib_link_constraints(FileData *fd, ID *id, ListBase *conlist)
                }
 
                switch (con->type) {
+               case CONSTRAINT_TYPE_PYTHON:
+                       {
+                               bPythonConstraint *data;
+                               data= (bPythonConstraint*)con->data;
+                               data->tar = newlibadr(fd, id->lib, data->tar);
+                               data->text = newlibadr(fd, id->lib, data->text);
+                               //IDP_LibLinkProperty(data->prop, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd);
+                       }
+                       break;
                case CONSTRAINT_TYPE_ACTION:
                        {
                                bActionConstraint *data;
@@ -1717,6 +1726,11 @@ static void direct_link_constraints(FileData *fd, ListBase *lb)
        link_list(fd, lb);
        for (cons=lb->first; cons; cons=cons->next) {
                cons->data = newdataadr(fd, cons->data);
+               if (cons->type == CONSTRAINT_TYPE_PYTHON) {
+                       bPythonConstraint *data= cons->data;
+                       data->prop = newdataadr(fd, data->prop);
+                       IDP_DirectLinkProperty(data->prop, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd);
+               }
        }
 }
 
@@ -6885,6 +6899,13 @@ static void expand_constraints(FileData *fd, Main *mainvar, ListBase *lb)
 
        for (curcon=lb->first; curcon; curcon=curcon->next) {
                switch (curcon->type) {
+               case CONSTRAINT_TYPE_PYTHON:
+                       {
+                               bPythonConstraint *data = (bPythonConstraint*)curcon->data;
+                               expand_doit(fd, mainvar, data->tar);
+                               expand_doit(fd, mainvar, data->text);
+                               break;
+                       }
                case CONSTRAINT_TYPE_ACTION:
                        {
                                bActionConstraint *data = (bActionConstraint*)curcon->data;
index 226561ab97be53175bf1e567f84bdc9b9b34f4c1..2ac3063f2484898286af3d7d987b47e26a6a4ffa 100644 (file)
@@ -702,6 +702,16 @@ static void write_constraints(WriteData *wd, ListBase *conlist)
                switch (con->type) {
                case CONSTRAINT_TYPE_NULL:
                        break;
+               case CONSTRAINT_TYPE_PYTHON:
+                       {
+                               bPythonConstraint *data = (bPythonConstraint*) con->data;
+                               writestruct(wd, DATA, "bPythonConstraint", 1, data);
+                               
+                               /*Write ID Properties -- and copy this comment EXACTLY for easy finding
+                                of library blocks that implement this.*/
+                               IDP_WriteProperty(data->prop, wd);
+                       }
+                       break;
                case CONSTRAINT_TYPE_TRACKTO:
                        writestruct(wd, DATA, "bTrackToConstraint", 1, con->data);
                        break;
index db55bc5c7d744735007c377792876611934088a0..68327bdd68466e0a51a729ba26c60582b65a25d0 100644 (file)
@@ -38,6 +38,9 @@ struct ListBase;
 struct Object;
 struct bConstraint;
 struct bConstraintChannel;
+struct Text;
+
+/* generic constraint editing functions */
 
 struct bConstraint *add_new_constraint(short type);
 
@@ -58,5 +61,9 @@ char *get_con_subtarget_name(struct bConstraint *con, struct Object *target);
 void rename_constraint(struct Object *ob, struct bConstraint *con, char *newname);
 
 
+/* two special functions for PyConstraints */
+char *buildmenu_pyconstraints(struct Text *con_text, int *pyconindex);
+void validate_pyconstraint_cb(void *arg1, void *arg2);
+
 #endif
 
index 7430745418760c477dd98b2f91c0c32ac968409d..3179d1035ffcf505a82e924ebed3ca17c00e6617 100644 (file)
 #include "DNA_object_types.h"
 
 struct Action;
+struct Text;
+#ifndef __cplusplus
+struct PyObject;
+#endif
 
 /* channels reside in Object or Action (ListBase) constraintChannels */
 typedef struct bConstraintChannel{
@@ -59,6 +63,18 @@ typedef struct bConstraint{
        float           enforce;
 } bConstraint;
 
+/* Python Script Constraint */
+typedef struct bPythonConstraint {
+       Object *tar;                    /* object to use as target (if required) */
+       char subtarget[32];     /* bone to use as subtarget (if required) */
+       
+       struct Text *text;              /* text-buffer (containing script) to execute */
+       IDProperty *prop;               /* 'id-properties' used to store custom properties for constraint */
+       
+       int flag;                               /* general settings/state indicators accessed by bitmapping */
+       int pad;
+} bPythonConstraint;
+
 /* Single-target subobject constraints */
 typedef struct bKinematicConstraint{
        Object          *tar;
@@ -350,6 +366,10 @@ typedef struct bClampToConstraint {
 
 #define LIMIT_NOPARENT 0x01
 
+/* python constraint -> flag */
+#define PYCON_USETARGETS       0x01
+#define PYCON_SCRIPTERROR      0x02
+
 #define CONSTRAINT_DRAW_PIVOT 0x40
 
 /* important: these defines need to match up with PHY_DynamicTypes headerfile */
index d7db680a45837785c364655e1c7fe14629a6fe17..34271efc4f1ddc02cca6ceff442bfb76fe2fe3e4 100644 (file)
@@ -46,11 +46,16 @@ struct SpaceScript; /* DNA_space_types.h */
 struct Script; /* BPI_script.h */
 struct ScrArea; /* DNA_screen_types.h */
 struct bScreen; /* DNA_screen_types.h */
-
+struct bPythonConstraint; /* DNA_constraint_types.h */
 #ifdef __cplusplus
 extern "C" {
 #endif
 
+       void BPY_pyconstraint_eval(struct bPythonConstraint *con, float obmat[][4], short ownertype, void *ownerdata, float targetmat[][4]);
+       void BPY_pyconstraint_settings(void *arg1, void *arg2);
+       int BPY_pyconstraint_targets(struct bPythonConstraint *con, float targetmat[][4]);
+       int BPY_is_pyconstraint(struct Text *text);
+       
        void BPY_start_python( int argc, char **argv );
        void BPY_end_python( void );
        void BPY_post_start_python( void );
index f8a3c269292f85e0045986b200ed9a67f539caee..6ee06c64cc7569f603e45d6ce278abe7d124ae7e 100644 (file)
@@ -40,6 +40,7 @@
 #include "BIF_space.h"
 #include "BIF_screen.h"
 #include "BIF_toolbox.h"
+#include "BKE_action.h"        /* for get_pose_channel() */
 #include "BKE_library.h"
 #include "BKE_object.h"                /* during_scriptlink() */
 #include "BKE_text.h"
@@ -47,6 +48,7 @@
 #include "DNA_curve_types.h" /* for struct IpoDriver */
 #include "DNA_ID.h" /* ipo driver */
 #include "DNA_object_types.h" /* ipo driver */
+#include "DNA_constraint_types.h" /* for pyconstraint */
 
 #include "DNA_screen_types.h"
 #include "DNA_userdef_types.h" /* for U.pythondir */
 #include "api2_2x/Draw.h"
 #include "api2_2x/Object.h"
 #include "api2_2x/Registry.h"
+#include "api2_2x/Pose.h"
 #include "api2_2x/bpy.h" /* for the new "bpy" module */
 
+/*these next two are for pyconstraints*/
+#include "api2_2x/IDProp.h"
+#include "api2_2x/matrix.h"
+
 /* for scriptlinks */
 #include "DNA_lamp_types.h"
 #include "DNA_camera_types.h"
@@ -86,7 +93,6 @@
 /* for pydrivers (ipo drivers defined by one-line Python expressions) */
 PyObject *bpy_pydriver_Dict = NULL;
 
-
 /*
  * set up a weakref list for Armatures
  *    creates list in __main__ module dict 
@@ -160,6 +166,7 @@ PyObject *importText( char *name );
 void init_ourImport( void );
 void init_ourReload( void );
 PyObject *blender_import( PyObject * self, PyObject * args );
+PyObject *RunPython2( Text * text, PyObject * globaldict, PyObject *localdict );
 
 
 void BPY_Err_Handle( char *script_name );
@@ -462,10 +469,11 @@ void BPY_Err_Handle( char *script_name )
 
        PyErr_Fetch( &exception, &err, &tb );
 
-       if( !exception && !tb ) {
-               printf( "FATAL: spurious exception\n" );
-               return;
-       }
+       if (!script_name) script_name = "untitled";
+       //if( !exception && !tb ) {
+       //      printf( "FATAL: spurious exception\n" );
+       //      return;
+       //}
 
        strcpy( g_script_error.filename, script_name );
 
@@ -1138,6 +1146,376 @@ static float pydriver_error(IpoDriver *driver) {
        return 0.0f;
 }
 
+
+/********PyConstraints*********/
+
+int BPY_is_pyconstraint(Text *text)
+{
+       TextLine *tline = text->lines.first;
+
+       if (tline && (tline->len > 10)) {
+               char *line = tline->line;
+
+               /* Expected format: #BPYCONSTRAINT
+                * The actual checks are forgiving, so slight variations also work. */
+               if (line && line[0] == '#' && strstr(line, "BPYCONSTRAINT")) return 1;
+       }
+       return 0;
+}
+
+/* This evals py constraints. It is passed all the arguments the normal constraints recieve */
+void BPY_pyconstraint_eval(bPythonConstraint *con, float obmat[][4], short ownertype, void *ownerdata, float targetmat[][4])
+{
+       PyObject *srcmat, *tarmat, *idprop;
+       PyObject *globals;
+       PyObject *gval;
+       PyObject *pyargs, *retval;
+       MatrixObject *retmat;
+       int row, col;
+       
+       if ( !con->text ) return;
+       if ( con->flag & PYCON_SCRIPTERROR) return;
+       
+       globals = CreateGlobalDictionary();
+       
+       srcmat = newMatrixObject( (float*)obmat, 4, 4, Py_NEW );
+       tarmat = newMatrixObject( (float*)targetmat, 4, 4, Py_NEW );
+       idprop = BPy_Wrap_IDProperty( NULL, con->prop, NULL);
+       
+/*  since I can't remember what the armature weakrefs do, I'll just leave this here
+    commented out.  This function was based on pydrivers, and it might still be relevent.
+       if( !setup_armature_weakrefs()){
+               fprintf( stderr, "Oops - weakref dict setup\n");
+               return result;
+       }
+*/
+       retval = RunPython( con->text, globals );
+
+       if ( retval == NULL ) {
+               BPY_Err_Handle(con->text->id.name);
+               ReleaseGlobalDictionary( globals );
+               con->flag |= PYCON_SCRIPTERROR;
+       
+               /* free temp objects */
+               Py_XDECREF( idprop );
+               Py_XDECREF( srcmat );
+               Py_XDECREF( tarmat );
+               return;
+       }
+
+       if (retval) {Py_XDECREF( retval );}
+       retval = NULL;
+       
+       gval = PyDict_GetItemString(globals, "doConstraint");
+       if (!gval) {
+               ReleaseGlobalDictionary( globals );
+       
+               /* free temp objects */
+               Py_XDECREF( idprop );
+               Py_XDECREF( srcmat );
+               Py_XDECREF( tarmat );
+               printf("ERROR: no doConstraint function in constraint!\n");
+               return;
+       }
+       
+       /* Now for the fun part! Try and find the functions we need. */
+       if (PyFunction_Check(gval) ) {
+               pyargs = Py_BuildValue("OOO", srcmat, tarmat, idprop);
+               retval = PyObject_CallObject(gval, pyargs);
+               Py_XDECREF( pyargs );
+       } else {
+               printf("ERROR: doConstraint is supposed to be a function!\n");
+               con->flag |= PYCON_SCRIPTERROR;
+               ReleaseGlobalDictionary( globals );
+               
+               Py_XDECREF( idprop );
+               Py_XDECREF( srcmat );
+               Py_XDECREF( tarmat );
+               return;
+       }
+       
+       if (!retval) {
+               BPY_Err_Handle(con->text->id.name);
+               con->flag |= PYCON_SCRIPTERROR;
+               
+               /* free temp objects */
+               ReleaseGlobalDictionary( globals );
+               
+               Py_XDECREF( idprop );
+               Py_XDECREF( srcmat );
+               Py_XDECREF( tarmat );
+               return;
+       }
+       
+       
+       if (!PyObject_TypeCheck(retval, &matrix_Type)) {
+               printf("Error in PyConstraint - doConstraint: Function not returning a matrix!\n");
+               con->flag |= PYCON_SCRIPTERROR;
+               ReleaseGlobalDictionary( globals );
+               
+               Py_XDECREF( idprop );
+               Py_XDECREF( srcmat );
+               Py_XDECREF( tarmat );
+               Py_XDECREF( retval );
+               return;
+       }
+       
+       retmat = (MatrixObject*) retval;
+       if (retmat->rowSize != 4 || retmat->colSize != 4) {
+               printf("Error in PyConstraint - doConstraint: Matrix returned is the wrong size!\n");
+               con->flag |= PYCON_SCRIPTERROR;
+               ReleaseGlobalDictionary( globals );
+               
+               Py_XDECREF( idprop );
+               Py_XDECREF( srcmat );
+               Py_XDECREF( tarmat );
+               Py_XDECREF( retval );
+               return;
+       }       
+
+       /* this is the reverse of code taken from newMatrix() */
+       for(row = 0; row < 4; row++) {
+               for(col = 0; col < 4; col++) {
+                       obmat[row][col] = retmat->contigPtr[row*4+col];
+               }
+       }
+       
+       /* clear globals */
+       ReleaseGlobalDictionary( globals );
+       
+       /* free temp objects */
+       Py_XDECREF( idprop );
+       Py_XDECREF( srcmat );
+       Py_XDECREF( tarmat );
+       Py_XDECREF( retval );
+}
+
+/* This evaluates whether constraint uses targets, and also the target matrix 
+ * Return code of 0 = doesn't use targets, 1 = uses targets + matrix set, -1 = uses targets + matrix not set
+ */
+int BPY_pyconstraint_targets(bPythonConstraint *con, float targetmat[][4])
+{
+       PyObject *tar, *subtar;
+       PyObject *tarmat, *idprop;
+       PyObject *globals;
+       PyObject *gval, *gval2;
+       PyObject *pyargs, *retval;
+       MatrixObject *retmat;
+       bPoseChannel *pchan;
+       int row, col;
+       
+       if ( !con->text ) return 0;
+       if ( con->flag & PYCON_SCRIPTERROR) return 0;
+       
+       globals = CreateGlobalDictionary();
+       
+       tar = Object_CreatePyObject( con->tar );
+       if ( con->tar )
+               pchan = get_pose_channel( con->tar->pose, con->subtarget );
+       else
+               pchan = NULL;
+       subtar = PyPoseBone_FromPosechannel( pchan );
+       
+       tarmat = newMatrixObject( (float*)targetmat, 4, 4, Py_NEW );
+       idprop = BPy_Wrap_IDProperty( NULL, con->prop, NULL);
+       
+/*  since I can't remember what the armature weakrefs do, I'll just leave this here
+    commented out.  This function was based on pydrivers, and it might still be relevent.
+       if( !setup_armature_weakrefs()){
+               fprintf( stderr, "Oops - weakref dict setup\n");
+               return result;
+       }
+*/
+       retval = RunPython( con->text, globals );
+
+       if ( retval == NULL ) {
+               BPY_Err_Handle(con->text->id.name);
+               ReleaseGlobalDictionary( globals );
+               con->flag |= PYCON_SCRIPTERROR;
+       
+               /* free temp objects */
+               Py_XDECREF( tar );
+               Py_XDECREF( subtar );
+               Py_XDECREF( idprop );
+               Py_XDECREF( tarmat );
+               return 0;
+       }
+
+       if (retval) {Py_XDECREF( retval );}
+       retval = NULL;
+       
+       /* try to find USE_TARGET global constant */
+       gval = PyDict_GetItemString(globals, "USE_TARGET");
+       if (!gval) {
+               ReleaseGlobalDictionary( globals );
+       
+               /* free temp objects */
+               Py_XDECREF( tar );
+               Py_XDECREF( subtar );
+               Py_XDECREF( idprop );
+               Py_XDECREF( tarmat );
+               return 0;
+       }
+       
+       /* try to find doTarget function to set the target matrix */
+       gval2 = PyDict_GetItemString(globals, "doTarget");
+       if (!gval2) {
+               ReleaseGlobalDictionary( globals );
+       
+               /* free temp objects */
+               Py_XDECREF( tar );
+               Py_XDECREF( subtar );
+               Py_XDECREF( idprop );
+               Py_XDECREF( tarmat );
+               return -1;
+       }
+       
+       /* Now for the fun part! Try and find the functions we need.*/
+       if (PyFunction_Check(gval2) ) {
+               pyargs = Py_BuildValue("OOOO", tar, subtar, tarmat, idprop);
+               retval = PyObject_CallObject(gval2, pyargs);
+               Py_XDECREF( pyargs );
+       } else {
+               printf("ERROR: doTarget is supposed to be a function!\n");
+               con->flag |= PYCON_SCRIPTERROR;
+               ReleaseGlobalDictionary( globals );
+               
+               Py_XDECREF( tar );
+               Py_XDECREF( subtar );
+               Py_XDECREF( idprop );
+               Py_XDECREF( tarmat );
+               return -1;
+       }
+       
+       if (!retval) {
+               BPY_Err_Handle(con->text->id.name);
+               con->flag |= PYCON_SCRIPTERROR;
+               
+               /* free temp objects */
+               ReleaseGlobalDictionary( globals );
+               
+               Py_XDECREF( tar );
+               Py_XDECREF( subtar );
+               Py_XDECREF( idprop );
+               Py_XDECREF( tarmat );
+               return -1;
+       }
+       
+       if (!PyObject_TypeCheck(retval, &matrix_Type)) {
+               ReleaseGlobalDictionary( globals );
+               
+               Py_XDECREF( tar );
+               Py_XDECREF( subtar );
+               Py_XDECREF( idprop );
+               Py_XDECREF( tarmat );
+               Py_XDECREF( retval );
+               return -1;
+       }
+       
+       retmat = (MatrixObject*) retval;
+       if (retmat->rowSize != 4 || retmat->colSize != 4) {
+               printf("Error in PyConstraint - doTarget: Matrix returned is the wrong size!\n");
+               con->flag |= PYCON_SCRIPTERROR;
+               ReleaseGlobalDictionary( globals );
+               
+               Py_XDECREF( tar );
+               Py_XDECREF( subtar );
+               Py_XDECREF( idprop );
+               Py_XDECREF( tarmat );
+               Py_XDECREF( retval );
+               return -1;
+       }       
+
+       /* this is the reverse of code taken from newMatrix() */
+       for(row = 0; row < 4; row++) {
+               for(col = 0; col < 4; col++) {
+                       targetmat[row][col] = retmat->contigPtr[row*4+col];
+               }
+       }
+       
+       /* clear globals */
+       ReleaseGlobalDictionary( globals );
+       
+       /* free temp objects */
+       Py_XDECREF( tar );
+       Py_XDECREF( subtar );
+       Py_XDECREF( idprop );
+       Py_XDECREF( tarmat );
+       Py_XDECREF( retval );
+       return 1;
+}
+
+/* This draws+handles the user-defined interface for editing pyconstraints idprops */
+void BPY_pyconstraint_settings(void *arg1, void *arg2)
+{
+       bPythonConstraint *con= (bPythonConstraint *)arg1;
+       PyObject *idprop;
+       PyObject *globals;
+       PyObject *gval;
+       PyObject *retval;
+       
+       if ( !con->text ) return;
+       if ( con->flag & PYCON_SCRIPTERROR) return;
+       
+       globals = CreateGlobalDictionary();
+       
+       idprop = BPy_Wrap_IDProperty( NULL, con->prop, NULL);
+       
+       retval = RunPython( con->text, globals );
+
+       if ( retval == NULL ) {
+               BPY_Err_Handle(con->text->id.name);
+               ReleaseGlobalDictionary( globals );
+               con->flag |= PYCON_SCRIPTERROR;
+       
+               /* free temp objects */
+               Py_XDECREF( idprop );
+               return;
+       }
+
+       if (retval) {Py_XDECREF( retval );}
+       retval = NULL;
+       
+       gval = PyDict_GetItemString(globals, "getSettings");
+       if (!gval) {
+               printf("ERROR: no getSettings function in constraint!");
+               
+               /* free temp objects */
+               ReleaseGlobalDictionary( globals );
+               Py_XDECREF( idprop );
+               return;
+       }
+       
+       /* Now for the fun part! Try and find the functions we need. */
+       if (PyFunction_Check(gval) ) {
+               retval = PyObject_CallFunction(gval, "O", idprop);
+       } else {
+               printf("ERROR: getSettings is supposed to be a function!\n");
+               ReleaseGlobalDictionary( globals );
+               
+               Py_XDECREF( idprop );
+               return;
+       }
+       
+       if (!retval) {
+               BPY_Err_Handle(con->text->id.name);
+               con->flag |= PYCON_SCRIPTERROR;
+               
+               /* free temp objects */
+               ReleaseGlobalDictionary( globals );
+               Py_XDECREF( idprop );
+               return;
+       }
+       else {
+               /* clear globals */
+               ReleaseGlobalDictionary( globals );
+               
+               /* free temp objects */
+               Py_XDECREF( idprop );
+               return;
+       }
+}
+
 /* Update function, it gets rid of pydrivers global dictionary, forcing
  * BPY_pydriver_eval to recreate it. This function is used to force
  * reloading the Blender text module "pydrivers.py", if available, so
index 55e7e480d6b124bac1cf35d771dbe4616e5cfadd..71dbef5f2d55dbcafc9d19f431ef2368075c6461 100644 (file)
@@ -1918,6 +1918,8 @@ static PyObject *M_Constraint_TypeDict( void )
                                PyInt_FromLong( CONSTRAINT_TYPE_RIGIDBODYJOINT ) );
                PyConstant_Insert( d, "CLAMPTO", 
                                PyInt_FromLong( CONSTRAINT_TYPE_CLAMPTO ) );
+               PyConstant_Insert( d, "PYTHON",
+                               PyInt_FromLong( CONSTRAINT_TYPE_PYTHON ) );
        }
        return S;
 }
index bb5ac2ba68268f85c353525af7675716326f44cb..7d2c5dd5f003befda844bf93a2d2894aab80512c 100644 (file)
 #include "DNA_vfont_types.h"
 #include "DNA_view3d_types.h"
 #include "DNA_world_types.h"
+#include "DNA_text_types.h"
 
 #include "BKE_anim.h"
 #include "BKE_armature.h"
 #include "BSE_edit.h"
 
 #include "BDR_editobject.h"
+#include "BPY_extern.h"
 
 #include "butspace.h" // own module
 
@@ -328,6 +330,9 @@ void get_constraint_typestring (char *str, void *con_v)
        bConstraint *con= con_v;
 
        switch (con->type){
+       case CONSTRAINT_TYPE_PYTHON:
+               strcpy(str, "Script");
+               return;
        case CONSTRAINT_TYPE_CHILDOF:
                strcpy (str, "Child Of");
                return;
@@ -508,6 +513,7 @@ void autocomplete_vgroup(char *str, void *arg_v)
        }
 }
 
+/* draw panel showing settings for a constraint */
 static void draw_constraint (uiBlock *block, ListBase *list, bConstraint *con, short *xco, short *yco)
 {
        Object *ob= OBACT, *target;
@@ -599,10 +605,60 @@ static void draw_constraint (uiBlock *block, ListBase *list, bConstraint *con, s
        }
        else {
                switch (con->type){
+               case CONSTRAINT_TYPE_PYTHON:
+                       {
+                               bPythonConstraint *data = con->data;
+                               uiBut *but2;
+                               static int pyconindex=0;
+                               char *menustr;
+                               
+                               height = 90;
+                               uiDefBut(block, ROUNDBOX, B_DIFF, "", *xco-10, *yco-height, width+40, height-1, NULL, 5.0, 0.0, 12, rb_col, ""); 
+                               
+                               uiDefBut(block, LABEL, B_CONSTRAINT_TEST, "Script:", *xco+60, *yco-24, 55, 18, NULL, 0.0, 0.0, 0.0, 0.0, ""); 
+                               
+                               /* do the scripts menu */
+                               menustr = buildmenu_pyconstraints(data->text, &pyconindex);
+                               but2 = uiDefButI(block, MENU, B_CONSTRAINT_TEST, menustr,
+                                     *xco+120, *yco-24, 150, 20, &pyconindex,
+                                     0.0, 1.0, 0, 0, "Set the Script Constraint to use");
+                               uiButSetFunc(but2, validate_pyconstraint_cb, data, &pyconindex);
+                               MEM_freeN(menustr);     
+                               
+                               uiDefBut(block, LABEL, B_CONSTRAINT_TEST, "Target:", *xco+60, *yco-48, 55, 18, NULL, 0.0, 0.0, 0.0, 0.0, ""); 
+                               if (data->flag & PYCON_USETARGETS) {
+                                       /* Draw target parameters */ 
+                                       uiBlockBeginAlign(block);
+                                       uiDefIDPoinBut(block, test_obpoin_but, ID_OB, B_CONSTRAINT_CHANGETARGET, "OB:", *xco+120, *yco-48, 150, 18, &data->tar, "Target Object"); 
+
+                                       if (is_armature_target) {
+                                               but= uiDefBut(block, TEX, B_CONSTRAINT_CHANGETARGET, "BO:", *xco+120, *yco-66,150,18, &data->subtarget, 0, 24, 0, 0, "Subtarget Bone");
+                                               uiButSetCompleteFunc(but, autocomplete_bone, (void *)data->tar);
+                                       }
+                                       else {
+                                               strcpy (data->subtarget, "");
+                                       }
+                                       
+                                       uiBlockEndAlign(block);
+                               }
+                               else {
+                                       /* Draw indication that no target needed */
+                                       uiDefBut(block, LABEL, B_CONSTRAINT_TEST, "Not Applicable", *xco+120, *yco-48, 150, 18, NULL, 0.0, 0.0, 0.0, 0.0, ""); 
+                               }
+                               
+                               /* settings */
+                               uiBlockBeginAlign(block);
+                               but=uiDefBut(block, BUT, B_CONSTRAINT_TEST, "Options", *xco, *yco-88, (width/2),18, NULL, 0, 24, 0, 0, "Change some of the constraint's settings.");
+                               uiButSetFunc(but, BPY_pyconstraint_settings, data, NULL);
+                               
+                               uiDefBut(block, BUT, B_CONSTRAINT_TEST, "Refresh", *xco+((width/2)+10), *yco-88, (width/2),18, NULL, 0, 24, 0, 0, "Force constraint to refresh it's settings");
+                               uiBlockEndAlign(block);
+                       }
+                       break;
                case CONSTRAINT_TYPE_ACTION:
                        {
                                bActionConstraint *data = con->data;
-
+                               
                                height = 88;
                                uiDefBut(block, ROUNDBOX, B_DIFF, "", *xco-10, *yco-height, width+40,height-1, NULL, 5.0, 0.0, 12, rb_col, ""); 
                                
@@ -1336,8 +1392,13 @@ static uiBlock *add_constraintmenu(void *arg_unused)
        }
        uiDefBut(block, SEPR, 0, "",                                    0, yco-=6, 120, 6, NULL, 0.0, 0.0, 0, 0, "");
 
+       uiDefBut(block, BUTM, B_CONSTRAINT_ADD_PYTHON, "Script", 0, yco-=20, 160, 19, NULL, 0.0, 0.0, 1, 0, "");
+       
+       uiDefBut(block, SEPR, 0, "",                                    0, yco-=6, 120, 6, NULL, 0.0, 0.0, 0, 0, "");
+       
        uiDefBut(block, BUTM, B_CONSTRAINT_ADD_NULL,"Null",             0, yco-=20, 160, 19, NULL, 0.0, 0.0, 1, 0, "");
        
+       
        uiTextBoundsBlock(block, 50);
        uiBlockSetDirection(block, UI_DOWN);
                
@@ -1360,7 +1421,17 @@ void do_constraintbuts(unsigned short event)
                if(ob->pose) ob->pose->flag |= POSE_RECALC;     // checks & sorts pose channels
                DAG_scene_sort(G.scene);
                break;
-               
+       
+       case B_CONSTRAINT_ADD_PYTHON:
+               {
+                       bConstraint *con;
+                       
+                       con = add_new_constraint(CONSTRAINT_TYPE_PYTHON);
+                       add_constraint_to_active(ob, con);
+
+                       BIF_undo_push("Add constraint");
+               }
+               break;
        case B_CONSTRAINT_ADD_NULL:
                {
                        bConstraint *con;
index 2eef0f55978e640c6eab8c6ecc4098674db28701..32cfde2eb86a802345889147a2bef6f1714b9c26 100644 (file)
@@ -45,6 +45,7 @@
 #include "DNA_object_types.h"
 #include "DNA_scene_types.h"
 #include "DNA_screen_types.h"
+#include "DNA_text_types.h"
 #include "DNA_view3d_types.h"
 
 #include "BKE_action.h"
@@ -52,6 +53,7 @@
 #include "BKE_constraint.h"
 #include "BKE_depsgraph.h"
 #include "BKE_global.h"
+#include "BKE_main.h"
 #include "BKE_ipo.h"
 #include "BKE_object.h"
 #include "BKE_utildefines.h"
@@ -65,6 +67,8 @@
 #include "BIF_space.h"
 #include "BIF_toolbox.h"
 
+#include "BPY_extern.h"
+
 #include "blendef.h"
 #include "nla.h"
 #include "mydevice.h"
@@ -247,7 +251,14 @@ char *get_con_subtarget_name(bConstraint *con, Object *target)
         * to the name for this constraints subtarget ... NULL otherwise
         */
        switch (con->type) {
-
+               case CONSTRAINT_TYPE_PYTHON:
+               {
+                       bPythonConstraint *data = con->data;
+                       if (data->flag & PYCON_USETARGETS) {
+                               if (data->tar==target) return data->subtarget;
+                       }
+               }
+               break;
                case CONSTRAINT_TYPE_ACTION:
                {
                        bActionConstraint *data = con->data;
@@ -376,6 +387,51 @@ static void test_constraints (Object *owner, const char* substring)
                        curcon->flag &= ~CONSTRAINT_DISABLE;
                        
                        switch (curcon->type){
+                               case CONSTRAINT_TYPE_PYTHON:
+                               {
+                                       bPythonConstraint *data = curcon->data;
+                                       float dummy_matrix[4][4];
+                                       
+                                       /* is there are valid script? */
+                                       if (!data->text) {
+                                               curcon->flag |= CONSTRAINT_DISABLE;
+                                               break;
+                                       }
+                                       else if (!BPY_is_pyconstraint(data->text)) {
+                                               curcon->flag |= CONSTRAINT_DISABLE;
+                                               break;
+                                       }
+                                       data->flag &= ~PYCON_SCRIPTERROR;
+                                       
+                                       /* does the constraint require target input? */
+                                       if (BPY_pyconstraint_targets(data, dummy_matrix))
+                                               data->flag |= PYCON_USETARGETS;
+                                       else
+                                               data->flag &= ~PYCON_USETARGETS;
+                                       
+                                       /* check whether we have a valid target */
+                                       if (data->flag & PYCON_USETARGETS) {
+                                               /* validate target */
+                                               if (!exist_object(data->tar)) {
+                                                       data->tar = NULL;
+                                                       curcon->flag |= CONSTRAINT_DISABLE;
+                                                       break;
+                                               }
+                                               
+                                               if ( (data->tar == owner) &&
+                                                        (!get_named_bone(get_armature(owner), 
+                                                                                         data->subtarget))) {
+                                                       curcon->flag |= CONSTRAINT_DISABLE;
+                                                       break;
+                                               }
+                                       }
+                                       else {
+                                               /* don't hold onto target */
+                                               data->tar = NULL;
+                                               BLI_strncpy(data->subtarget, "", 32);
+                                       }
+                               }
+                                       break;
                                case CONSTRAINT_TYPE_ACTION:
                                {
                                        bActionConstraint *data = curcon->data;
@@ -688,21 +744,21 @@ void add_constraint(int only_IK)
        else {
                if(pchanact) {
                        if(pchansel)
-                               nr= pupmenu("Add Constraint to Active Bone%t|Copy Location%x1|Copy Rotation%x2|Copy Scale%x8|Limit Location%x13|Limit Rotation%x14|Limit Scale%x15|Track To%x3|Floor%x4|Locked Track%x5|Stretch To%x7|Action%x16");
+                               nr= pupmenu("Add Constraint to Active Bone%t|Copy Location%x1|Copy Rotation%x2|Copy Scale%x8|%l|Limit Location%x13|Limit Rotation%x14|Limit Scale%x15|%l|Track To%x3|Floor%x4|Locked Track%x5|Stretch To%x7|Action%x16|%l|Script%x18");
                        else if(obsel && obsel->type==OB_CURVE)
-                               nr= pupmenu("Add Constraint to Active Object%t|Copy Location%x1|Copy Rotation%x2|Copy Scale%x8|Limit Location%x13|Limit Rotation%x14|Limit Scale%x15|Track To%x3|Floor%x4|Locked Track%x5|Follow Path%x6|Clamp To%x17|Stretch To%x7|Action%x16");
+                               nr= pupmenu("Add Constraint to Active Object%t|Copy Location%x1|Copy Rotation%x2|Copy Scale%x8|%l|Limit Location%x13|Limit Rotation%x14|Limit Scale%x15|%l|Track To%x3|Floor%x4|Locked Track%x5|Follow Path%x6|Clamp To%x17|Stretch To%x7|Action%x16|%l|Script%x18");
                        else if(obsel)
-                               nr= pupmenu("Add Constraint to Active Object%t|Copy Location%x1|Copy Rotation%x2|Copy Scale%x8|Limit Location%x13|Limit Rotation%x14|Limit Scale%x15|Track To%x3|Floor%x4|Locked Track%x5|Stretch To%x7|Action%x16");
+                               nr= pupmenu("Add Constraint to Active Object%t|Copy Location%x1|Copy Rotation%x2|Copy Scale%x8|%l|Limit Location%x13|Limit Rotation%x14|Limit Scale%x15|%l|Track To%x3|Floor%x4|Locked Track%x5|Stretch To%x7|Action%x16|%l|Script%x18");
                        else
-                               nr= pupmenu("Add Constraint to New Empty Object%t|Copy Location%x1|Copy Rotation%x2|Copy Scale%x8|Limit Location%x13|Limit Rotation%x14|Limit Scale%x15|Track To%x3|Floor%x4|Locked Track%x5|Stretch To%x7");
+                               nr= pupmenu("Add Constraint to New Empty Object%t|Copy Location%x1|Copy Rotation%x2|Copy Scale%x8|%l|Limit Location%x13|Limit Rotation%x14|Limit Scale%x15|%l|Track To%x3|Floor%x4|Locked Track%x5|Stretch To%x7|%l|Script%x18");
                }
                else {
                        if(obsel && obsel->type==OB_CURVE)
-                               nr= pupmenu("Add Constraint to Active Object%t|Copy Location%x1|Copy Rotation%x2|Copy Scale%x8|Limit Location%x13|Limit Rotation%x14|Limit Scale%x15|Track To%x3|Floor%x4|Locked Track%x5|Follow Path%x6|Clamp To%x17");
+                               nr= pupmenu("Add Constraint to Active Object%t|Copy Location%x1|Copy Rotation%x2|Copy Scale%x8|Limit Location%x13|Limit Rotation%x14|Limit Scale%x15|%l|Track To%x3|Floor%x4|Locked Track%x5|Follow Path%x6|Clamp To%x17|%l|Script%x18");
                        else if(obsel)
-                               nr= pupmenu("Add Constraint to Active Object%t|Copy Location%x1|Copy Rotation%x2|Copy Scale%x8|Limit Location%x13|Limit Rotation%x14|Limit Scale%x15|Track To%x3|Floor%x4|Locked Track%x5");
+                               nr= pupmenu("Add Constraint to Active Object%t|Copy Location%x1|Copy Rotation%x2|Copy Scale%x8|Limit Location%x13|Limit Rotation%x14|Limit Scale%x15|%l|Track To%x3|Floor%x4|Locked Track%x5|%l|Script%x18");
                        else
-                               nr= pupmenu("Add Constraint to New Empty Object%t|Copy Location%x1|Copy Rotation%x2|Copy Scale%x8|Limit Location%x13|Limit Rotation%x14|Limit Scale%x15|Track To%x3|Floor%x4|Locked Track%x5");
+                               nr= pupmenu("Add Constraint to New Empty Object%t|Copy Location%x1|Copy Rotation%x2|Copy Scale%x8|Limit Location%x13|Limit Rotation%x14|Limit Scale%x15|%l|Track To%x3|Floor%x4|Locked Track%x5|%l|Script%x18");
                }
        }
        
@@ -762,6 +818,21 @@ void add_constraint(int only_IK)
                        cu->flag |= CU_PATH;
                        con = add_new_constraint(CONSTRAINT_TYPE_CLAMPTO);
                }
+               else if(nr==18) {       
+                       char *menustr;
+                       int scriptint= 0, dummy_active=0;
+                       
+                       /* popup a list of usable scripts */
+                       menustr = buildmenu_pyconstraints(NULL, &dummy_active);
+                       scriptint = pupmenu(menustr);
+                       MEM_freeN(menustr);
+                       
+                       /* only add constraint if a script was chosen */
+                       if (scriptint) {
+                               con = add_new_constraint(CONSTRAINT_TYPE_PYTHON);
+                               validate_pyconstraint_cb(con->data, &scriptint);
+                       }
+               }
                
                if(con==NULL) return;   /* paranoia */
                
@@ -921,3 +992,53 @@ void rename_constraint(Object *ob, bConstraint *con, char *oldname)
        
 }
 
+/* ********************** CONSTRAINT-SPECIFIC STUFF ********************* */
+/* ------------- PyConstraints ------------------ */
+
+/* this callback sets the text-file to be used for selected menu item */
+void validate_pyconstraint_cb(void *arg1, void *arg2)
+{
+       bPythonConstraint *data = arg1;
+       Text *text;
+       int index = *((int *)arg2);
+       int i;
+       
+       /* innovative use of a for loop to search */
+       for (text=G.main->text.first, i=1; text && index!=i; i++, text=text->id.next);
+       data->text = text;
+}
+
+/* this returns a string for the list of usable pyconstraint script names */
+char *buildmenu_pyconstraints(Text *con_text, int *pyconindex)
+{
+       Text *text;
+       char *menustr = MEM_callocN(128, "menustr pyconstraints");
+       char *name, stmp[128];
+       int buf = 128;
+       int used = strlen("Scripts: %t") + 1;
+       int i;
+               
+       sprintf(menustr, "%s", "Scripts: %t");
+       
+       for (text=G.main->text.first, i=1; text; i++, text=text->id.next) {
+               /* this is important to ensure that right script is shown as active */
+               if (text == con_text) *pyconindex = i;
+               
+               /* menu entry is length of name + 3(len(|%X)) + 6 characters for the int.*/
+               if (BPY_is_pyconstraint(text)) {
+                       name= text->id.name;
+                       if (strlen(name)+used+10 >= buf) {
+                               char *newbuf = MEM_callocN(buf+128, "menustr pyconstraints 2");
+                               memcpy(newbuf, menustr, used);
+                               MEM_freeN(menustr);
+                               menustr = newbuf;
+                               buf += 128;
+                       }
+                       sprintf(stmp, "|%s%%x%d", name, i);
+                       strcat(menustr, stmp);
+                       used += strlen(name)+10;
+               }
+       }
+       
+       return menustr;
+}
index 92a5a49a681631bb02291d4fe66367173312975a..32268c35210d529540c451b9c423f6bed0b6b42c 100644 (file)
@@ -52,6 +52,8 @@
 #include "DNA_screen_types.h"
 #include "DNA_space_types.h"
 #include "DNA_text_types.h"
+#include "DNA_constraint_types.h"
+#include "DNA_action_types.h"
 
 #include "BIF_drawtext.h"
 #include "BIF_interface.h"
 #include "BIF_screen.h"
 #include "BIF_space.h"
 #include "BIF_toolbox.h"
+
 #include "BKE_global.h"
 #include "BKE_library.h"
 #include "BKE_main.h"
 #include "BKE_sca.h"
 #include "BKE_text.h"
+#include "BKE_depsgraph.h"
+
 #include "BSE_filesel.h"
 
 #include "BPY_extern.h"
@@ -135,27 +140,68 @@ void do_text_buttons(unsigned short event)
                break;
                
        case B_TEXTDELETE:
-               
-               text= st->text;
-               if (!text) return;
-               
-               /* make the previous text active, if its not there make the next text active */
-               if (st->text->id.prev) {
-                       st->text = st->text->id.prev;
-                       pop_space_text(st);
-               } else if (st->text->id.next) {
-                       st->text = st->text->id.next;
-                       pop_space_text(st);
-               }
+               {
+                       Object *obt;
+                       bConstraint *con;
+                       int update;
                        
-               BPY_clear_bad_scriptlinks(text);
-               free_text_controllers(text);
-               
-               unlink_text(text);
-               free_libblock(&G.main->text, text);
-               
-               allqueue(REDRAWTEXT, 0);
-               allqueue(REDRAWHEADERS, 0);
+                       text= st->text;
+                       if (!text) return;
+                       
+                       /* make the previous text active, if its not there make the next text active */
+                       if (st->text->id.prev) {
+                               st->text = st->text->id.prev;
+                               pop_space_text(st);
+                       } else if (st->text->id.next) {
+                               st->text = st->text->id.next;
+                               pop_space_text(st);
+                       }
+                       
+                       /*check all pyconstraints*/
+                       for (obt=G.main->object.first; obt; obt=obt->id.next) {
+                               update = 0;
+                               if(obt->type==OB_ARMATURE && obt->pose) {
+                                       bPoseChannel *pchan;
+                                       for(pchan= obt->pose->chanbase.first; pchan; pchan= pchan->next) {
+                                               for (con = pchan->constraints.first; con; con=con->next) {
+                                                       if (con->type==CONSTRAINT_TYPE_PYTHON) {
+                                                               bPythonConstraint *data = con->data;
+                                                               if (data->text==text) data->text = NULL;
+                                                               update = 1;
+                                                               
+                                                       }
+                                               }
+                                       }
+                               }
+                               for (con = obt->constraints.first; con; con=con->next) {
+                                       if (con->type==CONSTRAINT_TYPE_PYTHON) {
+                                               bPythonConstraint *data = con->data;
+                                               if (data->text==text) data->text = NULL;
+                                               update = 1;
+                                       }
+                               }
+                               
+                               if (update) {
+                                       DAG_object_flush_update(G.scene, obt, OB_RECALC_DATA);
+                               }
+                       }
+                       
+                       BPY_clear_bad_scriptlinks(text);
+                       free_text_controllers(text);
+                       
+                       unlink_text(text);
+                       free_libblock(&G.main->text, text);
+                       
+                       allqueue(REDRAWTEXT, 0);
+                       allqueue(REDRAWHEADERS, 0);
+                       
+                       /*for if any object constraints were changed.*/
+                       allqueue(REDRAWVIEW3D, 0);
+                       allqueue(REDRAWBUTSOBJECT, 0);
+                       allqueue(REDRAWBUTSEDIT, 0);
+                       
+                       BIF_undo_push("Delete Text");
+               }
                break;
                
 /*
@@ -265,6 +311,42 @@ static void do_text_filemenu(void *arg, int event)
        case 6:
                run_python_script(st);
                break;
+       case 7:
+       {
+               Object *obt;
+               bConstraint *con;
+               short update;
+               
+               /* check all pyconstraints */
+               for (obt=G.main->object.first; obt; obt=obt->id.next) {
+                       update = 0;
+                       if(obt->type==OB_ARMATURE && obt->pose) {
+                               bPoseChannel *pchan;
+                               for(pchan= obt->pose->chanbase.first; pchan; pchan= pchan->next) {
+                                       for (con = pchan->constraints.first; con; con=con->next) {
+                                               if (con->type==CONSTRAINT_TYPE_PYTHON) {
+                                                       bPythonConstraint *data = con->data;
+                                                       if (data->text==text) data->flag = 0;
+                                                       update = 1;
+                                                       
+                                               }
+                                       }
+                               }
+                       }
+                       for (con = obt->constraints.first; con; con=con->next) {
+                               if (con->type==CONSTRAINT_TYPE_PYTHON) {
+                                       bPythonConstraint *data = con->data;
+                                       if (data->text==text) data->flag = 0;
+                                       update = 1;
+                               }
+                       }
+                       
+                       if (update) {
+                               DAG_object_flush_update(G.scene, obt, OB_RECALC_DATA);
+                       }
+               }
+       }
+               break;
        default:
                break;
        }
@@ -608,13 +690,23 @@ static uiBlock *text_filemenu(void *arg_unused)
 
        uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "New|Alt N", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 0, 1, "");
        uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Open...|Alt O", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 0, 2, "");
+       
        if(text) {
                uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Reopen|Alt R", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 0, 3, "");
+               
                uiDefBut(block, SEPR, 0, "",        0, yco-=6, menuwidth, 6, NULL, 0.0, 0.0, 0, 0, "");
+               
                uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Save|Alt S", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 0, 4, "");
                uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Save As...", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 0, 5, "");
+               
                uiDefBut(block, SEPR, 0, "",        0, yco-=6, menuwidth, 6, NULL, 0.0, 0.0, 0, 0, "");
+               
                uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Run Python Script|Alt P", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 0, 6, "");
+               
+               if (BPY_is_pyconstraint(text))
+                       uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Refresh All PyConstraints", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 0, 7, "");
+                       
+               uiDefBut(block, SEPR, 0, "",        0, yco-=6, menuwidth, 6, NULL, 0.0, 0.0, 0, 0, "");
        }
        
        uiDefIconTextBlockBut(block, text_template_scriptsmenu, NULL, ICON_RIGHTARROW_THIN, "Script Templates", 0, yco-=20, 120, 19, "");