diff options
author | Joshua Leung <aligorith@gmail.com> | 2007-06-18 11:41:21 +0400 |
---|---|---|
committer | Joshua Leung <aligorith@gmail.com> | 2007-06-18 11:41:21 +0400 |
commit | 01e8789f3f950a15aea9947ebadb1dcdcccf6a7e (patch) | |
tree | 6c9d3fa3b93d427728c1a0ce619f765227c9e677 /source | |
parent | 770291b9ea1ec03d98b6bae4fd2a2d3f0091be41 (diff) |
== PyConstraints ==
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
Diffstat (limited to 'source')
-rw-r--r-- | source/blender/blenkernel/bad_level_call_stubs/stubs.c | 11 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/constraint.c | 88 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/idprop.c | 1 | ||||
-rw-r--r-- | source/blender/blenloader/intern/readfile.c | 21 | ||||
-rw-r--r-- | source/blender/blenloader/intern/writefile.c | 10 | ||||
-rw-r--r-- | source/blender/include/BIF_editconstraint.h | 7 | ||||
-rw-r--r-- | source/blender/makesdna/DNA_constraint_types.h | 20 | ||||
-rw-r--r-- | source/blender/python/BPY_extern.h | 7 | ||||
-rw-r--r-- | source/blender/python/BPY_interface.c | 388 | ||||
-rw-r--r-- | source/blender/python/api2_2x/Constraint.c | 2 | ||||
-rw-r--r-- | source/blender/src/buttons_object.c | 75 | ||||
-rw-r--r-- | source/blender/src/editconstraint.c | 137 | ||||
-rw-r--r-- | source/blender/src/header_text.c | 132 |
13 files changed, 862 insertions, 37 deletions
diff --git a/source/blender/blenkernel/bad_level_call_stubs/stubs.c b/source/blender/blenkernel/bad_level_call_stubs/stubs.c index 344f763608c..e76c53fc72c 100644 --- a/source/blender/blenkernel/bad_level_call_stubs/stubs.c +++ b/source/blender/blenkernel/bad_level_call_stubs/stubs.c @@ -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){} diff --git a/source/blender/blenkernel/intern/constraint.c b/source/blender/blenkernel/intern/constraint.c index 97f5ed18cbe..9c03db824af 100644 --- a/source/blender/blenkernel/intern/constraint.c +++ b/source/blender/blenkernel/intern/constraint.c @@ -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; diff --git a/source/blender/blenkernel/intern/idprop.c b/source/blender/blenkernel/intern/idprop.c index 4ff4073fdbe..bb4c66da0b1 100644 --- a/source/blender/blenkernel/intern/idprop.c +++ b/source/blender/blenkernel/intern/idprop.c @@ -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); } diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index 10f9be743ea..7cfef325f63 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -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; diff --git a/source/blender/blenloader/intern/writefile.c b/source/blender/blenloader/intern/writefile.c index 226561ab97b..2ac3063f248 100644 --- a/source/blender/blenloader/intern/writefile.c +++ b/source/blender/blenloader/intern/writefile.c @@ -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; diff --git a/source/blender/include/BIF_editconstraint.h b/source/blender/include/BIF_editconstraint.h index db55bc5c7d7..68327bdd684 100644 --- a/source/blender/include/BIF_editconstraint.h +++ b/source/blender/include/BIF_editconstraint.h @@ -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 diff --git a/source/blender/makesdna/DNA_constraint_types.h b/source/blender/makesdna/DNA_constraint_types.h index 74307454187..3179d1035ff 100644 --- a/source/blender/makesdna/DNA_constraint_types.h +++ b/source/blender/makesdna/DNA_constraint_types.h @@ -39,6 +39,10 @@ #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 */ diff --git a/source/blender/python/BPY_extern.h b/source/blender/python/BPY_extern.h index d7db680a458..34271efc4f1 100644 --- a/source/blender/python/BPY_extern.h +++ b/source/blender/python/BPY_extern.h @@ -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 ); diff --git a/source/blender/python/BPY_interface.c b/source/blender/python/BPY_interface.c index f8a3c269292..6ee06c64cc7 100644 --- a/source/blender/python/BPY_interface.c +++ b/source/blender/python/BPY_interface.c @@ -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 */ @@ -66,8 +68,13 @@ #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 diff --git a/source/blender/python/api2_2x/Constraint.c b/source/blender/python/api2_2x/Constraint.c index 55e7e480d6b..71dbef5f2d5 100644 --- a/source/blender/python/api2_2x/Constraint.c +++ b/source/blender/python/api2_2x/Constraint.c @@ -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; } diff --git a/source/blender/src/buttons_object.c b/source/blender/src/buttons_object.c index bb5ac2ba682..7d2c5dd5f00 100644 --- a/source/blender/src/buttons_object.c +++ b/source/blender/src/buttons_object.c @@ -109,6 +109,7 @@ #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" @@ -143,6 +144,7 @@ #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; diff --git a/source/blender/src/editconstraint.c b/source/blender/src/editconstraint.c index 2eef0f55978..32cfde2eb86 100644 --- a/source/blender/src/editconstraint.c +++ b/source/blender/src/editconstraint.c @@ -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; +} diff --git a/source/blender/src/header_text.c b/source/blender/src/header_text.c index 92a5a49a681..32268c35210 100644 --- a/source/blender/src/header_text.c +++ b/source/blender/src/header_text.c @@ -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" @@ -59,11 +61,14 @@ #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, ""); |