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