Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--source/blender/python/BPY_extern.h2
-rw-r--r--source/blender/python/BPY_interface.c175
-rw-r--r--source/blender/python/BPY_menus.c589
-rw-r--r--source/blender/python/BPY_menus.h98
-rw-r--r--source/blender/python/SConscript1
-rw-r--r--source/blender/src/header_info.c51
-rw-r--r--source/blender/src/header_script.c103
-rw-r--r--source/creator/creator.c4
8 files changed, 977 insertions, 46 deletions
diff --git a/source/blender/python/BPY_extern.h b/source/blender/python/BPY_extern.h
index a791788cd9b..3feb5606059 100644
--- a/source/blender/python/BPY_extern.h
+++ b/source/blender/python/BPY_extern.h
@@ -49,7 +49,7 @@ struct _object; // forward declaration for PyObject !
void BPY_start_python(void);
void BPY_end_python(void);
-void BPY_syspath_append_pythondir(void);
+void BPY_post_start_python(void);
int BPY_Err_getLinenumber(void);
const char *BPY_Err_getFilename(void);
/* void BPY_Err_Handle(struct Text *text); */
diff --git a/source/blender/python/BPY_interface.c b/source/blender/python/BPY_interface.c
index 8f6e813a9e2..c8f8e825fa3 100644
--- a/source/blender/python/BPY_interface.c
+++ b/source/blender/python/BPY_interface.c
@@ -41,12 +41,14 @@
#include <MEM_guardedalloc.h>
#include <BLI_blenlib.h> /* for BLI_last_slash() */
+#include <BIF_interface.h> /* for pupmenu */
#include <BIF_space.h>
#include <BIF_screen.h>
#include <BKE_global.h>
#include <BKE_library.h>
#include <BKE_main.h>
#include <BKE_text.h>
+#include <BKE_utildefines.h>
#include <DNA_camera_types.h>
#include <DNA_ID.h>
#include <DNA_lamp_types.h>
@@ -62,6 +64,7 @@
#include <DNA_userdef_types.h> /* for U.pythondir */
#include "BPY_extern.h"
+#include "BPY_menus.h"
#include "api2_2x/EXPP_interface.h"
#include "api2_2x/constant.h"
@@ -135,6 +138,9 @@ void BPY_end_python(void)
}
Py_Finalize();
+
+ BPyMenu_RemoveAllEntries(); /* freeing bpymenu mem */
+
return;
}
@@ -198,7 +204,7 @@ void init_syspath(void)
else
printf ("Warning: could not determine argv[0] path\n");
- if (U.pythondir) {
+ if (U.pythondir && strcmp(U.pythondir, "")) {
p = Py_BuildValue("s", U.pythondir);
syspath_append(p); /* append to module search path */
}
@@ -256,15 +262,17 @@ void init_syspath(void)
}
/*****************************************************************************/
-/* Description: This function adds the user defined folder for Python */
-/* scripts to sys.path. This is done in init_syspath, too, but */
-/* when Blender's main() runs BPY_start_python(), U.pythondir */
-/* isn't set yet, so we provide this function to be executed */
-/* after U.pythondir is defined. */
+/* Description: This function finishes Python initialization in Blender. */
+/* Because U.pythondir (user defined dir for scripts) isn't */
+/* initialized when BPY_start_Python needs to be executed, we */
+/* postpone adding U.pythondir to sys.path and also BPyMenus */
+/* (mechanism to register scripts in Blender menus) for when */
+/* that dir info is available. */
/*****************************************************************************/
-void BPY_syspath_append_pythondir(void)
+void BPY_post_start_python(void)
{
syspath_append(Py_BuildValue("s", U.pythondir));
+ BPyMenu_Init(); /* get dynamic menus (registered scripts) data */
}
/*****************************************************************************/
@@ -304,10 +312,15 @@ PyObject *traceback_getFilename(PyObject *tb)
/* Description: Blender Python error handler. This catches the error and */
/* stores filename and line number in a global */
/*****************************************************************************/
-void BPY_Err_Handle(Text *text)
+void BPY_Err_Handle(char *script_name)
{
PyObject *exception, *err, *tb, *v;
+ if (!script_name) {
+ printf("Error: script has NULL name\n");
+ return;
+ }
+
PyErr_Fetch(&exception, &err, &tb);
if (!exception && !tb) {
@@ -315,7 +328,7 @@ void BPY_Err_Handle(Text *text)
return;
}
- strcpy(g_script_error.filename, GetName(text));
+ strcpy(g_script_error.filename, script_name);
if (exception && PyErr_GivenExceptionMatches(exception, PyExc_SyntaxError)) {
/* no traceback available when SyntaxError */
@@ -350,7 +363,7 @@ void BPY_Err_Handle(Text *text)
v = PyObject_GetAttrString(tb, "tb_next");
if (v == Py_None || strcmp(PyString_AsString(traceback_getFilename(v)),
- GetName(text))) {
+ script_name)) {
break;
}
@@ -378,7 +391,7 @@ void BPY_Err_Handle(Text *text)
int BPY_txt_do_python(struct SpaceText* st)
{
PyObject *py_dict, *py_result;
- BPy_constant *tracer;
+ BPy_constant *info;
Script *script = G.main->script.first;
if (!st->text) return 0;
@@ -415,26 +428,144 @@ int BPY_txt_do_python(struct SpaceText* st)
script->py_globaldict = py_dict;
-/* We will insert a constant dict at this script's namespace, with the name
- * of the script. Later more info can be added, if necessary. */
- tracer = (BPy_constant *)M_constant_New(); /*create a constant object*/
- if (tracer) {
- constant_insert(tracer, "name", PyString_FromString(script->id.name+2));
+ info = (BPy_constant *)M_constant_New();
+ if (info) {
+ constant_insert(info, "name", PyString_FromString(script->id.name+2));
+ Py_INCREF (Py_None);
+ constant_insert(info, "arg", Py_None);
+ PyDict_SetItemString(py_dict, "__script__", (PyObject *)info);
}
- PyDict_SetItemString(py_dict, "__script__", (PyObject *)tracer);
-
clearScriptLinks ();
py_result = RunPython (st->text, py_dict); /* Run the script */
if (!py_result) { /* Failed execution of the script */
- BPY_Err_Handle(st->text);
+ BPY_Err_Handle(GetName(st->text));
+ ReleaseGlobalDictionary(py_dict);
+ free_libblock(&G.main->script, script);
+ //BPY_end_python();
+ //BPY_start_python();
+
+ return 0;
+ }
+ else {
+ Py_DECREF (py_result);
+ script->flags &=~SCRIPT_RUNNING;
+ if (!script->flags) {
+ ReleaseGlobalDictionary(py_dict);
+ script->py_globaldict = NULL;
+ free_libblock(&G.main->script, script);
+ }
+ }
+
+ return 1; /* normal return */
+}
+
+/*****************************************************************************/
+/* Description: This function executes the script chosen from a menu. */
+/* Notes: It is called by the ui code in src/header_???.c when a user */
+/* clicks on a menu entry that refers to a script. */
+/* Scripts are searched in the BPyMenuTable, using the given */
+/* menutype and event values to know which one was chosen. */
+/*****************************************************************************/
+int BPY_menu_do_python(short menutype, int event)
+{
+ PyObject *py_dict, *py_result, *pyarg = NULL;
+ BPy_constant *info;
+ BPyMenu *pym;
+ BPySubMenu *pysm;
+ FILE *fp = NULL;
+ char filestr[FILE_MAXDIR+FILE_MAXFILE];
+ Script *script = G.main->script.first;
+
+ if ((menutype < 0) || (menutype > PYMENU_TOTAL) || (event < 0))
+ return 0;
+
+ pym = BPyMenuTable[menutype];
+
+ while (event--) {
+ if (pym) pym = pym->next;
+ else break;
+ }
+
+ if (!pym) return 0;
+
+/* if there are submenus, let the user choose one from a pupmenu that we
+ * create here.*/
+ pysm = pym->submenus;
+ if (pysm) {
+ char *pupstr;
+ int arg;
+
+ pupstr = BPyMenu_CreatePupmenuStr(pym, menutype);
+
+ if (pupstr) {
+ arg = pupmenu(pupstr);
+ MEM_freeN(pupstr);
+
+ if (arg >= 0) {
+ while (arg--) pysm = pysm->next;
+ pyarg = PyString_FromString(pysm->arg);
+ }
+ else return 0;
+ }
+ }
+
+ if (!pyarg) {/* no submenus */
+ Py_INCREF (Py_None);
+ pyarg = Py_None;
+ }
+
+ BLI_make_file_string(NULL, filestr, U.pythondir, pym->filename);
+ fp = fopen(filestr, "r");
+ if (!fp) { /* later also support userhome/.blender/scripts/ or whatever */
+ printf("Error loading script: couldn't open file %s\n", filestr);
+ return 0;
+ }
+
+ /* Create a new script structure and initialize it: */
+ script = alloc_libblock(&G.main->script, ID_SCRIPT, pym->name);
+
+ if (!script) {
+ printf("couldn't allocate memory for Script struct!");
+ fclose(fp);
+ return 0;
+ }
+
+ script->id.us = 1;
+ script->filename = NULL; /* it's a Blender Text script */
+ script->flags = SCRIPT_RUNNING;
+ script->py_draw = NULL;
+ script->py_event = NULL;
+ script->py_button = NULL;
+
+ py_dict = CreateGlobalDictionary();
+
+ script->py_globaldict = py_dict;
+
+ info = (BPy_constant *)M_constant_New();
+ if (info) {
+ constant_insert(info, "name", PyString_FromString(script->id.name+2));
+ constant_insert(info, "arg", pyarg);
+ PyDict_SetItemString(py_dict, "__script__", (PyObject *)info);
+ }
+
+ clearScriptLinks ();
+
+ py_result = PyRun_File(fp, pym->filename, Py_file_input, py_dict, py_dict);
+
+ fclose(fp);
+
+ if (!py_result) { /* Failed execution of the script */
+
+ BPY_Err_Handle(script->id.name+2);
+ PyErr_Print();
ReleaseGlobalDictionary(py_dict);
free_libblock(&G.main->script, script);
- BPY_end_python();
- BPY_start_python();
+ // BPY_end_python();
+ // BPY_start_python();
return 0;
}
@@ -595,7 +726,7 @@ void BPY_do_pyscript(struct ID *id, short event)
if (!ret)
{
/* Failed execution of the script */
- BPY_Err_Handle ((Text*) scriptlink->scripts[index]);
+ BPY_Err_Handle (scriptlink->scripts[index]->name+2);
BPY_end_python ();
BPY_start_python ();
}
diff --git a/source/blender/python/BPY_menus.c b/source/blender/python/BPY_menus.c
new file mode 100644
index 00000000000..b5d9610d3d8
--- /dev/null
+++ b/source/blender/python/BPY_menus.c
@@ -0,0 +1,589 @@
+/*
+ *
+ * ***** 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 *****
+*/
+
+/* This is the main file responsible for having bpython scripts accessible
+ * from Blender menus. To know more, please start with its header file.
+*/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#ifndef WIN32
+#include <dirent.h>
+#else
+#include "BLI_winstuff.h"
+#include <io.h>
+#include <direct.h>
+#endif
+
+#include "BKE_utildefines.h"
+#include "BLI_blenlib.h"
+#include "MEM_guardedalloc.h"
+
+#include <DNA_userdef_types.h> /* for U.pythondir */
+
+#include "BPY_extern.h"
+#include "BPY_menus.h"
+
+#include <errno.h>
+
+#define BPYMENU_DATAFILE ".Bpymenus"
+
+static int bpymenu_group_atoi (char *str)
+{
+ if (!strcmp(str, "Import")) return PYMENU_IMPORT;
+ else if (!strcmp(str, "Export")) return PYMENU_EXPORT;
+ else if (!strcmp(str, "Misc")) return PYMENU_MISC;
+ else return -1;
+}
+
+char *BPyMenu_group_itoa (short menugroup)
+{
+ switch (menugroup) {
+ case PYMENU_IMPORT:
+ return "Import";
+ break;
+ case PYMENU_EXPORT:
+ return "Export";
+ break;
+ case PYMENU_MISC:
+ return "Misc";
+ break;
+ }
+ return NULL;
+}
+
+/* BPyMenu_CreatePupmenuStr:
+ * build and return a meaninful string to be used by pupmenu(). The
+ * string is made of a bpymenu name as title and its submenus as possible
+ * choices for the user.
+*/
+char *BPyMenu_CreatePupmenuStr(BPyMenu *pym, short menugroup)
+{
+ BPySubMenu *pysm = pym->submenus;
+ char str[1024], str2[100];
+ int i = 0, rlen;
+
+ if (!pym || !pysm) return NULL;
+
+ str[0] = '\0';
+
+ snprintf(str2, sizeof(str2), "%s: %s%%t",
+ BPyMenu_group_itoa(menugroup), pym->name);
+ strcat(str, str2);
+
+ while (pysm) {
+ snprintf(str2, sizeof(str2), "|%s%%x%d", pysm->name, i);
+ rlen = sizeof(str) - strlen(str);
+ strncat(str, str2, rlen);
+ i++;
+ pysm = pysm->next;
+ }
+
+ return BLI_strdup(str);
+}
+
+static void bpymenu_RemoveAllSubEntries (BPySubMenu *smenu)
+{
+ BPySubMenu *tmp;
+
+ while (smenu) {
+ tmp = smenu->next;
+ if (smenu->name) MEM_freeN(smenu->name);
+ if (smenu->arg) MEM_freeN(smenu->arg);
+ MEM_freeN(smenu);
+ smenu = tmp;
+ }
+ return;
+}
+
+void BPyMenu_RemoveAllEntries (void)
+{
+ BPyMenu *tmp, *pymenu;
+ int i;
+
+ for (i = 0; i < PYMENU_TOTAL; i++) {
+ pymenu = BPyMenuTable[i];
+ while (pymenu) {
+ tmp = pymenu->next;
+ if (pymenu->name) MEM_freeN(pymenu->name);
+ if (pymenu->filename) MEM_freeN(pymenu->filename);
+ if (pymenu->tooltip) MEM_freeN(pymenu->tooltip);
+ if (pymenu->submenus) bpymenu_RemoveAllSubEntries(pymenu->submenus);
+ MEM_freeN(pymenu);
+ pymenu = tmp;
+ }
+ BPyMenuTable[i] = NULL;
+ }
+ return;
+}
+
+static BPyMenu *bpymenu_FindEntry (short group, char *name)
+{
+ BPyMenu *pymenu;
+
+ if ((group <0) || (group >= PYMENU_TOTAL)) return NULL;
+
+ pymenu = BPyMenuTable[group];
+
+ while (pymenu) {
+ if (!strcmp(pymenu->name, name)) return pymenu;
+ pymenu = pymenu->next;
+ }
+
+ return NULL;
+}
+
+static void bpymenu_set_tooltip (BPyMenu *pymenu, char *tip)
+{
+ if (!pymenu) return;
+
+ if (pymenu->tooltip) MEM_freeN(pymenu->tooltip);
+ pymenu->tooltip = BLI_strdup(tip);
+
+ return;
+}
+
+/* bpymenu_AddEntry:
+ * try to find an existing pymenu entry with the given type and name;
+ * if found, update it with new info, otherwise create a new one and fill it.
+ */
+static BPyMenu *bpymenu_AddEntry (short group, char *name, char *fname, char *tooltip)
+{
+ BPyMenu *menu, **iter;
+
+ if ((group < 0) || (group >= PYMENU_TOTAL)) return NULL;
+ if (!name || !fname) return NULL;
+
+ menu = bpymenu_FindEntry (group, name); /* already exists? */
+
+ if (menu) {
+ printf("\nWarning: script %s's menu name is already in use.\n", fname);
+ printf("Edit the script and change its Name: '%s' field, please.\n", name);
+ return NULL;
+ }
+
+ menu = MEM_mallocN(sizeof(BPyMenu), "pymenu");
+
+ if (!menu) return NULL;
+
+ menu->name = BLI_strdup(name);
+ menu->filename = BLI_strdup(fname);
+ menu->tooltip = NULL;
+ if (tooltip) menu->tooltip = BLI_strdup(tooltip);
+ menu->submenus = NULL;
+ menu->next = NULL;
+
+ iter = &BPyMenuTable[group];
+ while (*iter) iter = &((*iter)->next);
+
+ *iter = menu;
+
+ return menu;
+}
+
+/* bpymenu_AddSubEntry:
+ * add a submenu to an existing python menu.
+ */
+static int bpymenu_AddSubEntry (BPyMenu *mentry, char *name, char *arg)
+{
+ BPySubMenu *smenu, **iter;
+
+ smenu = MEM_mallocN(sizeof(BPySubMenu), "pysubmenu");
+ if (!smenu) return -1;
+
+ smenu->name = BLI_strdup(name);
+ smenu->arg = BLI_strdup(arg);
+ smenu->next = NULL;
+
+ if (!smenu->name || !smenu->arg) return -1;
+
+ iter = &(mentry->submenus);
+ while (*iter) iter = &((*iter)->next);
+
+ *iter = smenu;
+
+ return 0;
+}
+
+/* bpymenu_CreateFromFile:
+ * parse the bpymenus data file where Python menu data is stored;
+ * based on this data, create and fill the pymenu structs.
+ */
+static void bpymenu_CreateFromFile (void)
+{
+ FILE *fp;
+ char line[255], w1[255], w2[255], tooltip[255], *tip;
+ int parsing;
+ short group;
+ BPyMenu *pymenu = NULL;
+
+ /* init global bpymenu table (it is a list of pointers to struct BPyMenus
+ * for each available cathegory: import, export, etc.) */
+ for (group = 0; group < PYMENU_TOTAL; group++)
+ BPyMenuTable[group] = NULL;
+
+ /* let's try to open the file with bpymenu data */
+ BLI_make_file_string (NULL, line, BLI_gethome(), BPYMENU_DATAFILE);
+
+ fp = fopen(line, "rb");
+
+ if (!fp) {
+ printf("BPyMenus error: couldn't open config file %s.\n", line);
+ return;
+ }
+
+ while (fgets(line, 255, fp)) { /* parsing file lines */
+
+ switch (line[0]) { /* check first char */
+ case '#': /* comment */
+ continue;
+ break;
+ case '\n':
+ continue;
+ break;
+ default:
+ parsing = sscanf(line, "%s {\n", w1); /* menu group */
+ break;
+ }
+
+ if (parsing == 1) { /* got menu group string */
+ group = bpymenu_group_atoi(w1);
+ if (group < 0) { /* invalid type */
+ printf("BPyMenus error parsing config file: wrong group: %s.\n", w1);
+ continue;
+ }
+ }
+ else continue;
+
+ while (1) {
+ tip = NULL; /* optional tooltip */
+ fgets(line, 255, fp);
+ if (line[0] == '}') break;
+ else if (line[0] == '\n') continue;
+ else if (line[0] == '\'') { /* menu entry */
+ parsing = sscanf(line, "'%[^']' %s '%[^']'\n", w1, w2, tooltip);
+
+ if (parsing <= 0) { /* invalid line, get rid of it */
+ fgets(line, 255, fp); /* add error report later */
+ }
+ else if (parsing == 3) tip = tooltip; /* has tooltip */
+
+ pymenu = bpymenu_AddEntry(group, w1, w2, tip);
+ if (!pymenu) {
+ puts("BpyMenus error: couldn't create bpymenu entry.\n");
+ return;
+ }
+ }
+ else if (line[0] == '|' && line[1] == '_') { /* menu sub-entry */
+ if (!pymenu) continue; /* no menu yet, skip this line */
+ sscanf(line, "|_%[^:]: %s\n", w1, w2);
+ bpymenu_AddSubEntry(pymenu, w1, w2);
+ }
+ }
+ }
+ return;
+}
+
+/* bpymenu_WriteDataFile:
+ * writes the registered scripts info to the user's home dir, for faster
+ * access when the scripts dir hasn't changed.
+*/
+static void bpymenu_WriteDataFile(void)
+{
+ BPyMenu *pymenu;
+ BPySubMenu *smenu;
+ FILE *fp;
+ char fname[FILE_MAXDIR+FILE_MAXFILE];
+ int i;
+
+ BLI_make_file_string(NULL, fname, BLI_gethome(), BPYMENU_DATAFILE);
+
+ fp = fopen(fname, "w");
+ if (!fp) {
+ printf("BPyMenus error: couldn't write %s file.", fname);
+ return;
+ }
+
+ fprintf(fp, "# Blender: registered menu entries for bpython scripts\n");
+
+ for (i = 0; i < PYMENU_TOTAL; i++) {
+ pymenu = BPyMenuTable[i];
+ if (!pymenu) continue;
+ fprintf(fp, "\n%s {\n", BPyMenu_group_itoa(i));
+ while (pymenu) {
+ fprintf(fp, "'%s' %s", pymenu->name, pymenu->filename);
+ if (pymenu->tooltip) fprintf(fp, " '%s'\n", pymenu->tooltip);
+ else fprintf(fp, "\n");
+ smenu = pymenu->submenus;
+ while (smenu) {
+ fprintf(fp, "|_%s: %s\n", smenu->name, smenu->arg);
+ smenu = smenu->next;
+ }
+ pymenu = pymenu->next;
+ }
+ fprintf(fp, "}\n");
+ }
+}
+
+/* BPyMenu_PrintAllEntries:
+ * useful for debugging.
+*/
+void BPyMenu_PrintAllEntries(void)
+{
+ BPyMenu *pymenu;
+ BPySubMenu *smenu;
+ int i;
+
+ printf("# Blender: registered menu entries for bpython scripts\n");
+
+ for (i = 0; i < PYMENU_TOTAL; i++) {
+ pymenu = BPyMenuTable[i];
+ printf("\n%s {\n", BPyMenu_group_itoa(i));
+ while (pymenu) {
+ printf("'%s' %s", pymenu->name, pymenu->filename);
+ if (pymenu->tooltip) printf(" '%s'\n", pymenu->tooltip);
+ else printf("\n");
+ smenu = pymenu->submenus;
+ while (smenu) {
+ printf("|_%s: %s\n", smenu->name, smenu->arg);
+ smenu = smenu->next;
+ }
+ pymenu = pymenu->next;
+ }
+ printf("}\n");
+ }
+}
+
+/* bpymenu_CreateFromDir:
+ * this function scans the scripts dir looking for .py files with the
+ * right header and menu info, using that to fill the bpymenu structs.
+ * Speed is important.
+*/
+static void bpymenu_CreateFromDir (void)
+{
+ DIR *dir;
+ FILE *fp;
+ struct dirent *dir_entry;
+ BPyMenu *pymenu;
+ char *s, *fname, str[FILE_MAXFILE+FILE_MAXDIR];
+ char line[100], w[100];
+ char name[100], submenu[100], subarg[100], tooltip[100];
+ int res = 0;
+
+ dir = opendir(U.pythondir);
+
+ if (!dir) {
+ printf("BPyMenus error: couldn't open python dir %s.\n", U.pythondir);
+ return;
+ }
+
+ /* init global bpymenu table (it is a list of pointers to struct BPyMenus
+ * for each available group: import, export, etc.) */
+ for (res = 0; res < PYMENU_TOTAL; res++)
+ BPyMenuTable[res] = NULL;
+
+/* we scan the dir for filenames ending with .py and starting with the
+ * right 'magic number': '#!BPY'. All others are ignored. */
+
+ while ((dir_entry = readdir(dir)) != NULL) {
+ fname = dir_entry->d_name;
+ /* ignore anything starting with a dot */
+ if (fname[0] == '.') continue; /* like . and .. */
+
+ /* also skip filenames whose extension isn't '.py' */
+ s = strstr(fname, ".py");
+ if (!s || *(s+3) != '\0') continue;
+
+ BLI_make_file_string(NULL, str, U.pythondir, fname);
+
+ fp = fopen(str, "rb");
+
+ if (!fp) {
+ printf("BPyMenus error: couldn't open %s.\n", str);
+ return;
+ }
+
+ /* finally, look for the start string '#!BPY', with
+ * or w/o white space(s) between #! and BPY */
+ fgets(line, 100, fp);
+ if (line[0] != '#' || line[1] != '!') goto discard;
+
+ if (!strstr (line, "BPY")) goto discard;
+
+ /* file passed the tests, look for the three double-quotes */
+ while (fgets(line, 100, fp)) {
+ if (line[0] == '"' && line[1] == '"' && line[2] == '"') {
+ res = 1; /* found */
+ break;
+ }
+ }
+
+ if (!res) goto discard;
+
+ /* Now we're ready to get the registration info. A little more structure
+ * was imposed to their format, for speed. The registration
+ * lines must appear between the first pair of triple double-quotes and
+ * follow this order (the single-quotes are part of the format):
+ *
+ * Name: 'script name for the menu'
+ * Group: 'group name' (defines menu)
+ * Submenu: 'submenu name' related_1word_arg
+ * Tooltip: 'tooltip for the menu'
+ *
+ * notes:
+ * - there may be more than one submenu line, or none:
+ * submenus and the tooltip are optional;
+ * - only the first letter of each token is checked, both lower
+ * and upper cases, so that's all that matters for recognition:
+ * n 'script name' is enough for the name line, for example. */
+
+ /* first the name: */
+ res = fscanf(fp, "%[^']'%[^'\r\n]'\n", w, name);
+ if ((res != 2) || (w[0] != 'n' && w[0] != 'N')) {
+ printf("BPyMenus error: wrong 'name' line in %s.\n", str);
+ printf("%s | %s\n", w, name);
+ goto discard;
+ }
+
+ line[0] = '\0'; /* used as group for this part */
+
+ /* the group: */
+ res = fscanf(fp, "%[^']'%[^'\r\n]'\n", w, line);
+ if ((res != 2) || (w[0] != 'g' && w[0] != 'G')) {
+ printf("BPyMenus error: wrong 'group' line in %s.\n", str);
+ printf("'%s' | '%s'\n", w, name);
+ goto discard;
+ }
+
+ res = bpymenu_group_atoi(line);
+ if (res < 0) {
+ printf("BPyMenus error: unknown 'group' %s in %s.\n", line, str);
+ goto discard;
+ }
+
+ pymenu = bpymenu_AddEntry(res, name, fname, NULL);
+ if (!pymenu) {
+ printf("BPyMenus error: couldn't create entry for %s.\n", str);
+ fclose(fp);
+ closedir(dir);
+ return;
+ }
+
+ /* the (optional) submenu(s): */
+ while (fgets (line, 100, fp)) {
+ res = sscanf(line, "%[^']'%[^'\r\n]'%s\n", w, submenu, subarg);
+ if ((res != 3) || (w[0] != 's' && w[0] != 'S')) break;
+ bpymenu_AddSubEntry(pymenu, submenu, subarg);
+ }
+
+ /* the (optional) tooltip: */
+ res = sscanf(line, "%[^']'%[^'\r\n]'\n", w, tooltip);
+ if ((res == 2) && (w[0] == 't' || w[0] == 'T')) {
+ bpymenu_set_tooltip (pymenu, tooltip);
+ }
+
+discard:
+ fclose (fp);
+ continue;
+ }
+
+ closedir(dir);
+ return;
+}
+
+/* BPyMenu_Init:
+ * import the bpython menus data to Blender, either from:
+ * - the BPYMENU_DATAFILE file (~/.Bpymenus) or
+ * - the scripts dir, case it's newer than the datafile (then update the file).
+ * then fill the bpymenu table with this data.
+*/
+void BPyMenu_Init(void)
+{
+ char fname[FILE_MAXDIR+FILE_MAXFILE];
+ struct stat st;
+ time_t tdir, tfile;
+ int result = 0;
+
+ result = stat(U.pythondir, &st);
+
+ if (result == -1) {
+ /*
+ printf ("\nScripts dir: %s\nError: %s\n", U.pythondir, strerror(errno));
+ printf ("Please go to 'Info window -> File Paths tab' and set a valid "
+ "path for\nthe Blender Python scripts dir.\n");
+ */
+ return;
+ }
+
+ if (!S_ISDIR(st.st_mode)) {
+ /*
+ printf ("\nScripts dir: %s is not a directory!", U.pythondir);
+ printf ("Please go to 'Info window -> File Paths tab' and set a valid "
+ "path for\nthe Blender Python scripts dir.\n");
+ */
+ return;
+ }
+
+ printf("Registering scripts in Blender menus:\n");
+
+ tdir = st.st_mtime;
+
+ BLI_make_file_string(NULL, fname, BLI_gethome(), BPYMENU_DATAFILE);
+
+ result = stat(fname, &st);
+ if ((result == -1) || !S_ISREG(st.st_mode)) tfile = 0;
+ else tfile = st.st_mtime;
+
+ /* comparing dates */
+
+ if (tdir > tfile) { /* if so, dir is newer */
+ printf("Getting menu data from dir: %s\n", U.pythondir);
+ bpymenu_CreateFromDir();
+ bpymenu_WriteDataFile(); /* recreate the file */
+ return;
+ }
+ else { /* file is newer */
+ printf("Getting menu data from file: %s\n", fname);
+ bpymenu_CreateFromFile();
+ }
+
+ return;
+}
+
+
diff --git a/source/blender/python/BPY_menus.h b/source/blender/python/BPY_menus.h
new file mode 100644
index 00000000000..8451a122f06
--- /dev/null
+++ b/source/blender/python/BPY_menus.h
@@ -0,0 +1,98 @@
+/*
+ *
+ * ***** 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 *****
+*/
+
+#ifndef BPY_MENUS_H
+#define BPY_MENUS_H
+
+/* This header exposes BPyMenu related public declarations. The implementation
+ * adds 'dynamic' menus to Blender, letting scripts register themselves in any
+ * of a few pre-defined (trivial to upgrade) places in menus. These places or
+ * slots are called groups here (Import, Export, etc). This is how it works:
+ * - scripts at some specific folder (only the user pref U.pythondir, right
+ * now) are scanned for registration info.
+ * - this data is also saved to a .Bpymenus file at the user's home dir and
+ * only re-created when the scripts folder gets modified.
+ * - on start-up Blender uses this info to fill a table, which is used to
+ * create the menu entries when they are needed (see header_info.c or
+ * header_script.c, under source/blender/src/, for examples).
+*/
+
+/* These two structs hold py menu/submenu info.
+ * BPyMenu holds a script's name (as should appear in the menu) and filename,
+ * plus an optional list of submenus. Each submenu is related to a string
+ * (arg) that the script can get from the __script__ pydict, to know which
+ * submenu was chosen. */
+
+typedef struct BPySubMenu {
+ char *name;
+ char *arg;
+ struct BPySubMenu *next;
+} BPySubMenu;
+
+typedef struct BPyMenu {
+ char *name;
+ char *filename;
+ char *tooltip;
+ struct BPySubMenu *submenus;
+ struct BPyMenu *next;
+} BPyMenu;
+
+/* Scripts can be added to only a few pre-defined places in menus, like
+ * File->Import, File->Export, etc. (for speed and better control).
+ * To make a new menu 'slot' available for scripts:
+ * - add an entry to the enum below, right before PYMENU_TOTAL, of course;
+ * - update the bpymenu_group_atoi() and BPyMenu_group_itoa() functions in
+ * BPY_menus.c;
+ * - add the necessary code to the header_***.c file in
+ * source/blender/src/, like done in header_info.c for import/export;
+ * - update the bpython registering function and its documentation to include
+ * the new group.
+*/
+typedef enum {
+ PYMENU_IMPORT,
+ PYMENU_EXPORT,
+ PYMENU_MISC,
+ PYMENU_TOTAL
+} PYMENUHOOKS;
+
+/* BPyMenuTable holds all registered pymenus, as linked lists for each menu
+ * where they can appear (see PYMENUHOOKS enum above).
+*/
+BPyMenu *BPyMenuTable[PYMENU_TOTAL];
+
+/* public functions: */
+void BPyMenu_Init(void);
+void BPyMenu_RemoveAllEntries(void);
+void BPyMenu_PrintAllEntries(void);
+char *BPyMenu_CreatePupmenuStr(BPyMenu *pym, short group);
+char *BPyMenu_group_itoa (short group);
+
+#endif /* BPY_MENUS_H */
diff --git a/source/blender/python/SConscript b/source/blender/python/SConscript
index 212af83a235..02a9444b128 100644
--- a/source/blender/python/SConscript
+++ b/source/blender/python/SConscript
@@ -11,6 +11,7 @@ python_env.Append (CXXFLAGS = cxxflags)
python_env.Append (CPPDEFINES = defines)
source_files = ['BPY_interface.c',
+ 'BPY_menus.c',
'api2_2x/Blender.c',
'api2_2x/Sys.c',
'api2_2x/Registry.c',
diff --git a/source/blender/src/header_info.c b/source/blender/src/header_info.c
index 834c2613ea3..08b0a934f59 100644
--- a/source/blender/src/header_info.c
+++ b/source/blender/src/header_info.c
@@ -108,6 +108,7 @@
#include "MEM_guardedalloc.h"
#include "BPY_extern.h"
+#include "BPY_menus.h"
#include "render.h" // for R - should use BKE_bad_level_calls.h instead?
@@ -661,16 +662,13 @@ static void do_info_file_importmenu(void *arg, int event)
ScrArea *sa;
if(curarea->spacetype==SPACE_INFO) {
- sa= closest_bigger_area();
+ sa= find_biggest_area_of_type(SPACE_SCRIPT);
+ if (!sa) sa= closest_bigger_area();
areawinset(sa->win);
}
- /* these are no defines, easier this way, the codes are in the function below */
- switch(event) {
-
- case 0:
- break;
- }
+ BPY_menu_do_python(PYMENU_IMPORT, event);
+
allqueue(REDRAWINFO, 0);
}
@@ -678,14 +676,19 @@ static uiBlock *info_file_importmenu(void *arg_unused)
{
uiBlock *block;
short yco = 20, menuwidth = 120;
+ BPyMenu *pym;
+ int i = 0;
block= uiNewBlock(&curarea->uiblocks, "importmenu", UI_EMBOSSP, UI_HELV, G.curscreen->mainwin);
uiBlockSetButmFunc(block, do_info_file_importmenu, NULL);
//uiBlockSetXOfs(block, -50); // offset to parent button
-
- // uiDefBut(block, BUTM, 1, "Python scripts go here somehow!", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 1, 0, "");
- uiDefBut(block, SEPR, 0, "", 0, yco-=6, menuwidth, 6, NULL, 0.0, 0.0, 0, 0, "");
-
+
+ for (pym = BPyMenuTable[PYMENU_IMPORT]; pym; pym = pym->next, i++) {
+ uiDefBut(block, BUTM, 1, pym->name, 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 1, i, pym->tooltip?pym->tooltip:pym->filename);
+ }
+
+ //uiDefBut(block, SEPR, 0, "", 0, yco-=6, menuwidth, 6, NULL, 0.0, 0.0, 0, 0, "");
+
uiBlockSetDirection(block, UI_RIGHT);
uiTextBoundsBlock(block, 60);
@@ -701,8 +704,12 @@ static void do_info_file_exportmenu(void *arg, int event)
areawinset(sa->win);
}
+ /* events >=3 are registered bpython scripts */
+ if (event >= 3) BPY_menu_do_python(PYMENU_EXPORT, event - 3);
+
/* these are no defines, easier this way, the codes are in the function below */
- switch(event) {
+
+ else switch(event) {
case 0:
write_vrml_fs();
@@ -720,15 +727,24 @@ static void do_info_file_exportmenu(void *arg, int event)
static uiBlock *info_file_exportmenu(void *arg_unused)
{
uiBlock *block;
- short yco = 20;
+ short yco = 20, menuwidth = 120;
+ BPyMenu *pym;
+ int i = 0;
block= uiNewBlock(&curarea->uiblocks, "exportmenu", UI_EMBOSSP, UI_HELV, G.curscreen->mainwin);
uiBlockSetButmFunc(block, do_info_file_exportmenu, NULL);
//uiBlockSetXOfs(block, -50); // offset to parent button
- uiDefBut(block, BUTM, 1, "VRML 1.0...|Ctrl F2", 0, yco-=20, 120, 19, NULL, 0.0, 0.0, 1, 0, "");
- uiDefBut(block, BUTM, 1, "DXF...|Shift F2", 0, yco-=20, 120, 19, NULL, 0.0, 0.0, 1, 1, "");
- uiDefBut(block, BUTM, 1, "Videoscape...|Alt W", 0, yco-=20, 120, 19, NULL, 0.0, 0.0, 1, 2, "");
+ uiDefBut(block, BUTM, 1, "VRML 1.0...|Ctrl F2", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 1, 0, "");
+ uiDefBut(block, BUTM, 1, "DXF...|Shift F2", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 1, 1, "");
+ uiDefBut(block, BUTM, 1, "Videoscape...|Alt W", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 1, 2, "");
+
+ uiDefBut(block, SEPR, 0, "", 0, yco-=6, menuwidth, 6, NULL, 0.0, 0.0, 0, 0, "");
+
+/* note that we acount for the 3 previous entries with i+3: */
+ for (pym = BPyMenuTable[PYMENU_EXPORT]; pym; pym = pym->next, i++) {
+ uiDefBut(block, BUTM, 1, pym->name, 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 1, i+3, pym->tooltip?pym->tooltip:pym->filename);
+ }
uiBlockSetDirection(block, UI_RIGHT);
uiTextBoundsBlock(block, 60);
@@ -736,7 +752,6 @@ static uiBlock *info_file_exportmenu(void *arg_unused)
return block;
}
-
static void do_info_filemenu(void *arg, int event)
{
ScrArea *sa;
@@ -857,7 +872,7 @@ static uiBlock *info_filemenu(void *arg_unused)
uiDefBut(block, SEPR, 0, "", 0, yco-=6, menuwidth, 6, NULL, 0.0, 0.0, 0, 0, "");
uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Append...|Shift F1", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 1, 3, "");
-// uiDefIconTextBlockBut(block, info_file_importmenu, NULL, ICON_RIGHTARROW_THIN, "Import", 0, yco-=20, menuwidth, 19, "");
+ uiDefIconTextBlockBut(block, info_file_importmenu, NULL, ICON_RIGHTARROW_THIN, "Import", 0, yco-=20, menuwidth, 19, "");
uiDefIconTextBlockBut(block, info_file_exportmenu, NULL, ICON_RIGHTARROW_THIN, "Export", 0, yco-=20, menuwidth, 19, "");
uiDefBut(block, SEPR, 0, "", 0, yco-=6, menuwidth, 6, NULL, 0.0, 0.0, 0, 0, "");
diff --git a/source/blender/src/header_script.c b/source/blender/src/header_script.c
index e66fea68cf4..a56cede0746 100644
--- a/source/blender/src/header_script.c
+++ b/source/blender/src/header_script.c
@@ -66,13 +66,82 @@
#include "BKE_library.h"
#include "BKE_main.h"
#include "BKE_sca.h"
-#include "BPY_extern.h"
#include "BSE_filesel.h"
+#include "BPY_extern.h"
+#include "BPY_menus.h"
+
#include "blendef.h"
#include "mydevice.h"
/* ********************** SCRIPT ****************************** */
+
+/* action executed after clicking in Scripts menu */
+static void do_scripts_submenus(void *int_arg, int event)
+{
+ SpaceScript *sc= curarea->spacedata.first;
+ Script *script= sc->script;
+ ScrArea *sa;
+ int menutype = (int)int_arg;
+
+ BPY_menu_do_python (menutype, event);
+
+ //allqueue(REDRAWSCRIPT, 0);
+}
+
+static uiBlock *script_scripts_submenus(void *int_menutype)
+{
+ uiBlock *block;
+ short yco = 20, menuwidth = 120;
+ BPyMenu *pym;
+ int i = 0, menutype = (int)int_menutype;
+
+ if ((menutype < 0) || (menutype > PYMENU_TOTAL)) return;
+
+ block= uiNewBlock(&curarea->uiblocks, "importmenu", UI_EMBOSSP, UI_HELV, G.curscreen->mainwin);
+ uiBlockSetButmFunc(block, do_scripts_submenus, int_menutype);
+ //uiBlockSetXOfs(block, -50); // offset to parent button
+
+ for (pym = BPyMenuTable[menutype]; pym; pym = pym->next, i++) {
+ uiDefBut(block, BUTM, 1, pym->name, 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 1, i, pym->tooltip?pym->tooltip:pym->filename);
+ }
+
+ uiDefBut(block, SEPR, 0, "", 0, yco-=6, menuwidth, 6, NULL, 0.0, 0.0, 0, 0, "");
+
+ uiBlockSetDirection(block, UI_RIGHT);
+ uiTextBoundsBlock(block, 60);
+
+ return block;
+}
+
+/* Scripts menu */
+static uiBlock *script_scriptsmenu(void *arg_unused)
+{
+ //SpaceScript *sc= curarea->spacedata.first;
+ //Script *script= sc->script;
+ uiBlock *block;
+ short yco = 0, menuwidth = 120;
+ int i;
+
+ block= uiNewBlock(&curarea->uiblocks, "script_scriptsmenu", UI_EMBOSSP, UI_HELV, curarea->headwin);
+ //uiBlockSetButmFunc(block, do_script_scriptsmenu, NULL);
+
+ for (i = 0; i < PYMENU_TOTAL; i++) {
+ uiDefIconTextBlockBut(block, script_scripts_submenus, (void *)i, ICON_RIGHTARROW_THIN, BPyMenu_group_itoa(i), 0, yco-=20, menuwidth, 19, "");
+ }
+
+ if(curarea->headertype==HEADERTOP) {
+ uiBlockSetDirection(block, UI_DOWN);
+ }
+ else {
+ uiBlockSetDirection(block, UI_TOP);
+ uiBlockFlipOrder(block);
+ }
+
+ uiTextBoundsBlock(block, 50);
+ return block;
+}
+
void do_script_buttons(unsigned short event)
{
SpaceScript *sc= curarea->spacedata.first;
@@ -121,7 +190,7 @@ void script_buttons(void)
{
uiBlock *block;
SpaceScript *sc= curarea->spacedata.first;
- short xco = 8;
+ short xco = 8, xmax;
char naam[256];
if (!sc || sc->spacetype != SPACE_SCRIPT) return;
@@ -138,9 +207,34 @@ void script_buttons(void)
windowtype_pup(), xco,0,XIC+10,YIC, &(curarea->butspacetype), 1.0,
SPACEICONMAX, 0, 0, "Displays Current Window Type. Click for menu"
" of available types.");
+ xco += XIC+14;
+
+ uiBlockSetEmboss(block, UI_EMBOSSN);
+ if(curarea->flag & HEADER_NO_PULLDOWN) {
+ uiDefIconButS(block, TOG|BIT|0, B_FLIPINFOMENU, ICON_DISCLOSURE_TRI_RIGHT,
+ xco,2,XIC,YIC-2,
+ &(curarea->flag), 0, 0, 0, 0, "Enables display of pulldown menus");
+ } else {
+ uiDefIconButS(block, TOG|BIT|0, B_FLIPINFOMENU, ICON_DISCLOSURE_TRI_DOWN,
+ xco,2,XIC,YIC-2,
+ &(curarea->flag), 0, 0, 0, 0, "Hides pulldown menus");
+ }
+ uiBlockSetEmboss(block, UI_EMBOSS);
+ xco+=XIC;
+
+ /* pull down menus */
+ if((curarea->flag & HEADER_NO_PULLDOWN)==0) {
+ uiBlockSetEmboss(block, UI_EMBOSSP);
+
+ xmax= GetButStringLength("Scripts");
+ uiDefBlockBut(block,script_scriptsmenu, NULL, "Scripts", xco, 0, xmax, 20, "");
+ xco+=xmax;
+ }
+
+ uiBlockSetEmboss(block, UI_EMBOSSX);
+ xco += 10;
/* FULL WINDOW */
- xco= 25;
if(curarea->full)
uiDefIconBut(block, BUT,B_FULL, ICON_SPLITSCREEN, xco+=XIC,0,XIC,YIC, 0, 0,
0, 0, 0, "Returns to multiple views window (CTRL+Up arrow)");
@@ -149,7 +243,7 @@ void script_buttons(void)
0, 0, 0, "Makes current window full screen (CTRL+Down arrow)");
/* STD SCRIPT BUTTONS */
- xco+= 2*XIC;
+ xco += 2*XIC;
xco= std_libbuttons(block, xco, 0, 0, NULL, B_SCRIPTBROWSE, (ID*)sc->script, 0, &(sc->menunr), 0, 0, 0, 0, 0);
/* always as last */
@@ -158,5 +252,6 @@ void script_buttons(void)
uiDrawBlock(block);
}
+
/* ********************** SCRIPT ****************************** */
diff --git a/source/creator/creator.c b/source/creator/creator.c
index c208666f06d..6b536f8e91a 100644
--- a/source/creator/creator.c
+++ b/source/creator/creator.c
@@ -429,9 +429,11 @@ int main(int argc, char **argv)
* so we provide the BPY_ function below to append the user defined
* pythondir to Python's sys.path at this point. Simply putting
* BIF_init() before BPY_start_python() crashes Blender at startup.
+ * Update: now this function also inits the bpymenus, which also depend
+ * on U.pythondir.
*/
- BPY_syspath_append_pythondir();
+ BPY_post_start_python();
}
else {
BPY_start_python();