fe78fb97de661f9a162bd26ca6facd9425271ab5
[platform/upstream/python-gobject.git] / gi / pygenum.c
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 #ifdef HAVE_CONFIG_H
23 #  include <config.h>
24 #endif
25
26 #include <pyglib.h>
27 #include "pygobject-private.h"
28 #include "pygi.h"
29 #include "pygi-type.h"
30
31 #include "pygenum.h"
32
33 GQuark pygenum_class_key;
34
35 PYGLIB_DEFINE_TYPE("gobject.GEnum", PyGEnum_Type, PyGEnum);
36
37 static PyObject *
38 pyg_enum_val_new(PyObject* subclass, GType gtype, PyObject *intval)
39 {
40     PyObject *args, *item;
41     args = Py_BuildValue("(O)", intval);
42     item =  (&PYGLIB_PyLong_Type)->tp_new((PyTypeObject*)subclass, args, NULL);
43     Py_DECREF(args);
44     if (!item)
45         return NULL;
46     ((PyGEnum*)item)->gtype = gtype;
47
48     return item;
49 }
50
51 static PyObject *
52 pyg_enum_richcompare(PyGEnum *self, PyObject *other, int op)
53 {
54     static char warning[256];
55
56     if (!PYGLIB_PyLong_Check(other)) {
57         Py_INCREF(Py_NotImplemented);
58         return Py_NotImplemented;
59     }
60
61     if (PyObject_TypeCheck(other, &PyGEnum_Type) && ((PyGEnum*)other)->gtype != self->gtype) {
62         g_snprintf(warning, sizeof(warning), "comparing different enum types: %s and %s",
63                    g_type_name(self->gtype), g_type_name(((PyGEnum*)other)->gtype));
64         if (PyErr_Warn(PyExc_Warning, warning))
65             return NULL;
66     }
67
68     return pyg_integer_richcompare((PyObject *)self, other, op);
69 }
70
71 static PyObject *
72 pyg_enum_repr(PyGEnum *self)
73 {
74     PyObject *module;
75     GEnumClass *enum_class;
76     const char *value;
77     guint index;
78     char *namespace, *module_str;
79     static char tmp[256];
80     long l;
81
82     module = PyObject_GetAttrString ((PyObject *)self, "__module__");
83     if (module == NULL)
84         return NULL;
85
86     if (!PYGLIB_PyUnicode_Check (module)) {
87         Py_DECREF (module);
88         return NULL;
89     }
90
91     enum_class = g_type_class_ref(self->gtype);
92     g_assert(G_IS_ENUM_CLASS(enum_class));
93
94     l = PYGLIB_PyLong_AS_LONG(self);
95     for (index = 0; index < enum_class->n_values; index++)
96         if (l == enum_class->values[index].value)
97             break;
98
99     module_str = PYGLIB_PyUnicode_AsString (module);
100     namespace = g_strrstr (module_str, ".");
101     if (namespace == NULL) {
102         namespace = module_str;
103     } else {
104         namespace += 1;
105     }
106
107     value = enum_class->values[index].value_name;
108     if (value)
109         sprintf(tmp, "<enum %s of type %s.%s>", value,
110                 namespace, Py_TYPE (self)->tp_name);
111     else
112         sprintf(tmp, "<enum %ld of type %s.%s>", PYGLIB_PyLong_AS_LONG(self),
113                 namespace, Py_TYPE (self)->tp_name);
114     Py_DECREF (module);
115     g_type_class_unref(enum_class);
116
117     return PYGLIB_PyUnicode_FromString(tmp);
118 }
119
120 static PyObject *
121 pyg_enum_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
122 {
123     static char *kwlist[] = { "value", NULL };
124     long value;
125     PyObject *pytc, *values, *ret, *intvalue;
126     GType gtype;
127     GEnumClass *eclass;
128
129     if (!PyArg_ParseTupleAndKeywords(args, kwargs, "l", kwlist, &value))
130         return NULL;
131
132     pytc = PyObject_GetAttrString((PyObject *)type, "__gtype__");
133     if (!pytc)
134         return NULL;
135
136     if (!PyObject_TypeCheck(pytc, &PyGTypeWrapper_Type)) {
137         Py_DECREF(pytc);
138         PyErr_SetString(PyExc_TypeError,
139                         "__gtype__ attribute not a typecode");
140         return NULL;
141     }
142
143     gtype = pyg_type_from_object(pytc);
144     Py_DECREF(pytc);
145
146     eclass = G_ENUM_CLASS(g_type_class_ref(gtype));
147
148     /* A check that 0 < value < eclass->n_values was here but got
149      * removed: enumeration values do not need to be consequitive,
150      * e.g. GtkPathPriorityType values are not.
151      */
152
153     values = PyObject_GetAttrString((PyObject *)type, "__enum_values__");
154     if (!values) {
155         g_type_class_unref(eclass);
156         return NULL;
157     }
158
159     /* Note that size of __enum_values__ dictionary can easily be less
160      * than 'n_values'.  This happens if some values of the enum are
161      * numerically equal, e.g. gtk.ANCHOR_N == gtk.ANCHOR_NORTH.
162      * Johan said that "In retrospect, using a dictionary to store the
163      * values might not have been that good", but we need to keep
164      * backward compatibility.
165      */
166     if (!PyDict_Check(values) || PyDict_Size(values) > eclass->n_values) {
167         PyErr_SetString(PyExc_TypeError, "__enum_values__ badly formed");
168         Py_DECREF(values);
169         g_type_class_unref(eclass);
170         return NULL;
171     }
172
173     g_type_class_unref(eclass);
174
175     intvalue = PYGLIB_PyLong_FromLong(value);
176     ret = PyDict_GetItem(values, intvalue);
177     Py_DECREF(intvalue);
178     Py_DECREF(values);
179     if (ret)
180         Py_INCREF(ret);
181     else
182         PyErr_Format(PyExc_ValueError, "invalid enum value: %ld", value);
183
184     return ret;
185 }
186
187 PyObject*
188 pyg_enum_from_gtype (GType gtype, int value)
189 {
190     PyObject *pyclass, *values, *retval, *intvalue;
191
192     g_return_val_if_fail(gtype != G_TYPE_INVALID, NULL);
193
194     /* Get a wrapper class by:
195      * 1. check for one attached to the gtype
196      * 2. lookup one in a typelib
197      * 3. creating a new one
198      */
199     pyclass = (PyObject*)g_type_get_qdata(gtype, pygenum_class_key);
200     if (!pyclass)
201         pyclass = pygi_type_import_by_g_type(gtype);
202     if (!pyclass)
203         pyclass = pyg_enum_add(NULL, g_type_name(gtype), NULL, gtype);
204     if (!pyclass)
205         return PYGLIB_PyLong_FromLong(value);
206
207     values = PyDict_GetItemString(((PyTypeObject *)pyclass)->tp_dict,
208                                   "__enum_values__");
209     intvalue = PYGLIB_PyLong_FromLong(value);
210     retval = PyDict_GetItem(values, intvalue);
211     if (retval) {
212         Py_INCREF(retval);
213     }
214     else {
215         PyErr_Clear();
216         retval = pyg_enum_val_new(pyclass, gtype, intvalue);
217     }
218     Py_DECREF(intvalue);
219
220     return retval;
221 }
222
223 /*
224  * pyg_enum_add
225  * Dynamically create a class derived from PyGEnum based on the given GType.
226  */
227 PyObject *
228 pyg_enum_add (PyObject *   module,
229               const char * typename,
230               const char * strip_prefix,
231               GType        gtype)
232 {
233     PyGILState_STATE state;
234     PyObject *instance_dict, *stub, *values, *o;
235     GEnumClass *eclass;
236     int i;
237
238     g_return_val_if_fail(typename != NULL, NULL);
239     if (!g_type_is_a (gtype, G_TYPE_ENUM)) {
240         PyErr_Format (PyExc_TypeError, "Trying to register gtype '%s' as enum when in fact it is of type '%s'",
241                       g_type_name (gtype), g_type_name (G_TYPE_FUNDAMENTAL (gtype)));
242         return NULL;
243     }
244
245     state = pyglib_gil_state_ensure();
246
247     /* Create a new type derived from GEnum. This is the same as:
248      * >>> stub = type(typename, (GEnum,), {})
249      */
250     instance_dict = PyDict_New();
251     stub = PyObject_CallFunction((PyObject *)&PyType_Type, "s(O)O",
252                                  typename, (PyObject *)&PyGEnum_Type,
253                                  instance_dict);
254     Py_DECREF(instance_dict);
255     if (!stub) {
256         PyErr_SetString(PyExc_RuntimeError, "can't create const");
257         pyglib_gil_state_release(state);
258         return NULL;
259     }
260
261     ((PyTypeObject *)stub)->tp_flags &= ~Py_TPFLAGS_BASETYPE;
262     ((PyTypeObject *)stub)->tp_new = pyg_enum_new;
263
264     if (module)
265         PyDict_SetItemString(((PyTypeObject *)stub)->tp_dict,
266                              "__module__",
267                              PYGLIB_PyUnicode_FromString(PyModule_GetName(module)));
268
269     g_type_set_qdata(gtype, pygenum_class_key, stub);
270
271     o = pyg_type_wrapper_new(gtype);
272     PyDict_SetItemString(((PyTypeObject *)stub)->tp_dict, "__gtype__", o);
273     Py_DECREF(o);
274
275     if (module) {
276         /* Add it to the module name space */
277         PyModule_AddObject(module, (char*)typename, stub);
278         Py_INCREF(stub);
279     }
280
281     /* Register enum values */
282     eclass = G_ENUM_CLASS(g_type_class_ref(gtype));
283
284     values = PyDict_New();
285     for (i = 0; i < eclass->n_values; i++) {
286         PyObject *item, *intval;
287       
288         intval = PYGLIB_PyLong_FromLong(eclass->values[i].value);
289         item = pyg_enum_val_new(stub, gtype, intval);
290         PyDict_SetItem(values, intval, item);
291         Py_DECREF(intval);
292
293         if (module) {
294             char *prefix;
295
296             prefix = g_strdup(pyg_constant_strip_prefix(eclass->values[i].value_name, strip_prefix));
297             PyModule_AddObject(module, prefix, item);
298             g_free(prefix);
299
300             Py_INCREF(item);
301         }
302     }
303
304     PyDict_SetItemString(((PyTypeObject *)stub)->tp_dict,
305                          "__enum_values__", values);
306     Py_DECREF(values);
307
308     g_type_class_unref(eclass);
309
310     pyglib_gil_state_release(state);
311     return stub;
312 }
313
314 static PyObject *
315 pyg_enum_reduce(PyObject *self, PyObject *args)
316 {
317     if (!PyArg_ParseTuple(args, ":GEnum.__reduce__"))
318         return NULL;
319
320     return Py_BuildValue("(O(i)O)", Py_TYPE(self), PYGLIB_PyLong_AsLong(self),
321                          PyObject_GetAttrString(self, "__dict__"));
322 }
323
324 static PyObject *
325 pyg_enum_get_value_name(PyGEnum *self, void *closure)
326 {
327   GEnumClass *enum_class;
328   GEnumValue *enum_value;
329   PyObject *retval;
330
331   enum_class = g_type_class_ref(self->gtype);
332   g_assert(G_IS_ENUM_CLASS(enum_class));
333
334   enum_value = g_enum_get_value(enum_class, PYGLIB_PyLong_AS_LONG(self));
335
336   retval = PYGLIB_PyUnicode_FromString(enum_value->value_name);
337   g_type_class_unref(enum_class);
338
339   return retval;
340 }
341
342 static PyObject *
343 pyg_enum_get_value_nick(PyGEnum *self, void *closure)
344 {
345   GEnumClass *enum_class;
346   GEnumValue *enum_value;
347   PyObject *retval;
348
349   enum_class = g_type_class_ref(self->gtype);
350   g_assert(G_IS_ENUM_CLASS(enum_class));
351
352   enum_value = g_enum_get_value(enum_class, PYGLIB_PyLong_AS_LONG(self));
353
354   retval = PYGLIB_PyUnicode_FromString(enum_value->value_nick);
355   g_type_class_unref(enum_class);
356
357   return retval;
358 }
359
360
361 static PyMethodDef pyg_enum_methods[] = {
362     { "__reduce__", (PyCFunction)pyg_enum_reduce, METH_VARARGS },
363     { NULL, NULL, 0 }
364 };
365
366 static PyGetSetDef pyg_enum_getsets[] = {
367     { "value_name", (getter)pyg_enum_get_value_name, (setter)0 },
368     { "value_nick", (getter)pyg_enum_get_value_nick, (setter)0 },
369     { NULL, 0, 0 }
370 };
371
372 void
373 pygobject_enum_register_types(PyObject *d)
374 {
375     pygenum_class_key        = g_quark_from_static_string("PyGEnum::class");
376
377     PyGEnum_Type.tp_base = &PYGLIB_PyLong_Type;
378 #if PY_VERSION_HEX < 0x03000000
379     PyGEnum_Type.tp_new = pyg_enum_new;
380 #else
381     PyGEnum_Type.tp_new = PyLong_Type.tp_new;
382     PyGEnum_Type.tp_hash = PyLong_Type.tp_hash;
383 #endif
384     PyGEnum_Type.tp_repr = (reprfunc)pyg_enum_repr;
385     PyGEnum_Type.tp_str = (reprfunc)pyg_enum_repr;
386     PyGEnum_Type.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE;
387     PyGEnum_Type.tp_richcompare = (richcmpfunc)pyg_enum_richcompare;
388     PyGEnum_Type.tp_methods = pyg_enum_methods;
389     PyGEnum_Type.tp_getset = pyg_enum_getsets;
390     PYGOBJECT_REGISTER_GTYPE(d, PyGEnum_Type, "GEnum", G_TYPE_ENUM);
391 }