]> git.0d.be Git - empathy.git/blobdiff - libempathy/empathy-chatroom-manager.c
Updated Czech translation
[empathy.git] / libempathy / empathy-chatroom-manager.c
index 184031ff7094516850f64b88ebc75db747077daa..3a117b18e85176c4eae84acc889db8a8aa98711d 100644 (file)
  */
 
 #include "config.h"
+#include "empathy-chatroom-manager.h"
 
-#include <string.h>
-#include <sys/types.h>
 #include <sys/stat.h>
+#include <tp-account-widgets/tpaw-utils.h>
+#include <telepathy-glib/telepathy-glib-dbus.h>
 
-#include <libxml/parser.h>
-#include <libxml/tree.h>
-
-#include <telepathy-glib/account-manager.h>
-#include <telepathy-glib/interfaces.h>
-#include <telepathy-glib/util.h>
-
-#include "empathy-tp-chat.h"
-#include "empathy-chatroom-manager.h"
+#include "empathy-client-factory.h"
 #include "empathy-utils.h"
 
 #define DEBUG_FLAG EMPATHY_DEBUG_OTHER
 #include "empathy-debug.h"
 
 #define CHATROOMS_XML_FILENAME "chatrooms.xml"
-#define CHATROOMS_DTD_FILENAME "empathy-chatroom-manager.dtd"
+#define CHATROOMS_DTD_RESOURCENAME "/org/gnome/Empathy/empathy-chatroom-manager.dtd"
 #define SAVE_TIMER 4
 
 static EmpathyChatroomManager *chatroom_manager_singleton = NULL;
 
+static void observe_channels_cb (TpSimpleObserver *observer,
+    TpAccount *account,
+    TpConnection *connection,
+    GList *channels,
+    TpChannelDispatchOperation *dispatch_operation,
+    GList *requests,
+    TpObserveChannelsContext *context,
+    gpointer user_data);
+
 #define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyChatroomManager)
 typedef struct
 {
@@ -57,6 +59,10 @@ typedef struct
   /* source id of the autosave timer */
   gint save_timer_id;
   gboolean ready;
+  GFileMonitor *monitor;
+  gboolean writing;
+
+  TpBaseClient *observer;
 } EmpathyChatroomManagerPriv;
 
 enum {
@@ -91,6 +97,8 @@ chatroom_manager_file_save (EmpathyChatroomManager *manager)
 
   priv = GET_PRIV (manager);
 
+  priv->writing = TRUE;
+
   doc = xmlNewDoc ((const xmlChar *) "1.0");
   root = xmlNewNode (NULL, (const xmlChar *) "chatrooms");
   xmlDocSetRootElement (doc, root);
@@ -133,6 +141,7 @@ chatroom_manager_file_save (EmpathyChatroomManager *manager)
 
   xmlMemoryDump ();
 
+  priv->writing = FALSE;
   return TRUE;
 }
 
