]> git.0d.be Git - empathy.git/blobdiff - libempathy-gtk/empathy-chat.c
Do not export symbols outside the empathy_ namespace.
[empathy.git] / libempathy-gtk / empathy-chat.c
index f68a828261e70037b252e11e178e8b998c80c121..2594ac201699978c0b16dc2dcb1b7dd74afe1b97 100644 (file)
 #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"
 #define COMPOSING_STOP_TIMEOUT 5
 
 struct _EmpathyChatPriv {
-       EmpathyContactManager *manager;
        EmpathyLogManager     *log_manager;
        EmpathyTpChat         *tp_chat;
-       EmpathyChatWindow      *window;
-       GtkTooltips           *tooltips;
+       EmpathyChatWindow     *window;
+       McAccount             *account;
+       MissionControl        *mc;
        guint                  composing_stop_timeout_id;
        gboolean               sensitive;
        gchar                 *id;
@@ -76,7 +78,8 @@ struct _EmpathyChatPriv {
        GList                 *compositors;
        guint                  scroll_idle_id;
        gboolean               first_tp_chat;
-       EmpathyTime            last_log_timestamp;
+       time_t                 last_log_timestamp;
+       gboolean               is_first_char;
        /* Used to automatically shrink a window that has temporarily
         * grown due to long input. 
         */
@@ -94,60 +97,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,
@@ -157,10 +160,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)
 {
@@ -169,6 +251,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",
@@ -186,7 +279,7 @@ empathy_chat_class_init (EmpathyChatClass *klass)
                              G_SIGNAL_RUN_LAST,
                              0,
                              NULL, NULL,
-                             empathy_marshal_VOID__OBJECT_BOOLEAN,
+                             _empathy_marshal_VOID__OBJECT_BOOLEAN,
                              G_TYPE_NONE,
                              2, EMPATHY_TYPE_MESSAGE, G_TYPE_BOOLEAN);
 
@@ -216,13 +309,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,
@@ -233,17 +326,18 @@ empathy_chat_init (EmpathyChat *chat)
                      "wrap-mode", GTK_WRAP_WORD_CHAR,
                      NULL);
 
-       priv = GET_PRIV (chat);
-
-       priv->manager = empathy_contact_manager_new ();
        priv->log_manager = empathy_log_manager_new ();
-       priv->tooltips = g_object_ref_sink (gtk_tooltips_new ());
        priv->default_window_height = -1;
        priv->vscroll_visible = FALSE;
        priv->sensitive = TRUE;
        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",
@@ -300,15 +394,22 @@ chat_finalize (GObject *object)
        g_list_free (priv->compositors);
 
        chat_composing_remove_timeout (chat);
-       g_object_unref (chat->account);
-       g_object_unref (priv->manager);
        g_object_unref (priv->log_manager);
-       g_object_unref (priv->tooltips);
+
+       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);
        }
@@ -392,7 +493,7 @@ chat_input_text_view_send (EmpathyChat *chat)
 
        g_free (msg);
 
-       chat->is_first_char = TRUE;
+       priv->is_first_char = TRUE;
 }
 
 static void
@@ -402,7 +503,7 @@ chat_message_received_cb (EmpathyTpChat  *tp_chat,
 {
        EmpathyChatPriv *priv;
        EmpathyContact  *sender;
-       EmpathyTime      timestamp;
+       time_t           timestamp;
 
        priv = GET_PRIV (chat);
 
@@ -433,14 +534,19 @@ chat_message_received_cb (EmpathyTpChat  *tp_chat,
                // FIXME: empathy_sound_play (EMPATHY_SOUND_CHAT);
        }
 
+       /* We received a message so the contact is no more composing */
+       chat_state_changed_cb (tp_chat, sender,
+                              TP_CHANNEL_CHAT_STATE_ACTIVE,
+                              chat);
+
        g_signal_emit (chat, signals[NEW_MESSAGE], 0, message, FALSE);
 }
 
 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;
@@ -473,7 +579,7 @@ chat_send_error_cb (EmpathyTpChat                 *tp_chat,
        g_free (str);
 }
 
-void 
+static void 
 chat_sent_message_add (EmpathyChat  *chat,
                       const gchar *str)
 {
@@ -511,7 +617,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;
@@ -538,7 +644,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;
@@ -567,7 +673,7 @@ chat_sent_message_get_last (EmpathyChat *chat)
 static gboolean
 chat_input_key_press_event_cb (GtkWidget   *widget,
                               GdkEventKey *event,
-                              EmpathyChat  *chat)
+                              EmpathyChat *chat)
 {
        EmpathyChatPriv *priv;
        GtkAdjustment  *adj;
@@ -576,10 +682,6 @@ chat_input_key_press_event_cb (GtkWidget   *widget,
 
        priv = GET_PRIV (chat);
 
-       if (event->keyval == GDK_Tab && !(event->state & GDK_CONTROL_MASK)) {
-               return TRUE;
-       }
-
        /* Catch ctrl+up/down so we can traverse messages we sent */
        if ((event->state & GDK_CONTROL_MASK) && 
            (event->keyval == GDK_Up || 
@@ -607,7 +709,8 @@ chat_input_key_press_event_cb (GtkWidget   *widget,
        }
 
        /* Catch enter but not ctrl/shift-enter */
-       if (IS_ENTER (event->keyval) && !(event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))) {
+       if (IS_ENTER (event->keyval) &&
+           !(event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))) {
                GtkTextView *view;
 
                /* This is to make sure that kinput2 gets the enter. And if
@@ -628,11 +731,12 @@ chat_input_key_press_event_cb (GtkWidget   *widget,
 
        text_view_sw = gtk_widget_get_parent (GTK_WIDGET (chat->view));
 
-       if (IS_ENTER (event->keyval) && (event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))) {
-               /* Newline for shift-enter. */
+       if (IS_ENTER (event->keyval) &&
+           (event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))) {
+               /* Newline for shift/control-enter. */
                return FALSE;
        }
-       else if ((event->state & GDK_CONTROL_MASK) != GDK_CONTROL_MASK &&
+       else if (!(event->state & GDK_CONTROL_MASK) &&
                 event->keyval == GDK_Page_Up) {
                adj = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (text_view_sw));
                gtk_adjustment_set_value (adj, adj->value - adj->page_size);
@@ -648,6 +752,10 @@ chat_input_key_press_event_cb (GtkWidget   *widget,
                return TRUE;
        }
 
+       if (EMPATHY_CHAT_GET_CLASS (chat)->key_press_event) {
+               return EMPATHY_CHAT_GET_CLASS (chat)->key_press_event (chat, event);
+       }
+
        return FALSE;
 }
 
@@ -682,7 +790,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;
@@ -701,7 +809,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);
@@ -927,8 +1035,7 @@ chat_text_populate_popup_cb (GtkTextView *view,
 
        smiley_menu = empathy_chat_view_get_smiley_menu (
                G_CALLBACK (chat_insert_smiley_activate_cb),
-               chat,
-               priv->tooltips);
+               chat);
        gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), smiley_menu);
 
        /* Add the spell check menu item. */
@@ -1075,10 +1182,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 +1257,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);
@@ -1360,10 +1467,14 @@ 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));
 
        if (priv->first_tp_chat) {
                chat_add_logs (chat);
@@ -1402,6 +1513,7 @@ empathy_chat_set_tp_chat (EmpathyChat   *chat,
                EMPATHY_CHAT_GET_CLASS (chat)->set_tp_chat (chat, tp_chat);
        }
 
+       g_object_notify (G_OBJECT (chat), "tp-chat");
 }
 
 const gchar *
@@ -1414,6 +1526,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)
 {