1 /* -*- Mode: C; c-basic-offset: 4 -*-
2  * pygtk- Python bindings for the GTK toolkit.
3  * Copyright (C) 1998-2003  James Henstridge
4  * Copyright (C) 2004       Johan Dahlin
5  *
6  *   pygenum.c: GEnum wrapper
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, see <http://www.gnu.org/licenses/>.
20  */
21 
22 #include <config.h>
23 
24 #include "pygi-type.h"
25 #include "pygi-util.h"
26 #include "pygi-type.h"
27 #include "pygi-basictype.h"
28 #include "pygenum.h"
29 #include "pygboxed.h"
30 
31 GQuark pygenum_class_key;
32 
33 PYGI_DEFINE_TYPE("gobject.GEnum", PyGEnum_Type, PyGEnum);
34 
35 static PyObject *
pyg_enum_val_new(PyObject * subclass,GType gtype,PyObject * intval)36 pyg_enum_val_new(PyObject* subclass, GType gtype, PyObject *intval)
37 {
38     PyObject *args, *item;
39     args = Py_BuildValue("(O)", intval);
40     item =  (&PyLong_Type)->tp_new((PyTypeObject*)subclass, args, NULL);
41     Py_DECREF(args);
42     if (!item)
43 	return NULL;
44     ((PyGEnum*)item)->gtype = gtype;
45 
46     return item;
47 }
48 
49 static PyObject *
pyg_enum_richcompare(PyGEnum * self,PyObject * other,int op)50 pyg_enum_richcompare(PyGEnum *self, PyObject *other, int op)
51 {
52     static char warning[256];
53 
54     if (!PyLong_Check (other)) {
55 	Py_INCREF(Py_NotImplemented);
56 	return Py_NotImplemented;
57     }
58 
59     if (PyObject_TypeCheck(other, &PyGEnum_Type) && ((PyGEnum*)other)->gtype != self->gtype) {
60 	g_snprintf(warning, sizeof(warning), "comparing different enum types: %s and %s",
61 		   g_type_name(self->gtype), g_type_name(((PyGEnum*)other)->gtype));
62 	if (PyErr_Warn(PyExc_Warning, warning))
63 	    return NULL;
64     }
65 
66     return pyg_integer_richcompare((PyObject *)self, other, op);
67 }
68 
69 static PyObject *
pyg_enum_repr(PyGEnum * self)70 pyg_enum_repr(PyGEnum *self)
71 {
72     PyObject *module;
73     GEnumClass *enum_class;
74     const char *value;
75     guint index;
76     char *namespace, *module_str;
77     static char tmp[256];
78     long l;
79 
80     module = PyObject_GetAttrString ((PyObject *)self, "__module__");
81     if (module == NULL)
82         return NULL;
83 
84     if (!PyUnicode_Check (module)) {
85         Py_DECREF (module);
86         return NULL;
87     }
88 
89     enum_class = g_type_class_ref(self->gtype);
90     g_assert(G_IS_ENUM_CLASS(enum_class));
91 
92     l = PyLong_AS_LONG ((PyObject*)self);
93     for (index = 0; index < enum_class->n_values; index++)
94         if (l == enum_class->values[index].value)
95             break;
96 
97     module_str = PyUnicode_AsUTF8 (module);
98     namespace = g_strrstr (module_str, ".");
99     if (namespace == NULL) {
100         namespace = module_str;
101     } else {
102         namespace += 1;
103     }
104 
105     value = enum_class->values[index].value_name;
106     if (value)
107         sprintf(tmp, "<enum %s of type %s.%s>", value,
108                 namespace, Py_TYPE (self)->tp_name);
109     else
110         sprintf(tmp, "<enum %ld of type %s.%s>", PyLong_AS_LONG ((PyObject*)self),
111                 namespace, Py_TYPE (self)->tp_name);
112     Py_DECREF (module);
113     g_type_class_unref(enum_class);
114 
115     return PyUnicode_FromString (tmp);
116 }
117 
118 static PyObject *
pyg_enum_new(PyTypeObject * type,PyObject * args,PyObject * kwargs)119 pyg_enum_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
120 {
121     static char *kwlist[] = { "value", NULL };
122     long value;
123     PyObject *pytc, *values, *ret, *intvalue;
124     GType gtype;
125     GEnumClass *eclass;
126 
127     if (!PyArg_ParseTupleAndKeywords(args, kwargs, "l", kwlist, &value))
128 	return NULL;
129 
130     pytc = PyObject_GetAttrString((PyObject *)type, "__gtype__");
131     if (!pytc)
132 	return NULL;
133 
134     if (!PyObject_TypeCheck(pytc, &PyGTypeWrapper_Type)) {
135 	Py_DECREF(pytc);
136 	PyErr_SetString(PyExc_TypeError,
137 			"__gtype__ attribute not a typecode");
138 	return NULL;
139     }
140 
141     gtype = pyg_type_from_object(pytc);
142     Py_DECREF(pytc);
143 
144     eclass = G_ENUM_CLASS(g_type_class_ref(gtype));
145 
146     /* A check that 0 < value < eclass->n_values was here but got
147      * removed: enumeration values do not need to be consequitive,
148      * e.g. GtkPathPriorityType values are not.
149      */
150 
151     values = PyObject_GetAttrString((PyObject *)type, "__enum_values__");
152     if (!values) {
153 	g_type_class_unref(eclass);
154 	return NULL;
155     }
156 
157     /* Note that size of __enum_values__ dictionary can easily be less
158      * than 'n_values'.  This happens if some values of the enum are
159      * numerically equal, e.g. gtk.ANCHOR_N == gtk.ANCHOR_NORTH.
160      * Johan said that "In retrospect, using a dictionary to store the
161      * values might not have been that good", but we need to keep
162      * backward compatibility.
163      */
164     if (!PyDict_Check(values) || (gsize)PyDict_Size(values) > eclass->n_values) {
165 	PyErr_SetString(PyExc_TypeError, "__enum_values__ badly formed");
166 	Py_DECREF(values);
167 	g_type_class_unref(eclass);
168 	return NULL;
169     }
170 
171     g_type_class_unref(eclass);
172 
173     intvalue = PyLong_FromLong(value);
174     ret = PyDict_GetItem(values, intvalue);
175     Py_DECREF(intvalue);
176     Py_DECREF(values);
177     if (ret)
178         Py_INCREF(ret);
179     else
180 	PyErr_Format(PyExc_ValueError, "invalid enum value: %ld", value);
181 
182     return ret;
183 }
184 
185 PyObject*
pyg_enum_from_gtype(GType gtype,int value)186 pyg_enum_from_gtype (GType gtype, int value)
187 {
188     PyObject *pyclass, *values, *retval, *intvalue;
189 
190     g_return_val_if_fail(gtype != G_TYPE_INVALID, NULL);
191 
192     /* Get a wrapper class by:
193      * 1. check for one attached to the gtype
194      * 2. lookup one in a typelib
195      * 3. creating a new one
196      */
197     pyclass = (PyObject*)g_type_get_qdata(gtype, pygenum_class_key);
198     if (!pyclass)
199         pyclass = pygi_type_import_by_g_type(gtype);
200     if (!pyclass)
201         pyclass = pyg_enum_add(NULL, g_type_name(gtype), NULL, gtype);
202     if (!pyclass)
203 	return PyLong_FromLong(value);
204 
205     values = PyDict_GetItemString(((PyTypeObject *)pyclass)->tp_dict,
206 				  "__enum_values__");
207     intvalue = PyLong_FromLong(value);
208     retval = PyDict_GetItem(values, intvalue);
209     if (retval) {
210 	Py_INCREF(retval);
211     }
212     else {
213 	PyErr_Clear();
214 	retval = pyg_enum_val_new(pyclass, gtype, intvalue);
215     }
216     Py_DECREF(intvalue);
217 
218     return retval;
219 }
220 
221 /*
222  * pyg_enum_add
223  * Dynamically create a class derived from PyGEnum based on the given GType.
224  */
225 PyObject *
pyg_enum_add(PyObject * module,const char * typename,const char * strip_prefix,GType gtype)226 pyg_enum_add (PyObject *   module,
227 	      const char * typename,
228 	      const char * strip_prefix,
229 	      GType        gtype)
230 {
231     PyGILState_STATE state;
232     PyObject *instance_dict, *stub, *values, *o;
233     GEnumClass *eclass;
234     guint i;
235 
236     g_return_val_if_fail(typename != NULL, NULL);
237     if (!g_type_is_a (gtype, G_TYPE_ENUM)) {
238         PyErr_Format (PyExc_TypeError, "Trying to register gtype '%s' as enum when in fact it is of type '%s'",
239                       g_type_name (gtype), g_type_name (G_TYPE_FUNDAMENTAL (gtype)));
240         return NULL;
241     }
242 
243     state = PyGILState_Ensure();
244 
245     /* Create a new type derived from GEnum. This is the same as:
246      * >>> stub = type(typename, (GEnum,), {})
247      */
248     instance_dict = PyDict_New();
249     stub = PyObject_CallFunction((PyObject *)&PyType_Type, "s(O)O",
250                                  typename, (PyObject *)&PyGEnum_Type,
251                                  instance_dict);
252     Py_DECREF(instance_dict);
253     if (!stub) {
254 	PyErr_SetString(PyExc_RuntimeError, "can't create const");
255 	PyGILState_Release(state);
256 	return NULL;
257     }
258 
259     ((PyTypeObject *)stub)->tp_flags &= ~Py_TPFLAGS_BASETYPE;
260 
261     if (module)
262 	PyDict_SetItemString(((PyTypeObject *)stub)->tp_dict,
263 			     "__module__",
264 			     PyUnicode_FromString (PyModule_GetName(module)));
265 
266     g_type_set_qdata(gtype, pygenum_class_key, stub);
267 
268     o = pyg_type_wrapper_new(gtype);
269     PyDict_SetItemString(((PyTypeObject *)stub)->tp_dict, "__gtype__", o);
270     Py_DECREF(o);
271 
272     if (module) {
273 	/* Add it to the module name space */
274 	PyModule_AddObject(module, (char*)typename, stub);
275 	Py_INCREF(stub);
276     }
277 
278     /* Register enum values */
279     eclass = G_ENUM_CLASS(g_type_class_ref(gtype));
280 
281     values = PyDict_New();
282     for (i = 0; i < eclass->n_values; i++) {
283 	PyObject *item, *intval;
284 
285         intval = PyLong_FromLong(eclass->values[i].value);
286 	item = pyg_enum_val_new(stub, gtype, intval);
287 	PyDict_SetItem(values, intval, item);
288         Py_DECREF(intval);
289 
290 	if (module) {
291 	    char *prefix;
292 
293 	    prefix = g_strdup(pyg_constant_strip_prefix(eclass->values[i].value_name, strip_prefix));
294 	    PyModule_AddObject(module, prefix, item);
295 	    g_free(prefix);
296 
297 	    Py_INCREF(item);
298 	}
299     }
300 
301     PyDict_SetItemString(((PyTypeObject *)stub)->tp_dict,
302 			 "__enum_values__", values);
303     Py_DECREF(values);
304 
305     g_type_class_unref(eclass);
306 
307     PyGILState_Release(state);
308     return stub;
309 }
310 
311 static PyObject *
pyg_enum_reduce(PyObject * self,PyObject * args)312 pyg_enum_reduce(PyObject *self, PyObject *args)
313 {
314     if (!PyArg_ParseTuple(args, ":GEnum.__reduce__"))
315         return NULL;
316 
317     return Py_BuildValue("(O(i)O)", Py_TYPE(self), PyLong_AsLong (self),
318                          PyObject_GetAttrString(self, "__dict__"));
319 }
320 
321 static PyObject *
pyg_enum_get_value_name(PyGEnum * self,void * closure)322 pyg_enum_get_value_name(PyGEnum *self, void *closure)
323 {
324   GEnumClass *enum_class;
325   GEnumValue *enum_value;
326   PyObject *retval;
327   gint intvalue;
328 
329   if (!pygi_gint_from_py ((PyObject*) self, &intvalue))
330     return NULL;
331 
332   enum_class = g_type_class_ref(self->gtype);
333   g_assert(G_IS_ENUM_CLASS(enum_class));
334 
335   enum_value = g_enum_get_value(enum_class, intvalue);
336 
337   retval = pygi_utf8_to_py (enum_value->value_name);
338   g_type_class_unref(enum_class);
339 
340   return retval;
341 }
342 
343 static PyObject *
pyg_enum_get_value_nick(PyGEnum * self,void * closure)344 pyg_enum_get_value_nick(PyGEnum *self, void *closure)
345 {
346   GEnumClass *enum_class;
347   GEnumValue *enum_value;
348   PyObject *retval;
349   gint intvalue;
350 
351   if (!pygi_gint_from_py ((PyObject*) self, &intvalue))
352     return NULL;
353 
354   enum_class = g_type_class_ref(self->gtype);
355   g_assert(G_IS_ENUM_CLASS(enum_class));
356 
357   enum_value = g_enum_get_value(enum_class, intvalue);
358 
359   retval = pygi_utf8_to_py (enum_value->value_nick);
360 
361   g_type_class_unref(enum_class);
362 
363   return retval;
364 }
365 
366 
367 static PyMethodDef pyg_enum_methods[] = {
368     { "__reduce__", (PyCFunction)pyg_enum_reduce, METH_VARARGS },
369     { NULL, NULL, 0 }
370 };
371 
372 static PyGetSetDef pyg_enum_getsets[] = {
373     { "value_name", (getter)pyg_enum_get_value_name, (setter)0 },
374     { "value_nick", (getter)pyg_enum_get_value_nick, (setter)0 },
375     { NULL, 0, 0 }
376 };
377 
378 /**
379  * Returns 0 on success, or -1 and sets an exception.
380  */
381 int
pygi_enum_register_types(PyObject * d)382 pygi_enum_register_types(PyObject *d)
383 {
384     PyObject *pygtype;
385 
386     pygenum_class_key        = g_quark_from_static_string("PyGEnum::class");
387 
388     PyGEnum_Type.tp_base = &PyLong_Type;
389     PyGEnum_Type.tp_new = pyg_enum_new;
390     PyGEnum_Type.tp_hash = PyLong_Type.tp_hash;
391     PyGEnum_Type.tp_repr = (reprfunc)pyg_enum_repr;
392     PyGEnum_Type.tp_str = (reprfunc)pyg_enum_repr;
393     PyGEnum_Type.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE;
394     PyGEnum_Type.tp_richcompare = (richcmpfunc)pyg_enum_richcompare;
395     PyGEnum_Type.tp_methods = pyg_enum_methods;
396     PyGEnum_Type.tp_getset = pyg_enum_getsets;
397     PyGEnum_Type.tp_alloc = PyType_GenericAlloc;
398     if (PyType_Ready(&PyGEnum_Type))
399         return -1;
400 
401     pygtype = pyg_type_wrapper_new (G_TYPE_ENUM);
402     PyDict_SetItemString (PyGEnum_Type.tp_dict, "__gtype__", pygtype);
403     Py_DECREF (pygtype);
404 
405     PyDict_SetItemString(d, "GEnum", (PyObject *)&PyGEnum_Type);
406 
407     return 0;
408 }
409