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