]> git.0d.be Git - empathy.git/blobdiff - libempathy-gtk/empathy-chat.c
Updatre python binding
[empathy.git] / libempathy-gtk / empathy-chat.c
index 30ec5d53cc017676287f443190d66b1d9f31dab0..3494b02fd3ea2874e0ec82bab956039f445bb321 100644 (file)
@@ -1,7 +1,7 @@
 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
 /*
  * Copyright (C) 2002-2007 Imendio AB
- * Copyright (C) 2007 Collabora Ltd.
+ * Copyright (C) 2007-2008 Collabora Ltd.
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License as
 #include <glib/gi18n.h>
 #include <gtk/gtk.h>
 
+#include <libmissioncontrol/mission-control.h>
+
 #include <libempathy/empathy-contact-manager.h>
 #include <libempathy/empathy-log-manager.h>
 #include <libempathy/empathy-debug.h>
 #include <libempathy/empathy-utils.h>
-#include <libempathy/empathy-conf.h>
-#include <libempathy/empathy-marshal.h>
 
 #include "empathy-chat.h"
 #include "empathy-chat-window.h"
 #include "empathy-geometry.h"
+#include "empathy-conf.h"
 #include "empathy-preferences.h"
 #include "empathy-spell.h"
 #include "empathy-spell-dialog.h"
 #include "empathy-ui-utils.h"
+#include "empathy-gtk-marshal.h"
 
 #define GET_PRIV(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), EMPATHY_TYPE_CHAT, EmpathyChatPriv))
 
@@ -65,7 +67,9 @@
 struct _EmpathyChatPriv {
        EmpathyLogManager     *log_manager;
        EmpathyTpChat         *tp_chat;
-       EmpathyChatWindow      *window;
+       EmpathyChatWindow     *window;
+       McAccount             *account;
+       MissionControl        *mc;
        guint                  composing_stop_timeout_id;
        gboolean               sensitive;
        gchar                 *id;
@@ -75,6 +79,8 @@ struct _EmpathyChatPriv {
        guint                  scroll_idle_id;
        gboolean               first_tp_chat;
        time_t                 last_log_timestamp;
+       gboolean               is_first_char;
+       guint                  block_events_timeout_id;
        /* Used to automatically shrink a window that has temporarily
         * grown due to long input. 
         */
@@ -92,60 +98,60 @@ typedef struct {
        GtkTextIter  end;
 } EmpathyChatSpell;
 
