telepathygabble/src/properties-mixin.c
branchRCL_3
changeset 11 3404599e4dda
parent 9 46cc8e302e43
equal deleted inserted replaced
9:46cc8e302e43 11:3404599e4dda
     1 /*
       
     2  * properties-mixin.c - Source for GabblePropertiesMixin
       
     3  * Copyright (C) 2006 Collabora Ltd.
       
     4  * 
       
     5  *   @author Ole Andre Vadla Ravnaas <ole.andre.ravnaas@collabora.co.uk>
       
     6  *   @author Robert McQueen <robert.mcqueen@collabora.co.uk>
       
     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, write to the Free Software
       
    20  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
       
    21  */
       
    22 
       
    23 #include <dbus/dbus-glib.h>
       
    24 #include <stdio.h>
       
    25 #include <string.h>
       
    26 
       
    27 #define DEBUG_FLAG GABBLE_DEBUG_PROPERTIES
       
    28 
       
    29 #include "ansi.h"
       
    30 #include "debug.h"
       
    31 #include "properties-mixin.h"
       
    32 #include "properties-mixin-signals-marshal.h"
       
    33 #include "telepathy-errors.h"
       
    34 
       
    35 
       
    36 #ifdef EMULATOR
       
    37 #include "libgabble_wsd_solution.h"
       
    38 
       
    39 	GET_STATIC_VAR_FROM_TLS(offset_quark1,gabble_mixin,GQuark)
       
    40 	#define offset_quark1 (*GET_WSD_VAR_NAME(offset_quark1,gabble_mixin, s)())	
       
    41 	
       
    42 	GET_STATIC_VAR_FROM_TLS(offset_quark,gabble_mixin,GQuark)
       
    43 	#define offset_quark (*GET_WSD_VAR_NAME(offset_quark,gabble_mixin, s)())	
       
    44 	
       
    45 	
       
    46 #endif
       
    47 	
       
    48 struct _GabblePropertiesContext {
       
    49     GabblePropertiesMixinClass *mixin_cls;
       
    50     GabblePropertiesMixin *mixin;
       
    51 
       
    52     DBusGMethodInvocation *dbus_ctx;
       
    53     guint32 remaining;
       
    54     GValue **values;
       
    55 };
       
    56 
       
    57 struct _GabblePropertiesMixinPrivate {
       
    58     GObject *object;
       
    59     GabblePropertiesContext context;
       
    60 };
       
    61 
       
    62 /**
       
    63  * gabble_properties_mixin_class_get_offset_quark:
       
    64  *
       
    65  * Returns: the quark used for storing mixin offset on a GObjectClass
       
    66  */
       
    67 GQuark
       
    68 gabble_properties_mixin_class_get_offset_quark ()
       
    69 {
       
    70 #ifndef EMULATOR
       
    71   static GQuark offset_quark1= 0;
       
    72 #endif  
       
    73   if (!offset_quark1)
       
    74     offset_quark1 = g_quark_from_static_string("PropertiesMixinClassOffsetQuark");
       
    75   return offset_quark1;
       
    76 }
       
    77 
       
    78 /**
       
    79  * gabble_properties_mixin_get_offset_quark:
       
    80  *
       
    81  * Returns: the quark used for storing mixin offset on a GObject
       
    82  */
       
    83 GQuark
       
    84 gabble_properties_mixin_get_offset_quark ()
       
    85 {
       
    86 #ifndef EMULATOR
       
    87   static GQuark offset_quark = 0;
       
    88 #endif  
       
    89   if (!offset_quark)
       
    90     offset_quark = g_quark_from_static_string("PropertiesMixinOffsetQuark");
       
    91   return offset_quark;
       
    92 }
       
    93 
       
    94 void gabble_properties_mixin_class_init (GObjectClass *obj_cls,
       
    95                                          glong offset,
       
    96                                          const GabblePropertySignature *signatures,
       
    97                                          guint num_properties,
       
    98                                          GabblePropertiesSetFunc set_func)
       
    99 {
       
   100   GabblePropertiesMixinClass *mixin_cls;
       
   101 
       
   102   g_assert (G_IS_OBJECT_CLASS (obj_cls));
       
   103 
       
   104   g_type_set_qdata (G_OBJECT_CLASS_TYPE (obj_cls),
       
   105                     GABBLE_PROPERTIES_MIXIN_CLASS_OFFSET_QUARK,
       
   106                     GINT_TO_POINTER (offset));
       
   107 
       
   108   mixin_cls = GABBLE_PROPERTIES_MIXIN_CLASS (obj_cls);
       
   109 
       
   110   mixin_cls->signatures = signatures;
       
   111   mixin_cls->num_props = num_properties;
       
   112 
       
   113   mixin_cls->set_properties = set_func;
       
   114 
       
   115   mixin_cls->properties_changed_signal_id =
       
   116     g_signal_new ("properties-changed",
       
   117                   G_OBJECT_CLASS_TYPE (obj_cls),
       
   118                   G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
       
   119                   0,
       
   120                   NULL, NULL,
       
   121                   g_cclosure_marshal_VOID__BOXED,
       
   122                   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)))));
       
   123 
       
   124   mixin_cls->property_flags_changed_signal_id =
       
   125     g_signal_new ("property-flags-changed",
       
   126                   G_OBJECT_CLASS_TYPE (obj_cls),
       
   127                   G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
       
   128                   0,
       
   129                   NULL, NULL,
       
   130                   g_cclosure_marshal_VOID__BOXED,
       
   131                   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)))));
       
   132 }
       
   133 
       
   134 void gabble_properties_mixin_init (GObject *obj, glong offset)
       
   135 {
       
   136   GabblePropertiesMixinClass *mixin_cls;
       
   137   GabblePropertiesMixin *mixin;
       
   138   GabblePropertiesContext *ctx;
       
   139 
       
   140   g_assert (G_IS_OBJECT (obj));
       
   141 
       
   142   g_type_set_qdata (G_OBJECT_TYPE (obj),
       
   143                     GABBLE_PROPERTIES_MIXIN_OFFSET_QUARK,
       
   144                     GINT_TO_POINTER (offset));
       
   145 
       
   146   mixin = GABBLE_PROPERTIES_MIXIN (obj);
       
   147   mixin_cls = GABBLE_PROPERTIES_MIXIN_CLASS (G_OBJECT_GET_CLASS (obj));
       
   148 
       
   149   mixin->properties = g_new0 (GabbleProperty, mixin_cls->num_props);
       
   150 
       
   151   mixin->priv = g_new0 (GabblePropertiesMixinPrivate, 1);
       
   152   mixin->priv->object = obj;
       
   153 
       
   154   ctx = &mixin->priv->context;
       
   155   ctx->mixin_cls = mixin_cls;
       
   156   ctx->mixin = mixin;
       
   157   ctx->values = g_new0 (GValue *, mixin_cls->num_props);
       
   158 }
       
   159 
       
   160 void gabble_properties_mixin_finalize (GObject *obj)
       
   161 {
       
   162   GabblePropertiesMixin *mixin = GABBLE_PROPERTIES_MIXIN (obj);
       
   163   GabblePropertiesMixinClass *mixin_cls = GABBLE_PROPERTIES_MIXIN_CLASS (
       
   164                                             G_OBJECT_GET_CLASS (obj));
       
   165   GabblePropertiesContext *ctx = &mixin->priv->context;
       
   166   guint i;
       
   167 
       
   168   for (i = 0; i < mixin_cls->num_props; i++)
       
   169     {
       
   170       GabbleProperty *prop = &mixin->properties[i];
       
   171 
       
   172       if (prop->value)
       
   173         {
       
   174           g_value_unset (prop->value);
       
   175           g_free (prop->value);
       
   176         }
       
   177 
       
   178       if (ctx->values[i])
       
   179         {
       
   180           g_value_unset (ctx->values[i]);
       
   181         }
       
   182     }
       
   183 
       
   184   g_free (ctx->values);
       
   185 
       
   186   g_free (mixin->priv);
       
   187 
       
   188   g_free (mixin->properties);
       
   189 }
       
   190 
       
   191 gboolean
       
   192 gabble_properties_mixin_list_properties (GObject *obj, GPtrArray **ret, GError **error)
       
   193 {
       
   194   GabblePropertiesMixin *mixin = GABBLE_PROPERTIES_MIXIN (obj);
       
   195   GabblePropertiesMixinClass *mixin_cls = GABBLE_PROPERTIES_MIXIN_CLASS (
       
   196                                             G_OBJECT_GET_CLASS (obj));
       
   197   guint i;
       
   198 
       
   199   *ret = g_ptr_array_sized_new (mixin_cls->num_props);
       
   200 
       
   201   for (i = 0; i < mixin_cls->num_props; i++)
       
   202     {
       
   203       const GabblePropertySignature *sig = &mixin_cls->signatures[i];
       
   204       GabbleProperty *prop = &mixin->properties[i];
       
   205       const gchar *dbus_sig;
       
   206       GValue val = { 0, };
       
   207 
       
   208       switch (sig->type) {
       
   209         case G_TYPE_BOOLEAN:
       
   210           dbus_sig = "b";
       
   211           break;
       
   212         case G_TYPE_INT:
       
   213           dbus_sig = "i";
       
   214           break;
       
   215         case G_TYPE_UINT:
       
   216           dbus_sig = "u";
       
   217           break;
       
   218         case G_TYPE_STRING:
       
   219           dbus_sig = "s";
       
   220           break;
       
   221         default:
       
   222           g_assert_not_reached ();
       
   223           continue;
       
   224       };
       
   225 
       
   226       g_value_init (&val, TP_TYPE_PROPERTY_INFO_STRUCT);
       
   227       g_value_take_boxed (&val,
       
   228           dbus_g_type_specialized_construct (TP_TYPE_PROPERTY_INFO_STRUCT));
       
   229 
       
   230       dbus_g_type_struct_set (&val,
       
   231           0, i,
       
   232           1, sig->name,
       
   233           2, dbus_sig,
       
   234           3, prop->flags,
       
   235           G_MAXUINT);
       
   236 
       
   237       g_ptr_array_add (*ret, g_value_get_boxed (&val));
       
   238     }
       
   239 
       
   240   return TRUE;
       
   241 }
       
   242 
       
   243 gboolean
       
   244 gabble_properties_mixin_get_properties (GObject *obj, const GArray *properties, GPtrArray **ret, GError **error)
       
   245 {
       
   246   GabblePropertiesMixin *mixin = GABBLE_PROPERTIES_MIXIN (obj);
       
   247   GabblePropertiesMixinClass *mixin_cls = GABBLE_PROPERTIES_MIXIN_CLASS (
       
   248                                             G_OBJECT_GET_CLASS (obj));
       
   249   guint i;
       
   250 
       
   251   /* Check input property identifiers */
       
   252   for (i = 0; i < properties->len; i++)
       
   253     {
       
   254       guint prop_id = g_array_index (properties, guint, i);
       
   255 
       
   256       /* Valid? */
       
   257       if (prop_id >= mixin_cls->num_props)
       
   258         {
       
   259           g_set_error (error, TELEPATHY_ERRORS, InvalidArgument,
       
   260               "invalid property identifier %d", prop_id);
       
   261 
       
   262           return FALSE;
       
   263         }
       
   264 
       
   265       /* Permitted? */
       
   266       if (!gabble_properties_mixin_is_readable (obj, prop_id))
       
   267         {
       
   268           g_set_error (error, TELEPATHY_ERRORS, PermissionDenied,
       
   269               "permission denied for property identifier %d", prop_id);
       
   270 
       
   271           return FALSE;
       
   272         }
       
   273     }
       
   274 
       
   275   /* If we got this far, return the actual values */
       
   276   *ret = g_ptr_array_sized_new (properties->len);
       
   277 
       
   278   for (i = 0; i < properties->len; i++)
       
   279     {
       
   280       guint prop_id = g_array_index (properties, guint, i);
       
   281       GValue val_struct = { 0, };
       
   282 
       
   283       /* id/value struct */
       
   284       g_value_init (&val_struct, TP_TYPE_PROPERTY_VALUE_STRUCT);
       
   285       g_value_take_boxed (&val_struct,
       
   286           dbus_g_type_specialized_construct (TP_TYPE_PROPERTY_VALUE_STRUCT));
       
   287 
       
   288       dbus_g_type_struct_set (&val_struct,
       
   289           0, prop_id,
       
   290           1, mixin->properties[prop_id].value,
       
   291           G_MAXUINT);
       
   292 
       
   293       g_ptr_array_add (*ret, g_value_get_boxed (&val_struct));
       
   294     }
       
   295 
       
   296   return TRUE;
       
   297 }
       
   298 
       
   299 void
       
   300 gabble_properties_mixin_set_properties (GObject *obj,
       
   301                                         const GPtrArray *properties,
       
   302                                         DBusGMethodInvocation *context)
       
   303 {
       
   304   GabblePropertiesMixin *mixin = GABBLE_PROPERTIES_MIXIN (obj);
       
   305   GabblePropertiesMixinClass *mixin_cls = GABBLE_PROPERTIES_MIXIN_CLASS (
       
   306                                             G_OBJECT_GET_CLASS (obj));
       
   307   GabblePropertiesContext *ctx = &mixin->priv->context;
       
   308   GError *error = NULL;
       
   309   guint i;
       
   310 
       
   311   /* Is another SetProperties request already in progress? */
       
   312   if (ctx->dbus_ctx)
       
   313     {
       
   314       error = g_error_new (TELEPATHY_ERRORS, NotAvailable,
       
   315                            "A SetProperties request is already in progress");
       
   316       goto ERROR;
       
   317     }
       
   318 
       
   319   ctx->dbus_ctx = context;
       
   320   error = NULL;
       
   321 
       
   322   /* Check input property identifiers */
       
   323   for (i = 0; i < properties->len; i++)
       
   324     {
       
   325       GValue val_struct = { 0, };
       
   326       guint prop_id;
       
   327       GValue *prop_val;
       
   328 
       
   329       g_value_init (&val_struct, TP_TYPE_PROPERTY_VALUE_STRUCT);
       
   330       g_value_set_static_boxed (&val_struct, g_ptr_array_index (properties, i));
       
   331 
       
   332       dbus_g_type_struct_get (&val_struct,
       
   333           0, &prop_id,
       
   334           1, &prop_val,
       
   335           G_MAXUINT);
       
   336 
       
   337       /* Valid? */
       
   338       if (prop_id >= mixin_cls->num_props)
       
   339         {
       
   340           g_value_unset (prop_val);
       
   341 
       
   342           error = g_error_new (TELEPATHY_ERRORS, InvalidArgument,
       
   343                                "invalid property identifier %d", prop_id);
       
   344           goto ERROR;
       
   345         }
       
   346 
       
   347       /* Store the value in the context */
       
   348       ctx->remaining |= 1 << prop_id;
       
   349       ctx->values[prop_id] = prop_val;
       
   350 
       
   351       /* Permitted? */
       
   352       if (!gabble_properties_mixin_is_writable (obj, prop_id))
       
   353         {
       
   354           error = g_error_new (TELEPATHY_ERRORS, PermissionDenied,
       
   355                                "permission denied for property identifier %d", prop_id);
       
   356           goto ERROR;
       
   357         }
       
   358 
       
   359       /* Compatible type? */
       
   360       if (!g_value_type_compatible (G_VALUE_TYPE (prop_val),
       
   361                                     mixin_cls->signatures[prop_id].type))
       
   362         {
       
   363           error = g_error_new (TELEPATHY_ERRORS, NotAvailable,
       
   364                                "incompatible value type for property identifier %d",
       
   365                                prop_id);
       
   366           goto ERROR;
       
   367         }
       
   368     }
       
   369 
       
   370   if (mixin_cls->set_properties)
       
   371     {
       
   372       if (mixin_cls->set_properties (obj, ctx, &error))
       
   373         return;
       
   374     }
       
   375   else
       
   376     {
       
   377       gabble_properties_context_return (ctx, NULL);
       
   378       return;
       
   379     }
       
   380 
       
   381 ERROR:
       
   382   gabble_properties_context_return (ctx, error);
       
   383 }
       
   384 
       
   385 gboolean
       
   386 gabble_properties_mixin_has_property (GObject *obj, const gchar *name,
       
   387                                       guint *property)
       
   388 {
       
   389   GabblePropertiesMixinClass *mixin_cls = GABBLE_PROPERTIES_MIXIN_CLASS (
       
   390                                             G_OBJECT_GET_CLASS (obj));
       
   391   guint i;
       
   392 
       
   393   for (i = 0; i < mixin_cls->num_props; i++)
       
   394     {
       
   395       if (strcmp (mixin_cls->signatures[i].name, name) == 0)
       
   396         {
       
   397           if (property)
       
   398             *property = i;
       
   399 
       
   400           return TRUE;
       
   401         }
       
   402     }
       
   403 
       
   404   return FALSE;
       
   405 }
       
   406 
       
   407 gboolean
       
   408 gabble_properties_context_has (GabblePropertiesContext *ctx, guint property)
       
   409 {
       
   410   g_assert (property < ctx->mixin_cls->num_props);
       
   411 
       
   412   return (ctx->values[property] != NULL);
       
   413 }
       
   414 
       
   415 gboolean
       
   416 gabble_properties_context_has_other_than (GabblePropertiesContext *ctx, guint property)
       
   417 {
       
   418   g_assert (property < ctx->mixin_cls->num_props);
       
   419 
       
   420   return ((ctx->remaining & ~(1 << property)) != 0);
       
   421 }
       
   422 
       
   423 const GValue *
       
   424 gabble_properties_context_get (GabblePropertiesContext *ctx, guint property)
       
   425 {
       
   426   g_assert (property < ctx->mixin_cls->num_props);
       
   427 
       
   428   return ctx->values[property];
       
   429 }
       
   430 
       
   431 guint
       
   432 gabble_properties_context_get_value_count (GabblePropertiesContext *ctx)
       
   433 {
       
   434   guint i, n;
       
   435 
       
   436   n = 0;
       
   437   for (i = 0; i < ctx->mixin_cls->num_props; i++)
       
   438     {
       
   439       if (ctx->values[i])
       
   440         n++;
       
   441     }
       
   442 
       
   443   return n;
       
   444 }
       
   445 
       
   446 void
       
   447 gabble_properties_context_remove (GabblePropertiesContext *ctx, guint property)
       
   448 {
       
   449   g_assert (property < ctx->mixin_cls->num_props);
       
   450 
       
   451   ctx->remaining &= ~(1 << property);
       
   452 }
       
   453 
       
   454 void
       
   455 gabble_properties_context_return (GabblePropertiesContext *ctx, GError *error)
       
   456 {
       
   457   GObject *obj = ctx->mixin->priv->object;
       
   458   GArray *changed_props_val, *changed_props_flags;
       
   459   guint i;
       
   460 
       
   461   gabble_debug (DEBUG_FLAG, "%s", (error) ? "failure" : "success");
       
   462 
       
   463   changed_props_val = changed_props_flags = NULL;
       
   464 
       
   465   for (i = 0; i < ctx->mixin_cls->num_props; i++)
       
   466     {
       
   467       if (ctx->values[i])
       
   468         {
       
   469           if (!error)
       
   470             {
       
   471               gabble_properties_mixin_change_value (obj, i, ctx->values[i],
       
   472                                                     &changed_props_val);
       
   473 
       
   474               gabble_properties_mixin_change_flags (obj, i,
       
   475                   TP_PROPERTY_FLAG_READ, 0, &changed_props_flags);
       
   476             }
       
   477 
       
   478           g_value_unset (ctx->values[i]);
       
   479           ctx->values[i] = NULL;
       
   480         }
       
   481     }
       
   482 
       
   483   if (!error)
       
   484     {
       
   485       gabble_properties_mixin_emit_changed (obj, &changed_props_val);
       
   486       gabble_properties_mixin_emit_flags (obj, &changed_props_flags);
       
   487 
       
   488       dbus_g_method_return (ctx->dbus_ctx);
       
   489     }
       
   490   else
       
   491     {
       
   492       dbus_g_method_return_error (ctx->dbus_ctx, error);
       
   493       g_error_free (error);
       
   494     }
       
   495 
       
   496   ctx->dbus_ctx = NULL;
       
   497   ctx->remaining = 0;
       
   498 }
       
   499 
       
   500 gboolean
       
   501 gabble_properties_context_return_if_done (GabblePropertiesContext *ctx)
       
   502 {
       
   503   if (ctx->remaining == 0)
       
   504     {
       
   505       gabble_properties_context_return (ctx, NULL);
       
   506       return TRUE;
       
   507     }
       
   508 
       
   509   return FALSE;
       
   510 }
       
   511 
       
   512 #define RPTS_APPEND_FLAG_IF_SET(flag) \
       
   513   if (flags & flag) \
       
   514     { \
       
   515       if (i++ > 0) \
       
   516         g_string_append (str, "|"); \
       
   517       g_string_append (str, #flag + 17); \
       
   518     }
       
   519 
       
   520 static gchar *
       
   521 property_flags_to_string (TpPropertyFlags flags)
       
   522 {
       
   523   gint i = 0;
       
   524   GString *str;
       
   525 
       
   526   str = g_string_new ("[" ANSI_BOLD_OFF);
       
   527 
       
   528   RPTS_APPEND_FLAG_IF_SET (TP_PROPERTY_FLAG_READ);
       
   529   RPTS_APPEND_FLAG_IF_SET (TP_PROPERTY_FLAG_WRITE);
       
   530 
       
   531   g_string_append (str, ANSI_BOLD_ON "]");
       
   532 
       
   533   return g_string_free (str, FALSE);
       
   534 }
       
   535 
       
   536 static gboolean
       
   537 values_are_equal (const GValue *v1, const GValue *v2)
       
   538 {
       
   539   GType type = G_VALUE_TYPE (v1);
       
   540   const gchar *s1, *s2;
       
   541 
       
   542   switch (type) {
       
   543     case G_TYPE_BOOLEAN:
       
   544       return (g_value_get_boolean (v1) == g_value_get_boolean (v2));
       
   545 
       
   546     case G_TYPE_STRING:
       
   547       s1 = g_value_get_string (v1);
       
   548       s2 = g_value_get_string (v2);
       
   549 
       
   550       /* are they both NULL? */
       
   551       if (s1 == s2)
       
   552         return TRUE;
       
   553 
       
   554       /* is one of them NULL? */
       
   555       if (s1 == NULL || s2 == NULL)
       
   556         return FALSE;
       
   557 
       
   558       return (strcmp (s1, s2) == 0);
       
   559 
       
   560     case G_TYPE_UINT:
       
   561       return (g_value_get_uint (v1) == g_value_get_uint (v2));
       
   562 
       
   563     case G_TYPE_INT:
       
   564       return (g_value_get_int (v1) == g_value_get_int (v2));
       
   565   }
       
   566 
       
   567   return FALSE;
       
   568 }
       
   569 
       
   570 void
       
   571 gabble_properties_mixin_change_value (GObject *obj, guint prop_id,
       
   572                                       const GValue *new_value,
       
   573                                       GArray **props)
       
   574 {
       
   575   GabblePropertiesMixin *mixin = GABBLE_PROPERTIES_MIXIN (obj);
       
   576   GabblePropertiesMixinClass *mixin_cls = GABBLE_PROPERTIES_MIXIN_CLASS (
       
   577                                             G_OBJECT_GET_CLASS (obj));
       
   578   GabbleProperty *prop;
       
   579 
       
   580   g_assert (prop_id < mixin_cls->num_props);
       
   581 
       
   582   prop = &mixin->properties[prop_id];
       
   583 
       
   584   if (prop->value)
       
   585     {
       
   586       if (values_are_equal (prop->value, new_value))
       
   587         return;
       
   588     }
       
   589   else
       
   590     {
       
   591       prop->value = g_new0 (GValue, 1);
       
   592       g_value_init (prop->value, mixin_cls->signatures[prop_id].type);
       
   593     }
       
   594 
       
   595   g_value_copy (new_value, prop->value);
       
   596 
       
   597   if (props)
       
   598     {
       
   599       guint i;
       
   600 
       
   601       if (*props == NULL)
       
   602         {
       
   603           *props = g_array_sized_new (FALSE, FALSE, sizeof (guint),
       
   604                                       mixin_cls->num_props);
       
   605         }
       
   606 
       
   607       for (i = 0; i < (*props)->len; i++)
       
   608         {
       
   609           if (g_array_index (*props, guint, i) == prop_id)
       
   610             return;
       
   611         }
       
   612 
       
   613       g_array_append_val (*props, prop_id);
       
   614     }
       
   615   else
       
   616     {
       
   617       GArray *changed_props = g_array_sized_new (FALSE, FALSE,
       
   618                                                  sizeof (guint), 1);
       
   619       g_array_append_val (changed_props, prop_id);
       
   620 
       
   621       gabble_properties_mixin_emit_changed (obj, &changed_props);
       
   622     }
       
   623 }
       
   624 
       
   625 void
       
   626 gabble_properties_mixin_change_flags (GObject *obj, guint prop_id,
       
   627                                       TpPropertyFlags add,
       
   628                                       TpPropertyFlags remove,
       
   629                                       GArray **props)
       
   630 {
       
   631   GabblePropertiesMixin *mixin = GABBLE_PROPERTIES_MIXIN (obj);
       
   632   GabblePropertiesMixinClass *mixin_cls = GABBLE_PROPERTIES_MIXIN_CLASS (
       
   633                                             G_OBJECT_GET_CLASS (obj));
       
   634   GabbleProperty *prop;
       
   635   guint prev_flags;
       
   636 
       
   637   g_assert (prop_id < mixin_cls->num_props);
       
   638 
       
   639   prop = &mixin->properties[prop_id];
       
   640 
       
   641   prev_flags = prop->flags;
       
   642 
       
   643   prop->flags |= add;
       
   644   prop->flags &= ~remove;
       
   645 
       
   646   if (prop->flags == prev_flags)
       
   647     return;
       
   648 
       
   649   if (props)
       
   650     {
       
   651       guint i;
       
   652 
       
   653       if (*props == NULL)
       
   654         {
       
   655           *props = g_array_sized_new (FALSE, FALSE, sizeof (guint),
       
   656                                       mixin_cls->num_props);
       
   657         }
       
   658 
       
   659       for (i = 0; i < (*props)->len; i++)
       
   660         {
       
   661           if (g_array_index (*props, guint, i) == prop_id)
       
   662             return;
       
   663         }
       
   664 
       
   665       g_array_append_val (*props, prop_id);
       
   666     }
       
   667   else
       
   668     {
       
   669       GArray *changed_props = g_array_sized_new (FALSE, FALSE,
       
   670                                                  sizeof (guint), 1);
       
   671       g_array_append_val (changed_props, prop_id);
       
   672 
       
   673       gabble_properties_mixin_emit_flags (obj, &changed_props);
       
   674     }
       
   675 }
       
   676 
       
   677 void
       
   678 gabble_properties_mixin_emit_changed (GObject *obj, GArray **props)
       
   679 {
       
   680   GabblePropertiesMixin *mixin = GABBLE_PROPERTIES_MIXIN (obj);
       
   681   GabblePropertiesMixinClass *mixin_cls = GABBLE_PROPERTIES_MIXIN_CLASS (
       
   682                                             G_OBJECT_GET_CLASS (obj));
       
   683   GPtrArray *prop_arr;
       
   684   GValue prop_list = { 0, };
       
   685   guint i;
       
   686 
       
   687   if (*props == NULL)
       
   688     return;
       
   689 
       
   690   if ((*props)->len == 0)
       
   691     return;
       
   692 
       
   693   prop_arr = g_ptr_array_sized_new ((*props)->len);
       
   694 
       
   695   if (DEBUGGING)
       
   696     g_message (ANSI_BOLD_ON ANSI_FG_CYAN
       
   697             "%s: emitting properties changed for propert%s:\n",
       
   698             G_STRFUNC, ((*props)->len > 1) ? "ies" : "y");
       
   699 
       
   700   for (i = 0; i < (*props)->len; i++)
       
   701     {
       
   702       GValue prop_val = { 0, };
       
   703       guint prop_id = g_array_index (*props, guint, i);
       
   704 
       
   705       g_value_init (&prop_val, TP_TYPE_PROPERTY_VALUE_STRUCT);
       
   706       g_value_take_boxed (&prop_val,
       
   707           dbus_g_type_specialized_construct (TP_TYPE_PROPERTY_VALUE_STRUCT));
       
   708 
       
   709       dbus_g_type_struct_set (&prop_val,
       
   710           0, prop_id,
       
   711           1, mixin->properties[prop_id].value,
       
   712           G_MAXUINT);
       
   713 
       
   714       g_ptr_array_add (prop_arr, g_value_get_boxed (&prop_val));
       
   715 
       
   716       if (DEBUGGING)
       
   717         g_message ("  %s\n", mixin_cls->signatures[prop_id].name);
       
   718     }
       
   719 
       
   720   if (DEBUGGING)
       
   721     {
       
   722       g_message (ANSI_RESET);
       
   723       fflush (stdout);
       
   724     }
       
   725 
       
   726   g_signal_emit (obj, mixin_cls->properties_changed_signal_id, 0, prop_arr);
       
   727 
       
   728   g_value_init (&prop_list, TP_TYPE_PROPERTY_VALUE_LIST);
       
   729   g_value_take_boxed (&prop_list, prop_arr);
       
   730   g_value_unset (&prop_list);
       
   731 
       
   732   g_array_free (*props, TRUE);
       
   733   *props = NULL;
       
   734 }
       
   735 
       
   736 void
       
   737 gabble_properties_mixin_emit_flags (GObject *obj, GArray **props)
       
   738 {
       
   739   GabblePropertiesMixin *mixin = GABBLE_PROPERTIES_MIXIN (obj);
       
   740   GabblePropertiesMixinClass *mixin_cls = GABBLE_PROPERTIES_MIXIN_CLASS (
       
   741                                             G_OBJECT_GET_CLASS (obj));
       
   742   GPtrArray *prop_arr;
       
   743   GValue prop_list = { 0, };
       
   744   guint i;
       
   745 
       
   746   if (*props == NULL)
       
   747     return;
       
   748 
       
   749   if ((*props)->len == 0)
       
   750     return;
       
   751 
       
   752   prop_arr = g_ptr_array_sized_new ((*props)->len);
       
   753 
       
   754   if (DEBUGGING)
       
   755     g_message (ANSI_BOLD_ON ANSI_FG_WHITE
       
   756             "%s: emitting properties flags changed for propert%s:\n",
       
   757             G_STRFUNC, ((*props)->len > 1) ? "ies" : "y");
       
   758 
       
   759   for (i = 0; i < (*props)->len; i++)
       
   760     {
       
   761       GValue prop_val = { 0, };
       
   762       guint prop_id = g_array_index (*props, guint, i);
       
   763       guint prop_flags;
       
   764 
       
   765       prop_flags = mixin->properties[prop_id].flags;
       
   766 
       
   767       g_value_init (&prop_val, TP_TYPE_PROPERTY_FLAGS_STRUCT);
       
   768       g_value_take_boxed (&prop_val,
       
   769           dbus_g_type_specialized_construct (TP_TYPE_PROPERTY_FLAGS_STRUCT));
       
   770 
       
   771       dbus_g_type_struct_set (&prop_val,
       
   772           0, prop_id,
       
   773           1, prop_flags,
       
   774           G_MAXUINT);
       
   775 
       
   776       g_ptr_array_add (prop_arr, g_value_get_boxed (&prop_val));
       
   777 
       
   778       if (DEBUGGING)
       
   779         {
       
   780           gchar *str_flags = property_flags_to_string (prop_flags);
       
   781 
       
   782           g_message ("  %s's flags now: %s\n",
       
   783                   mixin_cls->signatures[prop_id].name, str_flags);
       
   784 
       
   785           g_free (str_flags);
       
   786         }
       
   787     }
       
   788 
       
   789   if (DEBUGGING)
       
   790     {
       
   791       g_message (ANSI_RESET);
       
   792       fflush (stdout);
       
   793     }
       
   794 
       
   795   g_signal_emit (obj, mixin_cls->property_flags_changed_signal_id, 0, prop_arr);
       
   796 
       
   797   g_value_init (&prop_list, TP_TYPE_PROPERTY_FLAGS_LIST);
       
   798   g_value_take_boxed (&prop_list, prop_arr);
       
   799   g_value_unset (&prop_list);
       
   800 
       
   801   g_array_free (*props, TRUE);
       
   802   *props = NULL;
       
   803 }
       
   804 
       
   805 gboolean
       
   806 gabble_properties_mixin_is_readable (GObject *obj, guint prop_id)
       
   807 {
       
   808   GabblePropertiesMixin *mixin = GABBLE_PROPERTIES_MIXIN (obj);
       
   809   GabblePropertiesMixinClass *mixin_cls = GABBLE_PROPERTIES_MIXIN_CLASS (
       
   810                                             G_OBJECT_GET_CLASS (obj));
       
   811 
       
   812   if (prop_id >= mixin_cls->num_props)
       
   813     return FALSE;
       
   814 
       
   815   return ((mixin->properties[prop_id].flags & TP_PROPERTY_FLAG_READ) != 0);
       
   816 }
       
   817 
       
   818 gboolean
       
   819 gabble_properties_mixin_is_writable (GObject *obj, guint prop_id)
       
   820 {
       
   821   GabblePropertiesMixin *mixin = GABBLE_PROPERTIES_MIXIN (obj);
       
   822   GabblePropertiesMixinClass *mixin_cls = GABBLE_PROPERTIES_MIXIN_CLASS (
       
   823                                             G_OBJECT_GET_CLASS (obj));
       
   824 
       
   825   if (prop_id >= mixin_cls->num_props)
       
   826     return FALSE;
       
   827 
       
   828   return ((mixin->properties[prop_id].flags & TP_PROPERTY_FLAG_WRITE) != 0);
       
   829 }
       
   830