forked from bartvdbraak/blender
BPython:
- New module + doc: Blender.Library: It's like File->Append, loads datablocks from .blend files. - small updates to fix warnings and accomodate for the new module, in readfile.[ch] - New Blender.sys module function: time, a wrapper of the PIL get time function. - Updated original makefile and scons builds.
This commit is contained in:
parent
e5e4c0fc4f
commit
382297b687
@ -214,6 +214,7 @@ 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);
|
||||
|
||||
BlendFileData* blo_read_blendafterruntime(int file, int actualsize, BlendReadError *error_r);
|
||||
|
||||
|
@ -48,6 +48,7 @@
|
||||
#include <stdlib.h> // for getenv atoi
|
||||
#include <fcntl.h> // for open
|
||||
#include <string.h> // for strcasecmp strrchr strncmp strstr
|
||||
#include <math.h> // for fabs
|
||||
|
||||
#ifndef WIN32
|
||||
#include <unistd.h> // for read close
|
||||
@ -2698,7 +2699,7 @@ static BHead *read_libblock(FileData *fd, Main *main, BHead *bhead, int flag, ID
|
||||
|
||||
ID *id;
|
||||
ListBase *lb;
|
||||
char *str;
|
||||
char *str = NULL;
|
||||
|
||||
if(bhead->code==ID_ID) {
|
||||
ID *linkedid= (ID *)(bhead + 1); /* BHEAD+DATA dependancy */
|
||||
@ -4862,13 +4863,12 @@ static void give_base_to_objects(Scene *sce, ListBase *lb)
|
||||
}
|
||||
#endif
|
||||
|
||||
static void append_named_part(SpaceFile *sfile, Main *mainvar, Scene *scene, char *name, int idcode)
|
||||
static void append_named_part(FileData *fd, Main *mainvar, Scene *scene, char *name, int idcode)
|
||||
{
|
||||
Object *ob;
|
||||
Base *base;
|
||||
BHead *bhead;
|
||||
ID *id;
|
||||
FileData *fd= (FileData*) sfile->libfiledata;
|
||||
int afbreek=0;
|
||||
|
||||
bhead = blo_firstbhead(fd);
|
||||
@ -4933,6 +4933,38 @@ static void append_id_part(FileData *fd, Main *mainvar, ID *id, ID **id_r)
|
||||
}
|
||||
}
|
||||
|
||||
/* this is a version of BLO_library_append needed by the BPython API, so
|
||||
* scripts can load data from .blend files -- see Blender.Library module.*/
|
||||
|
||||
/* append to G.scene */
|
||||
void BLO_script_library_append(BlendHandle *bh, char *dir, char *name, int idcode)
|
||||
{
|
||||
ListBase mainlist;
|
||||
Main *mainl;
|
||||
FileData *fd = (FileData *)bh;
|
||||
|
||||
mainlist.first= mainlist.last= G.main;
|
||||
G.main->next= NULL;
|
||||
|
||||
/* make mains */
|
||||
blo_split_main(&mainlist);
|
||||
|
||||
/* which one do we need? */
|
||||
mainl = blo_find_main(&mainlist, dir);
|
||||
|
||||
append_named_part(fd, mainl, G.scene, name, idcode);
|
||||
|
||||
/* make main consistant */
|
||||
expand_main(fd, mainl);
|
||||
|
||||
/* do this when expand found other libs */
|
||||
read_libraries(fd, &mainlist);
|
||||
|
||||
blo_join_main(&mainlist);
|
||||
G.main= mainlist.first;
|
||||
|
||||
lib_link_all(fd, G.main);
|
||||
}
|
||||
|
||||
/* append to G.scene */
|
||||
void BLO_library_append(SpaceFile *sfile, char *dir, int idcode)
|
||||
@ -4977,12 +5009,12 @@ void BLO_library_append(SpaceFile *sfile, char *dir, int idcode)
|
||||
mainl = blo_find_main(&mainlist, dir);
|
||||
|
||||
if(totsel==0) {
|
||||
append_named_part(sfile, mainl, G.scene, sfile->file, idcode);
|
||||
append_named_part(fd, mainl, G.scene, sfile->file, idcode);
|
||||
}
|
||||
else {
|
||||
for(a=0; a<sfile->totfile; a++) {
|
||||
if(sfile->filelist[a].flags & ACTIVE) {
|
||||
append_named_part(sfile, mainl, G.scene, sfile->filelist[a].relname, idcode);
|
||||
append_named_part(fd, mainl, G.scene, sfile->filelist[a].relname, idcode);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -5029,7 +5061,7 @@ static int mainvar_count_libread_blocks(Main *mainvar)
|
||||
|
||||
static void read_libraries(FileData *basefd, ListBase *mainlist)
|
||||
{
|
||||
Main *main= mainlist->first;
|
||||
Main *mainl= mainlist->first;
|
||||
Main *mainptr;
|
||||
ListBase *lbarray[30];
|
||||
int a, doit= 1;
|
||||
@ -5038,7 +5070,7 @@ static void read_libraries(FileData *basefd, ListBase *mainlist)
|
||||
doit= 0;
|
||||
|
||||
/* test 1: read libdata */
|
||||
mainptr= main->next;
|
||||
mainptr= mainl->next;
|
||||
|
||||
while(mainptr) {
|
||||
int tot= mainvar_count_libread_blocks(mainptr);
|
||||
@ -5092,7 +5124,7 @@ static void read_libraries(FileData *basefd, ListBase *mainlist)
|
||||
mainptr= mainptr->next;
|
||||
}
|
||||
}
|
||||
mainptr= main->next;
|
||||
mainptr= mainl->next;
|
||||
while(mainptr) {
|
||||
/* test if there are unread libblocks */
|
||||
a= set_listbasepointers(mainptr, lbarray);
|
||||
|
@ -18,6 +18,7 @@ source_files = ['BPY_interface.c',
|
||||
'api2_2x/World.c',
|
||||
'api2_2x/Lamp.c',
|
||||
'api2_2x/Lattice.c',
|
||||
'api2_2x/Library.c',
|
||||
'api2_2x/Curve.c',
|
||||
'api2_2x/Armature.c',
|
||||
'api2_2x/Bone.c',
|
||||
@ -53,6 +54,7 @@ source_files = ['BPY_interface.c',
|
||||
python_env.Append (CPPPATH = ['api2_2x',
|
||||
'../blenkernel',
|
||||
'../blenlib',
|
||||
'../blenloader',
|
||||
'../render/extern/include',
|
||||
'../makesdna',
|
||||
'#/intern/guardedalloc',
|
||||
|
@ -17,7 +17,7 @@
|
||||
*
|
||||
* 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.
|
||||
* 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.
|
||||
@ -35,205 +35,206 @@
|
||||
#include "Blender.h"
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Global variables */
|
||||
/* Global variables */
|
||||
/*****************************************************************************/
|
||||
PyObject *g_blenderdict;
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Function: Blender_Set */
|
||||
/* Python equivalent: Blender.Set */
|
||||
/* Function: Blender_Set */
|
||||
/* Python equivalent: Blender.Set */
|
||||
/*****************************************************************************/
|
||||
PyObject *Blender_Set (PyObject *self, PyObject *args)
|
||||
{
|
||||
char * name;
|
||||
PyObject * arg;
|
||||
int framenum;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "sO", &name, &arg))
|
||||
{
|
||||
/* TODO: Do we need to generate a nice error message here? */
|
||||
return (NULL);
|
||||
}
|
||||
char * name;
|
||||
PyObject * arg;
|
||||
int framenum;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "sO", &name, &arg))
|
||||
{
|
||||
/* TODO: Do we need to generate a nice error message here? */
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
if (StringEqual (name, "curframe"))
|
||||
{
|
||||
if (!PyArg_Parse(arg, "i", &framenum))
|
||||
{
|
||||
/* TODO: Do we need to generate a nice error message here? */
|
||||
return (NULL);
|
||||
}
|
||||
if (StringEqual (name, "curframe"))
|
||||
{
|
||||
if (!PyArg_Parse(arg, "i", &framenum))
|
||||
{
|
||||
/* TODO: Do we need to generate a nice error message here? */
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
G.scene->r.cfra = framenum;
|
||||
G.scene->r.cfra = framenum;
|
||||
|
||||
update_for_newframe();
|
||||
}
|
||||
else
|
||||
{
|
||||
return (PythonReturnErrorObject (PyExc_AttributeError,
|
||||
"bad request identifier"));
|
||||
}
|
||||
return ( PythonIncRef (Py_None) );
|
||||
update_for_newframe();
|
||||
}
|
||||
else
|
||||
{
|
||||
return (PythonReturnErrorObject (PyExc_AttributeError,
|
||||
"bad request identifier"));
|
||||
}
|
||||
return ( PythonIncRef (Py_None) );
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Function: Blender_Get */
|
||||
/* Python equivalent: Blender.Get */
|
||||
/* Function: Blender_Get */
|
||||
/* Python equivalent: Blender.Get */
|
||||
/*****************************************************************************/
|
||||
PyObject *Blender_Get (PyObject *self, PyObject *args)
|
||||
{
|
||||
PyObject * object;
|
||||
PyObject * dict;
|
||||
char * str;
|
||||
|
||||
if (!PyArg_ParseTuple (args, "O", &object))
|
||||
{
|
||||
/* TODO: Do we need to generate a nice error message here? */
|
||||
return (NULL);
|
||||
}
|
||||
PyObject * object;
|
||||
PyObject * dict;
|
||||
char * str;
|
||||
|
||||
if (!PyArg_ParseTuple (args, "O", &object))
|
||||
{
|
||||
/* TODO: Do we need to generate a nice error message here? */
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
if (PyString_Check (object))
|
||||
{
|
||||
str = PyString_AsString (object);
|
||||
if (PyString_Check (object))
|
||||
{
|
||||
str = PyString_AsString (object);
|
||||
|
||||
if (StringEqual (str, "curframe"))
|
||||
{
|
||||
return ( PyInt_FromLong (G.scene->r.cfra) );
|
||||
}
|
||||
if (StringEqual (str, "curtime"))
|
||||
{
|
||||
return ( PyFloat_FromDouble (frame_to_float (G.scene->r.cfra) ) );
|
||||
}
|
||||
if (StringEqual (str, "staframe"))
|
||||
{
|
||||
return ( PyInt_FromLong (G.scene->r.sfra) );
|
||||
}
|
||||
if (StringEqual (str, "endframe"))
|
||||
{
|
||||
return ( PyInt_FromLong (G.scene->r.efra) );
|
||||
}
|
||||
if (StringEqual (str, "filename"))
|
||||
{
|
||||
return ( PyString_FromString (G.sce) );
|
||||
}
|
||||
/* According to the old file (opy_blender.c), the following if
|
||||
statement is a quick hack and needs some clean up. */
|
||||
if (StringEqual (str, "vrmloptions"))
|
||||
{
|
||||
dict = PyDict_New ();
|
||||
if (StringEqual (str, "curframe"))
|
||||
{
|
||||
return ( PyInt_FromLong (G.scene->r.cfra) );
|
||||
}
|
||||
if (StringEqual (str, "curtime"))
|
||||
{
|
||||
return ( PyFloat_FromDouble (frame_to_float (G.scene->r.cfra) ) );
|
||||
}
|
||||
if (StringEqual (str, "staframe"))
|
||||
{
|
||||
return ( PyInt_FromLong (G.scene->r.sfra) );
|
||||
}
|
||||
if (StringEqual (str, "endframe"))
|
||||
{
|
||||
return ( PyInt_FromLong (G.scene->r.efra) );
|
||||
}
|
||||
if (StringEqual (str, "filename"))
|
||||
{
|
||||
return ( PyString_FromString (G.sce) );
|
||||
}
|
||||
/* According to the old file (opy_blender.c), the following if
|
||||
statement is a quick hack and needs some clean up. */
|
||||
if (StringEqual (str, "vrmloptions"))
|
||||
{
|
||||
dict = PyDict_New ();
|
||||
|
||||
PyDict_SetItemString (dict, "twoside",
|
||||
PyInt_FromLong (U.vrmlflag & USER_VRML_TWOSIDED));
|
||||
PyDict_SetItemString (dict, "twoside",
|
||||
PyInt_FromLong (U.vrmlflag & USER_VRML_TWOSIDED));
|
||||
|
||||
PyDict_SetItemString (dict, "layers",
|
||||
PyInt_FromLong (U.vrmlflag & USER_VRML_LAYERS));
|
||||
PyDict_SetItemString (dict, "layers",
|
||||
PyInt_FromLong (U.vrmlflag & USER_VRML_LAYERS));
|
||||
|
||||
PyDict_SetItemString (dict, "autoscale",
|
||||
PyInt_FromLong (U.vrmlflag & USER_VRML_AUTOSCALE));
|
||||
PyDict_SetItemString (dict, "autoscale",
|
||||
PyInt_FromLong (U.vrmlflag & USER_VRML_AUTOSCALE));
|
||||
|
||||
return (dict);
|
||||
} /* End 'quick hack' part. */
|
||||
if (StringEqual (str, "version"))
|
||||
{
|
||||
return ( PyInt_FromLong (G.version) );
|
||||
}
|
||||
/* TODO: Do we want to display a usefull message here that the
|
||||
requested data is unknown?
|
||||
else
|
||||
{
|
||||
return (PythonReturnErrorObject (..., "message") );
|
||||
}
|
||||
*/
|
||||
}
|
||||
else
|
||||
{
|
||||
return (PythonReturnErrorObject (PyExc_AttributeError,
|
||||
"expected string argument"));
|
||||
}
|
||||
return (dict);
|
||||
} /* End 'quick hack' part. */
|
||||
if (StringEqual (str, "version"))
|
||||
{
|
||||
return ( PyInt_FromLong (G.version) );
|
||||
}
|
||||
/* TODO: Do we want to display a usefull message here that the
|
||||
requested data is unknown?
|
||||
else
|
||||
{
|
||||
return (PythonReturnErrorObject (..., "message") );
|
||||
}
|
||||
*/
|
||||
}
|
||||
else
|
||||
{
|
||||
return (PythonReturnErrorObject (PyExc_AttributeError,
|
||||
"expected string argument"));
|
||||
}
|
||||
|
||||
return (PythonReturnErrorObject (PyExc_AttributeError,
|
||||
"bad request identifier"));
|
||||
return (PythonReturnErrorObject (PyExc_AttributeError,
|
||||
"bad request identifier"));
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Function: Blender_Redraw */
|
||||
/* Python equivalent: Blender.Redraw */
|
||||
/* Function: Blender_Redraw */
|
||||
/* Python equivalent: Blender.Redraw */
|
||||
/*****************************************************************************/
|
||||
PyObject *Blender_Redraw(PyObject *self, PyObject *args)
|
||||
{
|
||||
int wintype = SPACE_VIEW3D;
|
||||
int wintype = SPACE_VIEW3D;
|
||||
|
||||
if (!PyArg_ParseTuple (args, "|i", &wintype))
|
||||
{
|
||||
return EXPP_ReturnPyObjError (PyExc_TypeError,
|
||||
"expected int argument (or nothing)");
|
||||
}
|
||||
if (!PyArg_ParseTuple (args, "|i", &wintype))
|
||||
{
|
||||
return EXPP_ReturnPyObjError (PyExc_TypeError,
|
||||
"expected int argument (or nothing)");
|
||||
}
|
||||
|
||||
return M_Window_Redraw(self, Py_BuildValue("(i)", wintype));
|
||||
return M_Window_Redraw(self, Py_BuildValue("(i)", wintype));
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Function: Blender_ReleaseGlobalDict */
|
||||
/* Python equivalent: Blender.ReleaseGlobalDict */
|
||||
/* Description: Deprecated function. */
|
||||
/* Function: Blender_ReleaseGlobalDict */
|
||||
/* Python equivalent: Blender.ReleaseGlobalDict */
|
||||
/* Description: Deprecated function. */
|
||||
/*****************************************************************************/
|
||||
PyObject *Blender_ReleaseGlobalDict(PyObject *self, PyObject *args)
|
||||
{
|
||||
Py_INCREF(Py_None);
|
||||
return Py_None;
|
||||
return Py_None;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Function: Blender_Quit */
|
||||
/* Python equivalent: Blender.Quit */
|
||||
/* Function: Blender_Quit */
|
||||
/* Python equivalent: Blender.Quit */
|
||||
/*****************************************************************************/
|
||||
PyObject *Blender_Quit(PyObject *self)
|
||||
{
|
||||
exit_usiblender();
|
||||
|
||||
Py_INCREF(Py_None);
|
||||
return Py_None;
|
||||
return Py_None;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Function: initBlender */
|
||||
/* Function: initBlender */
|
||||
/*****************************************************************************/
|
||||
void M_Blender_Init (void)
|
||||
{
|
||||
PyObject * module;
|
||||
PyObject * dict;
|
||||
PyObject * module;
|
||||
PyObject * dict;
|
||||
|
||||
g_blenderdict = NULL;
|
||||
g_blenderdict = NULL;
|
||||
|
||||
/* TODO: create a docstring for the Blender module */
|
||||
module = Py_InitModule3("Blender", Blender_methods, NULL);
|
||||
/* TODO: create a docstring for the Blender module */
|
||||
module = Py_InitModule3("Blender", Blender_methods, NULL);
|
||||
|
||||
types_InitAll(); /* set all our pytypes to &PyType_Type*/
|
||||
|
||||
dict = PyModule_GetDict (module);
|
||||
g_blenderdict = dict;
|
||||
PyDict_SetItemString (dict, "Types", Types_Init());
|
||||
PyDict_SetItemString (dict, "sys", sys_Init());
|
||||
PyDict_SetItemString (dict, "Registry", Registry_Init());
|
||||
PyDict_SetItemString (dict, "Scene", Scene_Init());
|
||||
PyDict_SetItemString (dict, "Object", Object_Init());
|
||||
PyDict_SetItemString (dict, "Material", Material_Init());
|
||||
PyDict_SetItemString (dict, "Camera", Camera_Init());
|
||||
PyDict_SetItemString (dict, "Lamp", Lamp_Init());
|
||||
PyDict_SetItemString (dict, "Lattice", Lattice_Init());
|
||||
PyDict_SetItemString (dict, "Curve", Curve_Init());
|
||||
PyDict_SetItemString (dict, "Armature", Armature_Init());
|
||||
PyDict_SetItemString (dict, "Ipo", Ipo_Init());
|
||||
PyDict_SetItemString (dict, "IpoCurve", IpoCurve_Init());
|
||||
PyDict_SetItemString (dict, "Metaball", Metaball_Init());
|
||||
PyDict_SetItemString (dict, "Image", Image_Init());
|
||||
PyDict_SetItemString (dict, "Window", Window_Init());
|
||||
PyDict_SetItemString (dict, "Draw", Draw_Init());
|
||||
PyDict_SetItemString (dict, "BGL", BGL_Init());
|
||||
PyDict_SetItemString (dict, "Effect", Effect_Init());
|
||||
PyDict_SetItemString (dict, "Text", Text_Init());
|
||||
PyDict_SetItemString (dict, "World", World_Init());
|
||||
PyDict_SetItemString (dict, "Texture", Texture_Init());
|
||||
PyDict_SetItemString (dict, "NMesh", NMesh_Init());
|
||||
PyDict_SetItemString (dict, "Noise", Noise_Init());
|
||||
PyDict_SetItemString (dict, "Mathutils",Mathutils_Init());
|
||||
dict = PyModule_GetDict (module);
|
||||
g_blenderdict = dict;
|
||||
PyDict_SetItemString (dict, "Types", Types_Init());
|
||||
PyDict_SetItemString (dict, "sys", sys_Init());
|
||||
PyDict_SetItemString (dict, "Registry", Registry_Init());
|
||||
PyDict_SetItemString (dict, "Scene", Scene_Init());
|
||||
PyDict_SetItemString (dict, "Object", Object_Init());
|
||||
PyDict_SetItemString (dict, "Material", Material_Init());
|
||||
PyDict_SetItemString (dict, "Camera", Camera_Init());
|
||||
PyDict_SetItemString (dict, "Lamp", Lamp_Init());
|
||||
PyDict_SetItemString (dict, "Lattice", Lattice_Init());
|
||||
PyDict_SetItemString (dict, "Curve", Curve_Init());
|
||||
PyDict_SetItemString (dict, "Armature", Armature_Init());
|
||||
PyDict_SetItemString (dict, "Ipo", Ipo_Init());
|
||||
PyDict_SetItemString (dict, "IpoCurve", IpoCurve_Init());
|
||||
PyDict_SetItemString (dict, "Metaball", Metaball_Init());
|
||||
PyDict_SetItemString (dict, "Image", Image_Init());
|
||||
PyDict_SetItemString (dict, "Window", Window_Init());
|
||||
PyDict_SetItemString (dict, "Draw", Draw_Init());
|
||||
PyDict_SetItemString (dict, "BGL", BGL_Init());
|
||||
PyDict_SetItemString (dict, "Effect", Effect_Init());
|
||||
PyDict_SetItemString (dict, "Text", Text_Init());
|
||||
PyDict_SetItemString (dict, "World", World_Init());
|
||||
PyDict_SetItemString (dict, "Texture", Texture_Init());
|
||||
PyDict_SetItemString (dict, "NMesh", NMesh_Init());
|
||||
PyDict_SetItemString (dict, "Noise", Noise_Init());
|
||||
PyDict_SetItemString (dict, "Mathutils",Mathutils_Init());
|
||||
PyDict_SetItemString (dict, "Library", Library_Init());
|
||||
}
|
||||
|
@ -17,7 +17,7 @@
|
||||
*
|
||||
* 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.
|
||||
* 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.
|
||||
@ -51,7 +51,7 @@
|
||||
PyObject *M_Window_Redraw(PyObject *self, PyObject *args);
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Python API function prototypes for the Blender module. */
|
||||
/* Python API function prototypes for the Blender module. */
|
||||
/*****************************************************************************/
|
||||
PyObject *Blender_Set (PyObject *self, PyObject *args);
|
||||
PyObject *Blender_Get (PyObject *self, PyObject *args);
|
||||
@ -60,9 +60,9 @@ PyObject *Blender_ReleaseGlobalDict(PyObject *self, PyObject *args);
|
||||
PyObject *Blender_Quit(PyObject *self);
|
||||
|
||||
/*****************************************************************************/
|
||||
/* The following string definitions are used for documentation strings. */
|
||||
/* In Python these will be written to the console when doing a */
|
||||
/* Blender.__doc__ */
|
||||
/* The following string definitions are used for documentation strings. */
|
||||
/* In Python these will be written to the console when doing a */
|
||||
/* Blender.__doc__ */
|
||||
/*****************************************************************************/
|
||||
char Blender_Set_doc[] =
|
||||
"(request, data) - Update settings in Blender\n\
|
||||
@ -90,15 +90,15 @@ char Blender_Quit_doc[] =
|
||||
"() - Quit Blender. Experimental, please use with caution.";
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Python method structure definition. */
|
||||
/* Python method structure definition. */
|
||||
/*****************************************************************************/
|
||||
struct PyMethodDef Blender_methods[] = {
|
||||
{"Set", Blender_Set, METH_VARARGS, Blender_Set_doc},
|
||||
{"Get", Blender_Get, METH_VARARGS, Blender_Get_doc},
|
||||
{"Set", Blender_Set, METH_VARARGS, Blender_Set_doc},
|
||||
{"Get", Blender_Get, METH_VARARGS, Blender_Get_doc},
|
||||
{"Redraw", Blender_Redraw, METH_VARARGS, Blender_Redraw_doc},
|
||||
{"Quit", (PyCFunction)Blender_Quit, METH_NOARGS, Blender_Quit_doc},
|
||||
{"Quit", (PyCFunction)Blender_Quit, METH_NOARGS, Blender_Quit_doc},
|
||||
{"ReleaseGlobalDict", &Blender_ReleaseGlobalDict,
|
||||
METH_VARARGS, Blender_ReleaseGlobalDict_doc},
|
||||
METH_VARARGS, Blender_ReleaseGlobalDict_doc},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
|
360
source/blender/python/api2_2x/Library.c
Normal file
360
source/blender/python/api2_2x/Library.c
Normal file
@ -0,0 +1,360 @@
|
||||
/**
|
||||
* $Id$
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* ***** END GPL/BL DUAL LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
#include <Python.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "BKE_displist.h" /* for set_displist_onlyzero */
|
||||
#include "BKE_font.h" /* for text_to_curve */
|
||||
#include "BKE_library.h" /* for all_local */
|
||||
#include "BLO_readfile.h"
|
||||
#include "BLI_linklist.h"
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "gen_utils.h"
|
||||
#include "modules.h"
|
||||
|
||||
/**
|
||||
* Global variables.
|
||||
*/
|
||||
static BlendHandle *bpy_openlib; /* ptr to the open .blend file */
|
||||
static char *bpy_openlibname; /* its pathname */
|
||||
|
||||
/**
|
||||
* 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 *args);
|
||||
static PyObject *M_Library_Load(PyObject *self, PyObject *args);
|
||||
static PyObject *M_Library_LinkableGroups(PyObject *self);
|
||||
|
||||
/**
|
||||
* 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.";
|
||||
|
||||
/**
|
||||
* Python method structure definition for Blender.Library submodule.
|
||||
*/
|
||||
struct PyMethodDef M_Library_methods[] = {
|
||||
{"Open", M_Library_Open, METH_VARARGS, 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_VARARGS, Library_Datablocks_doc},
|
||||
{"Load", M_Library_Load, METH_VARARGS, Library_Load_doc},
|
||||
{"LinkableGroups",(PyCFunction)M_Library_LinkableGroups,
|
||||
METH_NOARGS, Library_LinkableGroups_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.
|
||||
*/
|
||||
PyObject *M_Library_Open(PyObject *self, PyObject *args)
|
||||
{
|
||||
char *fname = NULL;
|
||||
int len = 0;
|
||||
|
||||
if (!PyArg_ParseTuple (args, "s", &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 */
|
||||
}
|
||||
|
||||
bpy_openlib = BLO_blendhandle_from_file(fname);
|
||||
|
||||
if (!bpy_openlib) return Py_BuildValue("i", 0);
|
||||
|
||||
len = strlen(fname) + 1; /* +1 for terminating '\0' */
|
||||
|
||||
bpy_openlibname = MEM_mallocN(len, "bpy_openlibname");
|
||||
|
||||
if (bpy_openlibname)
|
||||
PyOS_snprintf (bpy_openlibname, len, "%s", fname);
|
||||
|
||||
return Py_BuildValue("i", 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the current .blend file, if any.
|
||||
*/
|
||||
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_INCREF(Py_None);
|
||||
return Py_None;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the filename of the currently open library file, if any.
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
PyObject *M_Library_Datablocks(PyObject *self, PyObject *args)
|
||||
{
|
||||
char *name = NULL;
|
||||
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 (!PyArg_ParseTuple (args, "s", &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, Py_BuildValue("s", (char *)l->link));
|
||||
counter++;
|
||||
}
|
||||
BLI_linklist_free(names, free); /* free linklist *and* each node's data */
|
||||
return list;
|
||||
}
|
||||
|
||||
Py_INCREF (Py_None);
|
||||
return Py_None;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a list with the names of all linkable groups in the
|
||||
* open library file.
|
||||
*/
|
||||
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);
|
||||
|
||||
if (names) {
|
||||
int counter = 0;
|
||||
list = PyList_New(BLI_linklist_length(names));
|
||||
for (l = names; l; l = l->next) {
|
||||
PyList_SET_ITEM(list, counter, Py_BuildValue("s", (char *)l->link));
|
||||
counter++;
|
||||
}
|
||||
BLI_linklist_free(names, free); /* free linklist *and* each node's data */
|
||||
return list;
|
||||
}
|
||||
|
||||
Py_INCREF (Py_None);
|
||||
return Py_None;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load (append) a given datablock of a given datablock type
|
||||
* to the current scene.
|
||||
*/
|
||||
PyObject *M_Library_Load(PyObject *self, PyObject *args)
|
||||
{
|
||||
char *name = NULL;
|
||||
char *base = NULL;
|
||||
int update = 1;
|
||||
int blocktype = 0;
|
||||
|
||||
if (!bpy_openlib) {
|
||||
return EXPP_ReturnPyObjError(PyExc_IOError,
|
||||
"no library file: you need to open one, first.");
|
||||
}
|
||||
|
||||
if (!PyArg_ParseTuple (args, "ss|i", &name, &base, &update)) {
|
||||
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");
|
||||
}
|
||||
|
||||
BLO_script_library_append(bpy_openlib, bpy_openlibname, name, blocktype);
|
||||
|
||||
if (update) {
|
||||
M_Library_Update(self);
|
||||
Py_DECREF(Py_None); /* incref'ed by above function */
|
||||
}
|
||||
|
||||
Py_INCREF (Py_None);
|
||||
return Py_None;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update all links and remake displists.
|
||||
*/
|
||||
PyObject *M_Library_Update(PyObject *self)
|
||||
{ /* code adapted from do_library_append in src/filesel.c: */
|
||||
Object *ob = NULL;
|
||||
Library *lib = NULL;
|
||||
|
||||
ob = G.main->object.first;
|
||||
set_displist_onlyzero(1);
|
||||
while (ob) {
|
||||
if (ob->id.lib) {
|
||||
if (ob->type==OB_FONT) {
|
||||
Curve *cu= ob->data;
|
||||
if (cu->nurb.first==0) text_to_curve(ob, 0);
|
||||
}
|
||||
makeDispList(ob);
|
||||
}
|
||||
else {
|
||||
if (ob->type == OB_MESH && ob->parent && ob->parent->type == OB_LATTICE)
|
||||
makeDispList(ob);
|
||||
}
|
||||
|
||||
ob = ob->id.next;
|
||||
}
|
||||
set_displist_onlyzero(0);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
Py_INCREF (Py_None);
|
||||
return Py_None;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the Blender.Library submodule.
|
||||
* Called by Blender_Init in Blender.c .
|
||||
* @return the registered submodule.
|
||||
*/
|
||||
PyObject *Library_Init (void)
|
||||
{
|
||||
PyObject *submod;
|
||||
|
||||
submod = Py_InitModule3("Blender.Library", M_Library_methods, M_Library_doc);
|
||||
|
||||
return submod;
|
||||
}
|
@ -50,6 +50,7 @@ CPPFLAGS += -I$(NAN_GUARDEDALLOC)/include
|
||||
CPPFLAGS += -I../../makesdna
|
||||
CPPFLAGS += -I../../blenkernel
|
||||
CPPFLAGS += -I../../blenlib
|
||||
CPPFLAGS += -I../../blenloader
|
||||
CPPFLAGS += -I../../include
|
||||
CPPFLAGS += -I../../render/extern/include
|
||||
CPPFLAGS += -I$(NAN_BMFONT)/include
|
||||
|
@ -30,6 +30,7 @@
|
||||
*/
|
||||
|
||||
#include "BKE_utildefines.h"
|
||||
#include "PIL_time.h"
|
||||
|
||||
#include "Sys.h"
|
||||
|
||||
@ -168,3 +169,10 @@ static PyObject *M_sys_splitext (PyObject *self, PyObject *args)
|
||||
|
||||
return Py_BuildValue("ss", path, ext);
|
||||
}
|
||||
|
||||
static PyObject *M_sys_time (PyObject *self)
|
||||
{
|
||||
double t = PIL_check_seconds_timer();
|
||||
return Py_BuildValue("d", t);
|
||||
}
|
||||
|
||||
|
@ -43,6 +43,7 @@
|
||||
static PyObject *M_sys_basename (PyObject *self, PyObject *args);
|
||||
static PyObject *M_sys_dirname (PyObject *self, PyObject *args);
|
||||
static PyObject *M_sys_splitext (PyObject *self, PyObject *args);
|
||||
static PyObject *M_sys_time (PyObject *self);
|
||||
|
||||
/*****************************************************************************/
|
||||
/* The following string definitions are used for documentation strings. */
|
||||
@ -65,6 +66,11 @@ static char M_sys_splitext_doc[]="(path) - Split 'path' in root and \
|
||||
extension:\n/this/that/file.ext -> ('/this/that/file','.ext').\n\
|
||||
Return the pair (root, extension).";
|
||||
|
||||
static char M_sys_time_doc[]="() - Return a float representing time elapsed \
|
||||
in seconds.\n\
|
||||
Each successive call is garanteed to return values greater than or\n\
|
||||
equal to the previous call.";
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Python method structure definition for Blender.sys module: */
|
||||
/*****************************************************************************/
|
||||
@ -72,6 +78,7 @@ struct PyMethodDef M_sys_methods[] = {
|
||||
{"basename", M_sys_basename, METH_VARARGS, M_sys_basename_doc},
|
||||
{"dirname", M_sys_dirname, METH_VARARGS, M_sys_dirname_doc},
|
||||
{"splitext", M_sys_splitext, METH_VARARGS, M_sys_splitext_doc},
|
||||
{"time", (PyCFunction)M_sys_time, METH_NOARGS, M_sys_time_doc},
|
||||
{NULL, NULL, 0, NULL}
|
||||
};
|
||||
|
||||
|
@ -29,6 +29,7 @@ The Blender Python API Reference
|
||||
- L{Ipo}
|
||||
- L{Lamp}
|
||||
- L{Lattice}
|
||||
- L{Library}
|
||||
- L{Material}
|
||||
- L{Mathutils}
|
||||
- L{Metaball}
|
||||
|
110
source/blender/python/api2_2x/doc/Library.py
Normal file
110
source/blender/python/api2_2x/doc/Library.py
Normal file
@ -0,0 +1,110 @@
|
||||
# Blender.Library submodule
|
||||
|
||||
"""
|
||||
The Blender.Library submodule.
|
||||
|
||||
Library
|
||||
=======
|
||||
|
||||
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 is a new, still experimental module.
|
||||
|
||||
Example::
|
||||
import Blender
|
||||
from Blender import Library
|
||||
|
||||
def f(name):
|
||||
open_library(name)
|
||||
|
||||
def open_library(name):
|
||||
Library.Open(name)
|
||||
groups = Library.LinkableGroups()
|
||||
|
||||
for db in groups:
|
||||
print "\nDATABLOCK %s:" % db
|
||||
for obname in Library.Datablocks(db):
|
||||
print obname
|
||||
|
||||
if 'Object' in groups:
|
||||
for obname in Library.Datablocks('Object'):
|
||||
Library.Load(obname, 'Object', 0) # note the 0...
|
||||
Library.Update()
|
||||
|
||||
Library.Close()
|
||||
b.Redraw()
|
||||
|
||||
b.Window.FileSelector(f, "Choose Library", "*.blend")
|
||||
|
||||
"""
|
||||
|
||||
def Open (filename):
|
||||
"""
|
||||
Open an existing .blend file. If there was already one open file, it is
|
||||
closed first.
|
||||
@type filename: string
|
||||
@param filename: The filename of a Blender file.
|
||||
@rtype: bool
|
||||
@return: 1 if succesful, 0 otherwise.
|
||||
"""
|
||||
|
||||
def Close ():
|
||||
"""
|
||||
Close the currently open library file, if any.
|
||||
"""
|
||||
|
||||
def getName ():
|
||||
"""
|
||||
Get the filename of the currently open library file.
|
||||
@rtype: string
|
||||
@return: The open library filename.
|
||||
"""
|
||||
|
||||
def LinkableGroups ():
|
||||
"""
|
||||
Get all the linkable group names from the currently open library file. These
|
||||
are the available groups for linking with the current scene. Ex: 'Object',
|
||||
'Mesh', 'Material', 'Text', etc.
|
||||
@rtype: list of strings
|
||||
@return: the list of linkable groups.
|
||||
"""
|
||||
|
||||
def Datablocks (group):
|
||||
"""
|
||||
Get all datablock objects of the given 'group' available in the currently
|
||||
open library file.
|
||||
@type group: string
|
||||
@param group: datablock group, see L{LinkableGroups}.
|
||||
"""
|
||||
|
||||
def Load (datablock, group, update = 1):
|
||||
"""
|
||||
Load the given datablock object from the current library file
|
||||
@type datablock: string
|
||||
@type group: string
|
||||
@type update: bool
|
||||
@param datablock: an available object name, as returned by L{Datablocks}.
|
||||
@param group: an available group name, as returned by L{LinkableGroups}.
|
||||
@param update: defines if Blender should be updated after loading this
|
||||
object. This means linking all objects and remaking all display lists,
|
||||
so it is potentially very slow.
|
||||
|
||||
@warn: If you plan to load more than one object in sequence, it is
|
||||
B{definitely recommended} to set 'update' to 0 in all calls to this
|
||||
function and after them call L{Update}.
|
||||
"""
|
||||
|
||||
def Update ():
|
||||
"""
|
||||
Update all links and display lists in Blender. This function should be
|
||||
called after a series of L{Load}(datablock, group, B{0}) calls to make
|
||||
everything behave nicely.
|
||||
@warn: to use this function, remember to set the third L{Load} parameter to
|
||||
zero or each loading will automatically update Blender, which will slow
|
||||
down your script and make you look like a lousy programmer.
|
||||
Enough warnings :)?
|
||||
"""
|
||||
|
@ -6,6 +6,8 @@ The Blender.sys submodule.
|
||||
sys
|
||||
===
|
||||
|
||||
B{New}: L{time}
|
||||
|
||||
This module provides a minimal set of helper functions and data. Its purpose
|
||||
is to avoid the need for the standard Python module 'os', in special 'os.path',
|
||||
though it is only meant for the simplest cases.
|
||||
@ -63,3 +65,11 @@ def splitext (path):
|
||||
@rtype: list with two strings
|
||||
@return: (root, ext)
|
||||
"""
|
||||
|
||||
def time ():
|
||||
"""
|
||||
Get the current time in seconds since a fixed value. Successive calls to
|
||||
this function are garanteed to return values greater than the previous call.
|
||||
@rtype: float
|
||||
@return: the elapsed time in seconds.
|
||||
"""
|
||||
|
@ -192,5 +192,6 @@ PyObject * Draw_Init (void);
|
||||
PyObject * BGL_Init (void);
|
||||
PyObject * Mathutils_Init (void);
|
||||
PyObject * NLA_Init (void);
|
||||
PyObject * Library_Init (void);
|
||||
|
||||
#endif /* EXPP_modules_h */
|
||||
|
Loading…
Reference in New Issue
Block a user