19cdb440d560bb599c6ba4bc8d0325414baac03e
[platform/upstream/pygobject2.git] / gi / pygi-property.c
1 /* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
2 /*
3  * Copyright (c) 2010  Collabora Ltd. <http://www.collabora.co.uk/>
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a copy
6  * of this software and associated documentation files (the "Software"), to
7  * deal in the Software without restriction, including without limitation the
8  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
9  * sell copies of the Software, and to permit persons to whom the Software is
10  * furnished to do so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be included in
13  * all copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21  * IN THE SOFTWARE.
22  */
23
24 #include "pygi-property.h"
25 #include "pygi-value.h"
26 #include "pygi-argument.h"
27 #include "pygparamspec.h"
28 #include "pygtype.h"
29
30 #include <girepository.h>
31
32 static GIPropertyInfo *
33 lookup_property_from_object_info (GIObjectInfo *info, const gchar *attr_name)
34 {
35     gssize n_infos;
36     gssize i;
37
38     n_infos = g_object_info_get_n_properties (info);
39     for (i = 0; i < n_infos; i++) {
40         GIPropertyInfo *property_info;
41
42         property_info = g_object_info_get_property (info, i);
43         g_assert (info != NULL);
44
45         if (strcmp (attr_name, g_base_info_get_name (property_info)) == 0) {
46             return property_info;
47         }
48
49         g_base_info_unref (property_info);
50     }
51
52     return NULL;
53 }
54
55 static GIPropertyInfo *
56 lookup_property_from_interface_info (GIInterfaceInfo *info,
57                                      const gchar *attr_name)
58 {
59     gssize n_infos;
60     gssize i;
61
62     n_infos = g_interface_info_get_n_properties (info);
63     for (i = 0; i < n_infos; i++) {
64         GIPropertyInfo *property_info;
65
66         property_info = g_interface_info_get_property (info, i);
67         g_assert (info != NULL);
68
69         if (strcmp (attr_name, g_base_info_get_name (property_info)) == 0) {
70             return property_info;
71         }
72
73         g_base_info_unref (property_info);
74     }
75
76     return NULL;
77 }
78
79 static GIPropertyInfo *
80 _pygi_lookup_property_from_g_type (GType g_type, const gchar *attr_name)
81 {
82     GIPropertyInfo *ret = NULL;
83     GIRepository *repository;
84     GIBaseInfo *info;
85
86     repository = g_irepository_get_default();
87     info = g_irepository_find_by_gtype (repository, g_type);
88     if (info == NULL)
89        return NULL;
90
91     if (GI_IS_OBJECT_INFO (info))
92         ret = lookup_property_from_object_info ((GIObjectInfo *) info,
93                                                 attr_name);
94     else if (GI_IS_INTERFACE_INFO (info))
95         ret = lookup_property_from_interface_info ((GIInterfaceInfo *) info,
96                                                    attr_name);
97
98     g_base_info_unref (info);
99     return ret;
100 }
101
102 PyObject *
103 pygi_call_do_get_property (PyObject *instance, GParamSpec *pspec)
104 {
105     PyObject *py_pspec;
106     PyObject *retval;
107
108     py_pspec = pyg_param_spec_new (pspec);
109     retval = PyObject_CallMethod (instance, "do_get_property", "O", py_pspec);
110     if (retval == NULL) {
111         PyErr_Print();
112     }
113
114     Py_DECREF (py_pspec);
115     if (retval) {
116         return retval;
117     }
118
119     Py_RETURN_NONE;
120 }
121
122 PyObject *
123 pygi_get_property_value (PyGObject *instance, GParamSpec *pspec)
124 {
125     GIPropertyInfo *property_info = NULL;
126     GValue value = { 0, };
127     PyObject *py_value = NULL;
128     GType fundamental;
129
130     if (!(pspec->flags & G_PARAM_READABLE)) {
131         PyErr_Format(PyExc_TypeError, "property %s is not readable",
132                      g_param_spec_get_name (pspec));
133         return NULL;
134     }
135
136     /* Fast path which calls the Python getter implementation directly.
137      * See: https://bugzilla.gnome.org/show_bug.cgi?id=723872 */
138     if (pyg_gtype_is_custom (pspec->owner_type)) {
139         return pygi_call_do_get_property ((PyObject *)instance, pspec);
140     }
141
142     Py_BEGIN_ALLOW_THREADS;
143     g_value_init (&value, G_PARAM_SPEC_VALUE_TYPE (pspec));
144     g_object_get_property (instance->obj, pspec->name, &value);
145     fundamental = G_TYPE_FUNDAMENTAL (G_VALUE_TYPE (&value));
146     Py_END_ALLOW_THREADS;
147
148
149     /* Fast path basic types which don't need GI type info. */
150     py_value = pygi_value_to_py_basic_type (&value, fundamental);
151     if (py_value) {
152         goto out;
153     }
154
155     /* Attempt to marshal through GI.
156      * The owner_type of the pspec gives us the exact type that introduced the
157      * property, even if it is a parent class of the instance in question. */
158     property_info = _pygi_lookup_property_from_g_type (pspec->owner_type, pspec->name);
159     if (property_info) {
160         GITypeInfo *type_info = NULL;
161         gboolean free_array = FALSE;
162         GIArgument arg = { 0, };
163
164         type_info = g_property_info_get_type (property_info);
165         arg = _pygi_argument_from_g_value (&value, type_info);
166
167         /* Arrays are special cased, see note in _pygi_argument_to_array. */
168         if (g_type_info_get_tag (type_info) == GI_TYPE_TAG_ARRAY) {
169             arg.v_pointer = _pygi_argument_to_array (&arg, NULL, NULL, NULL,
170                                                      type_info, &free_array);
171         }
172
173         py_value = _pygi_argument_to_object (&arg, type_info, GI_TRANSFER_NOTHING);
174
175         if (free_array) {
176             g_array_free (arg.v_pointer, FALSE);
177         }
178
179         g_base_info_unref (type_info);
180         g_base_info_unref (property_info);
181     }
182
183     /* Fallback to GValue marshalling. */
184     if (py_value == NULL) {
185         py_value = pyg_param_gvalue_as_pyobject (&value, TRUE, pspec);
186     }
187
188 out:
189     g_value_unset (&value);
190     return py_value;
191 }
192
193 PyObject *
194 pygi_get_property_value_by_name (PyGObject *self, gchar *param_name)
195 {
196     GParamSpec *pspec;
197
198     pspec = g_object_class_find_property (G_OBJECT_GET_CLASS(self->obj),
199                                           param_name);
200     if (!pspec) {
201         PyErr_Format (PyExc_TypeError,
202                       "object of type `%s' does not have property `%s'",
203                       g_type_name (G_OBJECT_TYPE (self->obj)), param_name);
204         return NULL;
205     }
206
207     return pygi_get_property_value (self, pspec);
208 }
209
210 gint
211 pygi_set_property_value (PyGObject *instance,
212                          GParamSpec *pspec,
213                          PyObject *py_value)
214 {
215     GIPropertyInfo *property_info = NULL;
216     GITypeInfo *type_info = NULL;
217     GITypeTag type_tag;
218     GITransfer transfer;
219     GValue value = { 0, };
220     GIArgument arg = { 0, };
221     gint ret_value = -1;
222
223     /* The owner_type of the pspec gives us the exact type that introduced the
224      * property, even if it is a parent class of the instance in question. */
225     property_info = _pygi_lookup_property_from_g_type (pspec->owner_type,
226                                                        pspec->name);
227     if (property_info == NULL)
228         goto out;
229
230     if (! (pspec->flags & G_PARAM_WRITABLE))
231         goto out;
232
233     type_info = g_property_info_get_type (property_info);
234     transfer = g_property_info_get_ownership_transfer (property_info);
235     arg = _pygi_argument_from_object (py_value, type_info, transfer);
236
237     if (PyErr_Occurred())
238         goto out;
239
240     g_value_init (&value, G_PARAM_SPEC_VALUE_TYPE (pspec));
241
242     /* FIXME: Lots of types still unhandled */
243     type_tag = g_type_info_get_tag (type_info);
244     switch (type_tag) {
245         case GI_TYPE_TAG_INTERFACE:
246         {
247             GIBaseInfo *info;
248             GIInfoType info_type;
249             GType type;
250
251             info = g_type_info_get_interface (type_info);
252             type = g_registered_type_info_get_g_type (info);
253             info_type = g_base_info_get_type (info);
254
255             g_base_info_unref (info);
256
257             switch (info_type) {
258                 case GI_INFO_TYPE_ENUM:
259                     g_value_set_enum (&value, arg.v_int);
260                     break;
261                 case GI_INFO_TYPE_INTERFACE:
262                 case GI_INFO_TYPE_OBJECT:
263                     g_value_set_object (&value, arg.v_pointer);
264                     break;
265                 case GI_INFO_TYPE_BOXED:
266                 case GI_INFO_TYPE_STRUCT:
267                 case GI_INFO_TYPE_UNION:
268                     if (g_type_is_a (type, G_TYPE_BOXED)) {
269                         g_value_set_boxed (&value, arg.v_pointer);
270                     } else if (g_type_is_a (type, G_TYPE_VARIANT)) {
271                         g_value_set_variant (&value, arg.v_pointer);
272                     } else {
273                         PyErr_Format (PyExc_NotImplementedError,
274                                       "Setting properties of type '%s' is not implemented",
275                                       g_type_name (type));
276                         goto out;
277                     }
278                     break;
279                 default:
280                     PyErr_Format (PyExc_NotImplementedError,
281                                   "Setting properties of type '%s' is not implemented",
282                                   g_type_name (type));
283                     goto out;
284             }
285             break;
286         }
287         case GI_TYPE_TAG_BOOLEAN:
288             g_value_set_boolean (&value, arg.v_boolean);
289             break;
290         case GI_TYPE_TAG_INT8:
291             g_value_set_schar (&value, arg.v_int8);
292             break;
293         case GI_TYPE_TAG_INT16:
294         case GI_TYPE_TAG_INT32:
295             if (G_VALUE_HOLDS_LONG (&value))
296                 g_value_set_long (&value, arg.v_long);
297             else
298                 g_value_set_int (&value, arg.v_int);
299             break;
300         case GI_TYPE_TAG_INT64:
301             if (G_VALUE_HOLDS_LONG (&value))
302                 g_value_set_long (&value, arg.v_long);
303             else
304                 g_value_set_int64 (&value, arg.v_int64);
305             break;
306         case GI_TYPE_TAG_UINT8:
307             g_value_set_uchar (&value, arg.v_uint8);
308             break;
309         case GI_TYPE_TAG_UINT16:
310         case GI_TYPE_TAG_UINT32:
311             if (G_VALUE_HOLDS_ULONG (&value))
312                 g_value_set_ulong (&value, arg.v_ulong);
313             else
314                 g_value_set_uint (&value, arg.v_uint);
315             break;
316         case GI_TYPE_TAG_UINT64:
317             if (G_VALUE_HOLDS_ULONG (&value))
318                 g_value_set_ulong (&value, arg.v_ulong);
319             else
320                 g_value_set_uint64 (&value, arg.v_uint64);
321             break;
322         case GI_TYPE_TAG_FLOAT:
323             g_value_set_float (&value, arg.v_float);
324             break;
325         case GI_TYPE_TAG_DOUBLE:
326             g_value_set_double (&value, arg.v_double);
327             break;
328         case GI_TYPE_TAG_GTYPE:
329             g_value_set_gtype (&value, arg.v_size);
330             break;
331         case GI_TYPE_TAG_UTF8:
332         case GI_TYPE_TAG_FILENAME:
333             g_value_set_string (&value, arg.v_string);
334             break;
335         case GI_TYPE_TAG_GHASH:
336             g_value_set_boxed (&value, arg.v_pointer);
337             break;
338         case GI_TYPE_TAG_GLIST:
339             if (G_VALUE_HOLDS_BOXED(&value))
340                 g_value_set_boxed (&value, arg.v_pointer);
341             else
342                 g_value_set_pointer (&value, arg.v_pointer);
343             break;
344         case GI_TYPE_TAG_ARRAY:
345         {
346             /* This is assumes GI_TYPE_TAG_ARRAY is always a GStrv
347              * https://bugzilla.gnome.org/show_bug.cgi?id=688232
348              */
349             GArray *arg_items = (GArray*) arg.v_pointer;
350             gchar** strings;
351             int i;
352
353             if (arg_items == NULL)
354                 goto out;
355
356             strings = g_new0 (char*, arg_items->len + 1);
357             for (i = 0; i < arg_items->len; ++i) {
358                 strings[i] = g_array_index (arg_items, GIArgument, i).v_string;
359             }
360             strings[arg_items->len] = NULL;
361             g_value_take_boxed (&value, strings);
362             g_array_free (arg_items, TRUE);
363             break;
364         }
365         default:
366             PyErr_Format (PyExc_NotImplementedError,
367                           "Setting properties of type %s is not implemented",
368                           g_type_tag_to_string (g_type_info_get_tag (type_info)));
369             goto out;
370     }
371
372     g_object_set_property (instance->obj, pspec->name, &value);
373     g_value_unset (&value);
374
375     ret_value = 0;
376
377 out:
378     if (property_info != NULL)
379         g_base_info_unref (property_info);
380     if (type_info != NULL)
381         g_base_info_unref (type_info);
382
383     return ret_value;
384 }
385