diff -r d0f3a028347a -r 59927b2d3b75 telepathygabble/src/properties-mixin.c --- a/telepathygabble/src/properties-mixin.c Tue Feb 02 01:10:06 2010 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,830 +0,0 @@ -/* - * properties-mixin.c - Source for GabblePropertiesMixin - * Copyright (C) 2006 Collabora Ltd. - * - * @author Ole Andre Vadla Ravnaas - * @author Robert McQueen - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include -#include -#include - -#define DEBUG_FLAG GABBLE_DEBUG_PROPERTIES - -#include "ansi.h" -#include "debug.h" -#include "properties-mixin.h" -#include "properties-mixin-signals-marshal.h" -#include "telepathy-errors.h" - - -#ifdef EMULATOR -#include "libgabble_wsd_solution.h" - - GET_STATIC_VAR_FROM_TLS(offset_quark1,gabble_mixin,GQuark) - #define offset_quark1 (*GET_WSD_VAR_NAME(offset_quark1,gabble_mixin, s)()) - - GET_STATIC_VAR_FROM_TLS(offset_quark,gabble_mixin,GQuark) - #define offset_quark (*GET_WSD_VAR_NAME(offset_quark,gabble_mixin, s)()) - - -#endif - -struct _GabblePropertiesContext { - GabblePropertiesMixinClass *mixin_cls; - GabblePropertiesMixin *mixin; - - DBusGMethodInvocation *dbus_ctx; - guint32 remaining; - GValue **values; -}; - -struct _GabblePropertiesMixinPrivate { - GObject *object; - GabblePropertiesContext context; -}; - -/** - * gabble_properties_mixin_class_get_offset_quark: - * - * Returns: the quark used for storing mixin offset on a GObjectClass - */ -GQuark -gabble_properties_mixin_class_get_offset_quark () -{ -#ifndef EMULATOR - static GQuark offset_quark1= 0; -#endif - if (!offset_quark1) - offset_quark1 = g_quark_from_static_string("PropertiesMixinClassOffsetQuark"); - return offset_quark1; -} - -/** - * gabble_properties_mixin_get_offset_quark: - * - * Returns: the quark used for storing mixin offset on a GObject - */ -GQuark -gabble_properties_mixin_get_offset_quark () -{ -#ifndef EMULATOR - static GQuark offset_quark = 0; -#endif - if (!offset_quark) - offset_quark = g_quark_from_static_string("PropertiesMixinOffsetQuark"); - return offset_quark; -} - -void gabble_properties_mixin_class_init (GObjectClass *obj_cls, - glong offset, - const GabblePropertySignature *signatures, - guint num_properties, - GabblePropertiesSetFunc set_func) -{ - GabblePropertiesMixinClass *mixin_cls; - - g_assert (G_IS_OBJECT_CLASS (obj_cls)); - - g_type_set_qdata (G_OBJECT_CLASS_TYPE (obj_cls), - GABBLE_PROPERTIES_MIXIN_CLASS_OFFSET_QUARK, - GINT_TO_POINTER (offset)); - - mixin_cls = GABBLE_PROPERTIES_MIXIN_CLASS (obj_cls); - - mixin_cls->signatures = signatures; - mixin_cls->num_props = num_properties; - - mixin_cls->set_properties = set_func; - - mixin_cls->properties_changed_signal_id = - g_signal_new ("properties-changed", - G_OBJECT_CLASS_TYPE (obj_cls), - G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, - 0, - NULL, NULL, - g_cclosure_marshal_VOID__BOXED, - G_TYPE_NONE, 1, (dbus_g_type_get_collection ("GPtrArray", (dbus_g_type_get_struct ("GValueArray", G_TYPE_UINT, G_TYPE_VALUE, G_TYPE_INVALID))))); - - mixin_cls->property_flags_changed_signal_id = - g_signal_new ("property-flags-changed", - G_OBJECT_CLASS_TYPE (obj_cls), - G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, - 0, - NULL, NULL, - g_cclosure_marshal_VOID__BOXED, - G_TYPE_NONE, 1, (dbus_g_type_get_collection ("GPtrArray", (dbus_g_type_get_struct ("GValueArray", G_TYPE_UINT, G_TYPE_UINT, G_TYPE_INVALID))))); -} - -void gabble_properties_mixin_init (GObject *obj, glong offset) -{ - GabblePropertiesMixinClass *mixin_cls; - GabblePropertiesMixin *mixin; - GabblePropertiesContext *ctx; - - g_assert (G_IS_OBJECT (obj)); - - g_type_set_qdata (G_OBJECT_TYPE (obj), - GABBLE_PROPERTIES_MIXIN_OFFSET_QUARK, - GINT_TO_POINTER (offset)); - - mixin = GABBLE_PROPERTIES_MIXIN (obj); - mixin_cls = GABBLE_PROPERTIES_MIXIN_CLASS (G_OBJECT_GET_CLASS (obj)); - - mixin->properties = g_new0 (GabbleProperty, mixin_cls->num_props); - - mixin->priv = g_new0 (GabblePropertiesMixinPrivate, 1); - mixin->priv->object = obj; - - ctx = &mixin->priv->context; - ctx->mixin_cls = mixin_cls; - ctx->mixin = mixin; - ctx->values = g_new0 (GValue *, mixin_cls->num_props); -} - -void gabble_properties_mixin_finalize (GObject *obj) -{ - GabblePropertiesMixin *mixin = GABBLE_PROPERTIES_MIXIN (obj); - GabblePropertiesMixinClass *mixin_cls = GABBLE_PROPERTIES_MIXIN_CLASS ( - G_OBJECT_GET_CLASS (obj)); - GabblePropertiesContext *ctx = &mixin->priv->context; - guint i; - - for (i = 0; i < mixin_cls->num_props; i++) - { - GabbleProperty *prop = &mixin->properties[i]; - - if (prop->value) - { - g_value_unset (prop->value); - g_free (prop->value); - } - - if (ctx->values[i]) - { - g_value_unset (ctx->values[i]); - } - } - - g_free (ctx->values); - - g_free (mixin->priv); - - g_free (mixin->properties); -} - -gboolean -gabble_properties_mixin_list_properties (GObject *obj, GPtrArray **ret, GError **error) -{ - GabblePropertiesMixin *mixin = GABBLE_PROPERTIES_MIXIN (obj); - GabblePropertiesMixinClass *mixin_cls = GABBLE_PROPERTIES_MIXIN_CLASS ( - G_OBJECT_GET_CLASS (obj)); - guint i; - - *ret = g_ptr_array_sized_new (mixin_cls->num_props); - - for (i = 0; i < mixin_cls->num_props; i++) - { - const GabblePropertySignature *sig = &mixin_cls->signatures[i]; - GabbleProperty *prop = &mixin->properties[i]; - const gchar *dbus_sig; - GValue val = { 0, }; - - switch (sig->type) { - case G_TYPE_BOOLEAN: - dbus_sig = "b"; - break; - case G_TYPE_INT: - dbus_sig = "i"; - break; - case G_TYPE_UINT: - dbus_sig = "u"; - break; - case G_TYPE_STRING: - dbus_sig = "s"; - break; - default: - g_assert_not_reached (); - continue; - }; - - g_value_init (&val, TP_TYPE_PROPERTY_INFO_STRUCT); - g_value_take_boxed (&val, - dbus_g_type_specialized_construct (TP_TYPE_PROPERTY_INFO_STRUCT)); - - dbus_g_type_struct_set (&val, - 0, i, - 1, sig->name, - 2, dbus_sig, - 3, prop->flags, - G_MAXUINT); - - g_ptr_array_add (*ret, g_value_get_boxed (&val)); - } - - return TRUE; -} - -gboolean -gabble_properties_mixin_get_properties (GObject *obj, const GArray *properties, GPtrArray **ret, GError **error) -{ - GabblePropertiesMixin *mixin = GABBLE_PROPERTIES_MIXIN (obj); - GabblePropertiesMixinClass *mixin_cls = GABBLE_PROPERTIES_MIXIN_CLASS ( - G_OBJECT_GET_CLASS (obj)); - guint i; - - /* Check input property identifiers */ - for (i = 0; i < properties->len; i++) - { - guint prop_id = g_array_index (properties, guint, i); - - /* Valid? */ - if (prop_id >= mixin_cls->num_props) - { - g_set_error (error, TELEPATHY_ERRORS, InvalidArgument, - "invalid property identifier %d", prop_id); - - return FALSE; - } - - /* Permitted? */ - if (!gabble_properties_mixin_is_readable (obj, prop_id)) - { - g_set_error (error, TELEPATHY_ERRORS, PermissionDenied, - "permission denied for property identifier %d", prop_id); - - return FALSE; - } - } - - /* If we got this far, return the actual values */ - *ret = g_ptr_array_sized_new (properties->len); - - for (i = 0; i < properties->len; i++) - { - guint prop_id = g_array_index (properties, guint, i); - GValue val_struct = { 0, }; - - /* id/value struct */ - g_value_init (&val_struct, TP_TYPE_PROPERTY_VALUE_STRUCT); - g_value_take_boxed (&val_struct, - dbus_g_type_specialized_construct (TP_TYPE_PROPERTY_VALUE_STRUCT)); - - dbus_g_type_struct_set (&val_struct, - 0, prop_id, - 1, mixin->properties[prop_id].value, - G_MAXUINT); - - g_ptr_array_add (*ret, g_value_get_boxed (&val_struct)); - } - - return TRUE; -} - -void -gabble_properties_mixin_set_properties (GObject *obj, - const GPtrArray *properties, - DBusGMethodInvocation *context) -{ - GabblePropertiesMixin *mixin = GABBLE_PROPERTIES_MIXIN (obj); - GabblePropertiesMixinClass *mixin_cls = GABBLE_PROPERTIES_MIXIN_CLASS ( - G_OBJECT_GET_CLASS (obj)); - GabblePropertiesContext *ctx = &mixin->priv->context; - GError *error = NULL; - guint i; - - /* Is another SetProperties request already in progress? */ - if (ctx->dbus_ctx) - { - error = g_error_new (TELEPATHY_ERRORS, NotAvailable, - "A SetProperties request is already in progress"); - goto ERROR; - } - - ctx->dbus_ctx = context; - error = NULL; - - /* Check input property identifiers */ - for (i = 0; i < properties->len; i++) - { - GValue val_struct = { 0, }; - guint prop_id; - GValue *prop_val; - - g_value_init (&val_struct, TP_TYPE_PROPERTY_VALUE_STRUCT); - g_value_set_static_boxed (&val_struct, g_ptr_array_index (properties, i)); - - dbus_g_type_struct_get (&val_struct, - 0, &prop_id, - 1, &prop_val, - G_MAXUINT); - - /* Valid? */ - if (prop_id >= mixin_cls->num_props) - { - g_value_unset (prop_val); - - error = g_error_new (TELEPATHY_ERRORS, InvalidArgument, - "invalid property identifier %d", prop_id); - goto ERROR; - } - - /* Store the value in the context */ - ctx->remaining |= 1 << prop_id; - ctx->values[prop_id] = prop_val; - - /* Permitted? */ - if (!gabble_properties_mixin_is_writable (obj, prop_id)) - { - error = g_error_new (TELEPATHY_ERRORS, PermissionDenied, - "permission denied for property identifier %d", prop_id); - goto ERROR; - } - - /* Compatible type? */ - if (!g_value_type_compatible (G_VALUE_TYPE (prop_val), - mixin_cls->signatures[prop_id].type)) - { - error = g_error_new (TELEPATHY_ERRORS, NotAvailable, - "incompatible value type for property identifier %d", - prop_id); - goto ERROR; - } - } - - if (mixin_cls->set_properties) - { - if (mixin_cls->set_properties (obj, ctx, &error)) - return; - } - else - { - gabble_properties_context_return (ctx, NULL); - return; - } - -ERROR: - gabble_properties_context_return (ctx, error); -} - -gboolean -gabble_properties_mixin_has_property (GObject *obj, const gchar *name, - guint *property) -{ - GabblePropertiesMixinClass *mixin_cls = GABBLE_PROPERTIES_MIXIN_CLASS ( - G_OBJECT_GET_CLASS (obj)); - guint i; - - for (i = 0; i < mixin_cls->num_props; i++) - { - if (strcmp (mixin_cls->signatures[i].name, name) == 0) - { - if (property) - *property = i; - - return TRUE; - } - } - - return FALSE; -} - -gboolean -gabble_properties_context_has (GabblePropertiesContext *ctx, guint property) -{ - g_assert (property < ctx->mixin_cls->num_props); - - return (ctx->values[property] != NULL); -} - -gboolean -gabble_properties_context_has_other_than (GabblePropertiesContext *ctx, guint property) -{ - g_assert (property < ctx->mixin_cls->num_props); - - return ((ctx->remaining & ~(1 << property)) != 0); -} - -const GValue * -gabble_properties_context_get (GabblePropertiesContext *ctx, guint property) -{ - g_assert (property < ctx->mixin_cls->num_props); - - return ctx->values[property]; -} - -guint -gabble_properties_context_get_value_count (GabblePropertiesContext *ctx) -{ - guint i, n; - - n = 0; - for (i = 0; i < ctx->mixin_cls->num_props; i++) - { - if (ctx->values[i]) - n++; - } - - return n; -} - -void -gabble_properties_context_remove (GabblePropertiesContext *ctx, guint property) -{ - g_assert (property < ctx->mixin_cls->num_props); - - ctx->remaining &= ~(1 << property); -} - -void -gabble_properties_context_return (GabblePropertiesContext *ctx, GError *error) -{ - GObject *obj = ctx->mixin->priv->object; - GArray *changed_props_val, *changed_props_flags; - guint i; - - gabble_debug (DEBUG_FLAG, "%s", (error) ? "failure" : "success"); - - changed_props_val = changed_props_flags = NULL; - - for (i = 0; i < ctx->mixin_cls->num_props; i++) - { - if (ctx->values[i]) - { - if (!error) - { - gabble_properties_mixin_change_value (obj, i, ctx->values[i], - &changed_props_val); - - gabble_properties_mixin_change_flags (obj, i, - TP_PROPERTY_FLAG_READ, 0, &changed_props_flags); - } - - g_value_unset (ctx->values[i]); - ctx->values[i] = NULL; - } - } - - if (!error) - { - gabble_properties_mixin_emit_changed (obj, &changed_props_val); - gabble_properties_mixin_emit_flags (obj, &changed_props_flags); - - dbus_g_method_return (ctx->dbus_ctx); - } - else - { - dbus_g_method_return_error (ctx->dbus_ctx, error); - g_error_free (error); - } - - ctx->dbus_ctx = NULL; - ctx->remaining = 0; -} - -gboolean -gabble_properties_context_return_if_done (GabblePropertiesContext *ctx) -{ - if (ctx->remaining == 0) - { - gabble_properties_context_return (ctx, NULL); - return TRUE; - } - - return FALSE; -} - -#define RPTS_APPEND_FLAG_IF_SET(flag) \ - if (flags & flag) \ - { \ - if (i++ > 0) \ - g_string_append (str, "|"); \ - g_string_append (str, #flag + 17); \ - } - -static gchar * -property_flags_to_string (TpPropertyFlags flags) -{ - gint i = 0; - GString *str; - - str = g_string_new ("[" ANSI_BOLD_OFF); - - RPTS_APPEND_FLAG_IF_SET (TP_PROPERTY_FLAG_READ); - RPTS_APPEND_FLAG_IF_SET (TP_PROPERTY_FLAG_WRITE); - - g_string_append (str, ANSI_BOLD_ON "]"); - - return g_string_free (str, FALSE); -} - -static gboolean -values_are_equal (const GValue *v1, const GValue *v2) -{ - GType type = G_VALUE_TYPE (v1); - const gchar *s1, *s2; - - switch (type) { - case G_TYPE_BOOLEAN: - return (g_value_get_boolean (v1) == g_value_get_boolean (v2)); - - case G_TYPE_STRING: - s1 = g_value_get_string (v1); - s2 = g_value_get_string (v2); - - /* are they both NULL? */ - if (s1 == s2) - return TRUE; - - /* is one of them NULL? */ - if (s1 == NULL || s2 == NULL) - return FALSE; - - return (strcmp (s1, s2) == 0); - - case G_TYPE_UINT: - return (g_value_get_uint (v1) == g_value_get_uint (v2)); - - case G_TYPE_INT: - return (g_value_get_int (v1) == g_value_get_int (v2)); - } - - return FALSE; -} - -void -gabble_properties_mixin_change_value (GObject *obj, guint prop_id, - const GValue *new_value, - GArray **props) -{ - GabblePropertiesMixin *mixin = GABBLE_PROPERTIES_MIXIN (obj); - GabblePropertiesMixinClass *mixin_cls = GABBLE_PROPERTIES_MIXIN_CLASS ( - G_OBJECT_GET_CLASS (obj)); - GabbleProperty *prop; - - g_assert (prop_id < mixin_cls->num_props); - - prop = &mixin->properties[prop_id]; - - if (prop->value) - { - if (values_are_equal (prop->value, new_value)) - return; - } - else - { - prop->value = g_new0 (GValue, 1); - g_value_init (prop->value, mixin_cls->signatures[prop_id].type); - } - - g_value_copy (new_value, prop->value); - - if (props) - { - guint i; - - if (*props == NULL) - { - *props = g_array_sized_new (FALSE, FALSE, sizeof (guint), - mixin_cls->num_props); - } - - for (i = 0; i < (*props)->len; i++) - { - if (g_array_index (*props, guint, i) == prop_id) - return; - } - - g_array_append_val (*props, prop_id); - } - else - { - GArray *changed_props = g_array_sized_new (FALSE, FALSE, - sizeof (guint), 1); - g_array_append_val (changed_props, prop_id); - - gabble_properties_mixin_emit_changed (obj, &changed_props); - } -} - -void -gabble_properties_mixin_change_flags (GObject *obj, guint prop_id, - TpPropertyFlags add, - TpPropertyFlags remove, - GArray **props) -{ - GabblePropertiesMixin *mixin = GABBLE_PROPERTIES_MIXIN (obj); - GabblePropertiesMixinClass *mixin_cls = GABBLE_PROPERTIES_MIXIN_CLASS ( - G_OBJECT_GET_CLASS (obj)); - GabbleProperty *prop; - guint prev_flags; - - g_assert (prop_id < mixin_cls->num_props); - - prop = &mixin->properties[prop_id]; - - prev_flags = prop->flags; - - prop->flags |= add; - prop->flags &= ~remove; - - if (prop->flags == prev_flags) - return; - - if (props) - { - guint i; - - if (*props == NULL) - { - *props = g_array_sized_new (FALSE, FALSE, sizeof (guint), - mixin_cls->num_props); - } - - for (i = 0; i < (*props)->len; i++) - { - if (g_array_index (*props, guint, i) == prop_id) - return; - } - - g_array_append_val (*props, prop_id); - } - else - { - GArray *changed_props = g_array_sized_new (FALSE, FALSE, - sizeof (guint), 1); - g_array_append_val (changed_props, prop_id); - - gabble_properties_mixin_emit_flags (obj, &changed_props); - } -} - -void -gabble_properties_mixin_emit_changed (GObject *obj, GArray **props) -{ - GabblePropertiesMixin *mixin = GABBLE_PROPERTIES_MIXIN (obj); - GabblePropertiesMixinClass *mixin_cls = GABBLE_PROPERTIES_MIXIN_CLASS ( - G_OBJECT_GET_CLASS (obj)); - GPtrArray *prop_arr; - GValue prop_list = { 0, }; - guint i; - - if (*props == NULL) - return; - - if ((*props)->len == 0) - return; - - prop_arr = g_ptr_array_sized_new ((*props)->len); - - if (DEBUGGING) - g_message (ANSI_BOLD_ON ANSI_FG_CYAN - "%s: emitting properties changed for propert%s:\n", - G_STRFUNC, ((*props)->len > 1) ? "ies" : "y"); - - for (i = 0; i < (*props)->len; i++) - { - GValue prop_val = { 0, }; - guint prop_id = g_array_index (*props, guint, i); - - g_value_init (&prop_val, TP_TYPE_PROPERTY_VALUE_STRUCT); - g_value_take_boxed (&prop_val, - dbus_g_type_specialized_construct (TP_TYPE_PROPERTY_VALUE_STRUCT)); - - dbus_g_type_struct_set (&prop_val, - 0, prop_id, - 1, mixin->properties[prop_id].value, - G_MAXUINT); - - g_ptr_array_add (prop_arr, g_value_get_boxed (&prop_val)); - - if (DEBUGGING) - g_message (" %s\n", mixin_cls->signatures[prop_id].name); - } - - if (DEBUGGING) - { - g_message (ANSI_RESET); - fflush (stdout); - } - - g_signal_emit (obj, mixin_cls->properties_changed_signal_id, 0, prop_arr); - - g_value_init (&prop_list, TP_TYPE_PROPERTY_VALUE_LIST); - g_value_take_boxed (&prop_list, prop_arr); - g_value_unset (&prop_list); - - g_array_free (*props, TRUE); - *props = NULL; -} - -void -gabble_properties_mixin_emit_flags (GObject *obj, GArray **props) -{ - GabblePropertiesMixin *mixin = GABBLE_PROPERTIES_MIXIN (obj); - GabblePropertiesMixinClass *mixin_cls = GABBLE_PROPERTIES_MIXIN_CLASS ( - G_OBJECT_GET_CLASS (obj)); - GPtrArray *prop_arr; - GValue prop_list = { 0, }; - guint i; - - if (*props == NULL) - return; - - if ((*props)->len == 0) - return; - - prop_arr = g_ptr_array_sized_new ((*props)->len); - - if (DEBUGGING) - g_message (ANSI_BOLD_ON ANSI_FG_WHITE - "%s: emitting properties flags changed for propert%s:\n", - G_STRFUNC, ((*props)->len > 1) ? "ies" : "y"); - - for (i = 0; i < (*props)->len; i++) - { - GValue prop_val = { 0, }; - guint prop_id = g_array_index (*props, guint, i); - guint prop_flags; - - prop_flags = mixin->properties[prop_id].flags; - - g_value_init (&prop_val, TP_TYPE_PROPERTY_FLAGS_STRUCT); - g_value_take_boxed (&prop_val, - dbus_g_type_specialized_construct (TP_TYPE_PROPERTY_FLAGS_STRUCT)); - - dbus_g_type_struct_set (&prop_val, - 0, prop_id, - 1, prop_flags, - G_MAXUINT); - - g_ptr_array_add (prop_arr, g_value_get_boxed (&prop_val)); - - if (DEBUGGING) - { - gchar *str_flags = property_flags_to_string (prop_flags); - - g_message (" %s's flags now: %s\n", - mixin_cls->signatures[prop_id].name, str_flags); - - g_free (str_flags); - } - } - - if (DEBUGGING) - { - g_message (ANSI_RESET); - fflush (stdout); - } - - g_signal_emit (obj, mixin_cls->property_flags_changed_signal_id, 0, prop_arr); - - g_value_init (&prop_list, TP_TYPE_PROPERTY_FLAGS_LIST); - g_value_take_boxed (&prop_list, prop_arr); - g_value_unset (&prop_list); - - g_array_free (*props, TRUE); - *props = NULL; -} - -gboolean -gabble_properties_mixin_is_readable (GObject *obj, guint prop_id) -{ - GabblePropertiesMixin *mixin = GABBLE_PROPERTIES_MIXIN (obj); - GabblePropertiesMixinClass *mixin_cls = GABBLE_PROPERTIES_MIXIN_CLASS ( - G_OBJECT_GET_CLASS (obj)); - - if (prop_id >= mixin_cls->num_props) - return FALSE; - - return ((mixin->properties[prop_id].flags & TP_PROPERTY_FLAG_READ) != 0); -} - -gboolean -gabble_properties_mixin_is_writable (GObject *obj, guint prop_id) -{ - GabblePropertiesMixin *mixin = GABBLE_PROPERTIES_MIXIN (obj); - GabblePropertiesMixinClass *mixin_cls = GABBLE_PROPERTIES_MIXIN_CLASS ( - G_OBJECT_GET_CLASS (obj)); - - if (prop_id >= mixin_cls->num_props) - return FALSE; - - return ((mixin->properties[prop_id].flags & TP_PROPERTY_FLAG_WRITE) != 0); -} -