/* * Copyright (c) 2004 Jean-Yves Lefort * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of Jean-Yves Lefort nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include "config.h" #include #include #define NO_IMPORT_PYGOBJECT #include #include #include "streamtuner.h" #include "pst-category.h" #include "pst-handler.h" #include "pst-handler-config.h" #include "pst-handler-field.h" #include "pst-stream.h" #include "pst-helpers.h" #include "pst-transfer-session.h" /*** attributes **************************************************************/ static int pst_handler_set_label (PSTHandler *self, PyObject *value, void *closure); static int pst_handler_set_description (PSTHandler *self, PyObject *value, void *closure); static int pst_handler_set_home (PSTHandler *self, PyObject *value, void *closure); static int pst_handler_set_icon (PSTHandler *self, PyObject *value, void *closure); static int pst_handler_set_stock_categories (PSTHandler *self, PyObject *value, void *closure); static int pst_handler_set_flags (PSTHandler *self, PyObject *value, void *closure); static int pst_handler_set_stream_version (PSTHandler *self, PyObject *value, void *closure); static PyGetSetDef getsetters[] = { { "label", NULL, (setter) pst_handler_set_label }, { "description", NULL, (setter) pst_handler_set_description }, { "home", NULL, (setter) pst_handler_set_home }, { "icon", NULL, (setter) pst_handler_set_icon }, { "stock_categories", NULL, (setter) pst_handler_set_stock_categories }, { "flags", NULL, (setter) pst_handler_set_flags }, { "stream_version", NULL, (setter) pst_handler_set_stream_version }, { NULL } }; /*** methods *****************************************************************/ static PyObject *pst_handler_add_field (PSTHandler *self, PyObject *args); static PyObject *pst_handler_notice (PSTHandler *self, PyObject *args); static PyMethodDef methods[] = { { "add_field", (PyCFunction) pst_handler_add_field, METH_VARARGS }, { "notice", (PyCFunction) pst_handler_notice, METH_VARARGS }, { NULL } }; /*** members *****************************************************************/ static PyMemberDef members[] = { { "config", T_OBJECT, G_STRUCT_OFFSET(PSTHandler, config), READONLY }, { NULL } }; /*** type object *************************************************************/ static PyObject *pst_handler_new (PyTypeObject *type, PyObject *args, PyObject *keywords); PyTypeObject PSTHandler_Type = { PyObject_HEAD_INIT(NULL) 0, /* ob_size */ "ST.Handler", /* tp_name */ sizeof(PSTHandler), /* tp_basicsize */ 0, /* tp_itemsize */ NULL, /* tp_dealloc */ NULL, /* tp_print */ NULL, /* tp_getattr */ NULL, /* tp_setattr */ NULL, /* tp_compare */ NULL, /* tp_repr */ NULL, /* tp_as_number */ NULL, /* tp_as_sequence */ NULL, /* tp_as_mapping */ NULL, /* tp_hash */ NULL, /* tp_call */ NULL, /* tp_str */ NULL, /* tp_getattro */ NULL, /* tp_setattro */ NULL, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ NULL, /* tp_doc */ NULL, /* tp_traverse */ NULL, /* tp_clear */ NULL, /* tp_richcompare */ 0, /* tp_weaklistoffset */ NULL, /* tp_iter */ NULL, /* tp_iternext */ methods, /* tp_methods */ members, /* tp_members */ getsetters, /* tp_getset */ NULL, /* tp_base */ NULL, /* tp_dict */ NULL, /* tp_descr_get */ NULL, /* tp_descr_set */ 0, /* tp_dictoffset */ NULL, /* tp_init */ NULL, /* tp_alloc */ pst_handler_new /* tp_new */ }; /*** type definitions ********************************************************/ typedef gboolean (* Converter) (PyObject *object, gpointer ptr); /*** function declarations ***************************************************/ static void pst_handler_bind_events (PSTHandler *self); static int pst_handler_set_string (PSTHandler *self, PyObject *value, void (*func) (STHandler *, const char *)); static gboolean pst_handler_reload_cb (PythonCategory *category, GNode **categories, GList **streams, PSTCallbackInfo *info, GError **err); static gboolean pst_handler_reload_multiple_cb (GNode **categories, GHashTable **streams, PSTCallbackInfo *info, GError **err); static gboolean pst_handler_reload_parse_result (PyObject *result, Converter converter1, gpointer ptr1, Converter converter2, gpointer ptr2); static gboolean pst_handler_reload_convert (PyObject *sequence, int n, Converter converter, gpointer ptr); static GtkWidget *pst_handler_preferences_widget_new_cb (PSTCallbackInfo *info); /*** type methods ************************************************************/ static PyObject * pst_handler_new (PyTypeObject *type, PyObject *args, PyObject *keywords) { PSTHandler *self; static char *keyword_list[] = { "name", NULL }; const char *name; if (! PyArg_ParseTupleAndKeywords(args, keywords, "s", keyword_list, &name)) return NULL; self = (PSTHandler *) type->tp_alloc(type, 0); if (! self) return NULL; self->handler = st_handler_new(name); self->config = pst_handler_config_new(self->handler); if (! self->config) return NULL; pst_handler_bind_events(self); return (PyObject *) self; } static void pst_handler_bind_events (PSTHandler *self) { static const struct { STHandlerEvent event; gpointer cb; } private_events[] = { { ST_HANDLER_EVENT_CATEGORY_NEW, pst_category_new_cb }, { ST_HANDLER_EVENT_CATEGORY_FREE, pst_category_free_cb }, { ST_HANDLER_EVENT_STREAM_NEW, pst_stream_new_cb }, { ST_HANDLER_EVENT_STREAM_FIELD_SET, pst_stream_field_set_cb }, { ST_HANDLER_EVENT_STREAM_FIELD_GET, pst_stream_field_get_cb }, { ST_HANDLER_EVENT_STREAM_FREE, pst_stream_free_cb } }; static const struct { STHandlerEvent event; char *method_name; gpointer cb; } public_events[] = { { ST_HANDLER_EVENT_RELOAD, "reload", pst_handler_reload_cb }, { ST_HANDLER_EVENT_RELOAD_MULTIPLE, "reload_multiple", pst_handler_reload_multiple_cb }, { ST_HANDLER_EVENT_STREAM_RESOLVE, "stream_resolve", pst_stream_cb }, { ST_HANDLER_EVENT_STREAM_TUNE_IN, "stream_tune_in", pst_stream_cb }, { ST_HANDLER_EVENT_STREAM_RECORD, "stream_record", pst_stream_cb }, { ST_HANDLER_EVENT_STREAM_BROWSE, "stream_browse", pst_stream_cb }, { ST_HANDLER_EVENT_STREAM_DELETE, "stream_delete", pst_stream_cb }, { ST_HANDLER_EVENT_STREAM_TUNE_IN_MULTIPLE, "stream_tune_in_multiple", pst_stream_tune_in_multiple_cb }, { ST_HANDLER_EVENT_STREAM_STOCK_FIELD_GET, "stream_get_stock_field", pst_stream_stock_field_get_cb }, { ST_HANDLER_EVENT_STREAM_MODIFY, "stream_modify", pst_stream_modify_cb }, { ST_HANDLER_EVENT_PREFERENCES_WIDGET_NEW, "preferences_widget_new", pst_handler_preferences_widget_new_cb } }; int i; /* private events (not exposed to the Python layer) */ for (i = 0; i < G_N_ELEMENTS(private_events); i++) st_handler_bind(self->handler, private_events[i].event, private_events[i].cb, NULL); /* public events (exposed to the Python layer) */ for (i = 0; i < G_N_ELEMENTS(public_events); i++) if (PyObject_HasAttrString((PyObject *) self, public_events[i].method_name)) { PyObject *pattr; pattr = PyObject_GetAttrString((PyObject *) self, public_events[i].method_name); if (PyMethod_Check(pattr)) { PSTCallbackInfo *info = g_new(PSTCallbackInfo, 1); Py_INCREF(self); info->object = (PyObject *) self; info->method = public_events[i].method_name; st_handler_bind(self->handler, public_events[i].event, public_events[i].cb, info); } Py_DECREF(pattr); } } static int pst_handler_set_string (PSTHandler *self, PyObject *value, void (*func) (STHandler *, const char *)) { const char *str; g_return_val_if_fail(self != NULL, -1); if (! value) { PyErr_SetString(PyExc_TypeError, _("cannot unset member")); return -1; } str = PyString_AsString(value); if (! str) return -1; func(self->handler, str); return 0; } static int pst_handler_set_label (PSTHandler *self, PyObject *value, void *closure) { return pst_handler_set_string(self, value, st_handler_set_label); } static int pst_handler_set_description (PSTHandler *self, PyObject *value, void *closure) { return pst_handler_set_string(self, value, st_handler_set_description); } static int pst_handler_set_home (PSTHandler *self, PyObject *value, void *closure) { return pst_handler_set_string(self, value, st_handler_set_home); } static int pst_handler_set_icon (PSTHandler *self, PyObject *value, void *closure) { GObject *pixbuf; if (! value) { PyErr_SetString(PyExc_TypeError, _("cannot unset icon")); return -1; } pixbuf = pst_pygobject_get(value, GDK_TYPE_PIXBUF); if (! pixbuf) return -1; st_handler_set_icon_from_pixbuf(self->handler, GDK_PIXBUF(pixbuf)); return 0; } static int pst_handler_set_stock_categories (PSTHandler *self, PyObject *value, void *closure) { if (value) { GNode *categories; if (! pst_categories_sequence_as_gnode(value, &categories)) return -1; st_handler_set_stock_categories(self->handler, categories); } else st_handler_set_stock_categories(self->handler, NULL); return 0; } static int pst_handler_set_flags (PSTHandler *self, PyObject *value, void *closure) { if (value) { int flags; flags = PyInt_AsLong(value); if (flags == -1) return -1; st_handler_set_flags(self->handler, flags); } else st_handler_set_flags(self->handler, 0); return 0; } static int pst_handler_set_stream_version (PSTHandler *self, PyObject *value, void *closure) { if (value) { int version; version = PyInt_AsLong(value); if (version == -1) return -1; st_handler_set_stream_version(self->handler, version); } else st_handler_set_stream_version(self->handler, 0); return 0; } /*** object methods **********************************************************/ static PyObject * pst_handler_add_field (PSTHandler *self, PyObject *args) { PSTHandlerField *pfield; if (! PyArg_ParseTuple(args, "O!", &PSTHandlerField_Type, &pfield)) return NULL; st_handler_add_field(self->handler, pfield->field); return pst_none(); } static PyObject * pst_handler_notice (PSTHandler *self, PyObject *args) { const char *message; if (! PyArg_ParseTuple(args, "s", &message)) return NULL; st_handler_notice(self->handler, "%s", message); return pst_none(); } /*** streamtuner callbacks ***************************************************/ static gboolean pst_handler_reload_cb (PythonCategory *category, GNode **categories, GList **streams, PSTCallbackInfo *info, GError **err) { PyObject *result; gboolean status; PST_THREADS_ENTER result = PyObject_CallMethod(info->object, info->method, "O", category->p); status = pst_handler_reload_parse_result(result, (Converter) pst_categories_sequence_as_gnode, categories, (Converter) pst_streams_sequence_as_glist, streams); if (! status) pst_set_error(err); Py_XDECREF(result); PST_THREADS_LEAVE return status; } static gboolean pst_handler_reload_multiple_cb (GNode **categories, GHashTable **streams, PSTCallbackInfo *info, GError **err) { PyObject *result; gboolean status; PST_THREADS_ENTER result = PyObject_CallMethod(info->object, info->method, NULL); status = pst_handler_reload_parse_result(result, (Converter) pst_categories_sequence_as_gnode, categories, (Converter) pst_streams_mapping_as_ghashtable, streams); if (! status) pst_set_error(err); Py_XDECREF(result); PST_THREADS_LEAVE return status; } static gboolean pst_handler_reload_parse_result (PyObject *result, Converter converter1, gpointer ptr1, Converter converter2, gpointer ptr2) { int len; g_return_val_if_fail(converter1 != NULL, FALSE); g_return_val_if_fail(ptr1 != NULL, FALSE); g_return_val_if_fail(converter2 != NULL, FALSE); g_return_val_if_fail(ptr2 != NULL, FALSE); if (! result) return FALSE; len = PySequence_Length(result); if (len == -1) return FALSE; if (len != 2) { PyErr_SetString(PyExc_TypeError, _("sequence length must be 2")); return FALSE; } if (! pst_handler_reload_convert(result, 0, converter1, ptr1)) return FALSE; if (! pst_handler_reload_convert(result, 1, converter2, ptr2)) return FALSE; return TRUE; } static gboolean pst_handler_reload_convert (PyObject *sequence, int n, Converter converter, gpointer ptr) { PyObject *object; gboolean status; g_return_val_if_fail(sequence != NULL, FALSE); g_return_val_if_fail(converter != NULL, FALSE); g_return_val_if_fail(ptr != NULL, FALSE); object = PySequence_ITEM(sequence, n); if (! object) return FALSE; status = converter(object, ptr); Py_DECREF(object); return status; } static GtkWidget * pst_handler_preferences_widget_new_cb (PSTCallbackInfo *info) { PyObject *result; GtkWidget *widget = NULL; PST_THREADS_ENTER result = PyObject_CallMethod(info->object, info->method, NULL); if (result) { widget = (GtkWidget *) pst_pygobject_get(result, GTK_TYPE_WIDGET); if (! widget) PyErr_Print(); Py_DECREF(result); } else PyErr_Print(); PST_THREADS_LEAVE return widget; } /*** C API *******************************************************************/ gboolean pst_handler_register (PyObject *module) { PyTypeObject *ptr = &PSTHandler_Type; g_return_val_if_fail(module != NULL, FALSE); if (PyType_Ready(&PSTHandler_Type) < 0) return FALSE; Py_INCREF(&PSTHandler_Type); PyModule_AddObject(module, "Handler", (PyObject *) ptr); PyModule_AddIntConstant(module, "HANDLER_NO_CATEGORIES", ST_HANDLER_NO_CATEGORIES); PyModule_AddIntConstant(module, "HANDLER_CONFIRM_DELETION", ST_HANDLER_CONFIRM_DELETION); PyModule_AddIntConstant(module, "HANDLER_STOCK_FIELD_NAME", ST_HANDLER_STOCK_FIELD_NAME); PyModule_AddIntConstant(module, "HANDLER_STOCK_FIELD_GENRE", ST_HANDLER_STOCK_FIELD_GENRE); PyModule_AddIntConstant(module, "HANDLER_STOCK_FIELD_DESCRIPTION", ST_HANDLER_STOCK_FIELD_DESCRIPTION); PyModule_AddIntConstant(module, "HANDLER_STOCK_FIELD_HOMEPAGE", ST_HANDLER_STOCK_FIELD_HOMEPAGE); PyModule_AddIntConstant(module, "HANDLER_STOCK_FIELD_URI_LIST", ST_HANDLER_STOCK_FIELD_URI_LIST); return TRUE; }