diff --git a/source/blender/blenkernel/BKE_library.h b/source/blender/blenkernel/BKE_library.h index cc6d15f7bcb..b0e82a3b7f7 100644 --- a/source/blender/blenkernel/BKE_library.h +++ b/source/blender/blenkernel/BKE_library.h @@ -47,6 +47,7 @@ void *copy_libblock(void *rt); void id_lib_extern(struct ID *id); void id_us_plus(struct ID *id); +int dup_id(struct ListBase *lb, struct ID *id, const char *name); int new_id(struct ListBase *lb, struct ID *id, const char *name); struct ListBase *wich_libbase(struct Main *mainlib, short type); diff --git a/source/blender/blenkernel/intern/library.c b/source/blender/blenkernel/intern/library.c index 1dbbe2f92f2..d8fab3cf696 100644 --- a/source/blender/blenkernel/intern/library.c +++ b/source/blender/blenkernel/intern/library.c @@ -788,7 +788,7 @@ static void sort_alpha_id(ListBase *lb, ID *id) } -int new_id(ListBase *lb, ID *id, const char *tname) +int dup_id(ListBase *lb, ID *id, const char *tname) /* only for local blocks: external en indirect blocks already have a unique ID */ /* return 1: created a new name */ { @@ -876,12 +876,21 @@ int new_id(ListBase *lb, ID *id, const char *tname) /* this format specifier is from hell... */ sprintf(id->name+2, "%s.%.3d", left, nr); } - - sort_alpha_id(lb, id); - return 1; } +int new_id(ListBase *lb, ID *id, const char *tname) +/* only for local blocks: external en indirect blocks already have a unique ID */ +/* return 1: created a new name */ +{ + int result = dup_id( lb, id, tname ); + + if( result ) + sort_alpha_id(lb, id); + + return result; +} + // next to indirect usage in read/writefile also in editobject.c scene.c void clear_id_newpoins() { diff --git a/source/blender/blenloader/BLO_readfile.h b/source/blender/blenloader/BLO_readfile.h index 2d1a8b7db05..7f621410a4f 100644 --- a/source/blender/blenloader/BLO_readfile.h +++ b/source/blender/blenloader/BLO_readfile.h @@ -219,7 +219,7 @@ BLO_blendhandle_close( char *BLO_gethome(void); int BLO_has_bfile_extension(char *str); void BLO_library_append(struct SpaceFile *sfile, char *dir, int idcode); -void BLO_script_library_append(BlendHandle *bh, char *dir, char *name, int idcode, short flag); +void BLO_script_library_append(BlendHandle *bh, char *dir, char *name, int idcode, short flag, struct Scene *scene); BlendFileData* blo_read_blendafterruntime(int file, int actualsize, BlendReadError *error_r); diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index dd4ba03aa10..9aebcd75116 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -7282,7 +7282,7 @@ static void append_id_part(FileData *fd, Main *mainvar, ID *id, ID **id_r) /* common routine to append/link something from a library */ -static void library_append( SpaceFile *sfile, char *dir, int idcode, +static void library_append( Scene *scene, SpaceFile *sfile, char *dir, int idcode, int totsel, FileData *fd) { Main *mainl; @@ -7299,13 +7299,13 @@ static void library_append( SpaceFile *sfile, char *dir, int idcode, curlib= mainl->curlib; if(totsel==0) { - append_named_part(fd, mainl, G.scene, sfile->file, idcode, sfile->flag); + append_named_part(fd, mainl, scene, sfile->file, idcode, sfile->flag); } else { int a; for(a=0; atotfile; a++) { if(sfile->filelist[a].flags & ACTIVE) { - append_named_part(fd, mainl, G.scene, sfile->filelist[a].relname, idcode, sfile->flag); + append_named_part(fd, mainl, scene, sfile->filelist[a].relname, idcode, sfile->flag); } } } @@ -7333,9 +7333,9 @@ static void library_append( SpaceFile *sfile, char *dir, int idcode, /* give a base to loose objects. If group append, do it for objects too */ if(idcode==ID_GR) - give_base_to_objects(G.scene, &(G.main->object), (sfile->flag & FILE_LINK)?NULL:curlib); + give_base_to_objects(scene, &(G.main->object), (sfile->flag & FILE_LINK)?NULL:curlib); else - give_base_to_objects(G.scene, &(G.main->object), NULL); + give_base_to_objects(scene, &(G.main->object), NULL); /* has been removed... erm, why? s..ton) */ /* 20040907: looks like they are give base already in append_named_part(); -Nathan L */ @@ -7353,7 +7353,8 @@ static void library_append( SpaceFile *sfile, char *dir, int idcode, /* append to G.scene */ /* this should probably be moved into the Python code anyway */ -void BLO_script_library_append(BlendHandle *bh, char *dir, char *name, int idcode, short flag) +void BLO_script_library_append(BlendHandle *bh, char *dir, char *name, + int idcode, short flag, Scene *scene ) { SpaceFile sfile; @@ -7364,7 +7365,7 @@ void BLO_script_library_append(BlendHandle *bh, char *dir, char *name, int idcod /* try to append the requested object */ - library_append( &sfile, dir, idcode, 0, (FileData *)bh ); + library_append( scene, &sfile, dir, idcode, 0, (FileData *)bh ); /* do we need to do this? */ DAG_scene_sort(G.scene); @@ -7407,7 +7408,7 @@ void BLO_library_append(SpaceFile *sfile, char *dir, int idcode) if(sfile->flag & FILE_AUTOSELECT) scene_deselect_all(G.scene); - library_append( sfile, dir, idcode, totsel, fd ); + library_append( G.scene, sfile, dir, idcode, totsel, fd ); /* when not linking (appending)... */ if((sfile->flag & FILE_LINK)==0) { diff --git a/source/blender/python/BPY_interface.c b/source/blender/python/BPY_interface.c index c8173866f97..3b54033d897 100644 --- a/source/blender/python/BPY_interface.c +++ b/source/blender/python/BPY_interface.c @@ -159,6 +159,7 @@ void BPY_start_python( int argc, char **argv ) static int argc_copy = 0; static char **argv_copy = NULL; int first_time = argc; + char *str, version[16]; /* we keep a copy of the values of argc and argv so that the game engine * can call BPY_start_python(0, NULL) whenever a game ends, without having @@ -178,8 +179,8 @@ void BPY_start_python( int argc, char **argv ) * print an error if not found. See init_syspath() for the * rest of our init msgs. */ - // Py_GetVersion() returns a ptr to astatic string - printf( "Compiled with Python version %.5s.\n", Py_GetVersion() ); + + printf( "Compiled with Python version %s.\n", PY_VERSION ); //Initialize the TOP-LEVEL modules PyImport_ExtendInittab(BPy_Inittab_Modules); diff --git a/source/blender/python/api2_2x/Blender.c b/source/blender/python/api2_2x/Blender.c index 324975fb739..165b242fc04 100644 --- a/source/blender/python/api2_2x/Blender.c +++ b/source/blender/python/api2_2x/Blender.c @@ -964,7 +964,7 @@ void M_Blender_Init(void) PyDict_SetItemString(dict, "Key", Key_Init()); PyDict_SetItemString(dict, "Lamp", Lamp_Init()); PyDict_SetItemString(dict, "Lattice", Lattice_Init()); - PyDict_SetItemString(dict, "Library", Library_Init()); + PyDict_SetItemString(dict, "Library", oldLibrary_Init()); PyDict_SetItemString(dict, "Material", Material_Init()); PyDict_SetItemString(dict, "Mesh", Mesh_Init()); PyDict_SetItemString(dict, "Metaball", Metaball_Init()); diff --git a/source/blender/python/api2_2x/Library.c b/source/blender/python/api2_2x/Library.c index 4a22540a0ae..4251b83585d 100644 --- a/source/blender/python/api2_2x/Library.c +++ b/source/blender/python/api2_2x/Library.c @@ -28,11 +28,15 @@ * * This is a new part of Blender. * - * Contributor(s): Willian P. Germano + * Contributor(s): Willian P. Germano, Campbell Barton, Ken Hughes * * ***** END GPL/BL DUAL LICENSE BLOCK ***** */ +/************************************************************/ +/* Original library module code */ +/************************************************************/ + #include #include "DNA_curve_types.h" @@ -64,14 +68,13 @@ static PyObject *M_Library_Close( PyObject * self ); static PyObject *M_Library_GetName( PyObject * self ); static PyObject *M_Library_Update( PyObject * self ); static PyObject *M_Library_Datablocks( PyObject * self, PyObject * args ); -static PyObject *M_Library_Load( PyObject * self, PyObject * args ); +static PyObject *oldM_Library_Load( PyObject * self, PyObject * args ); static PyObject *M_Library_LinkableGroups( PyObject * self ); static PyObject *M_Library_LinkedLibs( PyObject * self ); PyObject *Library_Init( void ); void EXPP_Library_Close( void ); - /** * Module doc strings. */ @@ -119,7 +122,7 @@ static char Library_LinkedLibs_doc[] = /** * Python method structure definition for Blender.Library submodule. */ -struct PyMethodDef M_Library_methods[] = { +struct PyMethodDef oldM_Library_methods[] = { {"Open", M_Library_Open, METH_VARARGS, Library_Open_doc}, {"Close", ( PyCFunction ) M_Library_Close, METH_NOARGS, Library_Close_doc}, @@ -129,7 +132,7 @@ struct PyMethodDef M_Library_methods[] = { Library_Update_doc}, {"Datablocks", M_Library_Datablocks, METH_VARARGS, Library_Datablocks_doc}, - {"Load", M_Library_Load, METH_VARARGS, Library_Load_doc}, + {"Load", oldM_Library_Load, METH_VARARGS, Library_Load_doc}, {"LinkableGroups", ( PyCFunction ) M_Library_LinkableGroups, METH_NOARGS, Library_LinkableGroups_doc}, {"LinkedLibs", ( PyCFunction ) M_Library_LinkedLibs, @@ -144,7 +147,7 @@ struct PyMethodDef M_Library_methods[] = { * Only one can be open at a time, so this function also closes * the previously opened file, if any. */ -PyObject *M_Library_Open( PyObject * self, PyObject * args ) +static PyObject *M_Library_Open( PyObject * self, PyObject * args ) { char *fname = NULL; char filename[FILE_MAXDIR+FILE_MAXFILE]; @@ -193,7 +196,7 @@ PyObject *M_Library_Open( PyObject * self, PyObject * args ) /** * Close the current .blend file, if any. */ -PyObject *M_Library_Close( PyObject * self ) +static PyObject *M_Library_Close( PyObject * self ) { if( bpy_openlib ) { BLO_blendhandle_close( bpy_openlib ); @@ -228,7 +231,7 @@ void EXPP_Library_Close( void ) /** * Get the filename of the currently open library file, if any. */ -PyObject *M_Library_GetName( PyObject * self ) +static PyObject *M_Library_GetName( PyObject * self ) { if( bpy_openlib && bpy_openlibname ) return Py_BuildValue( "s", bpy_openlibname ); @@ -241,7 +244,7 @@ PyObject *M_Library_GetName( PyObject * self ) * Return a list with all items of a given datablock type * (like 'Object', 'Mesh', etc.) in the open library file. */ -PyObject *M_Library_Datablocks( PyObject * self, PyObject * args ) +static PyObject *M_Library_Datablocks( PyObject * self, PyObject * args ) { char *name = NULL; int blocktype = 0; @@ -288,7 +291,7 @@ PyObject *M_Library_Datablocks( PyObject * self, PyObject * args ) * Return a list with the names of all linkable groups in the * open library file. */ -PyObject *M_Library_LinkableGroups( PyObject * self ) +static PyObject *M_Library_LinkableGroups( PyObject * self ) { LinkNode *l = NULL, *names = NULL; PyObject *list = NULL; @@ -317,7 +320,7 @@ PyObject *M_Library_LinkableGroups( PyObject * self ) /** * Return a list with the names of all externally linked libs used in the current Blend file */ -PyObject *M_Library_LinkedLibs( PyObject * self ) +static PyObject *M_Library_LinkedLibs( PyObject * self ) { int counter = 0; Library *li; @@ -335,7 +338,7 @@ PyObject *M_Library_LinkedLibs( PyObject * self ) * Load (append) a given datablock of a given datablock type * to the current scene. */ -PyObject *M_Library_Load( PyObject * self, PyObject * args ) +static PyObject *oldM_Library_Load( PyObject * self, PyObject * args ) { char *name = NULL; char *base = NULL; @@ -360,9 +363,9 @@ PyObject *M_Library_Load( PyObject * self, PyObject * args ) "no such Blender datablock type" ); if (linked) - BLO_script_library_append( bpy_openlib, bpy_openlibname, name, blocktype, FILE_LINK); + BLO_script_library_append( bpy_openlib, bpy_openlibname, name, blocktype, FILE_LINK, G.scene); else - BLO_script_library_append( bpy_openlib, bpy_openlibname, name, blocktype, 0); + BLO_script_library_append( bpy_openlib, bpy_openlibname, name, blocktype, 0, G.scene); if( update ) { M_Library_Update( self ); @@ -394,7 +397,7 @@ PyObject *M_Library_Load( PyObject * self, PyObject * args ) /** * Update all links and remake displists. */ -PyObject *M_Library_Update( PyObject * self ) +static PyObject *M_Library_Update( PyObject * self ) { /* code adapted from do_library_append in src/filesel.c: */ Library *lib = NULL; @@ -425,12 +428,751 @@ PyObject *M_Library_Update( PyObject * self ) * Called by Blender_Init in Blender.c . * @return the registered submodule. */ -PyObject *Library_Init( void ) +PyObject *oldLibrary_Init( void ) { PyObject *submod; - submod = Py_InitModule3( "Blender.Library", M_Library_methods, + submod = Py_InitModule3( "Blender.Library", oldM_Library_methods, M_Library_doc ); return submod; } + +/************************************************************/ +/* New library (LibData) module code */ +/************************************************************/ + +#include "Library.h" + +/* if this module supercedes the old library module, include these instead */ +#if 0 +#include "BLI_blenlib.h" +#include "MEM_guardedalloc.h" + +#include "DNA_curve_types.h" +#include "DNA_object_types.h" +#include "DNA_space_types.h" /* for line linked */ +#include "BKE_library.h" /* for all_local */ +#include "BKE_utildefines.h" +#include "BKE_global.h" +#include "BKE_main.h" +#include "BLO_readfile.h" +#include "BLI_linklist.h" + +#include "Object.h" +#include "gen_utils.h" +#endif + +#include "gen_library.h" + +/* Helper function */ + +/* + * Try to open a library, set Python exceptions as necessary if not + * successful. On success, return a valid handle; othewise return NULL. + */ + +static BlendHandle *open_library( char *filename, char *longFilename ) +{ + char globalFilename[FILE_MAX]; + BlendHandle *openlib = NULL; + + /* get complete file name if necessary */ + BLI_strncpy( longFilename, filename, FILE_MAX ); + BLI_convertstringcode( longFilename, G.sce, 0 ); + + /* throw exceptions for wrong file type, cyclic reference */ + if( !BLO_has_bfile_extension(longFilename) ) { + PyErr_SetString( PyExc_ValueError, "file not a library" ); + return NULL; + } + if( BLI_streq(G.main->name, longFilename) ) { + PyErr_SetString( PyExc_ValueError, + "cannot use current file as library" ); + return NULL; + } + + /* G.sce = last file loaded, save for UI and restore after opening file */ + BLI_strncpy(globalFilename, G.sce, sizeof(globalFilename)); + openlib = BLO_blendhandle_from_file( longFilename ); + BLI_strncpy(G.sce, globalFilename, sizeof(globalFilename)); + + /* if failed, set that exception code too */ + if( !openlib ) + PyErr_SetString( PyExc_IOError, "library not found" ); + + return openlib; +} + +/* + * Create a specific type of LibraryData object. These are used for + * .append() and .link() access, for iterators, and (for Blender Objects) + * for defining "pseudo objects" for scene linking. + */ + +static PyObject *CreatePyObject_LibData( int idtype, int kind, + void *name, void *iter, char *filename ) +{ + BPy_LibraryData *seq = PyObject_NEW( BPy_LibraryData, &LibraryData_Type); + seq->iter = iter; /* the name list (for iterators) */ + seq->type = idtype; /* the Blender ID type */ + seq->kind = kind; /* used by Blender Objects */ + seq->name = name; /* object name, iterator name list, or NULL */ + /* save the library name */ + BLI_strncpy( seq->filename, filename, strlen(filename)+1 ); + return (PyObject *)seq; +} + +/* + * Link/append data to the current .blend file, or create a pseudo object + * which can be linked/appended to a scene. + */ + +static PyObject *lib_link_or_append( BPy_LibraryData *self, PyObject * args, + int mode ) +{ + char *name; + + /* get the name of the data used wants to append */ + if( !PyArg_ParseTuple( args, "s", &name ) ) + return EXPP_ReturnPyObjError( PyExc_TypeError, + "expected a string" ); + + /* + * For everything except objects, just add to Blender's DB. For objects, + * create an APPEND or LINK "pseudo object" for the Scene module. + */ + if( self->type != ID_OB ) + return LibraryData_importLibData( self, name, 0, NULL ); + else { + /* + * If this is already a pseudo object, throw an exception: re-linking + * or re-appending is not allowed + */ + if( self->kind != OTHER ) + return EXPP_ReturnPyObjError( PyExc_ValueError, + "object has already been marked for append or link" ); + + /* otherwise, create a pseudo object ready for appending or linking */ + + return CreatePyObject_LibData( ID_OB, mode, + BLI_strdupn( name, strlen( name ) ), NULL, self->filename ); + } +} + +/* + * Perform the actual link or append operation. This procedure is also + * called externally from the Scene module using a "pseudo Object" so we + * can be sure objects get linked to a scene. + */ + +PyObject *LibraryData_importLibData( BPy_LibraryData *self, char *name, + int mode, Scene *scene ) +{ + char longFilename[FILE_MAX]; + char *finalName; + BlendHandle *openlib; + Library *lib; + LinkNode *names, *ptr; + ID idtest, *id; + ListBase *lb; + + /* try to open the library */ + openlib = open_library( self->filename, longFilename ); + if( !openlib ) + return NULL; + + /* find all datablocks for the specified type */ + names = BLO_blendhandle_get_datablock_names ( openlib, self->type ); + + /* now check for a match to the user-specified name */ + for( ptr = names; ptr; ptr = ptr->next ) + if( strcmp( ptr->link, name ) == 0 ) break; + BLI_linklist_free( names, free ); + + /* if no match, throw exception */ + if( !ptr ) { + BLO_blendhandle_close( openlib ); + return EXPP_ReturnPyObjError( PyExc_ValueError, + "library does not contain specified item" ); + } + + /* + * Figure out what the datablock will be named after it's imported. If + * it's a link, nothing to do. If it's an append, find what it might + * be renamed to. + */ + + if( mode == FILE_LINK ) + finalName = name; + else { /* for appends, build a fake ID block, then try to dup it */ + strncpy( idtest.name+2, name, strlen(name)+1 ); + *((short *)&idtest.name) = self->type; + idtest.newid = NULL; + idtest.lib = NULL; + dup_id( NULL, &idtest, self->name ); + finalName = idtest.name+2; + } + + /* import from the libary */ + BLO_script_library_append( openlib, longFilename, name, self->type, mode, + scene ); + + /* + * locate the library. If this is an append, make the data local. If it + * is link, we need the library for later + */ + for( lib = G.main->library.first; lib; lib = lib->id.next ) + if( strcmp( longFilename, lib->name ) == 0 ) { + if( mode != FILE_LINK ) { + all_local( lib ); + lib = NULL; + } + break; + } + + /* done with library; close it */ + BLO_blendhandle_close( openlib ); + + /* find the base for this type */ + lb = wich_libbase( G.main, self->type ); + + /* + * Search the base for the datablock. For link, lib points to library, + * otherwise it's NULL. + */ + for( id = lb->first; id; id = id->next ) { + if( id->lib == lib && id->name[2]==finalName[0] && + strcmp(id->name+2, finalName)==0 ) + return GetPyObjectFromID( id ); + } + + /* if we get here, something's really wrong */ + return EXPP_ReturnPyObjError( PyExc_RuntimeError, + "could not find data after reading from library" ); +} + +/************************************************************ + * Python LibraryData_Type getseters + ************************************************************/ + +/* .append(): make a local copy of the library's data (except for objects) */ + +static PyObject *LibraryData_getAppend( BPy_LibraryData *self, PyObject * args) +{ + return lib_link_or_append( self, args, OBJECT_IS_APPEND ); +} + +/* .link(): make a link to the library's data (except for objects) */ + +static PyObject *LibraryData_getLink( BPy_LibraryData *self, PyObject * args) +{ + return lib_link_or_append( self, args, OBJECT_IS_LINK ); +} + +/************************************************************************ + * Python LibraryData_Type iterator + ************************************************************************/ + +/* Create and initialize the interator indices */ + +static PyObject *LibraryData_getIter( BPy_LibraryData * self ) +{ + char longFilename[FILE_MAX]; + BlendHandle *openlib; + LinkNode *names; + + /* try to open library */ + openlib = open_library( self->filename, longFilename ); + + /* if failed, return exception */ + if( !openlib ) + return NULL; + + /* find all datablocks for the specified type */ + names = BLO_blendhandle_get_datablock_names ( openlib, self->type ); + + /* close library*/ + BLO_blendhandle_close( openlib ); + + /* build an iterator object for the name list */ + return CreatePyObject_LibData( self->type, OTHER, names, + names, self->filename ); +} + +/* Return next name. */ + +static PyObject *LibraryData_nextIter( BPy_LibraryData * self ) +{ + LinkNode *ptr = (LinkNode *)self->iter; + PyObject *ob; + + /* if at the end of list, clean up */ + if( !ptr ) { + /* If name list is still allocated, free storage. This check is + * necessary since iter.next() can technically be called repeatedly */ + if( self->name ) { + BLI_linklist_free( (LinkNode *)self->name, free ); + self->name = NULL; + } + return EXPP_ReturnPyObjError( PyExc_StopIteration, + "iterator at end" ); + } + + /* otherwise, return the next name in the list */ + ob = PyString_FromString( ptr->link ); + ptr = ptr->next; + self->iter = ptr; + return ob; +} + +/************************************************************************ + * Python LibraryData_type methods structure + ************************************************************************/ + +static struct PyMethodDef BPy_LibraryData_methods[] = { + {"append", (PyCFunction)LibraryData_getAppend, METH_VARARGS, + "(str) - create new data from library"}, + {"link", (PyCFunction)LibraryData_getLink, METH_VARARGS, + "(str) - link data from library"}, + {NULL, NULL, 0, NULL} +}; + +/* Deallocate object and its data */ + +static void LibraryData_dealloc( BPy_LibraryData * self ) +{ + if( self->name ) + MEM_freeN( self->name ); + + PyObject_DEL( self ); +} + +/* Display representation of what Library Data is wrapping */ + +static PyObject *LibraryData_repr( BPy_LibraryData * self ) +{ + char *linkstate = ""; + char *str; + + switch (self->type) { + case ID_OB: + /* objects can be lib data or pseudo objects */ + switch( self->kind ) { + case OBJECT_IS_APPEND : + linkstate = ", appended"; + break; + case OBJECT_IS_LINK : + linkstate = ", linked"; + break; + default: + break; + } + str = "Object"; + break; + case ID_SCE: + str = "Scene"; + break; + case ID_ME: + str = "Mesh"; + break; + case ID_CU: + str = "Curve"; + break; + case ID_MB: + str = "Metaball"; + break; + case ID_MA: + str = "Material"; + break; + case ID_TE: + str = "Texture"; + break; + case ID_IM: + str = "Image"; + break; + case ID_LT: + str = "Lattice"; + break; + case ID_LA: + str = "Lamp"; + break; + case ID_CA: + str = "Camera"; + break; + case ID_IP: + str = "Ipo"; + break; + case ID_WO: + str = "World"; + break; + case ID_VF: + str = "Font"; + break; + case ID_TXT: + str = "Text"; + break; + case ID_SO: + str = "Sound"; + break; + case ID_GR: + str = "Group"; + break; + case ID_AR: + str = "Armature"; + break; + case ID_AC: + str = "Action"; + break; + default: + return EXPP_ReturnPyObjError( PyExc_RuntimeError, + "unsupported ID type" ); + } + + return PyString_FromFormat( "[Library Data (%s%s)]", str, linkstate ); +} + +PyTypeObject LibraryData_Type = { + PyObject_HEAD_INIT( NULL ) /* required py macro */ + 0, /* ob_size */ + /* For printing, in format "." */ + "Blender LibData", /* char *tp_name; */ + sizeof( BPy_LibraryData ), /* int tp_basicsize; */ + 0, /* tp_itemsize; For allocation */ + + /* Methods to implement standard operations */ + + ( destructor ) LibraryData_dealloc,/* destructor tp_dealloc; */ + NULL, /* printfunc tp_print; */ + NULL, /* getattrfunc tp_getattr; */ + NULL, /* setattrfunc tp_setattr; */ + ( cmpfunc ) NULL, /* cmpfunc tp_compare; */ + ( reprfunc ) LibraryData_repr, /* reprfunc tp_repr; */ + + /* Method suites for standard classes */ + + NULL, /* PyNumberMethods *tp_as_number; */ + NULL, /* PySequenceMethods *tp_as_sequence; */ + NULL, /* PyMappingMethods *tp_as_mapping; */ + + /* More standard operations (here for binary compatibility) */ + + NULL, /* hashfunc tp_hash; */ + NULL, /* ternaryfunc tp_call; */ + NULL, /* reprfunc tp_str; */ + NULL, /* getattrofunc tp_getattro; */ + NULL, /* setattrofunc tp_setattro; */ + + /* Functions to access object as input/output buffer */ + NULL, /* PyBufferProcs *tp_as_buffer; */ + + /*** Flags to define presence of optional/expanded features ***/ + Py_TPFLAGS_DEFAULT, /* long tp_flags; */ + + NULL, /* char *tp_doc; Documentation string */ + /*** Assigned meaning in release 2.0 ***/ + /* call function for all accessible objects */ + NULL, /* traverseproc tp_traverse; */ + + /* delete references to contained objects */ + NULL, /* inquiry tp_clear; */ + + /*** Assigned meaning in release 2.1 ***/ + /*** rich comparisons ***/ + NULL, /* richcmpfunc tp_richcompare; */ + + /*** weak reference enabler ***/ + 0, /* long tp_weaklistoffset; */ + + /*** Added in release 2.2 ***/ + /* Iterators */ + (getiterfunc)LibraryData_getIter, /* getiterfunc tp_iter; */ + (iternextfunc)LibraryData_nextIter, /* iternextfunc tp_iternext; */ + + /*** Attribute descriptor and subclassing stuff ***/ + BPy_LibraryData_methods, /* struct PyMethodDef *tp_methods; */ + NULL, /* struct PyMemberDef *tp_members; */ + NULL, /* struct PyGetSetDef *tp_getset; */ + NULL, /* struct _typeobject *tp_base; */ + NULL, /* PyObject *tp_dict; */ + NULL, /* descrgetfunc tp_descr_get; */ + NULL, /* descrsetfunc tp_descr_set; */ + 0, /* long tp_dictoffset; */ + NULL, /* initproc tp_init; */ + NULL, /* allocfunc tp_alloc; */ + NULL, /* newfunc tp_new; */ + /* Low-level free-memory routine */ + NULL, /* freefunc tp_free; */ + /* For PyObject_IS_GC */ + NULL, /* inquiry tp_is_gc; */ + NULL, /* PyObject *tp_bases; */ + /* method resolution order */ + NULL, /* PyObject *tp_mro; */ + NULL, /* PyObject *tp_cache; */ + NULL, /* PyObject *tp_subclasses; */ + NULL, /* PyObject *tp_weaklist; */ + NULL +}; + +/* + * Create a LibraryData object for a specific type of Blender Group (ID_OB, + * ID_MA, etc). These can then be used to link or append the data. + */ + +static PyObject *LibraryData_CreatePyObject( BPy_Library *self, void *mode ) +{ + return CreatePyObject_LibData( (int)mode, OTHER, NULL, NULL, + self->filename ); +} + +/************************************************************ + * Python Library_Type getseters + ************************************************************/ + +/* + * Return the library's filename. + */ + +static PyObject *Library_getFilename( BPy_Library * self ) +{ + return PyString_FromString( self->filename ); +} + +/* + * Set/change the library's filename. + */ + +static int Library_setFilename( BPy_Library * self, PyObject * args ) +{ + char *filename = PyString_AsString( args ); + if( !filename ) + return EXPP_ReturnIntError( PyExc_TypeError, "expected a string" ); + + BLI_strncpy( self->filename, filename, sizeof(self->filename) ); + return 0; +} + +/************************************************************************ + * Python Library_type attributes get/set structure + ************************************************************************/ + +static PyGetSetDef Library_getseters[] = { + {"filename", + (getter)Library_getFilename, (setter)Library_setFilename, + "library filename", + NULL}, + {"objects", + (getter)LibraryData_CreatePyObject, (setter)NULL, + "objects from the library", + (void *)ID_OB}, + {"scenes", + (getter)LibraryData_CreatePyObject, (setter)NULL, + "scenes from the library", + (void *)ID_SCE}, + {"meshes", + (getter)LibraryData_CreatePyObject, (setter)NULL, + "meshes from the library", + (void *)ID_ME}, + {"curves", + (getter)LibraryData_CreatePyObject, (setter)NULL, + "curves from the library", + (void *)ID_CU}, + {"metaballs", + (getter)LibraryData_CreatePyObject, (setter)NULL, + "metaballs from the library", + (void *)ID_MB}, + {"lattices", + (getter)LibraryData_CreatePyObject, (setter)NULL, + "lattices from the library", + (void *)ID_LT}, + {"lamps", + (getter)LibraryData_CreatePyObject, (setter)NULL, + "lamps from the library", + (void *)ID_LA}, + {"cameras", + (getter)LibraryData_CreatePyObject, (setter)NULL, + "cameras from the library", + (void *)ID_CA}, + {"materials", + (getter)LibraryData_CreatePyObject, (setter)NULL, + "objects from the library", + (void *)ID_MA}, + {"textures", + (getter)LibraryData_CreatePyObject, (setter)NULL, + "textures from the library", + (void *)ID_TE}, + {"images", + (getter)LibraryData_CreatePyObject, (setter)NULL, + "images from the library", + (void *)ID_IM}, + {"ipos", + (getter)LibraryData_CreatePyObject, (setter)NULL, + "ipos from the library", + (void *)ID_IP}, + {"worlds", + (getter)LibraryData_CreatePyObject, (setter)NULL, + "worlds from the library", + (void *)ID_WO}, + {"fonts", + (getter)LibraryData_CreatePyObject, (setter)NULL, + "fonts from the library", + (void *)ID_VF}, + {"texts", + (getter)LibraryData_CreatePyObject, (setter)NULL, + "texts from the library", + (void *)ID_TXT}, + {"groups", + (getter)LibraryData_CreatePyObject, (setter)NULL, + "groups from the library", + (void *)ID_GR}, + {"sounds", + (getter)LibraryData_CreatePyObject, (setter)NULL, + "sounds from the library", + (void *)ID_SO}, + {"actions", + (getter)LibraryData_CreatePyObject, (setter)NULL, + "actions from the library", + (void *)ID_AC}, + {"armatures", + (getter)LibraryData_CreatePyObject, (setter)NULL, + "armatures from the library", + (void *)ID_AR}, + {NULL,NULL,NULL,NULL,NULL} /* Sentinel */ +}; + +/* + * Define a new library and create a library object. We don't actually test + * if the library is valid here since we have to do it when the file is + * actually accessed later. + */ + +static PyObject *M_Library_Load(PyObject *self, PyObject * args) +{ + char *filename; + BPy_Library *lib; + + if( !PyArg_ParseTuple( args, "s", &filename ) ) + return EXPP_ReturnPyObjError( PyExc_TypeError, + "expected a string" ); + + /* try to create a new object */ + lib = (BPy_Library *)PyObject_NEW( BPy_Library, &Library_Type ); + if( !lib ) + return NULL; + + /* assign the library filename for future use, then return */ + BLI_strncpy( lib->filename, filename, sizeof(lib->filename) ); + + return (PyObject *)lib; +} + +static struct PyMethodDef M_Library_methods[] = { + {"load", (PyCFunction)M_Library_Load, METH_VARARGS, + "(string) - declare a .blend file for use as a library"}, + {NULL, NULL, 0, NULL} +}; + +/*****************************************************************************/ +/* Python Library_Type structure definition: */ +/*****************************************************************************/ +PyTypeObject Library_Type = { + PyObject_HEAD_INIT( NULL ) /* required py macro */ + 0, /* ob_size */ + /* For printing, in format "." */ + "Blender Library", /* char *tp_name; */ + sizeof( BPy_Library ), /* int tp_basicsize; */ + 0, /* tp_itemsize; For allocation */ + + /* Methods to implement standard operations */ + + NULL, /* destructor tp_dealloc; */ + NULL, /* printfunc tp_print; */ + NULL, /* getattrfunc tp_getattr; */ + NULL, /* setattrfunc tp_setattr; */ + ( cmpfunc ) NULL, /* cmpfunc tp_compare; */ + ( reprfunc ) NULL, /* reprfunc tp_repr; */ + + /* Method suites for standard classes */ + + NULL, /* PyNumberMethods *tp_as_number; */ + NULL, /* PySequenceMethods *tp_as_sequence; */ + NULL, /* PyMappingMethods *tp_as_mapping; */ + + /* More standard operations (here for binary compatibility) */ + + NULL, /* hashfunc tp_hash; */ + NULL, /* ternaryfunc tp_call; */ + NULL, /* reprfunc tp_str; */ + NULL, /* getattrofunc tp_getattro; */ + NULL, /* setattrofunc tp_setattro; */ + + /* Functions to access object as input/output buffer */ + NULL, /* PyBufferProcs *tp_as_buffer; */ + + /*** Flags to define presence of optional/expanded features ***/ + Py_TPFLAGS_DEFAULT, /* long tp_flags; */ + + NULL, /* char *tp_doc; Documentation string */ + /*** Assigned meaning in release 2.0 ***/ + /* call function for all accessible objects */ + NULL, /* traverseproc tp_traverse; */ + + /* delete references to contained objects */ + NULL, /* inquiry tp_clear; */ + + /*** Assigned meaning in release 2.1 ***/ + /*** rich comparisons ***/ + NULL, /* richcmpfunc tp_richcompare; */ + + /*** weak reference enabler ***/ + 0, /* long tp_weaklistoffset; */ + + /*** Added in release 2.2 ***/ + /* Iterators */ + NULL, /* getiterfunc tp_iter; */ + NULL, /* iternextfunc tp_iternext; */ + + /*** Attribute descriptor and subclassing stuff ***/ + NULL, /* struct PyMethodDef *tp_methods; */ + NULL, /* struct PyMemberDef *tp_members; */ + Library_getseters, /* struct PyGetSetDef *tp_getset; */ + NULL, /* struct _typeobject *tp_base; */ + NULL, /* PyObject *tp_dict; */ + NULL, /* descrgetfunc tp_descr_get; */ + NULL, /* descrsetfunc tp_descr_set; */ + 0, /* long tp_dictoffset; */ + NULL, /* initproc tp_init; */ + NULL, /* allocfunc tp_alloc; */ + NULL, /* newfunc tp_new; */ + /* Low-level free-memory routine */ + NULL, /* freefunc tp_free; */ + /* For PyObject_IS_GC */ + NULL, /* inquiry tp_is_gc; */ + NULL, /* PyObject *tp_bases; */ + /* method resolution order */ + NULL, /* PyObject *tp_mro; */ + NULL, /* PyObject *tp_cache; */ + NULL, /* PyObject *tp_subclasses; */ + NULL, /* PyObject *tp_weaklist; */ + NULL +}; + +/* + * Library module initialization + */ + +static char M_newLibrary_doc[] = "The Blender.lib submodule"; + +PyObject *Library_Init( void ) +{ + PyObject *submodule; + + if( PyType_Ready( &Library_Type ) < 0 ) + return NULL; + if( PyType_Ready( &LibraryData_Type ) < 0 ) + return NULL; + + submodule = Py_InitModule3( "Blender.lib", M_Library_methods, + M_newLibrary_doc ); + return submodule; +} diff --git a/source/blender/python/api2_2x/Library.h b/source/blender/python/api2_2x/Library.h new file mode 100644 index 00000000000..28d4c34c9c6 --- /dev/null +++ b/source/blender/python/api2_2x/Library.h @@ -0,0 +1,76 @@ +/* + * $Id$ + * + * ***** BEGIN GPL/BL DUAL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. The Blender + * Foundation also sells licenses for use in proprietary software under + * the Blender License. See http://www.blender.org/BL/ for information + * about this. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. + * All rights reserved. + * + * This is a new part of Blender. + * + * Contributor(s): Ken Hughes + * + * ***** END GPL/BL DUAL LICENSE BLOCK ***** +*/ + +#ifndef EXPP_LIBRARY_H +#define EXPP_LIBRARY_H + +#include +#include "DNA_scene_types.h" +#include "BLI_linklist.h" + +#include "blendef.h" + +/*****************************************************************************/ +/* Python BPy_Library structure definition: */ +/*****************************************************************************/ +typedef struct { + PyObject_HEAD + char filename[FILE_MAXDIR + FILE_MAXFILE]; +} BPy_Library; + +typedef struct { + PyObject_HEAD + LinkNode *iter; + int type; + char filename[FILE_MAXDIR + FILE_MAXFILE]; + char *name; + enum { + OBJECT_IS_LINK, + OBJECT_IS_APPEND, + OTHER + } kind; +} BPy_LibraryData; + +extern PyTypeObject Library_Type; +extern PyTypeObject LibraryData_Type; + +#define BPy_LibraryData_Check(v) ((v)->ob_type == &LibraryData_Type) +#define BPy_Library_Check(v) ((v)->ob_type == &Library_Type) + +/*****************************************************************************/ +/* Module Blender.Library - public functions */ +/*****************************************************************************/ +PyObject *Library_Init( void ); +PyObject *LibraryData_importLibData( BPy_LibraryData *self, char *name, + int mode, Scene *scene ); + +#endif /* EXPP_LIBRARY_H */ diff --git a/source/blender/python/api2_2x/Main.c b/source/blender/python/api2_2x/Main.c index 4eddea59ab0..ef0804a9bb4 100644 --- a/source/blender/python/api2_2x/Main.c +++ b/source/blender/python/api2_2x/Main.c @@ -105,7 +105,7 @@ #include "NLA.h" #include "Main.h" #include "Scene.h" - +#include "Library.h" #include "Config.h" /* config pydata */ @@ -746,6 +746,8 @@ static char M_Main_doc[] = "The Blender.Main submodule"; PyObject *Main_Init( void ) { PyObject *submodule; + PyObject *dict; + if( PyType_Ready( &MainSeq_Type ) < 0 ) return NULL; @@ -753,6 +755,10 @@ PyObject *Main_Init( void ) return NULL; submodule = Py_InitModule3( "Blender.Main", NULL, M_Main_doc ); + dict = PyModule_GetDict( submodule ); + + PyDict_SetItemString( dict, "libraries", Library_Init( ) ); + /* Python Data Types */ PyModule_AddObject( submodule, "scenes", MainSeq_CreatePyObject(NULL, ID_SCE) ); diff --git a/source/blender/python/api2_2x/Scene.c b/source/blender/python/api2_2x/Scene.c index 13258b32f53..ff0eb3ff20d 100644 --- a/source/blender/python/api2_2x/Scene.c +++ b/source/blender/python/api2_2x/Scene.c @@ -72,6 +72,7 @@ struct View3D; #include "Metaball.h" #include "IDProp.h" #include "Text3d.h" +#include "Library.h" #include "gen_utils.h" #include "gen_library.h" @@ -1419,6 +1420,13 @@ static PyObject *SceneObSeq_link( BPy_SceneObSeq * self, PyObject *pyobj ) "Cannot modify scene objects while iterating" ); */ + if( PyTuple_Size(pyobj) == 1 ) { + BPy_LibraryData *seq = ( BPy_LibraryData * )PyTuple_GET_ITEM( pyobj, 0 ); + if( BPy_LibraryData_Check( seq ) ) + return LibraryData_importLibData( seq, seq->name, + ( seq->kind == OBJECT_IS_LINK ? FILE_LINK : 0 ), + self->bpyscene->scene ); + } return Scene_link(self->bpyscene, pyobj); } diff --git a/source/blender/python/api2_2x/doc/Bpy.py b/source/blender/python/api2_2x/doc/Bpy.py index b93cf4b100a..7bf3afe8249 100644 --- a/source/blender/python/api2_2x/doc/Bpy.py +++ b/source/blender/python/api2_2x/doc/Bpy.py @@ -130,6 +130,8 @@ Example:: @type armatures: L{dataIterator} @var actions: iterator for L{action} data @type actions: L{dataIterator} +@var libraries: L{New library} submodule +@type libraries: L{New library} """ diff --git a/source/blender/python/api2_2x/doc/LibData.py b/source/blender/python/api2_2x/doc/LibData.py new file mode 100644 index 00000000000..7884ea50659 --- /dev/null +++ b/source/blender/python/api2_2x/doc/LibData.py @@ -0,0 +1,137 @@ +# bpy.lib submodule + +""" +The bpy.libraries submodule. + +Libraries +========= + +This module provides access to objects stored in .blend files. With it scripts +can append from Blender files to the current scene, like the File->Append +menu entry in Blender does. It allows programmers to use .blend files as +data files for their scripts. + +@warn: This module is new and being considered as a replacement for the +L{original Library} module. Users should stay tuned to see +which module is supported in the end. + +Example:: + import bpy + + scn= bpy.scenes.active # get current scene + lib = bpy.libraries.load('//file.blend') # open file.blend + ob = scn.objects.link(lib.objects.append('Cube')) # append Cube object from library to current scene + mat = lib.objects.link('Material') # get a link to a material + me = ob.getData(mesh=1) # get mesh data + me.materials[0] = mat # assign linked material to mesh +""" + +def load(filename): + """ + Select an existing .blend file for use as a library. Unlike the + Library module, multiple libraries can be defined at the same time. + + @type filename: string + @param filename: The filename of a Blender file. Filenames starting with "//" will be loaded relative to the blend file's location. + @rtype: Library + @return: return a L{Library} object. + """ + +class Libraries: + """ + The Library object + ================== + This class provides a unified way to access and manipulate library types + in Blender. + It provides access to scenes, objects, meshes, curves, metaballs, + materials, textures, images, lattices, lamps, cameras, ipos, worlds, + fonts, texts, sounds, groups, armatures, and actions. + @ivar filename: The path to the library + @type filename: string + @ivar scenes: library L{scene} data + @type scenes: L{LibData} + @ivar objects: library L{object} data + @type objects: L{LibData} + @ivar meshes: library L{mesh} data + @type meshes: L{LibData} + @ivar curves: library L{curve} data + @type curves: L{LibData} + @ivar metaballs: library L{metaball} data + @type metaballs: L{LibData} + @ivar materials: library L{material} data + @type materials: L{LibData} + @ivar textures: library L{texture} data + @type textures: L{LibData} + @ivar images: library L{image} data + @type images: L{LibData} + @ivar lattices: library L{lattice} data + @type lattices: L{LibData} + @ivar lamps: library L{lamp} data + @type lamps: L{LibData} + @ivar cameras: library L{camera} data + @type cameras: L{LibData} + @ivar ipos: library L{ipo} data + @type ipos: L{LibData} + @ivar worlds: library L{world} data + @type worlds: L{LibData} + @ivar fonts: library L{font} data + @type fonts: L{LibData} + @ivar texts: library L{text} data + @type texts: L{LibData} + @ivar sounds: library L{sound} data + @type sounds: L{LibData} + @ivar groups: library L{group} data + @type groups: L{LibData} + @ivar armatures: library L{armature} data + @type armatures: L{LibData} + @ivar actions: library L{action} data + @type actions: L{LibData} + """ + +class LibData: + """ + Generic Library Data Access + =========================== + This class provides access to a specific type of library data. + """ + + def append(name): + """ + Append a new datablock from a library. The new copy + is added to the current .blend file. + + B{Note}: Blender Objects cannot be appended or linked without linking + them to a scene. For this reason, lib.objects.append() returns a + special "wrapper object" which must be passed to Scene.objects.link() + or bpy.scenes.active.link() in order to actually create the object. + So the following code will not create a new object:: + import bpy + + scn= bpy.scenes.active # get current scene + lib = bpy.libraries.load('//file.blend') # open file.blend + pseudoOb = lib.objects.append('Cube')) # get an object wrapper + But this code will:: + import bpy + + scn= bpy.scenes.active # get current scene + lib = bpy.libraries.load('//file.blend') # open file.blend + pseudoOb = lib.objects.append('Cube')) # get an object wrapper + ob = scn.objects.link(pseudoOb) # link to scene + @rtype: Blender data + @return: return a Blender datablock or object + @raise IOError: library cannot be read + @raise ValueError: library does not contain B{name} + """ + + def link(name): + """ + Link a new datablock from a library. The linked data is not copied + into the local .blend file. + + See L{append} for notes on special handling of Blender Objects. + @rtype: Blender data + @return: return a Blender datablock or object + @raise IOError: library cannot be read + @raise ValueError: library does not contain B{name} + """ + diff --git a/source/blender/python/api2_2x/doc/Library.py b/source/blender/python/api2_2x/doc/Library.py index 840884f1efb..c99739d939b 100644 --- a/source/blender/python/api2_2x/doc/Library.py +++ b/source/blender/python/api2_2x/doc/Library.py @@ -11,7 +11,9 @@ can append from Blender files to the current scene, like the File->Append menu entry in Blender does. It allows programmers to use .blend files as data files for their scripts. -@warn: This is a new, still experimental module. +@warn: This module is being considered for deprecation. Users should +consider using the L{new Library} module and stay tuned to see +which module is supported in the end. Example:: import Blender