-static void             empathy_chat_class_init           (EmpathyChatClass              *klass);
-static void             empathy_chat_init                 (EmpathyChat                   *chat);
-static void             chat_finalize                     (GObject                       *object);
-static void             chat_destroy_cb                   (EmpathyTpChat                 *tp_chat,
-                                                          EmpathyChat                   *chat);
-static void             chat_send                         (EmpathyChat                   *chat,
-                                                          const gchar                   *msg);
-static void             chat_input_text_view_send         (EmpathyChat                   *chat);
-static void             chat_message_received_cb          (EmpathyTpChat                 *tp_chat,
-                                                          EmpathyMessage                *message,
-                                                          EmpathyChat                   *chat);
-static void             chat_send_error_cb                (EmpathyTpChat                 *tp_chat,
-                                                          EmpathyMessage                *message,
-                                                          TelepathyChannelTextSendError  error_code,
-                                                          EmpathyChat                   *chat);
-void                    chat_sent_message_add             (EmpathyChat                   *chat,
-                                                          const gchar                   *str);
-const gchar *           chat_sent_message_get_next        (EmpathyChat                   *chat);
-const gchar *           chat_sent_message_get_last        (EmpathyChat                   *chat);
-static gboolean         chat_input_key_press_event_cb     (GtkWidget                     *widget,
-                                                          GdkEventKey                   *event,
-                                                          EmpathyChat                   *chat);
-static void             chat_input_text_buffer_changed_cb (GtkTextBuffer                 *buffer,
-                                                          EmpathyChat                   *chat);
-static gboolean         chat_text_view_focus_in_event_cb  (GtkWidget                     *widget,
-                                                          GdkEvent                      *event,
-                                                          EmpathyChat                   *chat);
-static void             chat_text_view_scroll_hide_cb     (GtkWidget                     *widget,
-                                                          EmpathyChat                   *chat);
-static void             chat_text_view_size_allocate_cb   (GtkWidget                     *widget,
-                                                          GtkAllocation                 *allocation,
-                                                          EmpathyChat                   *chat);
-static void             chat_text_view_realize_cb         (GtkWidget                     *widget,
-                                                          EmpathyChat                   *chat);
-static void             chat_text_populate_popup_cb       (GtkTextView                   *view,
-                                                          GtkMenu                       *menu,
-                                                          EmpathyChat                   *chat);
-static void             chat_text_check_word_spelling_cb  (GtkMenuItem                   *menuitem,
-                                                          EmpathyChatSpell              *chat_spell);
-static EmpathyChatSpell *chat_spell_new                   (EmpathyChat                   *chat,
-                                                          const gchar                   *word,
-                                                          GtkTextIter                    start,
-                                                          GtkTextIter                    end);
-static void             chat_spell_free                   (EmpathyChatSpell              *chat_spell);
-static void             chat_composing_start              (EmpathyChat                   *chat);
-static void             chat_composing_stop               (EmpathyChat                   *chat);
-static void             chat_composing_remove_timeout     (EmpathyChat                   *chat);
-static gboolean         chat_composing_stop_timeout_cb    (EmpathyChat                   *chat);
-static void             chat_state_changed_cb             (EmpathyTpChat                 *tp_chat,
-                                                          EmpathyContact                *contact,
-                                                          TelepathyChannelChatState      state,
-                                                          EmpathyChat                   *chat);
-static void             chat_add_logs                     (EmpathyChat                   *chat);
-static gboolean         chat_scroll_down_idle_func        (EmpathyChat                   *chat);
+static void             empathy_chat_class_init           (EmpathyChatClass       *klass);
+static void             empathy_chat_init                 (EmpathyChat            *chat);
+static void             chat_finalize                     (GObject                *object);
+static void             chat_destroy_cb                   (EmpathyTpChat          *tp_chat,
+                                                          EmpathyChat            *chat);
+static void             chat_send                         (EmpathyChat            *chat,
+                                                          const gchar            *msg);
+static void             chat_input_text_view_send         (EmpathyChat            *chat);
+static void             chat_message_received_cb          (EmpathyTpChat          *tp_chat,
+                                                          EmpathyMessage         *message,
+                                                          EmpathyChat            *chat);
+static void             chat_send_error_cb                (EmpathyTpChat          *tp_chat,
+                                                          EmpathyMessage         *message,
+                                                          TpChannelTextSendError  error_code,
+                                                          EmpathyChat            *chat);
+static void             chat_sent_message_add             (EmpathyChat            *chat,
+                                                          const gchar            *str);
+static const gchar *    chat_sent_message_get_next        (EmpathyChat            *chat);
+static const gchar *    chat_sent_message_get_last        (EmpathyChat            *chat);
+static gboolean         chat_input_key_press_event_cb     (GtkWidget              *widget,
+                                                          GdkEventKey            *event,
+                                                          EmpathyChat            *chat);
+static void             chat_input_text_buffer_changed_cb (GtkTextBuffer          *buffer,
+                                                          EmpathyChat            *chat);
+static gboolean         chat_text_view_focus_in_event_cb  (GtkWidget              *widget,
+                                                          GdkEvent               *event,
+                                                          EmpathyChat            *chat);
+static void             chat_text_view_scroll_hide_cb     (GtkWidget              *widget,
+                                                          EmpathyChat            *chat);
+static void             chat_text_view_size_allocate_cb   (GtkWidget              *widget,
+                                                          GtkAllocation          *allocation,
+                                                          EmpathyChat            *chat);
+static void             chat_text_view_realize_cb         (GtkWidget              *widget,
+                                                          EmpathyChat            *chat);
+static void             chat_text_populate_popup_cb       (GtkTextView            *view,
+                                                          GtkMenu                *menu,
+                                                          EmpathyChat            *chat);
+static void             chat_text_check_word_spelling_cb  (GtkMenuItem            *menuitem,
+                                                          EmpathyChatSpell       *chat_spell);
+static EmpathyChatSpell *chat_spell_new                   (EmpathyChat            *chat,
+                                                          const gchar            *word,
+                                                          GtkTextIter             start,
+                                                          GtkTextIter             end);
+static void             chat_spell_free                   (EmpathyChatSpell       *chat_spell);
+static void             chat_composing_start              (EmpathyChat            *chat);
+static void             chat_composing_stop               (EmpathyChat            *chat);
+static void             chat_composing_remove_timeout     (EmpathyChat            *chat);
+static gboolean         chat_composing_stop_timeout_cb    (EmpathyChat            *chat);
+static void             chat_state_changed_cb             (EmpathyTpChat          *tp_chat,
+                                                          EmpathyContact         *contact,
+                                                          TpChannelChatState      state,
+                                                          EmpathyChat            *chat);
+static void             chat_add_logs                     (EmpathyChat            *chat);
+static gboolean         chat_scroll_down_idle_func        (EmpathyChat            *chat);
 
 enum {
        COMPOSING,
@@ -155,10 +161,89 @@ enum {
        LAST_SIGNAL
 };
 
+enum {
+       PROP_0,
+       PROP_TP_CHAT
+};
+
 static guint signals[LAST_SIGNAL] = { 0 };
 
 G_DEFINE_TYPE (EmpathyChat, empathy_chat, G_TYPE_OBJECT);
 
+static void
+chat_get_property (GObject    *object,
+                  guint       param_id,
+                  GValue     *value,
+                  GParamSpec *pspec)
+{
+       EmpathyChatPriv *priv = GET_PRIV (object);
+
+       switch (param_id) {
+       case PROP_TP_CHAT:
+               g_value_set_object (value, priv->tp_chat);
+               break;
+       default:
+               G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+               break;
+       };
+}
+
+static void
+chat_set_property (GObject      *object,
+                  guint         param_id,
+                  const GValue *value,
+                  GParamSpec   *pspec)
+{
+       EmpathyChat *chat = EMPATHY_CHAT (object);
+
+       switch (param_id) {
+       case PROP_TP_CHAT:
+               empathy_chat_set_tp_chat (chat,
+                                         EMPATHY_TP_CHAT (g_value_get_object (value)));
+               break;
+       default:
+               G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+               break;
+       };
+}
+
+static void
+chat_status_changed_cb (MissionControl           *mc,
+                       TpConnectionStatus        status,
+                       McPresence                presence,
+                       TpConnectionStatusReason  reason,
+                       const gchar              *unique_name,
+                       EmpathyChat              *chat)
+{
+       EmpathyChatPriv *priv = GET_PRIV (chat);
+       McAccount       *account;
+
+       account = mc_account_lookup (unique_name);
+
+       if (status == TP_CONNECTION_STATUS_CONNECTED && !priv->tp_chat &&
+           empathy_account_equal (account, priv->account)) {
+               TpHandleType handle_type;
+
+               empathy_debug (DEBUG_DOMAIN,
+                              "Account reconnected, request a new Text channel");
+
+               if (empathy_chat_is_group_chat (chat)) {
+                       handle_type = TP_HANDLE_TYPE_ROOM;
+               } else {
+                       handle_type = TP_HANDLE_TYPE_CONTACT;
+               }
+
+               mission_control_request_channel_with_string_handle (mc,
+                                                                   priv->account,
+                                                                   TP_IFACE_CHANNEL_TYPE_TEXT,
+                                                                   priv->id,
+                                                                   handle_type,
+                                                                   NULL, NULL);
+       }
+
+       g_object_unref (account);
+}
+
 static void
 empathy_chat_class_init (EmpathyChatClass *klass)
 {
@@ -167,6 +252,17 @@ empathy_chat_class_init (EmpathyChatClass *klass)
        object_class = G_OBJECT_CLASS (klass);
 
        object_class->finalize = chat_finalize;
+       object_class->get_property = chat_get_property;
+       object_class->set_property = chat_set_property;
+
+       g_object_class_install_property (object_class,
+                                        PROP_TP_CHAT,
+                                        g_param_spec_object ("tp-chat",
+                                                             "Empathy tp chat",
+                                                             "The tp chat object",
+                                                             EMPATHY_TYPE_TP_CHAT,
+                                                             G_PARAM_CONSTRUCT |
+                                                             G_PARAM_READWRITE));
 
        signals[COMPOSING] =
                g_signal_new ("composing",
@@ -184,7 +280,7 @@ empathy_chat_class_init (EmpathyChatClass *klass)
                              G_SIGNAL_RUN_LAST,
                              0,
                              NULL, NULL,
-                             empathy_marshal_VOID__OBJECT_BOOLEAN,
+                             _empathy_gtk_marshal_VOID__OBJECT_BOOLEAN,
                              G_TYPE_NONE,
                              2, EMPATHY_TYPE_MESSAGE, G_TYPE_BOOLEAN);
 
@@ -214,13 +310,13 @@ empathy_chat_class_init (EmpathyChatClass *klass)
 static void
 empathy_chat_init (EmpathyChat *chat)
 {
-       EmpathyChatPriv *priv;
+       EmpathyChatPriv *priv = GET_PRIV (chat);
        GtkTextBuffer  *buffer;
 
        chat->view = empathy_chat_view_new ();
        chat->input_text_view = gtk_text_view_new ();
 
-       chat->is_first_char = TRUE;
+       priv->is_first_char = TRUE;
 
        g_object_set (chat->input_text_view,
                      "pixels-above-lines", 2,
@@ -231,8 +327,6 @@ empathy_chat_init (EmpathyChat *chat)
                      "wrap-mode", GTK_WRAP_WORD_CHAR,
                      NULL);
 
-       priv = GET_PRIV (chat);
-
        priv->log_manager = empathy_log_manager_new ();
        priv->default_window_height = -1;
        priv->vscroll_visible = FALSE;
@@ -240,6 +334,11 @@ empathy_chat_init (EmpathyChat *chat)
        priv->sent_messages = NULL;
        priv->sent_messages_index = -1;
        priv->first_tp_chat = TRUE;
+       priv->mc = empathy_mission_control_new ();
+
+       dbus_g_proxy_connect_signal (DBUS_G_PROXY (priv->mc), "AccountStatusChanged",
+                                    G_CALLBACK (chat_status_changed_cb),
+                                    chat, NULL);
 
        g_signal_connect (chat->input_text_view,
                          "key_press_event",
@@ -296,17 +395,30 @@ chat_finalize (GObject *object)
        g_list_free (priv->compositors);
 
        chat_composing_remove_timeout (chat);
-       g_object_unref (chat->account);
        g_object_unref (priv->log_manager);
 
+       dbus_g_proxy_disconnect_signal (DBUS_G_PROXY (priv->mc), "AccountStatusChanged",
+                                       G_CALLBACK (chat_status_changed_cb),
+                                       chat);
+       g_object_unref (priv->mc);
+
+
        if (priv->tp_chat) {
                g_object_unref (priv->tp_chat);
        }
 
+       if (priv->account) {
+               g_object_unref (priv->account);
+       }
+
        if (priv->scroll_idle_id) {
                g_source_remove (priv->scroll_idle_id);
        }
 
+       if (priv->block_events_timeout_id) {
+               g_source_remove (priv->block_events_timeout_id);
+       }
+
        g_free (priv->id);
 
        G_OBJECT_CLASS (empathy_chat_parent_class)->finalize (object);
@@ -329,6 +441,10 @@ chat_destroy_cb (EmpathyTpChat *tp_chat,
        empathy_chat_view_append_event (chat->view, _("Disconnected"));
        gtk_widget_set_sensitive (chat->input_text_view, FALSE);
 
+       if (priv->block_events_timeout_id != 0) {
+               g_source_remove (priv->block_events_timeout_id);
+       }
+
        if (EMPATHY_CHAT_GET_CLASS (chat)->set_tp_chat) {
                EMPATHY_CHAT_GET_CLASS (chat)->set_tp_chat (chat, NULL);
        }
@@ -386,7 +502,7 @@ chat_input_text_view_send (EmpathyChat *chat)
 
        g_free (msg);
 
-       chat->is_first_char = TRUE;
+       priv->is_first_char = TRUE;
 }
 
 static void
@@ -436,10 +552,10 @@ chat_message_received_cb (EmpathyTpChat  *tp_chat,
 }
 
 static void
-chat_send_error_cb (EmpathyTpChat                 *tp_chat,
-                   EmpathyMessage                *message,
-                   TelepathyChannelTextSendError  error_code,
-                   EmpathyChat                   *chat)
+chat_send_error_cb (EmpathyTpChat          *tp_chat,
+                   EmpathyMessage         *message,
+                   TpChannelTextSendError  error_code,
+                   EmpathyChat            *chat)
 {
        const gchar *error;
        gchar       *str;
@@ -472,7 +588,7 @@ chat_send_error_cb (EmpathyTpChat                 *tp_chat,
        g_free (str);
 }
 
-void 
+static void 
 chat_sent_message_add (EmpathyChat  *chat,
                       const gchar *str)
 {
@@ -510,7 +626,7 @@ chat_sent_message_add (EmpathyChat  *chat,
        priv->sent_messages_index = -1;
 }
 
-const gchar *
+static const gchar *
 chat_sent_message_get_next (EmpathyChat *chat)
 {
        EmpathyChatPriv *priv;
@@ -537,7 +653,7 @@ chat_sent_message_get_next (EmpathyChat *chat)
        return g_slist_nth_data (priv->sent_messages, priv->sent_messages_index);
 }
 
-const gchar *
+static const gchar *
 chat_sent_message_get_last (EmpathyChat *chat)
 {
        EmpathyChatPriv *priv;
@@ -683,7 +799,7 @@ chat_input_text_buffer_changed_cb (GtkTextBuffer *buffer,
                              EMPATHY_PREFS_CHAT_SPELL_CHECKER_ENABLED,
                              &spell_checker);
 
-       if (chat->is_first_char) {
+       if (priv->is_first_char) {
                GtkRequisition  req;
                gint            window_height;
                GtkWidget      *dialog;
@@ -702,7 +818,7 @@ chat_input_text_buffer_changed_cb (GtkTextBuffer *buffer,
                priv->last_input_height = req.height;
                priv->padding_height = window_height - req.height - allocation->height;
 
-               chat->is_first_char = FALSE;
+               priv->is_first_char = FALSE;
        }
 
        gtk_text_buffer_get_start_iter (buffer, &start);
@@ -1075,10 +1191,10 @@ chat_composing_stop_timeout_cb (EmpathyChat *chat)
 }
 
 static void
-chat_state_changed_cb (EmpathyTpChat             *tp_chat,
-                      EmpathyContact             *contact,
-                      TelepathyChannelChatState  state,
-                      EmpathyChat                *chat)
+chat_state_changed_cb (EmpathyTpChat      *tp_chat,
+                      EmpathyContact     *contact,
+                      TpChannelChatState  state,
+                      EmpathyChat        *chat)
 {
        EmpathyChatPriv *priv;
        GList          *l;
@@ -1150,7 +1266,7 @@ chat_add_logs (EmpathyChat *chat)
 
        /* Add messages from last conversation */
        messages = empathy_log_manager_get_last_messages (priv->log_manager,
-                                                         chat->account,
+                                                         priv->account,
                                                          empathy_chat_get_id (chat),
                                                          empathy_chat_is_group_chat (chat));
        num_messages  = g_list_length (messages);
@@ -1312,6 +1428,22 @@ empathy_chat_is_connected (EmpathyChat *chat)
        return (priv->tp_chat != NULL);
 }
 
+static const gchar *
+chat_get_window_id_for_geometry (EmpathyChat *chat)
+{
+       gboolean separate_windows;
+
+       empathy_conf_get_bool (empathy_conf_get (),
+                              EMPATHY_PREFS_UI_SEPARATE_CHAT_WINDOWS,
+                              &separate_windows);
+
+       if (separate_windows) {
+               return empathy_chat_get_id (chat);
+       } else {
+               return "chat-window";
+       }
+}
+
 void
 empathy_chat_save_geometry (EmpathyChat *chat,
                           gint        x,
@@ -1319,7 +1451,7 @@ empathy_chat_save_geometry (EmpathyChat *chat,
                           gint        w,
                           gint        h)
 {
-       empathy_geometry_save (empathy_chat_get_id (chat), x, y, w, h);
+       empathy_geometry_save (chat_get_window_id_for_geometry (chat), x, y, w, h);
 }
 
 void
@@ -1329,7 +1461,19 @@ empathy_chat_load_geometry (EmpathyChat *chat,
                           gint       *w,
                           gint       *h)
 {
-       empathy_geometry_load (empathy_chat_get_id (chat), x, y, w, h);
+       empathy_geometry_load (chat_get_window_id_for_geometry (chat), x, y, w, h);
+}
+
+static gboolean
+chat_block_events_timeout_cb (gpointer data)
+{
+       EmpathyChat     *chat = EMPATHY_CHAT (data);
+       EmpathyChatPriv *priv = GET_PRIV (chat);
+
+       chat->block_events = FALSE;
+       priv->block_events_timeout_id = 0;
+
+       return FALSE;
 }
 
 void
@@ -1337,7 +1481,6 @@ empathy_chat_set_tp_chat (EmpathyChat   *chat,
                          EmpathyTpChat *tp_chat)
 {
        EmpathyChatPriv *priv;
-       GList           *messages, *l;
 
        g_return_if_fail (EMPATHY_IS_CHAT (chat));
        g_return_if_fail (EMPATHY_IS_TP_CHAT (tp_chat));
@@ -1348,6 +1491,15 @@ empathy_chat_set_tp_chat (EmpathyChat   *chat,
                return;
        }
 
+       /* Block events for some time to avoid having "has come online" or
+        * "joined" messages. */
+       chat->block_events = TRUE;
+       if (priv->block_events_timeout_id != 0) {
+               g_source_remove (priv->block_events_timeout_id);
+       }
+       priv->block_events_timeout_id =
+               g_timeout_add_seconds (1, chat_block_events_timeout_cb, chat);
+
        if (priv->tp_chat) {
                g_signal_handlers_disconnect_by_func (priv->tp_chat,
                                                      chat_message_received_cb,
@@ -1360,10 +1512,15 @@ empathy_chat_set_tp_chat (EmpathyChat   *chat,
                                                      chat);
                g_object_unref (priv->tp_chat);
        }
+       if (priv->account) {
+               g_object_unref (priv->account);
+       }
 
        g_free (priv->id);
        priv->tp_chat = g_object_ref (tp_chat);
        priv->id = g_strdup (empathy_tp_chat_get_id (tp_chat));
+       priv->account = g_object_ref (empathy_tp_chat_get_account (tp_chat));
+       empathy_tp_chat_set_acknowledge (tp_chat, TRUE);
 
        if (priv->first_tp_chat) {
                chat_add_logs (chat);
@@ -1383,15 +1540,6 @@ empathy_chat_set_tp_chat (EmpathyChat   *chat,
                          G_CALLBACK (chat_destroy_cb),
                          chat);
 
-       /* Get pending messages */
-       empathy_tp_chat_set_acknowledge (tp_chat, TRUE);
-       messages = empathy_tp_chat_get_pendings (tp_chat);
-       for (l = messages; l; l = l->next) {
-               chat_message_received_cb (tp_chat, l->data, chat);
-               g_object_unref (l->data);
-       }
-       g_list_free (messages);
-
        if (!priv->sensitive) {
                gtk_widget_set_sensitive (chat->input_text_view, TRUE);
                empathy_chat_view_append_event (chat->view, _("Connected"));
@@ -1401,6 +1549,8 @@ empathy_chat_set_tp_chat (EmpathyChat   *chat,
        if (EMPATHY_CHAT_GET_CLASS (chat)->set_tp_chat) {
                EMPATHY_CHAT_GET_CLASS (chat)->set_tp_chat (chat, tp_chat);
        }
+
+       g_object_notify (G_OBJECT (chat), "tp-chat");
 }
 
 const gchar *
@@ -1413,6 +1563,14 @@ empathy_chat_get_id (EmpathyChat *chat)
        return priv->id;
 }
 
+McAccount *
+empathy_chat_get_account (EmpathyChat *chat)
+{
+       EmpathyChatPriv *priv = GET_PRIV (chat);
+
+       return priv->account;
+}
+
 void
 empathy_chat_clear (EmpathyChat *chat)
 {