/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2010 Collabora Ltd. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "pygi-property.h" #include "pygi-value.h" #include "pygi-argument.h" #include "pygparamspec.h" #include "pygi-type.h" #include static GIPropertyInfo * lookup_property_from_object_info (GIObjectInfo *info, const gchar *attr_name) { gssize n_infos; gint i; n_infos = g_object_info_get_n_properties (info); for (i = 0; i < n_infos; i++) { GIPropertyInfo *property_info; property_info = g_object_info_get_property (info, i); g_assert (info != NULL); if (strcmp (attr_name, g_base_info_get_name (property_info)) == 0) { return property_info; } g_base_info_unref (property_info); } return NULL; } static GIPropertyInfo * lookup_property_from_interface_info (GIInterfaceInfo *info, const gchar *attr_name) { gssize n_infos; gint i; n_infos = g_interface_info_get_n_properties (info); for (i = 0; i < n_infos; i++) { GIPropertyInfo *property_info; property_info = g_interface_info_get_property (info, i); g_assert (info != NULL); if (strcmp (attr_name, g_base_info_get_name (property_info)) == 0) { return property_info; } g_base_info_unref (property_info); } return NULL; } static GIPropertyInfo * _pygi_lookup_property_from_g_type (GType g_type, const gchar *attr_name) { GIPropertyInfo *ret = NULL; GIRepository *repository; GIBaseInfo *info; repository = g_irepository_get_default(); info = g_irepository_find_by_gtype (repository, g_type); if (info == NULL) return NULL; if (GI_IS_OBJECT_INFO (info)) ret = lookup_property_from_object_info ((GIObjectInfo *) info, attr_name); else if (GI_IS_INTERFACE_INFO (info)) ret = lookup_property_from_interface_info ((GIInterfaceInfo *) info, attr_name); g_base_info_unref (info); return ret; } PyObject * pygi_call_do_get_property (PyObject *instance, GParamSpec *pspec) { PyObject *py_pspec; PyObject *retval; py_pspec = pyg_param_spec_new (pspec); retval = PyObject_CallMethod (instance, "do_get_property", "O", py_pspec); Py_DECREF (py_pspec); return retval; } PyObject * pygi_get_property_value (PyGObject *instance, GParamSpec *pspec) { GIPropertyInfo *property_info = NULL; GValue value = { 0, }; PyObject *py_value = NULL; GType fundamental; gboolean handled; if (!(pspec->flags & G_PARAM_READABLE)) { PyErr_Format(PyExc_TypeError, "property %s is not readable", g_param_spec_get_name (pspec)); return NULL; } /* Fast path which calls the Python getter implementation directly. * See: https://bugzilla.gnome.org/show_bug.cgi?id=723872 */ if (pyg_gtype_is_custom (pspec->owner_type)) { return pygi_call_do_get_property ((PyObject *)instance, pspec); } Py_BEGIN_ALLOW_THREADS; g_value_init (&value, G_PARAM_SPEC_VALUE_TYPE (pspec)); g_object_get_property (instance->obj, pspec->name, &value); fundamental = G_TYPE_FUNDAMENTAL (G_VALUE_TYPE (&value)); Py_END_ALLOW_THREADS; /* Fast path basic types which don't need GI type info. */ py_value = pygi_value_to_py_basic_type (&value, fundamental, &handled); if (handled) { goto out; } /* Attempt to marshal through GI. * The owner_type of the pspec gives us the exact type that introduced the * property, even if it is a parent class of the instance in question. */ property_info = _pygi_lookup_property_from_g_type (pspec->owner_type, pspec->name); if (property_info) { GITypeInfo *type_info = NULL; gboolean free_array = FALSE; GIArgument arg = { 0, }; GITransfer transfer = GI_TRANSFER_NOTHING; type_info = g_property_info_get_type (property_info); arg = _pygi_argument_from_g_value (&value, type_info); /* Arrays are special cased, see note in _pygi_argument_to_array. */ if (g_type_info_get_tag (type_info) == GI_TYPE_TAG_ARRAY) { arg.v_pointer = _pygi_argument_to_array (&arg, NULL, NULL, NULL, type_info, &free_array); } else if (g_type_is_a (pspec->value_type, G_TYPE_BOXED)) { arg.v_pointer = g_value_dup_boxed (&value); transfer = GI_TRANSFER_EVERYTHING; } py_value = _pygi_argument_to_object (&arg, type_info, transfer); if (free_array) { g_array_free (arg.v_pointer, FALSE); } g_base_info_unref (type_info); g_base_info_unref (property_info); } /* Fallback to GValue marshalling. */ if (py_value == NULL) { py_value = pyg_param_gvalue_as_pyobject (&value, TRUE, pspec); } out: g_value_unset (&value); return py_value; } PyObject * pygi_get_property_value_by_name (PyGObject *self, gchar *param_name) { GParamSpec *pspec; pspec = g_object_class_find_property (G_OBJECT_GET_CLASS(self->obj), param_name); if (!pspec) { PyErr_Format (PyExc_TypeError, "object of type `%s' does not have property `%s'", g_type_name (G_OBJECT_TYPE (self->obj)), param_name); return NULL; } return pygi_get_property_value (self, pspec); } gint pygi_set_property_value (PyGObject *instance, GParamSpec *pspec, PyObject *py_value) { GIPropertyInfo *property_info = NULL; GITypeInfo *type_info = NULL; GITypeTag type_tag; GITransfer transfer; GValue value = { 0, }; GIArgument arg = { 0, }; gint ret_value = -1; /* The owner_type of the pspec gives us the exact type that introduced the * property, even if it is a parent class of the instance in question. */ property_info = _pygi_lookup_property_from_g_type (pspec->owner_type, pspec->name); if (property_info == NULL) goto out; if (! (pspec->flags & G_PARAM_WRITABLE)) goto out; type_info = g_property_info_get_type (property_info); transfer = g_property_info_get_ownership_transfer (property_info); arg = _pygi_argument_from_object (py_value, type_info, transfer); if (PyErr_Occurred()) goto out; g_value_init (&value, G_PARAM_SPEC_VALUE_TYPE (pspec)); /* FIXME: Lots of types still unhandled */ type_tag = g_type_info_get_tag (type_info); switch (type_tag) { case GI_TYPE_TAG_INTERFACE: { GIBaseInfo *info; GIInfoType info_type; GType type; info = g_type_info_get_interface (type_info); type = g_registered_type_info_get_g_type (info); info_type = g_base_info_get_type (info); g_base_info_unref (info); switch (info_type) { case GI_INFO_TYPE_ENUM: g_value_set_enum (&value, arg.v_int); break; case GI_INFO_TYPE_FLAGS: g_value_set_flags (&value, arg.v_uint); break; case GI_INFO_TYPE_INTERFACE: case GI_INFO_TYPE_OBJECT: g_value_set_object (&value, arg.v_pointer); break; case GI_INFO_TYPE_BOXED: case GI_INFO_TYPE_STRUCT: case GI_INFO_TYPE_UNION: if (g_type_is_a (type, G_TYPE_BOXED)) { g_value_set_boxed (&value, arg.v_pointer); } else if (g_type_is_a (type, G_TYPE_VARIANT)) { g_value_set_variant (&value, arg.v_pointer); } else { PyErr_Format (PyExc_NotImplementedError, "Setting properties of type '%s' is not implemented", g_type_name (type)); goto out; } break; default: PyErr_Format (PyExc_NotImplementedError, "Setting properties of type '%s' is not implemented", g_type_name (type)); goto out; } break; } case GI_TYPE_TAG_BOOLEAN: g_value_set_boolean (&value, arg.v_boolean); break; case GI_TYPE_TAG_INT8: g_value_set_schar (&value, arg.v_int8); break; case GI_TYPE_TAG_INT16: case GI_TYPE_TAG_INT32: if (G_VALUE_HOLDS_LONG (&value)) g_value_set_long (&value, arg.v_long); else g_value_set_int (&value, arg.v_int); break; case GI_TYPE_TAG_INT64: if (G_VALUE_HOLDS_LONG (&value)) g_value_set_long (&value, arg.v_long); else g_value_set_int64 (&value, arg.v_int64); break; case GI_TYPE_TAG_UINT8: g_value_set_uchar (&value, arg.v_uint8); break; case GI_TYPE_TAG_UINT16: case GI_TYPE_TAG_UINT32: if (G_VALUE_HOLDS_ULONG (&value)) g_value_set_ulong (&value, arg.v_ulong); else g_value_set_uint (&value, arg.v_uint); break; case GI_TYPE_TAG_UINT64: if (G_VALUE_HOLDS_ULONG (&value)) g_value_set_ulong (&value, arg.v_ulong); else g_value_set_uint64 (&value, arg.v_uint64); break; case GI_TYPE_TAG_FLOAT: g_value_set_float (&value, arg.v_float); break; case GI_TYPE_TAG_DOUBLE: g_value_set_double (&value, arg.v_double); break; case GI_TYPE_TAG_GTYPE: g_value_set_gtype (&value, arg.v_size); break; case GI_TYPE_TAG_UTF8: case GI_TYPE_TAG_FILENAME: g_value_set_string (&value, arg.v_string); break; case GI_TYPE_TAG_GHASH: g_value_set_boxed (&value, arg.v_pointer); break; case GI_TYPE_TAG_GLIST: if (G_VALUE_HOLDS_BOXED(&value)) g_value_set_boxed (&value, arg.v_pointer); else g_value_set_pointer (&value, arg.v_pointer); break; case GI_TYPE_TAG_ARRAY: { /* This is assumes GI_TYPE_TAG_ARRAY is always a GStrv * https://bugzilla.gnome.org/show_bug.cgi?id=688232 */ GArray *arg_items = (GArray*) arg.v_pointer; gchar** strings; guint i; if (arg_items == NULL) goto out; strings = g_new0 (char*, arg_items->len + 1); for (i = 0; i < arg_items->len; ++i) { strings[i] = g_array_index (arg_items, GIArgument, i).v_string; } strings[arg_items->len] = NULL; g_value_take_boxed (&value, strings); g_array_free (arg_items, TRUE); break; } default: PyErr_Format (PyExc_NotImplementedError, "Setting properties of type %s is not implemented", g_type_tag_to_string (g_type_info_get_tag (type_info))); goto out; } g_object_set_property (instance->obj, pspec->name, &value); g_value_unset (&value); ret_value = 0; out: if (property_info != NULL) g_base_info_unref (property_info); if (type_info != NULL) g_base_info_unref (type_info); return ret_value; }