From 5eaed0dbf78fca2bcf185da0abf1dbf41722deb2 Mon Sep 17 00:00:00 2001 From: Christopher Davis Date: Fri, 14 Jul 2006 10:02:13 +0000 Subject: finished up themes. time for textui stuff git-svn-id: http://svn.irssi.org/repos/irssi-python@4297 dbcabf3a-b0e7-0310-adc4-f8d773084564 --- objects/Makefile | 3 +- objects/factory.c | 3 + objects/factory.h | 1 + objects/theme-object.c | 195 +++++++++++++++++++++++++++++++++++++++++++++++++ objects/theme-object.h | 18 +++++ pyloader.c | 9 ++- pymodule.c | 24 ++++++ pythemes.c | 75 +++++++++++++++---- 8 files changed, 313 insertions(+), 15 deletions(-) create mode 100644 objects/theme-object.c create mode 100644 objects/theme-object.h diff --git a/objects/Makefile b/objects/Makefile index 8ebf7c1..39f6195 100644 --- a/objects/Makefile +++ b/objects/Makefile @@ -15,7 +15,8 @@ chatnet-object.o reconnect-object.o window-object.o textdest-object.o \ rawlog-object.o log-object.o logitem-object.o ignore-object.o \ dcc-object.o dcc-chat-object.o dcc-get-object.o dcc-send-object.o \ netsplit-object.o netsplit-server-object.o netsplit-channel-object.o \ -notifylist-object.o process-object.o command-object.o factory.o +notifylist-object.o process-object.o command-object.o theme-object.o \ +factory.o pyobjects.a: $(OBJ) ar r pyobjects.a $(OBJ) diff --git a/objects/factory.c b/objects/factory.c index 5b77427..bfc19ab 100644 --- a/objects/factory.c +++ b/objects/factory.c @@ -119,6 +119,9 @@ static int init_objects(void) if (!command_object_init()) return 0; + if (!theme_object_init()) + return 0; + return 1; } diff --git a/objects/factory.h b/objects/factory.h index 14a3b7c..b7eb0e7 100644 --- a/objects/factory.h +++ b/objects/factory.h @@ -32,6 +32,7 @@ #include "notifylist-object.h" #include "process-object.h" #include "command-object.h" +#include "theme-object.h" int factory_init(void); void factory_deinit(void); diff --git a/objects/theme-object.c b/objects/theme-object.c new file mode 100644 index 0000000..0f12217 --- /dev/null +++ b/objects/theme-object.c @@ -0,0 +1,195 @@ +#include +#include "pyirssi.h" +#include "pymodule.h" +#include "theme-object.h" +#include "factory.h" +#include "pycore.h" + +/* monitor "theme destroyed" signal */ +static void theme_cleanup(THEME_REC *rec) +{ + PyTheme *pytheme = signal_get_user_data(); + if (pytheme->data == rec) + { + pytheme->data = NULL; + pytheme->cleanup_installed = 0; + signal_remove_data("theme destroyed", theme_cleanup, pytheme); + } +} + +static void PyTheme_dealloc(PyTheme *self) +{ + if (self->cleanup_installed) + signal_remove_data("theme destroyed", theme_cleanup, self); + + self->ob_type->tp_free((PyObject*)self); +} + +static PyObject *PyTheme_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + PyTheme *self; + + self = (PyTheme *)type->tp_alloc(type, 0); + if (!self) + return NULL; + + return (PyObject *)self; +} + +/* Getters */ +/* specialized getters/setters */ +static PyGetSetDef PyTheme_getseters[] = { + {NULL} +}; + +/* Methods */ +PyDoc_STRVAR(PyTheme_format_expand_doc, + "format_expand(format, flags=0) -> str or None\n" +); +static PyObject *PyTheme_format_expand(PyTheme *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"format", "flags", NULL}; + char *format = ""; + int flags = 0; + char *ret; + PyObject *pyret; + + RET_NULL_IF_INVALID(self->data); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|i", kwlist, + &format, &flags)) + return NULL; + + if (flags == 0) + ret = theme_format_expand(self->data, format); + else + ret = theme_format_expand_data(self->data, (const char **)&format, 'n', 'n', + NULL, NULL, EXPAND_FLAG_ROOT | flags); + + if (ret) + { + pyret = PyString_FromString(ret); + g_free(ret); + return pyret; + } + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(PyTheme_get_format_doc, + "get_format(module, tag) -> str\n" +); +static PyObject *PyTheme_get_format(PyTheme *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"module", "tag", NULL}; + char *module = ""; + char *tag = ""; + THEME_REC *theme = self->data; + FORMAT_REC *formats; + MODULE_THEME_REC *modtheme; + int i; + + RET_NULL_IF_INVALID(self->data); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "ss", kwlist, + &module, &tag)) + return NULL; + + formats = g_hash_table_lookup(default_formats, module); + if (!formats) + return PyErr_Format(PyExc_KeyError, "unknown module, %s", module); + + for (i = 0; formats[i].def; i++) + { + if (formats[i].tag && !g_strcasecmp(formats[i].tag, tag)) + { + modtheme = g_hash_table_lookup(theme->modules, module); + if (modtheme && modtheme->formats[i]) + return PyString_FromString(modtheme->formats[i]); + else + return PyString_FromString(formats[i].def); + } + } + + return PyErr_Format(PyExc_KeyError, "unknown format tag, %s", tag); +} + +/* Methods for object */ +static PyMethodDef PyTheme_methods[] = { + {"format_expand", (PyCFunction)PyTheme_format_expand, METH_VARARGS | METH_KEYWORDS, + PyTheme_format_expand_doc}, + {"get_format", (PyCFunction)PyTheme_get_format, METH_VARARGS | METH_KEYWORDS, + PyTheme_get_format_doc}, + {NULL} /* Sentinel */ +}; + +PyTypeObject PyThemeType = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "Theme", /*tp_name*/ + sizeof(PyTheme), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + (destructor)PyTheme_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ + 0, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + PyTheme_methods, /* tp_methods */ + 0, /* tp_members */ + PyTheme_getseters, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + PyTheme_new, /* tp_new */ +}; + +/* Theme factory function */ +PyObject *pytheme_new(void *td) +{ + PyTheme *pytheme; + + pytheme = py_inst(PyTheme, PyThemeType); + if (!pytheme) + return NULL; + + pytheme->data = td; + signal_add_last_data("theme destroyed", theme_cleanup, pytheme); + pytheme->cleanup_installed = 1; + + return (PyObject *)pytheme; +} + +int theme_object_init(void) +{ + g_return_val_if_fail(py_module != NULL, 0); + + if (PyType_Ready(&PyThemeType) < 0) + return 0; + + Py_INCREF(&PyThemeType); + PyModule_AddObject(py_module, "Theme", (PyObject *)&PyThemeType); + + return 1; +} diff --git a/objects/theme-object.h b/objects/theme-object.h new file mode 100644 index 0000000..299d54d --- /dev/null +++ b/objects/theme-object.h @@ -0,0 +1,18 @@ +#ifndef _THEME_OBJECT_H_ +#define _THEME_OBJECT_H_ + +#include +#include "base-objects.h" + +typedef struct +{ + PyIrssiFinal_HEAD(void) +} PyTheme; + +extern PyTypeObject PyThemeType; + +int theme_object_init(void); +PyObject *pytheme_new(void *td); +#define pytheme_check(op) PyObject_TypeCheck(op, &PyThemeType) + +#endif diff --git a/pyloader.c b/pyloader.c index 03b5150..a087827 100644 --- a/pyloader.c +++ b/pyloader.c @@ -148,7 +148,14 @@ error: else printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, "error loading script %s", argv[0]); - Py_XDECREF(script); + if (script) + { + /* make sure to clean up any formats, signals, commands that may have been + attached before the exception took place */ + pyscript_cleanup(script); + Py_DECREF(script); + } + g_free(path); return 0; diff --git a/pymodule.c b/pymodule.c index d0d17f7..34413a5 100644 --- a/pymodule.c +++ b/pymodule.c @@ -1353,6 +1353,26 @@ error: } #endif +PyDoc_STRVAR(py_themes_reload_doc, + "themes_reload() -> None\n" +); +static PyObject *py_themes_reload(PyObject *self, PyObject *args) +{ + themes_reload(); + Py_RETURN_NONE; +} + +PyDoc_STRVAR(py_current_theme_doc, + "current_theme() -> Theme object\n" +); +static PyObject *py_current_theme(PyObject *self, PyObject *args) +{ + if (current_theme) + return pytheme_new(current_theme); + + Py_RETURN_NONE; +} + static PyMethodDef ModuleMethods[] = { {"prnt", (PyCFunction)py_prnt, METH_VARARGS | METH_KEYWORDS, py_prnt_doc}, @@ -1506,6 +1526,10 @@ static PyMethodDef ModuleMethods[] = { py_strip_codes_doc}, /*{"format_get_text", (PyCFunction)py_format_get_text, METH_VARARGS, py_format_get_text_doc},*/ + {"themes_reload", (PyCFunction)py_themes_reload, METH_NOARGS, + py_themes_reload_doc}, + {"current_theme", (PyCFunction)py_current_theme, METH_NOARGS, + py_current_theme_doc}, {NULL, NULL, 0, NULL} /* Sentinel */ }; diff --git a/pythemes.c b/pythemes.c index 009c2ab..1e03f10 100644 --- a/pythemes.c +++ b/pythemes.c @@ -5,15 +5,23 @@ #include "pymodule.h" #include "pyloader.h" +static void py_get_mod(char *full, int fullsz, const char *script) +{ + g_snprintf(full, fullsz, "irssi_python/%s.py", script); +} + /* Edited from Perl Themes.xs */ -int pythemes_printformat(TEXT_DEST_REC *dest, const char *script, const char *format, PyObject *argtup) +int pythemes_printformat(TEXT_DEST_REC *dest, const char *name, const char *format, PyObject *argtup) { char *arglist[MAX_FORMAT_PARAMS + 1]; THEME_REC *theme; char *str; + char script[256]; int formatnum; int i; - + + py_get_mod(script, sizeof script, name); + formatnum = format_find_tag(script, format); if (formatnum < 0) { PyErr_Format(PyExc_KeyError, "unregistered format '%s'", format); @@ -64,11 +72,14 @@ static void py_destroy_format_list(FORMAT_REC *recs) /* register a list of formats in this format: * [ (name, format), ... ] */ -int pythemes_register(const char *script, PyObject *list) +int pythemes_register(const char *name, PyObject *list) { + char script[256]; FORMAT_REC *formatrecs; int i; + py_get_mod(script, sizeof script, name); + if (!PyList_Check(list)) { PyErr_Format(PyExc_TypeError, "arg must be list"); @@ -89,7 +100,7 @@ int pythemes_register(const char *script, PyObject *list) formatrecs = g_new0(FORMAT_REC, PyList_Size(list) + 2); formatrecs[0].tag = g_strdup(script); - formatrecs[0].tag = g_strdup("Python script"); + formatrecs[0].def = g_strdup("Python script"); for (i = 0; i < PyList_Size(list); i++) { @@ -120,10 +131,13 @@ int pythemes_register(const char *script, PyObject *list) return 1; } -void pythemes_unregister(const char *script) +void pythemes_unregister(const char *name) { + char script[256]; FORMAT_REC *formats; + py_get_mod(script, sizeof script, name); + formats = g_hash_table_lookup(default_formats, script); if (!formats) return; @@ -132,27 +146,48 @@ void pythemes_unregister(const char *script) theme_unregister_module(script); } +/* XXX: test binding a PyCFunction to different sources. Not sure + if this is a good thing or not, but it seems to work */ PyDoc_STRVAR(py_printformat_doc, + "for Server objects:\n" + "printformat(target, level, format, ...) -> None\n" + "\n" + "For all else:\n" "printformat(level, format, ...) -> None\n" ); static PyObject *py_printformat(PyObject *self, PyObject *all) { - int level = 0; - char *format = ""; + int level; + char *format; + char *target; PyObject *args = NULL, *varargs = NULL; TEXT_DEST_REC dest; char *script; + int formatstart; - args = PySequence_GetSlice(all, 0, 2); + if (self && pyserver_check(self)) + formatstart = 3; + else + formatstart = 2; + + args = PySequence_GetSlice(all, 0, formatstart); if (!args) goto error; - varargs = PySequence_GetSlice(all, 2, PyTuple_Size(all)); + varargs = PySequence_GetSlice(all, formatstart, PyTuple_Size(all)); if (!varargs) goto error; - - if (!PyArg_ParseTuple(args, "is", &level, &format)) - goto error; + + if (self && pyserver_check(self)) + { + if (!PyArg_ParseTuple(args, "sis", &target, &level, &format)) + goto error; + } + else + { + if (!PyArg_ParseTuple(args, "is", &level, &format)) + goto error; + } script = pyloader_find_script_name(); if (!script) @@ -161,7 +196,20 @@ static PyObject *py_printformat(PyObject *self, PyObject *all) goto error; } - format_create_dest(&dest, NULL, NULL, level, NULL); + /* create the text dest depending on whether this function is called from + module level or as a method of one of the objects */ + if (self == NULL) /* module */ + format_create_dest(&dest, NULL, NULL, level, NULL); + else if (pyserver_check(self)) + format_create_dest(&dest, DATA(self), target, level, NULL); + else if (pywindow_check(self)) + format_create_dest(&dest, NULL, NULL, level, DATA(self)); + else if (pywindow_item_check(self)) + { + PyWindowItem *pywi = (PyWindowItem *)self; + format_create_dest(&dest, pywi->data->server, pywi->data->visible_name, level, NULL); + } + if (!pythemes_printformat(&dest, script, format, varargs)) goto error; @@ -177,6 +225,7 @@ error: return NULL; } +/* XXX: these funcs could be moved to pyutils.c */ static int py_add_module_func(PyMethodDef *mdef) { PyObject *func; -- cgit