[Telepathy-commits] [telepathy-glib/master] Implement some more of the Message mixin

Simon McVittie simon.mcvittie at collabora.co.uk
Thu Dec 18 10:41:27 PST 2008


20080414171827-53eee-a5a8fbc5ebf84872043d0047c7c717e7e9ebd670.gz
---
 examples/cm/echo-message-parts/chan.c |  102 +++------
 examples/cm/echo-message-parts/chan.h |    5 +-
 telepathy-glib/message-mixin.c        |  375 ++++++++++++++++++++++++++++-----
 telepathy-glib/message-mixin.h        |   53 +++--
 4 files changed, 386 insertions(+), 149 deletions(-)

diff --git a/examples/cm/echo-message-parts/chan.c b/examples/cm/echo-message-parts/chan.c
index 90e7fbd..a415bdb 100644
--- a/examples/cm/echo-message-parts/chan.c
+++ b/examples/cm/echo-message-parts/chan.c
@@ -16,11 +16,11 @@
 #include <telepathy-glib/base-connection.h>
 #include <telepathy-glib/channel-iface.h>
 #include <telepathy-glib/dbus.h>
+#include <telepathy-glib/gtypes.h>
 #include <telepathy-glib/interfaces.h>
 #include <telepathy-glib/svc-channel.h>
 #include <telepathy-glib/svc-generic.h>
 
-static void text_iface_init (gpointer iface, gpointer data);
 static void channel_iface_init (gpointer iface, gpointer data);
 
 G_DEFINE_TYPE_WITH_CODE (ExampleEcho2Channel,
@@ -29,7 +29,10 @@ G_DEFINE_TYPE_WITH_CODE (ExampleEcho2Channel,
     G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_DBUS_PROPERTIES,
       tp_dbus_properties_mixin_iface_init);
     G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL, channel_iface_init);
-    G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_TYPE_TEXT, text_iface_init);
+    G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_TYPE_TEXT,
+      tp_message_mixin_text_iface_init);
+    G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_INTERFACE_MESSAGE_PARTS,
+      tp_message_mixin_message_parts_iface_init);
     G_IMPLEMENT_INTERFACE (TP_TYPE_CHANNEL_IFACE, NULL);
     G_IMPLEMENT_INTERFACE (TP_TYPE_EXPORTABLE_CHANNEL, NULL))
 
@@ -74,6 +77,31 @@ example_echo_2_channel_init (ExampleEcho2Channel *self)
       ExampleEcho2ChannelPrivate);
 }
 
