/* -*- Mode: C; c-basic-offset: 4 -*- * pygtk- Python bindings for the GTK toolkit. * Copyright (C) 1998-2003 James Henstridge * Copyright (C) 2004 Johan Dahlin * * pygflags.c: GFlags wrapper * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . */ #include #include "pygi-type.h" #include "pygi-util.h" #include "pygi-type.h" #include "pygflags.h" #include "pygboxed.h" GQuark pygflags_class_key; PYGI_DEFINE_TYPE("gobject.GFlags", PyGFlags_Type, PyGFlags); static PyObject * pyg_flags_val_new(PyObject* subclass, GType gtype, PyObject *intval) { PyObject *args, *item; args = Py_BuildValue("(O)", intval); g_assert(PyObject_IsSubclass(subclass, (PyObject*) &PyGFlags_Type)); item = PyLong_Type.tp_new((PyTypeObject*)subclass, args, NULL); Py_DECREF(args); if (!item) return NULL; ((PyGFlags*)item)->gtype = gtype; return item; } static PyObject * pyg_flags_richcompare(PyGFlags *self, PyObject *other, int op) { static char warning[256]; if (!PyLong_Check (other)) { Py_INCREF(Py_NotImplemented); return Py_NotImplemented; } if (PyObject_TypeCheck(other, &PyGFlags_Type) && ((PyGFlags*)other)->gtype != self->gtype) { g_snprintf(warning, sizeof(warning), "comparing different flags types: %s and %s", g_type_name(self->gtype), g_type_name(((PyGFlags*)other)->gtype)); if (PyErr_Warn(PyExc_Warning, warning)) return NULL; } return pyg_integer_richcompare((PyObject *)self, other, op); } static char * generate_repr(GType gtype, guint value) { GFlagsClass *flags_class; char *retval = NULL, *tmp; guint i; flags_class = g_type_class_ref(gtype); g_assert(G_IS_FLAGS_CLASS(flags_class)); for (i = 0; i < flags_class->n_values; i++) { /* Some types (eg GstElementState in GStreamer 0.8) has flags with 0 values, * we're just ignore them for now otherwise they'll always show up */ if (flags_class->values[i].value == 0) continue; if ((value & flags_class->values[i].value) == flags_class->values[i].value) { if (retval) { tmp = g_strdup_printf("%s | %s", retval, flags_class->values[i].value_name); g_free(retval); retval = tmp; } else { retval = g_strdup_printf("%s", flags_class->values[i].value_name); } } } g_type_class_unref(flags_class); return retval; } static PyObject * pyg_flags_repr(PyGFlags *self) { char *tmp, *retval, *module_str, *namespace; PyObject *pyretval, *module; tmp = generate_repr(self->gtype, (guint)PyLong_AsUnsignedLongMask ((PyObject*)self)); module = PyObject_GetAttrString ((PyObject *)self, "__module__"); if (module == NULL) { g_free (tmp); return NULL; } if (!PyUnicode_Check (module)) { g_free (tmp); Py_DECREF (module); return NULL; } module_str = PyUnicode_AsUTF8 (module); namespace = g_strrstr (module_str, "."); if (namespace == NULL) { namespace = module_str; } else { namespace += 1; } if (tmp) retval = g_strdup_printf("", tmp, namespace, Py_TYPE (self)->tp_name); else retval = g_strdup_printf("", PyLong_AsUnsignedLongMask ((PyObject*)self), namespace, Py_TYPE (self)->tp_name); g_free(tmp); Py_DECREF (module); pyretval = PyUnicode_FromString (retval); g_free(retval); return pyretval; } static PyObject * pyg_flags_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) { static char *kwlist[] = { "value", NULL }; gulong value; PyObject *pytc, *values, *ret, *pyint; GType gtype; GFlagsClass *eclass; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "k", kwlist, &value)) return NULL; pytc = PyObject_GetAttrString((PyObject *)type, "__gtype__"); if (!pytc) return NULL; if (!PyObject_TypeCheck(pytc, &PyGTypeWrapper_Type)) { Py_DECREF(pytc); PyErr_SetString(PyExc_TypeError, "__gtype__ attribute not a typecode"); return NULL; } gtype = pyg_type_from_object(pytc); Py_DECREF(pytc); eclass = G_FLAGS_CLASS(g_type_class_ref(gtype)); values = PyObject_GetAttrString((PyObject *)type, "__flags_values__"); if (!values) { g_type_class_unref(eclass); return NULL; } if (!PyDict_Check(values)) { PyErr_SetString(PyExc_TypeError, "__flags_values__ badly formed"); Py_DECREF(values); g_type_class_unref(eclass); return NULL; } g_type_class_unref(eclass); pyint = PyLong_FromUnsignedLong (value); ret = PyDict_GetItem(values, pyint); if (!ret) { PyErr_Clear(); ret = pyg_flags_val_new((PyObject *)type, gtype, pyint); g_assert(ret != NULL); } else { Py_INCREF(ret); } Py_DECREF(pyint); Py_DECREF(values); return ret; } PyObject* pyg_flags_from_gtype (GType gtype, guint value) { PyObject *pyclass, *values, *retval, *pyint; if (PyErr_Occurred()) return PyLong_FromUnsignedLong (0); g_return_val_if_fail(gtype != G_TYPE_INVALID, NULL); /* Get a wrapper class by: * 1. check for one attached to the gtype * 2. lookup one in a typelib * 3. creating a new one */ pyclass = (PyObject*)g_type_get_qdata(gtype, pygflags_class_key); if (!pyclass) pyclass = pygi_type_import_by_g_type(gtype); if (!pyclass) pyclass = pyg_flags_add(NULL, g_type_name(gtype), NULL, gtype); if (!pyclass) return PyLong_FromUnsignedLong (value); values = PyDict_GetItemString(((PyTypeObject *)pyclass)->tp_dict, "__flags_values__"); pyint = PyLong_FromUnsignedLong (value); retval = PyDict_GetItem(values, pyint); if (!retval) { PyErr_Clear(); retval = pyg_flags_val_new(pyclass, gtype, pyint); g_assert(retval != NULL); } else { Py_INCREF(retval); } Py_DECREF(pyint); return retval; } /* * pyg_flags_add * Dynamically create a class derived from PyGFlags based on the given GType. */ PyObject * pyg_flags_add (PyObject * module, const char * typename, const char * strip_prefix, GType gtype) { PyGILState_STATE state; PyObject *instance_dict, *stub, *values, *o; GFlagsClass *eclass; guint i; g_return_val_if_fail(typename != NULL, NULL); if (!g_type_is_a(gtype, G_TYPE_FLAGS)) { g_warning("Trying to register gtype '%s' as flags when in fact it is of type '%s'", g_type_name(gtype), g_type_name(G_TYPE_FUNDAMENTAL(gtype))); return NULL; } state = PyGILState_Ensure(); /* Create a new type derived from GFlags. This is the same as: * >>> stub = type(typename, (GFlags,), {}) */ instance_dict = PyDict_New(); stub = PyObject_CallFunction((PyObject *)&PyType_Type, "s(O)O", typename, (PyObject *)&PyGFlags_Type, instance_dict); Py_DECREF(instance_dict); if (!stub) { PyErr_SetString(PyExc_RuntimeError, "can't create GFlags subtype"); PyGILState_Release(state); return NULL; } ((PyTypeObject *)stub)->tp_flags &= ~Py_TPFLAGS_BASETYPE; if (module) { PyDict_SetItemString(((PyTypeObject *)stub)->tp_dict, "__module__", PyUnicode_FromString (PyModule_GetName(module))); /* Add it to the module name space */ PyModule_AddObject(module, (char*)typename, stub); Py_INCREF(stub); } g_type_set_qdata(gtype, pygflags_class_key, stub); o = pyg_type_wrapper_new(gtype); PyDict_SetItemString(((PyTypeObject *)stub)->tp_dict, "__gtype__", o); Py_DECREF(o); /* Register flag values */ eclass = G_FLAGS_CLASS(g_type_class_ref(gtype)); values = PyDict_New(); for (i = 0; i < eclass->n_values; i++) { PyObject *item, *intval; intval = PyLong_FromUnsignedLong (eclass->values[i].value); g_assert(PyErr_Occurred() == NULL); item = pyg_flags_val_new(stub, gtype, intval); PyDict_SetItem(values, intval, item); Py_DECREF(intval); if (module) { char *prefix; prefix = g_strdup(pyg_constant_strip_prefix(eclass->values[i].value_name, strip_prefix)); Py_INCREF(item); PyModule_AddObject(module, prefix, item); g_free(prefix); } Py_DECREF(item); } PyDict_SetItemString(((PyTypeObject *)stub)->tp_dict, "__flags_values__", values); Py_DECREF(values); g_type_class_unref(eclass); PyGILState_Release(state); return stub; } static PyObject * pyg_flags_and(PyGFlags *a, PyGFlags *b) { if (!PyGFlags_Check(a) || !PyGFlags_Check(b)) return PyLong_Type.tp_as_number->nb_and((PyObject*)a, (PyObject*)b); return pyg_flags_from_gtype(a->gtype, (guint)(PyLong_AsUnsignedLongMask ((PyObject*)a) & PyLong_AsUnsignedLongMask ((PyObject*)b))); } static PyObject * pyg_flags_or(PyGFlags *a, PyGFlags *b) { if (!PyGFlags_Check(a) || !PyGFlags_Check(b)) return PyLong_Type.tp_as_number->nb_or((PyObject*)a, (PyObject*)b); return pyg_flags_from_gtype(a->gtype, (guint)(PyLong_AsUnsignedLongMask ((PyObject*)a) | PyLong_AsUnsignedLongMask ((PyObject*)b))); } static PyObject * pyg_flags_xor(PyGFlags *a, PyGFlags *b) { if (!PyGFlags_Check(a) || !PyGFlags_Check(b)) return PyLong_Type.tp_as_number->nb_xor((PyObject*)a, (PyObject*)b); return pyg_flags_from_gtype(a->gtype, (guint)(PyLong_AsUnsignedLongMask ((PyObject*)a) ^ PyLong_AsUnsignedLongMask ((PyObject*)b))); } static PyObject * pyg_flags_warn (PyObject *self, PyObject *args) { if (PyErr_Warn(PyExc_Warning, "unsupported arithmetic operation for flags type")) return NULL; Py_INCREF(Py_None); return Py_None; } static PyObject * pyg_flags_get_first_value_name(PyGFlags *self, void *closure) { GFlagsClass *flags_class; GFlagsValue *flags_value; PyObject *retval; flags_class = g_type_class_ref(self->gtype); g_assert(G_IS_FLAGS_CLASS(flags_class)); flags_value = g_flags_get_first_value(flags_class, (guint)PyLong_AsUnsignedLongMask ((PyObject*)self)); if (flags_value) retval = PyUnicode_FromString (flags_value->value_name); else { retval = Py_None; Py_INCREF(Py_None); } g_type_class_unref(flags_class); return retval; } static PyObject * pyg_flags_get_first_value_nick(PyGFlags *self, void *closure) { GFlagsClass *flags_class; GFlagsValue *flags_value; PyObject *retval; flags_class = g_type_class_ref(self->gtype); g_assert(G_IS_FLAGS_CLASS(flags_class)); flags_value = g_flags_get_first_value(flags_class, (guint)PyLong_AsUnsignedLongMask ((PyObject*)self)); if (flags_value) retval = PyUnicode_FromString (flags_value->value_nick); else { retval = Py_None; Py_INCREF(Py_None); } g_type_class_unref(flags_class); return retval; } static PyObject * pyg_flags_get_value_names(PyGFlags *self, void *closure) { GFlagsClass *flags_class; PyObject *retval; guint i; flags_class = g_type_class_ref(self->gtype); g_assert(G_IS_FLAGS_CLASS(flags_class)); retval = PyList_New(0); for (i = 0; i < flags_class->n_values; i++) { PyObject *value_name; if ((PyLong_AsUnsignedLongMask ((PyObject*)self) & flags_class->values[i].value) == flags_class->values[i].value) { value_name = PyUnicode_FromString (flags_class->values[i].value_name); PyList_Append (retval, value_name); Py_DECREF (value_name); } } g_type_class_unref(flags_class); return retval; } static PyObject * pyg_flags_get_value_nicks(PyGFlags *self, void *closure) { GFlagsClass *flags_class; PyObject *retval; guint i; flags_class = g_type_class_ref(self->gtype); g_assert(G_IS_FLAGS_CLASS(flags_class)); retval = PyList_New(0); for (i = 0; i < flags_class->n_values; i++) if ((PyLong_AsUnsignedLongMask ((PyObject*)self) & flags_class->values[i].value) == flags_class->values[i].value) { PyObject *py_nick = PyUnicode_FromString (flags_class->values[i].value_nick); PyList_Append(retval, py_nick); Py_DECREF (py_nick); } g_type_class_unref(flags_class); return retval; } static PyGetSetDef pyg_flags_getsets[] = { { "first_value_name", (getter)pyg_flags_get_first_value_name, (setter)0 }, { "first_value_nick", (getter)pyg_flags_get_first_value_nick, (setter)0 }, { "value_names", (getter)pyg_flags_get_value_names, (setter)0 }, { "value_nicks", (getter)pyg_flags_get_value_nicks, (setter)0 }, { NULL, 0, 0 } }; static PyNumberMethods pyg_flags_as_number = { (binaryfunc)pyg_flags_warn, /* nb_add */ (binaryfunc)pyg_flags_warn, /* nb_subtract */ (binaryfunc)pyg_flags_warn, /* nb_multiply */ (binaryfunc)pyg_flags_warn, /* nb_divide */ (binaryfunc)pyg_flags_warn, /* nb_remainder */ (ternaryfunc)pyg_flags_warn, /* nb_power */ 0, /* nb_negative */ 0, /* nb_positive */ 0, /* nb_absolute */ 0, /* nb_nonzero */ 0, /* nb_invert */ 0, /* nb_lshift */ 0, /* nb_rshift */ (binaryfunc)pyg_flags_and, /* nb_and */ (binaryfunc)pyg_flags_xor, /* nb_xor */ (binaryfunc)pyg_flags_or, /* nb_or */ }; /** * Returns 0 on success, or -1 and sets an exception. */ int pygi_flags_register_types(PyObject *d) { PyObject *pygtype; pygflags_class_key = g_quark_from_static_string("PyGFlags::class"); PyGFlags_Type.tp_base = &PyLong_Type; PyGFlags_Type.tp_new = pyg_flags_new; PyGFlags_Type.tp_hash = PyLong_Type.tp_hash; PyGFlags_Type.tp_repr = (reprfunc)pyg_flags_repr; PyGFlags_Type.tp_as_number = &pyg_flags_as_number; PyGFlags_Type.tp_str = (reprfunc)pyg_flags_repr; PyGFlags_Type.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE; PyGFlags_Type.tp_richcompare = (richcmpfunc)pyg_flags_richcompare; PyGFlags_Type.tp_getset = pyg_flags_getsets; PyGFlags_Type.tp_alloc = PyType_GenericAlloc; if (PyType_Ready(&PyGFlags_Type)) return -1; pygtype = pyg_type_wrapper_new (G_TYPE_FLAGS); PyDict_SetItemString (PyGFlags_Type.tp_dict, "__gtype__", pygtype); Py_DECREF (pygtype); PyDict_SetItemString(d, "GFlags", (PyObject *)&PyGFlags_Type); return 0; }