I was careful in selectively rolling back revisions, but if you've committed changes unrelated to BPY mixed with BPY changes, I might have reverted those too, so please double check.
1193 lines
35 KiB
C
1193 lines
35 KiB
C
/**
|
|
* $Id: Library.c 10943 2007-06-16 12:24:41Z campbellbarton $
|
|
*
|
|
* Blender.Library BPython module implementation.
|
|
* This submodule has functions to append data from .blend files.
|
|
*
|
|
* ***** 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): Willian P. Germano, Campbell Barton, Ken Hughes
|
|
*
|
|
* ***** END GPL/BL DUAL LICENSE BLOCK *****
|
|
*/
|
|
|
|
/************************************************************/
|
|
/* Original library module code */
|
|
/************************************************************/
|
|
|
|
#include <Python.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_font.h" /* for text_to_curve */
|
|
#include "BKE_utildefines.h"
|
|
#include "BKE_global.h"
|
|
#include "BKE_main.h"
|
|
#include "BLI_blenlib.h"
|
|
#include "BLO_readfile.h"
|
|
#include "BLI_linklist.h"
|
|
#include "MEM_guardedalloc.h"
|
|
#include "gen_utils.h"
|
|
|
|
/**
|
|
* Global variables.
|
|
*/
|
|
static BlendHandle *bpy_openlib = NULL; /* ptr to the open .blend file */
|
|
static char *bpy_openlibname = NULL; /* its pathname */
|
|
static int bpy_relative= 0;
|
|
|
|
/**
|
|
* Function prototypes for the Library submodule.
|
|
*/
|
|
static PyObject *M_Library_Open( PyObject * self, PyObject * args );
|
|
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 * value );
|
|
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.
|
|
*/
|
|
static char M_Library_doc[] = "The Blender.Library submodule:\n\n\
|
|
This module gives access to .blend files, using them as libraries of\n\
|
|
data that can be loaded into the current scene in Blender.";
|
|
|
|
static char Library_Open_doc[] =
|
|
"(filename) - Open the given .blend file for access to its objects.\n\
|
|
If another library file is still open, it's closed automatically.";
|
|
|
|
static char Library_Close_doc[] =
|
|
"() - Close the currently open library file, if any.";
|
|
|
|
static char Library_GetName_doc[] =
|
|
"() - Get the filename of the currently open library file, if any.";
|
|
|
|
static char Library_Datablocks_doc[] =
|
|
"(datablock) - List all datablocks of the given type in the currently\n\
|
|
open library file.\n\
|
|
(datablock) - datablock name as a string: Object, Mesh, etc.";
|
|
|
|
static char Library_Load_doc[] =
|
|
"(name, datablock [,update = 1]) - Append object 'name' of type 'datablock'\n\
|
|
from the open library file to the current scene.\n\
|
|
(name) - (str) the name of the object.\n\
|
|
(datablock) - (str) the datablock of the object.\n\
|
|
(update = 1) - (int) if non-zero, all display lists are recalculated and the\n\
|
|
links are updated. This is slow, set it to zero if you have more than one\n\
|
|
object to load, then call Library.Update() after loading them all.";
|
|
|
|
static char Library_Update_doc[] =
|
|
"() - Update the current scene, linking all loaded library objects and\n\
|
|
remaking all display lists. This is slow, call it only once after loading\n\
|
|
all objects (load each of them with update = 0:\n\
|
|
Library.Load(name, datablock, 0), or the update will be automatic, repeated\n\
|
|
for each loaded object.";
|
|
|
|
static char Library_LinkableGroups_doc[] =
|
|
"() - Get all linkable groups from the open .blend library file.";
|
|
|
|
static char Library_LinkedLibs_doc[] =
|
|
"() - Get all libs used in the the open .blend file.";
|
|
|
|
/**
|
|
* Python method structure definition for Blender.Library submodule.
|
|
*/
|
|
struct PyMethodDef oldM_Library_methods[] = {
|
|
{"Open", M_Library_Open, METH_O, Library_Open_doc},
|
|
{"Close", ( PyCFunction ) M_Library_Close, METH_NOARGS,
|
|
Library_Close_doc},
|
|
{"GetName", ( PyCFunction ) M_Library_GetName, METH_NOARGS,
|
|
Library_GetName_doc},
|
|
{"Update", ( PyCFunction ) M_Library_Update, METH_NOARGS,
|
|
Library_Update_doc},
|
|
{"Datablocks", M_Library_Datablocks, METH_O,
|
|
Library_Datablocks_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,
|
|
METH_NOARGS, Library_LinkedLibs_doc},
|
|
{NULL, NULL, 0, NULL}
|
|
};
|
|
|
|
/* Submodule Python functions: */
|
|
|
|
/**
|
|
* Open a new .blend file.
|
|
* Only one can be open at a time, so this function also closes
|
|
* the previously opened file, if any.
|
|
*/
|
|
static PyObject *M_Library_Open( PyObject * self, PyObject * value )
|
|
{
|
|
char *fname = PyString_AsString(value);
|
|
char filename[FILE_MAXDIR+FILE_MAXFILE];
|
|
char fname1[FILE_MAXDIR+FILE_MAXFILE];
|
|
|
|
int len = 0;
|
|
|
|
bpy_relative= 0; /* assume non relative each time we load */
|
|
|
|
if( !fname ) {
|
|
return EXPP_ReturnPyObjError( PyExc_TypeError,
|
|
"expected a .blend filename" );
|
|
}
|
|
|
|
if( bpy_openlib ) {
|
|
M_Library_Close( self );
|
|
Py_DECREF( Py_None ); /* incref'ed by above function */
|
|
}
|
|
|
|
/* copy the name to make it absolute so BLO_blendhandle_from_file doesn't complain */
|
|
BLI_strncpy(fname1, fname, sizeof(fname1));
|
|
BLI_convertstringcode(fname1, G.sce, 0); /* make absolute */
|
|
|
|
/* G.sce = last file loaded, save for UI and restore after opening file */
|
|
BLI_strncpy(filename, G.sce, sizeof(filename));
|
|
bpy_openlib = BLO_blendhandle_from_file( fname1 );
|
|
BLI_strncpy(G.sce, filename, sizeof(filename));
|
|
|
|
if( !bpy_openlib )
|
|
return EXPP_ReturnPyObjError( PyExc_IOError, "file not found" );
|
|
|
|
/* "//someblend.blend" enables relative paths */
|
|
if (sizeof(fname) > 2 && fname[0] == '/' && fname[1] == '/')
|
|
bpy_relative= 1; /* global that makes the library relative on loading */
|
|
|
|
len = strlen( fname1 ) + 1; /* +1 for terminating '\0' */
|
|
|
|
bpy_openlibname = MEM_mallocN( len, "bpy_openlibname" );
|
|
|
|
if( bpy_openlibname )
|
|
PyOS_snprintf( bpy_openlibname, len, "%s", fname1 );
|
|
|
|
Py_RETURN_TRUE;
|
|
}
|
|
|
|
/**
|
|
* Close the current .blend file, if any.
|
|
*/
|
|
static PyObject *M_Library_Close( PyObject * self )
|
|
{
|
|
if( bpy_openlib ) {
|
|
BLO_blendhandle_close( bpy_openlib );
|
|
bpy_openlib = NULL;
|
|
}
|
|
|
|
if( bpy_openlibname ) {
|
|
MEM_freeN( bpy_openlibname );
|
|
bpy_openlibname = NULL;
|
|
}
|
|
|
|
Py_RETURN_NONE;
|
|
}
|
|
|
|
/**
|
|
* helper function for 'atexit' clean-ups, used by BPY_end_python,
|
|
* declared in EXPP_interface.h.
|
|
*/
|
|
void EXPP_Library_Close( void )
|
|
{
|
|
if( bpy_openlib ) {
|
|
BLO_blendhandle_close( bpy_openlib );
|
|
bpy_openlib = NULL;
|
|
}
|
|
|
|
if( bpy_openlibname ) {
|
|
MEM_freeN( bpy_openlibname );
|
|
bpy_openlibname = NULL;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get the filename of the currently open library file, if any.
|
|
*/
|
|
static PyObject *M_Library_GetName( PyObject * self )
|
|
{
|
|
if( bpy_openlib && bpy_openlibname )
|
|
return Py_BuildValue( "s", bpy_openlibname );
|
|
|
|
Py_INCREF( Py_None );
|
|
return Py_None;
|
|
}
|
|
|
|
/**
|
|
* Return a list with all items of a given datablock type
|
|
* (like 'Object', 'Mesh', etc.) in the open library file.
|
|
*/
|
|
static PyObject *M_Library_Datablocks( PyObject * self, PyObject * value )
|
|
{
|
|
char *name = PyString_AsString(value);
|
|
int blocktype = 0;
|
|
LinkNode *l = NULL, *names = NULL;
|
|
PyObject *list = NULL;
|
|
|
|
if( !bpy_openlib ) {
|
|
return EXPP_ReturnPyObjError( PyExc_IOError,
|
|
"no library file: open one first with Blender.Lib_Open(filename)" );
|
|
}
|
|
|
|
if( !name ) {
|
|
return EXPP_ReturnPyObjError( PyExc_TypeError,
|
|
"expected a string (datablock type) as argument." );
|
|
}
|
|
|
|
blocktype = ( int ) BLO_idcode_from_name( name );
|
|
|
|
if( !blocktype ) {
|
|
return EXPP_ReturnPyObjError( PyExc_NameError,
|
|
"no such Blender datablock type" );
|
|
}
|
|
|
|
names = BLO_blendhandle_get_datablock_names( bpy_openlib, blocktype );
|
|
|
|
if( names ) {
|
|
int counter = 0;
|
|
list = PyList_New( BLI_linklist_length( names ) );
|
|
for( l = names; l; l = l->next ) {
|
|
PyList_SET_ITEM( list, counter,
|
|
PyString_FromString( ( char * ) l->link ) );
|
|
counter++;
|
|
}
|
|
BLI_linklist_free( names, free ); /* free linklist *and* each node's data */
|
|
return list;
|
|
}
|
|
|
|
Py_RETURN_NONE;
|
|
}
|
|
|
|
/**
|
|
* Return a list with the names of all linkable groups in the
|
|
* open library file.
|
|
*/
|
|
static PyObject *M_Library_LinkableGroups( PyObject * self )
|
|
{
|
|
LinkNode *l = NULL, *names = NULL;
|
|
PyObject *list = NULL;
|
|
|
|
if( !bpy_openlib ) {
|
|
return EXPP_ReturnPyObjError( PyExc_IOError,
|
|
"no library file: open one first with Blender.Lib_Open(filename)" );
|
|
}
|
|
|
|
names = BLO_blendhandle_get_linkable_groups( bpy_openlib );
|
|
list = PyList_New( BLI_linklist_length( names ) );
|
|
|
|
if( names ) {
|
|
int counter = 0;
|
|
|
|
for( l = names; l; l = l->next ) {
|
|
PyList_SET_ITEM( list, counter, PyString_FromString( ( char * ) l->link ) );
|
|
counter++;
|
|
}
|
|
BLI_linklist_free( names, free ); /* free linklist *and* each node's data */
|
|
return list;
|
|
}
|
|
return list;
|
|
}
|
|
|
|
/**
|
|
* Return a list with the names of all externally linked libs used in the current Blend file
|
|
*/
|
|
static PyObject *M_Library_LinkedLibs( PyObject * self )
|
|
{
|
|
int counter = 0;
|
|
Library *li;
|
|
PyObject *list;
|
|
|
|
list = PyList_New( BLI_countlist( &( G.main->library ) ) );
|
|
for (li= G.main->library.first; li; li= li->id.next) {
|
|
PyList_SET_ITEM( list, counter, PyString_FromString( li->name ));
|
|
counter++;
|
|
}
|
|
return list;
|
|
}
|
|
|
|
/**
|
|
* Load (append) a given datablock of a given datablock type
|
|
* to the current scene.
|
|
*/
|
|
static PyObject *oldM_Library_Load( PyObject * self, PyObject * args )
|
|
{
|
|
char *name = NULL;
|
|
char *base = NULL;
|
|
int update = 1;
|
|
int blocktype = 0;
|
|
int linked = 0;
|
|
|
|
if( !bpy_openlib ) {
|
|
return EXPP_ReturnPyObjError( PyExc_IOError,
|
|
"no library file: you need to open one, first." );
|
|
}
|
|
|
|
if( !PyArg_ParseTuple( args, "ss|ii", &name, &base, &update, &linked ) ) {
|
|
return EXPP_ReturnPyObjError( PyExc_TypeError,
|
|
"expected two strings as arguments." );
|
|
}
|
|
|
|
blocktype = ( int ) BLO_idcode_from_name( base );
|
|
|
|
if( !blocktype )
|
|
return EXPP_ReturnPyObjError( PyExc_NameError,
|
|
"no such Blender datablock type" );
|
|
|
|
if (linked)
|
|
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, G.scene);
|
|
|
|
if( update ) {
|
|
M_Library_Update( self );
|
|
Py_DECREF( Py_None ); /* incref'ed by above function */
|
|
}
|
|
|
|
if( bpy_relative ) {
|
|
/* and now find the latest append lib file */
|
|
Library *lib = G.main->library.first;
|
|
while( lib ) {
|
|
if( strcmp( bpy_openlibname, lib->name ) == 0 ) {
|
|
|
|
/* use the full path, this could have been read by other library even */
|
|
BLI_strncpy(lib->name, lib->filename, sizeof(lib->name));
|
|
|
|
/* uses current .blend file as reference */
|
|
BLI_makestringcode(G.sce, lib->name);
|
|
break;
|
|
}
|
|
lib = lib->id.next;
|
|
}
|
|
|
|
}
|
|
|
|
Py_INCREF( Py_None );
|
|
return Py_None;
|
|
}
|
|
|
|
/**
|
|
* Update all links and remake displists.
|
|
*/
|
|
static PyObject *M_Library_Update( PyObject * self )
|
|
{ /* code adapted from do_library_append in src/filesel.c: */
|
|
Library *lib = NULL;
|
|
|
|
/* Displist code that was here is obsolete... depending on what
|
|
* this function is supposed to do (it should technically be unnecessary)
|
|
* can be replaced with depgraph calls - zr
|
|
*/
|
|
|
|
if( bpy_openlibname ) {
|
|
strcpy( G.lib, bpy_openlibname );
|
|
|
|
/* and now find the latest append lib file */
|
|
lib = G.main->library.first;
|
|
while( lib ) {
|
|
if( strcmp( bpy_openlibname, lib->name ) == 0 )
|
|
break;
|
|
lib = lib->id.next;
|
|
}
|
|
all_local( lib, 0 );
|
|
}
|
|
|
|
Py_INCREF( Py_None );
|
|
return Py_None;
|
|
}
|
|
|
|
/**
|
|
* Initialize the Blender.Library submodule.
|
|
* Called by Blender_Init in Blender.c .
|
|
* @return the registered submodule.
|
|
*/
|
|
PyObject *oldLibrary_Init( void )
|
|
{
|
|
PyObject *submod;
|
|
|
|
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 * value,
|
|
int mode )
|
|
{
|
|
char *name = PyString_AsString(value);
|
|
|
|
/* get the name of the data used wants to append */
|
|
if( !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];
|
|
BlendHandle *openlib;
|
|
Library *lib;
|
|
LinkNode *names, *ptr;
|
|
ID *id;
|
|
ListBase *lb;
|
|
char newName[32];
|
|
|
|
/* 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 ) {
|
|
flag_all_listbases_ids(LIB_APPEND_TAG, 1);
|
|
|
|
/* see what new block will be called */
|
|
strncpy( newName, name, strlen(name)+1 );
|
|
check_for_dupid( wich_libbase(G.main, self->type), NULL, newName );
|
|
}
|
|
|
|
/* 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, 1 );
|
|
/* important we unset, otherwise these object wont
|
|
* link into other scenes from this blend file */
|
|
flag_all_listbases_ids(LIB_APPEND_TAG, 0);
|
|
}
|
|
break;
|
|
}
|
|
|
|
/* done with library; close it */
|
|
BLO_blendhandle_close( openlib );
|
|
|
|
/* this should not happen, but just in case */
|
|
if( !lib )
|
|
return EXPP_ReturnPyObjError( PyExc_RuntimeError,
|
|
"could not library" );
|
|
|
|
/* find the base for this type */
|
|
lb = wich_libbase( G.main, self->type );
|
|
|
|
/*
|
|
* Check for linked data matching the name first. Even if we are trying
|
|
* to append, if the data has already been linked we need to return it
|
|
* (it won't be appended from the library).
|
|
*/
|
|
for( id = lb->first; id; id = id->next ) {
|
|
if( id->lib == lib && id->name[2]==name[0] &&
|
|
strcmp(id->name+2, name)==0 )
|
|
return GetPyObjectFromID( id );
|
|
}
|
|
|
|
/*
|
|
* If we didn't find it, and we're appending, then try searching for the
|
|
* new datablock, possibly under a new name.
|
|
*/
|
|
if( mode != FILE_LINK )
|
|
for( id = lb->first; id; id = id->next ) {
|
|
if( id->lib == NULL && id->name[2]==newName[0] &&
|
|
strcmp(id->name+2, newName)==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 * value)
|
|
{
|
|
return lib_link_or_append( self, value, OBJECT_IS_APPEND );
|
|
}
|
|
|
|
/* .link(): make a link to the library's data (except for objects) */
|
|
|
|
static PyObject *LibraryData_getLink( BPy_LibraryData *self, PyObject * value)
|
|
{
|
|
return lib_link_or_append( self, value, 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_O,
|
|
"(str) - create new data from library"},
|
|
{"link", (PyCFunction)LibraryData_getLink, METH_O,
|
|
"(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 "<module>.<name>" */
|
|
"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 * value)
|
|
{
|
|
char *filename = PyString_AsString(value);
|
|
BPy_Library *lib;
|
|
|
|
if( !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_O,
|
|
"(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 "<module>.<name>" */
|
|
"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;
|
|
}
|