+static gboolean
+send_message (GObject *object,
+              TpMessageMixinOutgoingMessage *message)
+{
+  ExampleEcho2Channel *self = EXAMPLE_ECHO_2_CHANNEL (object);
+  TpHandleRepoIface *contact_repo = tp_base_connection_get_handles
+      (self->priv->conn, TP_HANDLE_TYPE_CONTACT);
+  time_t timestamp = time (NULL);
+  TpChannelTextMessageType message_type = message->message_type;
+  GPtrArray *parts = g_boxed_copy (dbus_g_type_get_collection ("GPtrArray",
+        TP_HASH_TYPE_MESSAGE_PART), message->parts);
+
+  tp_handle_ref (contact_repo, self->priv->handle);
+
+  /* This consumes the data, so we mustn't free it */
+  tp_message_mixin_take_received (object, timestamp, self->priv->handle,
+      message_type, parts);
+
+  /* "OK, we've sent the message" (after calling this, message must not be
+   * dereferenced) */
+  tp_message_mixin_sent (object, message, "");
+
+  return TRUE;
+}
+
 static GObject *
 constructor (GType type,
              guint n_props,
@@ -95,14 +123,10 @@ constructor (GType type,
   bus = tp_get_bus ();
   dbus_g_connection_register_g_object (bus, self->priv->object_path, object);
 
-  tp_text_mixin_init (object, G_STRUCT_OFFSET (ExampleEcho2Channel, text),
+  tp_message_mixin_init (object, G_STRUCT_OFFSET (ExampleEcho2Channel, text),
       contact_repo);
 
-  tp_text_mixin_set_message_types (object,
-      TP_CHANNEL_TEXT_MESSAGE_TYPE_NORMAL,
-      TP_CHANNEL_TEXT_MESSAGE_TYPE_ACTION,
-      TP_CHANNEL_TEXT_MESSAGE_TYPE_NOTICE,
-      G_MAXUINT);
+  tp_message_mixin_implement_sending (object, send_message);
 
   return object;
 }
@@ -255,7 +279,7 @@ finalize (GObject *object)
 
   g_free (self->priv->object_path);
 
-  tp_text_mixin_finalize (object);
+  tp_message_mixin_finalize (object);
 
   ((GObjectClass *) example_echo_2_channel_parent_class)->finalize (object);
 }
@@ -344,7 +368,7 @@ example_echo_2_channel_class_init (ExampleEcho2ChannelClass *klass)
       G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
   g_object_class_install_property (object_class, PROP_REQUESTED, param_spec);
 
-  tp_text_mixin_class_init (object_class,
+  tp_message_mixin_class_init (object_class,
       G_STRUCT_OFFSET (ExampleEcho2ChannelClass, text_class));
 
   klass->dbus_properties_class.interfaces = prop_interfaces;
@@ -445,64 +469,6 @@ channel_iface_init (gpointer iface,
 #undef IMPLEMENT
 }
 
-static void
-text_send (TpSvcChannelTypeText *iface,
-           guint type,
-           const gchar *text,
-           DBusGMethodInvocation *context)
-{
-  ExampleEcho2Channel *self = EXAMPLE_ECHO_2_CHANNEL (iface);
-  time_t timestamp = time (NULL);
-  gchar *echo;
-  guint echo_type = type;
-
-  /* Tell the client that the message was sent successfully. If it's possible
-   * to tell whether a message has been delivered, you should delay emitting
-   * this signal until it's actually been successful, and emit SendError
-   * instead if there was an error; if you can't tell, emit Sent immediately,
-   * like this */
-  tp_svc_channel_type_text_emit_sent ((GObject *) self, timestamp, type, text);
-
-  /* Pretend that the remote contact has replied. Normally,
-   * you'd call tp_text_mixin_receive in response to network events */
-
-  switch (type)
-    {
-    case TP_CHANNEL_TEXT_MESSAGE_TYPE_NORMAL:
-      echo = g_strdup_printf ("You said: %s", text);
-      break;
-    case TP_CHANNEL_TEXT_MESSAGE_TYPE_ACTION:
-      echo = g_strdup_printf ("notices that the user %s", text);
-      break;
-    case TP_CHANNEL_TEXT_MESSAGE_TYPE_NOTICE:
-      echo = g_strdup_printf ("You sent a notice: %s", text);
-      break;
-    default:
-      echo = g_strdup_printf ("You sent some weird message type, %u: \"%s\"",
-          type, text);
-      echo_type = TP_CHANNEL_TEXT_MESSAGE_TYPE_NORMAL;
-    }
-
-  tp_text_mixin_receive ((GObject *) self, echo_type, self->priv->handle,
-      timestamp, echo);
-
-  g_free (echo);
-
-  tp_svc_channel_type_text_return_from_send (context);
-}
-
-static void
-text_iface_init (gpointer iface,
-                 gpointer data)
-{
-  TpSvcChannelTypeTextClass *klass = iface;
-
-  tp_text_mixin_iface_init (iface, data);
-#define IMPLEMENT(x) tp_svc_channel_type_text_implement_##x (klass, text_##x)
-  IMPLEMENT (send);
-#undef IMPLEMENT
-}
-
 /* FIXME: enable this when Destroyable is supported */
 #if 0
 static void
diff --git a/examples/cm/echo-message-parts/chan.h b/examples/cm/echo-message-parts/chan.h
index 1117d58..f79d3c5 100644
--- a/examples/cm/echo-message-parts/chan.h
+++ b/examples/cm/echo-message-parts/chan.h
@@ -14,7 +14,7 @@
 
 #include <glib-object.h>
 #include <telepathy-glib/base-connection.h>
-#include <telepathy-glib/text-mixin.h>
+#include <telepathy-glib/message-mixin.h>
 
 G_BEGIN_DECLS
 
@@ -43,12 +43,13 @@ GType example_echo_2_channel_get_type (void);
 struct _ExampleEcho2ChannelClass {
     GObjectClass parent_class;
     TpTextMixinClass text_class;
+    TpMessageMixinClass text_class;
     TpDBusPropertiesMixinClass dbus_properties_class;
 };
 
 struct _ExampleEcho2Channel {
     GObject parent;
-    TpTextMixin text;
+    TpMessageMixin text;
 
     ExampleEcho2ChannelPrivate *priv;
 };
diff --git a/telepathy-glib/message-mixin.c b/telepathy-glib/message-mixin.c
index 75199d4..61e5b01 100644
--- a/telepathy-glib/message-mixin.c
+++ b/telepathy-glib/message-mixin.c
@@ -47,6 +47,7 @@
 #include <dbus/dbus-glib.h>
 #include <string.h>
 
+#include <telepathy-glib/dbus.h>
 #include <telepathy-glib/enums.h>
 #include <telepathy-glib/errors.h>
 #include <telepathy-glib/gtypes.h>
@@ -75,15 +76,32 @@
 
 struct _TpMessageMixinPrivate
 {
+  TpMessageMixinSendImpl send_message;
+
   TpHandleRepoIface *contact_repo;
   guint recv_id;
-  gboolean message_lost;
 
   GQueue *pending;
 
   GArray *msg_types;
 };
 
+#define TP_MESSAGE_MIXIN_CLASS_OFFSET_QUARK \
+  (tp_message_mixin_class_get_offset_quark ())
+#define TP_MESSAGE_MIXIN_CLASS_OFFSET(o) \
+  (GPOINTER_TO_UINT (g_type_get_qdata (G_OBJECT_CLASS_TYPE (o), \
+                                       TP_MESSAGE_MIXIN_CLASS_OFFSET_QUARK)))
+#define TP_MESSAGE_MIXIN_CLASS(o) \
+  ((TpMessageMixinClass *) tp_mixin_offset_cast (o, \
+    TP_MESSAGE_MIXIN_CLASS_OFFSET (o)))
+
+#define TP_MESSAGE_MIXIN_OFFSET_QUARK (tp_message_mixin_get_offset_quark ())
+#define TP_MESSAGE_MIXIN_OFFSET(o) \
+  (GPOINTER_TO_UINT (g_type_get_qdata (G_OBJECT_TYPE (o), \
+                                       TP_MESSAGE_MIXIN_OFFSET_QUARK)))
+#define TP_MESSAGE_MIXIN(o) \
+  ((TpMessageMixin *) tp_mixin_offset_cast (o, TP_MESSAGE_MIXIN_OFFSET (o)))
+
 /**
  * tp_message_mixin_class_get_offset_quark:
  *
@@ -91,7 +109,7 @@ struct _TpMessageMixinPrivate
  *
  * Returns: the quark used for storing mixin offset on a GObjectClass
  */
-GQuark
+static GQuark
 tp_message_mixin_class_get_offset_quark (void)
 {
   static GQuark offset_quark = 0;
@@ -110,7 +128,7 @@ tp_message_mixin_class_get_offset_quark (void)
  *
  * Returns: the quark used for storing mixin offset on a GObject
  */
-GQuark
+static GQuark
 tp_message_mixin_get_offset_quark (void)
 {
   static GQuark offset_quark = 0;
@@ -154,20 +172,14 @@ tp_message_mixin_class_init (GObjectClass *obj_cls,
 }
 
 
-typedef enum {
-    PENDING_TEXT_MESSAGE,       /* A message with text/plain content */
-    PENDING_NON_TEXT_MESSAGE,   /* A message with no text/plain content */
-    PENDING_DELIVERY_REPORT     /* A delivery report */
-} PendingType;
-
-
 typedef struct {
     guint32 id;
-    PendingType pending_type;
-    TpHandle sender;                        /* 0 for delivery reports */
-    time_t timestamp;                       /* 0 for delivery reports */
-    TpChannelTextMessageType message_type;  /* 0 for delivery reports */
-    GPtrArray *content;                     /* has exactly one item for d.r. */
+    TpHandle sender;
+    time_t timestamp;
+    TpChannelTextMessageType message_type;
+    GPtrArray *content;
+    TpChannelTextMessageFlags old_flags;
+    gchar *old_text;
 } PendingItem;
 
 
@@ -205,39 +217,84 @@ pending_item_free (PendingItem *pending,
 }
 
 
-#if 0
-static PendingItem *
-pending_item_new (void)
+static inline const gchar *
+value_force_string (const GValue *value)
 {
-  PendingItem *pending = g_slice_new0 (PendingItem);
+  if (value == NULL || !G_VALUE_HOLDS_STRING (value))
+    return NULL;
 
-  pending->content = g_ptr_array_sized_new (1);
-  return pending;
+  return g_value_get_string (value);
+}
+
+
+static inline void
+nullify_hash (GHashTable **hash)
+{
+  if (*hash != NULL)
+    return;
+
+  g_hash_table_destroy (*hash);
+  *hash = NULL;
+}
+
+
+static void
+subtract_from_hash (gpointer key,
+                    gpointer value,
+                    gpointer user_data)
+{
+  g_hash_table_remove (user_data, key);
 }
-#endif
 
 
 static TpChannelTextMessageFlags
-pending_item_get_text (PendingItem *item,
-                       GString *buffer)
+parts_to_text (const GPtrArray *parts,
+               GString *buffer)
 {
   guint i;
   TpChannelTextMessageFlags flags = 0;
-
-  g_return_val_if_fail (item->pending_type != PENDING_TEXT_MESSAGE, 0);
-  g_return_val_if_fail (item->content != NULL, 0);
-
-  for (i = 0; i < item->content->len; i++)
+  /* Lazily created hash tables, used as a sets: keys are borrowed
+   * "alternative" string values from @parts, value == key. */
+  /* Alternative IDs for which we have already extracted an alternative */
+  GHashTable *alternatives_used = NULL;
+  /* Alternative IDs for which we expect to extract text, but have not yet;
+   * cleared if the flag Channel_Text_Message_Flag_Non_Text_Content is set.
+   * At the end, if this contains any item not in alternatives_used,
+   * Channel_Text_Message_Flag_Non_Text_Content must be set. */
+  GHashTable *alternatives_needed = NULL;
+
+  for (i = 0; i < parts->len; i++)
     {
-      GHashTable *part = g_ptr_array_index (item->content, i);
+      GHashTable *part = g_ptr_array_index (parts, i);
+      const gchar *type = value_force_string (g_hash_table_lookup (part,
+            "type"));
+      const gchar *alternative = value_force_string (g_hash_table_lookup (part,
+            "alternative"));
 
-      if (!tp_strdiff (g_hash_table_lookup (part, "type"), "text/plain"))
+      if (!tp_strdiff (type, "text/plain"))
         {
           GValue *value;
-          /* FIXME: strictly, we should skip all but the first in any set of
-           * alternatives... in practice, this is only likely to affect XMPP,
-           * where this will result in getting *all* the languages for a
-           * multilingual message, rather than just one of them */
+
+          if (alternative != NULL && alternative[0] != '\0')
+            {
+              if (alternatives_used == NULL)
+                {
+                  /* We can't have seen an alternative for this part yet.
+                   * However, we need to create the hash table now */
+                  alternatives_used = g_hash_table_new (g_str_hash,
+                      g_str_equal);
+                }
+              else if (g_hash_table_lookup (alternatives_used,
+                    alternative) != NULL)
+                {
+                  /* we've seen a "better" alternative for this part already.
+                   * Skip it */
+                  continue;
+                }
+
+              g_hash_table_insert (alternatives_used, (gpointer) alternative,
+                  (gpointer) alternative);
+            }
 
           value = g_hash_table_lookup (part, "content");
 
@@ -251,9 +308,51 @@ pending_item_get_text (PendingItem *item,
                   g_value_get_boolean (value))
                 flags |= TP_CHANNEL_TEXT_MESSAGE_FLAG_TRUNCATED;
             }
+          else
+            {
+              flags |= TP_CHANNEL_TEXT_MESSAGE_FLAG_NON_TEXT_CONTENT;
+              nullify_hash (&alternatives_needed);
+            }
         }
+      else if ((flags & TP_CHANNEL_TEXT_MESSAGE_FLAG_NON_TEXT_CONTENT) != 0)
+        {
+          if (alternative == NULL || alternative[0] == '\0')
+            {
+              /* This part can't possibly have a text alternative
+               * (attached image or something, perhaps) */
+              flags |= TP_CHANNEL_TEXT_MESSAGE_FLAG_NON_TEXT_CONTENT;
+              nullify_hash (&alternatives_needed);
+            }
+          else
+            {
+              /* This part might have a text alternative later */
+              if (alternatives_needed == NULL)
+                alternatives_needed = g_hash_table_new (g_str_hash,
+                    g_str_equal);
+
+              g_hash_table_insert (alternatives_needed, (gpointer) alternative,
+                  (gpointer) alternative);
+            }
+        }
+    }
+
+  if ((flags & TP_CHANNEL_TEXT_MESSAGE_FLAG_NON_TEXT_CONTENT) == 0 &&
+      alternatives_needed != NULL)
+    {
+      if (alternatives_used != NULL)
+        g_hash_table_foreach (alternatives_used, subtract_from_hash,
+            alternatives_needed);
+
+      if (g_hash_table_size (alternatives_needed) > 0)
+        flags |= TP_CHANNEL_TEXT_MESSAGE_FLAG_NON_TEXT_CONTENT;
     }
 
+  if (alternatives_needed != NULL)
+    g_hash_table_destroy (alternatives_needed);
+
+  if (alternatives_used != NULL)
+    g_hash_table_destroy (alternatives_used);
+
   return flags;
 }
 
@@ -295,8 +394,6 @@ tp_message_mixin_init (GObject *obj,
   mixin->priv->recv_id = 0;
   mixin->priv->msg_types = g_array_sized_new (FALSE, FALSE, sizeof (guint),
       NUM_TP_CHANNEL_TEXT_MESSAGE_TYPES);
-
-  mixin->priv->message_lost = FALSE;
 }
 
 static void
@@ -407,14 +504,8 @@ tp_message_mixin_list_pending_messages_async (TpSvcChannelTypeText *iface,
       guint flags;
       GString *text;
 
-      if (msg->pending_type != PENDING_TEXT_MESSAGE)
-        {
-          /* No text/plain part */
-          continue;
-        }
-
       text = g_string_new ("");
-      flags = pending_item_get_text (msg, text);
+      flags = parts_to_text (msg->content, text);
 
       g_value_init (&val, pending_type);
       g_value_take_boxed (&val,
@@ -443,14 +534,11 @@ tp_message_mixin_list_pending_messages_async (TpSvcChannelTypeText *iface,
           PendingItem *msg = cur->data;
           GList *next = cur->next;
 
-          if (msg->pending_type == PENDING_TEXT_MESSAGE)
-            {
-              /* FIXME: need a hook here to send acknowledgements out on the
-               * network if the protocol requires it */
+          /* FIXME: need a hook here to send acknowledgements out on the
+           * network if the protocol requires it */
 
-              g_queue_delete_link (mixin->priv->pending, cur);
-              pending_item_free (msg, mixin->priv->contact_repo);
-            }
+          g_queue_delete_link (mixin->priv->pending, cur);
+          pending_item_free (msg, mixin->priv->contact_repo);
 
           cur = next;
         }
@@ -481,22 +569,176 @@ static void
 tp_message_mixin_get_message_types_async (TpSvcChannelTypeText *iface,
                                           DBusGMethodInvocation *context)
 {
-  GError e = { TP_ERRORS, TP_ERROR_NOT_IMPLEMENTED, "Not implemented" };
+  TpMessageMixin *mixin = TP_MESSAGE_MIXIN (iface);
 
-  dbus_g_method_return_error (context, &e);
+  tp_svc_channel_type_text_return_from_get_message_types (context,
+      mixin->priv->msg_types);
 }
 
+
+/**
+ * tp_message_mixin_take_received:
+ * @object: a channel with this mixin
+ * @timestamp: the time the message was received (if 0, time (NULL) will be
+ *  used)
+ * @sender: an owned reference to the handle of the sender, which is stolen
+ *  by the message mixin
+ * @message_type: the type of message
+ * @content: the content of the message, which is stolen by the message mixin
+ *
+ * Receive a message into the pending messages queue, where it will stay
+ * until acknowledged, and emit the ReceivedMessage signal. Also emit the
+ * Received signal if appropriate.
+ *
+ * Note that the sender and content arguments are *not* copied, and the caller
+ * loses ownership of them (this is to avoid lengthy and unnecessary copying
+ * of the content).
+ */
+void
+tp_message_mixin_take_received (GObject *object,
+                                time_t timestamp,
+                                TpHandle sender,
+                                TpChannelTextMessageType message_type,
+                                GPtrArray *content)
+{
+  TpMessageMixin *mixin = TP_MESSAGE_MIXIN (object);
+  PendingItem *pending = g_slice_new0 (PendingItem);
+  GString *text;
+
+  if (timestamp == 0)
+    timestamp = time (NULL);
+
+  /* FIXME: we don't check for overflow, so in highly pathological cases we
+   * might end up with multiple messages with the same ID */
+  pending->id = mixin->priv->recv_id++;
+  pending->sender = sender;
+  pending->timestamp = timestamp;
+  pending->message_type = message_type;
+  pending->content = content;
+  text = g_string_new ("");
+  pending->old_flags = parts_to_text (content, text);
+  pending->old_text = g_string_free (text, FALSE);
+
+  /* Queue it */
+
+  g_queue_push_tail (mixin->priv->pending, pending);
+
+  /* Signal it to clients */
+
+  tp_svc_channel_type_text_emit_received (object,
+      pending->id, timestamp, sender, message_type, pending->old_flags,
+      pending->old_text);
+
+  tp_svc_channel_interface_message_parts_emit_message_received (object,
+      pending->id, timestamp, sender, message_type, content);
+}
+
+
+#if 0
+void
+tp_message_mixin_take_delivery_report (GObject *object,
+                                       GHashTable *report)
+{
+}
+#endif
+
+
+struct _TpMessageMixinOutgoingMessagePrivate {
+    DBusGMethodInvocation *context;
+    gboolean message_parts:1;
+};
+
+
+void
+tp_message_mixin_sent (GObject *object,
+                       TpMessageMixinOutgoingMessage *message,
+                       const gchar *token)
+{
+  TpMessageMixin *mixin = TP_MESSAGE_MIXIN (object);
+  time_t now = time (NULL);
+  GString *string;
+
+  g_return_if_fail (mixin != NULL);
+  g_return_if_fail (object != NULL);
+  g_return_if_fail (message != NULL);
+  g_return_if_fail (message != NULL);
+  g_return_if_fail (message->parts != NULL);
+  g_return_if_fail (message->priv != NULL);
+  g_return_if_fail (message->priv->context != NULL);
+
+  if (token == NULL)
+    token = "";
+
+  /* emit Sent and MessageSent */
+
+  tp_svc_channel_interface_message_parts_emit_message_sent (object,
+      message->message_type, message->parts, token);
+  string = g_string_new ("");
+  parts_to_text (message->parts, string);
+  tp_svc_channel_type_text_emit_sent (object, now, message->message_type,
+      g_string_free (string, FALSE));
+
+  /* return successfully */
+
+  if (message->priv->message_parts)
+    {
+      tp_svc_channel_interface_message_parts_return_from_send_message (
+          message->priv->context, token);
+    }
+  else
+    {
+      tp_svc_channel_type_text_return_from_send (
+          message->priv->context);
+    }
+
+  message->priv->context = NULL;
+}
+
+
 static void
 tp_message_mixin_send_async (TpSvcChannelTypeText *iface,
                              guint message_type,
                              const gchar *text,
                              DBusGMethodInvocation *context)
 {
-  GError e = { TP_ERRORS, TP_ERROR_NOT_IMPLEMENTED, "Not implemented" };
+  TpMessageMixin *mixin = TP_MESSAGE_MIXIN (iface);
+  GPtrArray *parts;
+  GHashTable *table;
+  GValue *value;
+  TpMessageMixinOutgoingMessage *message;
 
-  dbus_g_method_return_error (context, &e);
+  if (mixin->priv->send_message == NULL)
+    {
+      tp_dbus_g_method_return_not_implemented (context);
+      return;
+    }
+
+  parts = g_ptr_array_sized_new (1);
+  table = g_hash_table_new_full (g_str_hash, g_str_equal,
+      NULL, (GDestroyNotify) tp_g_value_slice_free);
+
+  value = tp_g_value_slice_new (G_TYPE_STRING);
+  g_value_set_string (value, text);
+  g_hash_table_insert (table, "content", value);
+
+  value = tp_g_value_slice_new (G_TYPE_STRING);
+  g_value_set_static_string (value, "text/plain");
+  g_hash_table_insert (table, "type", value);
+
+  g_ptr_array_add (parts, table);
+
+  message = g_slice_new0 (TpMessageMixinOutgoingMessage);
+  message->flags = 0;
+  message->message_type = message_type;
+  message->parts = parts;
+  message->priv = g_slice_new0 (TpMessageMixinOutgoingMessagePrivate);
+  message->priv->context = context;
+  message->priv->message_parts = FALSE;
+
+  mixin->priv->send_message ((GObject *) iface, message);
 }
 
+
 static void
 tp_message_mixin_send_message_async (TpSvcChannelInterfaceMessageParts *iface,
                                      guint message_type,
@@ -504,11 +746,30 @@ tp_message_mixin_send_message_async (TpSvcChannelInterfaceMessageParts *iface,
                                      guint flags,
                                      DBusGMethodInvocation *context)
 {
-  GError e = { TP_ERRORS, TP_ERROR_NOT_IMPLEMENTED, "Not implemented" };
+  TpMessageMixin *mixin = TP_MESSAGE_MIXIN (iface);
+  TpMessageMixinOutgoingMessage *message;
 
-  dbus_g_method_return_error (context, &e);
+  if (mixin->priv->send_message == NULL)
+    {
+      tp_dbus_g_method_return_not_implemented (context);
+      return;
+    }
+
+  message = g_slice_new0 (TpMessageMixinOutgoingMessage);
+  message->flags = flags;
+  message->message_type = message_type;
+  /* FIXME: fix codegen so we get a GType-generator for
+   * TP_ARRAY_TYPE_MESSAGE_PART */
+  message->parts = g_boxed_copy (dbus_g_type_get_collection ("GPtrArray",
+        TP_HASH_TYPE_MESSAGE_PART), parts);
+  message->priv = g_slice_new0 (TpMessageMixinOutgoingMessagePrivate);
+  message->priv->context = context;
+  message->priv->message_parts = TRUE;
+
+  mixin->priv->send_message ((GObject *) iface, message);
 }
 
+
 /**
  * tp_message_mixin_iface_init:
  * @g_iface: A pointer to the #TpSvcChannelTypeTextClass in an object class
diff --git a/telepathy-glib/message-mixin.h b/telepathy-glib/message-mixin.h
index 81bd7e9..11b01e7 100644
--- a/telepathy-glib/message-mixin.h
+++ b/telepathy-glib/message-mixin.h
@@ -42,35 +42,44 @@ struct _TpMessageMixin {
   TpMessageMixinPrivate *priv;
 };
 
-#define TP_MESSAGE_MIXIN_CLASS_OFFSET_QUARK \
-  (tp_message_mixin_class_get_offset_quark ())
-#define TP_MESSAGE_MIXIN_CLASS_OFFSET(o) \
-  (GPOINTER_TO_UINT (g_type_get_qdata (G_OBJECT_CLASS_TYPE (o), \
-                                       TP_MESSAGE_MIXIN_CLASS_OFFSET_QUARK)))
-#define TP_MESSAGE_MIXIN_CLASS(o) \
-  ((TpMessageMixinClass *) tp_mixin_offset_cast (o, \
-    TP_MESSAGE_MIXIN_CLASS_OFFSET (o)))
-
-#define TP_MESSAGE_MIXIN_OFFSET_QUARK (tp_message_mixin_get_offset_quark ())
-#define TP_MESSAGE_MIXIN_OFFSET(o) \
-  (GPOINTER_TO_UINT (g_type_get_qdata (G_OBJECT_TYPE (o), \
-                                       TP_MESSAGE_MIXIN_OFFSET_QUARK)))
-#define TP_MESSAGE_MIXIN(o) \
-  ((TpMessageMixin *) tp_mixin_offset_cast (o, TP_MESSAGE_MIXIN_OFFSET (o)))
-
-GQuark tp_message_mixin_class_get_offset_quark (void);
-GQuark tp_message_mixin_get_offset_quark (void);
 
-void tp_message_mixin_class_init (GObjectClass *obj_cls, gsize offset);
+/* Receiving */
+void tp_message_mixin_take_received (GObject *object,
+    time_t timestamp, TpHandle sender, TpChannelTextMessageType message_type,
+    GPtrArray *content);
 
-void tp_message_mixin_init (GObject *obj, gsize offset,
-    TpHandleRepoIface *contact_repo);
-void tp_message_mixin_finalize (GObject *obj);
 
+/* Sending */
+typedef struct _TpMessageMixinOutgoingMessagePrivate
+    TpMessageMixinOutgoingMessagePrivate;
+
+typedef struct _TpMessageMixinOutgoingMessage {
+    guint flags;
+    guint message_type;
+    GPtrArray *parts;
+    TpMessageMixinOutgoingMessagePrivate *priv;
+} TpMessageMixinOutgoingMessage;
+
+typedef gboolean (*TpMessageMixinSendImpl) (GObject *object,
+    TpMessageMixinOutgoingMessage *message);
+
+void tp_message_mixin_sent (GObject *object,
+    TpMessageMixinOutgoingMessage *message, const gchar *token);
+
+
+/* Initialization */
 void tp_message_mixin_text_iface_init (gpointer g_iface, gpointer iface_data);
 void tp_message_mixin_message_parts_iface_init (gpointer g_iface,
     gpointer iface_data);
 
+void tp_message_mixin_class_init (GObjectClass *obj_cls, gsize offset);
+
+void tp_message_mixin_init (GObject *obj, gsize offset,
+    TpHandleRepoIface *contact_repo);
+void tp_message_mixin_implement_sending (GObject *object,
+    TpMessageMixinSendImpl send);
+void tp_message_mixin_finalize (GObject *obj);
+
 G_END_DECLS
 
 #endif
-- 
1.5.6.5




More information about the Telepathy-commits mailing list