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:
authorWillian Padovani Germano <wpgermano@gmail.com>2004-01-15 00:36:10 +0300
committerWillian Padovani Germano <wpgermano@gmail.com>2004-01-15 00:36:10 +0300
commit0d9d16e6e9127809b63188272b633c61c6240f31 (patch)
tree27a75c06d7be64b430a8caef255cdb0138dbaf00 /source/blender
parent5190faf513e8289c68d4e40d3ea4db6260493570 (diff)
Scripts in menus:
-- this finishes the heavier part (not counting tweaks and possible bugs) of letting scripts be accessed from Blender menus. Will explain more in emails to bf and bpython lists, but just check source/blender/python/BPY_menus.[hc] and source/blender/src/header_info.c and header_script.c for details. Scripts need a small update (registering info, basically a header) to be used. Scripts dir (user pref file paths: Python) must be set.
Diffstat (limited to 'source/blender')
-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
7 files changed, 974 insertions, 45 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 ****************************** */