@@ -175,7 +184,18 @@ add_chatroom (EmpathyChatroomManager *self,
 
   priv->chatrooms = g_list_prepend (priv->chatrooms, g_object_ref (chatroom));
 
-  g_signal_connect (chatroom, "notify",
+  /* Watch only those properties which are exported in the save file */
+  g_signal_connect (chatroom, "notify::name",
+      G_CALLBACK (chatroom_changed_cb), self);
+  g_signal_connect (chatroom, "notify::room",
+      G_CALLBACK (chatroom_changed_cb), self);
+  g_signal_connect (chatroom, "notify::account",
+      G_CALLBACK (chatroom_changed_cb), self);
+  g_signal_connect (chatroom, "notify::auto-connect",
+      G_CALLBACK (chatroom_changed_cb), self);
+  g_signal_connect (chatroom, "notify::always_urgent",
+      G_CALLBACK (chatroom_changed_cb), self);
+  g_signal_connect (chatroom, "notify::favorite",
       G_CALLBACK (chatroom_changed_cb), self);
 }
 
@@ -183,8 +203,7 @@ static void
 chatroom_manager_parse_chatroom (EmpathyChatroomManager *manager,
     xmlNodePtr node)
 {
-  EmpathyChatroomManagerPriv *priv;
-  EmpathyChatroom *chatroom;
+  EmpathyChatroom *chatroom = NULL;
   TpAccount *account;
   xmlNodePtr child;
   gchar *str;
@@ -193,8 +212,8 @@ chatroom_manager_parse_chatroom (EmpathyChatroomManager *manager,
   gchar *account_id;
   gboolean auto_connect;
   gboolean always_urgent;
-
-  priv = GET_PRIV (manager);
+  EmpathyClientFactory *factory;
+  GError *error = NULL;
 
   /* default values. */
   name = NULL;
@@ -243,10 +262,22 @@ chatroom_manager_parse_chatroom (EmpathyChatroomManager *manager,
       xmlFree (str);
     }
 
-  account = tp_account_manager_ensure_account (priv->account_manager,
-    account_id);
+  /* account has to be a valid Account object path */
+  if (!tp_dbus_check_valid_object_path (account_id, NULL) ||
+      !g_str_has_prefix (account_id, TP_ACCOUNT_OBJECT_PATH_BASE))
+    goto out;
+
+  factory = empathy_client_factory_dup ();
+
+  account = tp_simple_client_factory_ensure_account (
+          TP_SIMPLE_CLIENT_FACTORY (factory), account_id, NULL, &error);
+  g_object_unref (factory);
+
   if (account == NULL)
     {
+      DEBUG ("Failed to create account: %s", error->message);
+      g_error_free (error);
+
       g_free (name);
       g_free (room);
       g_free (account_id);
@@ -259,9 +290,11 @@ chatroom_manager_parse_chatroom (EmpathyChatroomManager *manager,
   add_chatroom (manager, chatroom);
   g_signal_emit (manager, signals[CHATROOM_ADDED], 0, chatroom);
 
+out:
   g_free (name);
   g_free (room);
   g_free (account_id);
+  tp_clear_object (&chatroom);
 }
 
 static gboolean
@@ -289,7 +322,7 @@ chatroom_manager_file_parse (EmpathyChatroomManager *manager,
       return FALSE;
     }
 
-  if (!empathy_xml_validate (doc, CHATROOMS_DTD_FILENAME))
+  if (!tpaw_xml_validate_from_resource (doc, CHATROOMS_DTD_RESOURCENAME))
     {
       g_warning ("Failed to validate file:'%s'", filename);
       xmlFreeDoc (doc);
@@ -326,8 +359,11 @@ chatroom_manager_get_all (EmpathyChatroomManager *manager)
       !chatroom_manager_file_parse (manager, priv->file))
     return FALSE;
 
-  priv->ready = TRUE;
-  g_object_notify (G_OBJECT (manager), "ready");
+  if (!priv->ready)
+    {
+      priv->ready = TRUE;
+      g_object_notify (G_OBJECT (manager), "ready");
+    }
 
   return TRUE;
 }
@@ -376,12 +412,52 @@ empathy_chatroom_manager_set_property (GObject *object,
     }
 }
 
+static void
+chatroom_manager_dispose (GObject *object)
+{
+  EmpathyChatroomManagerPriv *priv;
+
+  priv = GET_PRIV (object);
+
+  tp_clear_object (&priv->observer);
+  tp_clear_object (&priv->monitor);
+
+  (G_OBJECT_CLASS (empathy_chatroom_manager_parent_class)->dispose) (object);
+}
+
+static void
+clear_chatrooms (EmpathyChatroomManager *self)
+{
+  EmpathyChatroomManagerPriv *priv = GET_PRIV (self);
+  GList *l, *tmp;
+
+  tmp = priv->chatrooms;
+
+  /* Unreffing the chatroom may result in destroying the underlying
+   * EmpathyTpChat which will fire the invalidated signal and so make us
+   * re-call this function. We already set priv->chatrooms to NULL so we won't
+   * try to destroy twice the same objects. */
+  priv->chatrooms = NULL;
+
+  for (l = tmp; l != NULL; l = g_list_next (l))
+    {
+      EmpathyChatroom *chatroom = l->data;
+
+      g_signal_handlers_disconnect_by_func (chatroom, chatroom_changed_cb,
+          self);
+      g_signal_emit (self, signals[CHATROOM_REMOVED], 0, chatroom);
+
+      g_object_unref (chatroom);
+    }
+
+  g_list_free (tmp);
+}
+
 static void
 chatroom_manager_finalize (GObject *object)
 {
   EmpathyChatroomManager *self = EMPATHY_CHATROOM_MANAGER (object);
   EmpathyChatroomManagerPriv *priv;
-  GList *l;
 
   priv = GET_PRIV (object);
 
@@ -395,32 +471,47 @@ chatroom_manager_finalize (GObject *object)
       chatroom_manager_file_save (self);
     }
 
-  for (l = priv->chatrooms; l != NULL; l = g_list_next (l))
-    {
-      EmpathyChatroom *chatroom = l->data;
-
-      g_signal_handlers_disconnect_by_func (chatroom, chatroom_changed_cb,
-          self);
-
-      g_object_unref (chatroom);
-    }
+  clear_chatrooms (self);
 
-  g_list_free (priv->chatrooms);
   g_free (priv->file);
 
   (G_OBJECT_CLASS (empathy_chatroom_manager_parent_class)->finalize) (object);
 }
 
+static void
+file_changed_cb (GFileMonitor *monitor,
+    GFile *file,
+    GFile *other_file,
+    GFileMonitorEvent event_type,
+    gpointer user_data)
+{
+  EmpathyChatroomManager *self = user_data;
+  EmpathyChatroomManagerPriv *priv = GET_PRIV (self);
+
+  if (event_type != G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT)
+    return;
+
+  if (priv->writing)
+    return;
+
+  DEBUG ("chatrooms file changed; reloading list");
+
+  clear_chatrooms (self);
+  chatroom_manager_get_all (self);
+}
+
 static void
 account_manager_ready_cb (GObject *source_object,
     GAsyncResult *result,
     gpointer user_data)
 {
   EmpathyChatroomManager *self = EMPATHY_CHATROOM_MANAGER (user_data);
+  EmpathyChatroomManagerPriv *priv = GET_PRIV (self);
   TpAccountManager *manager = TP_ACCOUNT_MANAGER (source_object);
   GError *error = NULL;
+  GFile *file = NULL;
 
-  if (!tp_account_manager_prepare_finish (manager, result, &error))
+  if (!tp_proxy_prepare_finish (manager, result, &error))
     {
       DEBUG ("Failed to prepare account manager: %s", error->message);
       g_error_free (error);
@@ -429,7 +520,24 @@ account_manager_ready_cb (GObject *source_object,
 
   chatroom_manager_get_all (self);
 
+  /* Set up file monitor */
+  file = g_file_new_for_path (priv->file);
+
+  priv->monitor = g_file_monitor (file, 0, NULL, &error);
+  if (priv->monitor == NULL)
+    {
+      DEBUG ("Failed to create file monitor on %s: %s", priv->file,
+          error->message);
+
+      g_error_free (error);
+      goto out;
+    }
+
+  g_signal_connect (priv->monitor, "changed", G_CALLBACK (file_changed_cb),
+      self);
+
 out:
+  tp_clear_object (&file);
   g_object_unref (self);
 }
 
@@ -441,6 +549,7 @@ empathy_chatroom_manager_constructor (GType type,
   GObject *obj;
   EmpathyChatroomManager *self;
   EmpathyChatroomManagerPriv *priv;
+  GError *error = NULL;
 
   if (chatroom_manager_singleton != NULL)
     return g_object_ref (chatroom_manager_singleton);
@@ -459,7 +568,7 @@ empathy_chatroom_manager_constructor (GType type,
 
   priv->account_manager = tp_account_manager_dup ();
 
-  tp_account_manager_prepare_async (priv->account_manager, NULL,
+  tp_proxy_prepare_async (priv->account_manager, NULL,
       account_manager_ready_cb, g_object_ref (self));
 
   if (priv->file == NULL)
@@ -475,6 +584,24 @@ empathy_chatroom_manager_constructor (GType type,
       g_free (dir);
     }
 
+  /* Setup a room observer */
+  priv->observer = tp_simple_observer_new_with_am (priv->account_manager, TRUE,
+      "Empathy.ChatroomManager", TRUE, observe_channels_cb, self, NULL);
+
+  tp_base_client_take_observer_filter (priv->observer, tp_asv_new (
+      TP_PROP_CHANNEL_CHANNEL_TYPE, G_TYPE_STRING,
+        TP_IFACE_CHANNEL_TYPE_TEXT,
+      TP_PROP_CHANNEL_TARGET_HANDLE_TYPE, G_TYPE_UINT,
+        TP_HANDLE_TYPE_ROOM,
+      NULL));
+
+  if (!tp_base_client_register (priv->observer, &error))
+    {
+      g_critical ("Failed to register Observer: %s", error->message);
+
+      g_error_free (error);
+    }
+
   return obj;
 }
 
@@ -487,6 +614,7 @@ empathy_chatroom_manager_class_init (EmpathyChatroomManagerClass *klass)
   object_class->constructor = empathy_chatroom_manager_constructor;
   object_class->get_property = empathy_chatroom_manager_get_property;
   object_class->set_property = empathy_chatroom_manager_set_property;
+  object_class->dispose = chatroom_manager_dispose;
   object_class->finalize = chatroom_manager_finalize;
 
   param_spec = g_param_spec_string (
@@ -513,7 +641,7 @@ empathy_chatroom_manager_class_init (EmpathyChatroomManagerClass *klass)
       G_TYPE_FROM_CLASS (klass),
       G_SIGNAL_RUN_LAST,
       0, NULL, NULL,
-      g_cclosure_marshal_VOID__OBJECT,
+      g_cclosure_marshal_generic,
       G_TYPE_NONE,
       1, EMPATHY_TYPE_CHATROOM);
 
@@ -521,7 +649,7 @@ empathy_chatroom_manager_class_init (EmpathyChatroomManagerClass *klass)
       G_TYPE_FROM_CLASS (klass),
       G_SIGNAL_RUN_LAST,
       0, NULL, NULL,
-      g_cclosure_marshal_VOID__OBJECT,
+      g_cclosure_marshal_generic,
       G_TYPE_NONE,
       1, EMPATHY_TYPE_CHATROOM);
 
@@ -548,13 +676,9 @@ gboolean
 empathy_chatroom_manager_add (EmpathyChatroomManager *manager,
     EmpathyChatroom *chatroom)
 {
-  EmpathyChatroomManagerPriv *priv;
-
   g_return_val_if_fail (EMPATHY_IS_CHATROOM_MANAGER (manager), FALSE);
   g_return_val_if_fail (EMPATHY_IS_CHATROOM (chatroom), FALSE);
 
-  priv = GET_PRIV (manager);
-
   /* don't add more than once */
   if (!empathy_chatroom_manager_find (manager,
       empathy_chatroom_get_account (chatroom),
@@ -705,36 +829,11 @@ empathy_chatroom_manager_get_chatrooms (EmpathyChatroomManager *manager,
   return chatrooms;
 }
 
-guint
-empathy_chatroom_manager_get_count (EmpathyChatroomManager *manager,
-    TpAccount *account)
-{
-  EmpathyChatroomManagerPriv *priv;
-  GList *l;
-  guint count = 0;
-
-  g_return_val_if_fail (EMPATHY_IS_CHATROOM_MANAGER (manager), 0);
-
-  priv = GET_PRIV (manager);
-
-  if (!account)
-    return g_list_length (priv->chatrooms);
-
-  for (l = priv->chatrooms; l; l = l->next)
-    {
-      EmpathyChatroom *chatroom;
-
-      chatroom = l->data;
-
-      if (account == empathy_chatroom_get_account (chatroom))
-        count++;
-    }
-
-  return count;
-}
-
 static void
-chatroom_manager_chat_destroyed_cb (EmpathyTpChat *chat,
+chatroom_manager_chat_invalidated_cb (EmpathyTpChat *chat,
+  guint domain,
+  gint code,
+  gchar *message,
   gpointer manager)
 {
   EmpathyChatroomManagerPriv *priv = GET_PRIV (manager);
@@ -761,35 +860,48 @@ chatroom_manager_chat_destroyed_cb (EmpathyTpChat *chat,
     }
 }
 
-/* Called by EmpathyChatManager when we are handling a new group chat */
-void
-empathy_chatroom_manager_chat_handled (EmpathyChatroomManager *self,
-    EmpathyTpChat *chat,
-    TpAccount *account)
+static void
+observe_channels_cb (TpSimpleObserver *observer,
+    TpAccount *account,
+    TpConnection *connection,
+    GList *channels,
+    TpChannelDispatchOperation *dispatch_operation,
+    GList *requests,
+    TpObserveChannelsContext *context,
+    gpointer user_data)
 {
-  EmpathyChatroom *chatroom;
-  const gchar *roomname;
+  EmpathyChatroomManager *self = user_data;
+  GList *l;
 
-  roomname = empathy_tp_chat_get_id (chat);
+  for (l = channels; l != NULL; l = g_list_next (l))
+    {
+      EmpathyTpChat *tp_chat = l->data;
+      const gchar *roomname;
+      EmpathyChatroom *chatroom;
 
-  chatroom = empathy_chatroom_manager_find (self, account, roomname);
+      if (tp_proxy_get_invalidated ((TpChannel *) tp_chat) != NULL)
+        continue;
 
-  if (chatroom == NULL)
-    {
-      chatroom = empathy_chatroom_new_full (account, roomname, roomname,
-        FALSE);
-      empathy_chatroom_set_tp_chat (chatroom, chat);
-      empathy_chatroom_manager_add (self, chatroom);
-      g_object_unref (chatroom);
-    }
-  else
-    {
-        empathy_chatroom_set_tp_chat (chatroom, chat);
+      if (!EMPATHY_IS_TP_CHAT (tp_chat))
+        continue;
+
+      roomname = empathy_tp_chat_get_id (tp_chat);
+      chatroom = empathy_chatroom_manager_find (self, account, roomname);
+
+      if (chatroom == NULL)
+        {
+          chatroom = empathy_chatroom_new_full (account, roomname, roomname,
+            FALSE);
+          empathy_chatroom_manager_add (self, chatroom);
+          g_object_unref (chatroom);
+        }
+
+      empathy_chatroom_set_tp_chat (chatroom, tp_chat);
+
+      g_signal_connect (tp_chat, "invalidated",
+        G_CALLBACK (chatroom_manager_chat_invalidated_cb),
+        self);
     }
 
-  /* A TpChat is always destroyed as it only gets unreffed after the channel
-   * has been invalidated in the dispatcher..  */
-  g_signal_connect (chat, "destroy",
-    G_CALLBACK (chatroom_manager_chat_destroyed_cb),
-    self);
+  tp_observe_channels_context_accept (context);
 }