]> git.0d.be Git - empathy.git/blobdiff - libempathy-gtk/gossip-chat.c
Fix warning when selecting all accounts.
[empathy.git] / libempathy-gtk / gossip-chat.c
index ad8af12a5faf177ebf8266b065542cf217d038e5..a29c318185737c68ca986a6ca20be651fd640062 100644 (file)
 #include <gtk/gtk.h>
 #include <glade/glade.h>
 
-#include <libempathy/empathy-session.h>
 #include <libempathy/empathy-contact-manager.h>
 #include <libempathy/gossip-debug.h>
 #include <libempathy/gossip-utils.h>
 #include <libempathy/gossip-conf.h>
+#include <libempathy/empathy-marshal.h>
 
 #include "gossip-chat.h"
 #include "gossip-chat-window.h"
 #define COMPOSING_STOP_TIMEOUT 5
 
 struct _GossipChatPriv {
-       EmpathyTpChat    *tp_chat;
-       GossipChatWindow *window;
-
-       GtkTooltips      *tooltips;
-       guint             composing_stop_timeout_id;
-       gboolean          sensitive;
-       gchar            *id;
+       EmpathyContactManager *manager;
+       EmpathyTpChat         *tp_chat;
+       GossipChatWindow      *window;
+       GtkTooltips           *tooltips;
+       guint                  composing_stop_timeout_id;
+       gboolean               sensitive;
+       gchar                 *id;
+       GSList                *sent_messages;
+       gint                   sent_messages_index;
+       GList                 *compositors;
+       guint                  scroll_idle_id;
        /* Used to automatically shrink a window that has temporarily
         * grown due to long input. 
         */
-       gint              padding_height;
-       gint              default_window_height;
-       gint              last_input_height;
-       gboolean          vscroll_visible;
+       gint                   padding_height;
+       gint                   default_window_height;
+       gint                   last_input_height;
+       gboolean               vscroll_visible;
 };
 
 typedef struct {
@@ -98,6 +102,10 @@ static void             chat_input_text_view_send         (GossipChat      *chat
 static void             chat_message_received_cb          (EmpathyTpChat   *tp_chat,
                                                           GossipMessage   *message,
                                                           GossipChat      *chat);
+void                    chat_sent_message_add             (GossipChat      *chat,
+                                                          const gchar     *str);
+const gchar *           chat_sent_message_get_next        (GossipChat      *chat);
+const gchar *           chat_sent_message_get_last        (GossipChat      *chat);
 static gboolean         chat_input_key_press_event_cb     (GtkWidget       *widget,
                                                           GdkEventKey     *event,
                                                           GossipChat      *chat);
@@ -127,6 +135,11 @@ static void             chat_composing_start              (GossipChat      *chat
 static void             chat_composing_stop               (GossipChat      *chat);
 static void             chat_composing_remove_timeout     (GossipChat      *chat);
 static gboolean         chat_composing_stop_timeout_cb    (GossipChat      *chat);
+static void             chat_state_changed_cb             (EmpathyTpChat   *tp_chat,
+                                                          GossipContact   *contact,
+                                                          TelepathyChannelChatState  state,
+                                                          GossipChat      *chat);
+static gboolean         chat_scroll_down_idle_func        (GossipChat      *chat);
 
 enum {
        COMPOSING,
@@ -136,7 +149,7 @@ enum {
        LAST_SIGNAL
 };
 
-static guint chat_signals[LAST_SIGNAL] = { 0 };
+static guint signals[LAST_SIGNAL] = { 0 };
 
 G_DEFINE_TYPE (GossipChat, gossip_chat, G_TYPE_OBJECT);
 
@@ -149,7 +162,7 @@ gossip_chat_class_init (GossipChatClass *klass)
 
        object_class->finalize = chat_finalize;
 
-       chat_signals[COMPOSING] =
+       signals[COMPOSING] =
                g_signal_new ("composing",
                              G_OBJECT_CLASS_TYPE (object_class),
                              G_SIGNAL_RUN_LAST,
@@ -159,17 +172,17 @@ gossip_chat_class_init (GossipChatClass *klass)
                              G_TYPE_NONE,
                              1, G_TYPE_BOOLEAN);
 
-       chat_signals[NEW_MESSAGE] =
+       signals[NEW_MESSAGE] =
                g_signal_new ("new-message",
                              G_OBJECT_CLASS_TYPE (object_class),
                              G_SIGNAL_RUN_LAST,
                              0,
                              NULL, NULL,
-                             g_cclosure_marshal_VOID__OBJECT,
+                             empathy_marshal_VOID__OBJECT_BOOLEAN,
                              G_TYPE_NONE,
-                             1, GOSSIP_TYPE_MESSAGE);
+                             2, GOSSIP_TYPE_MESSAGE, G_TYPE_BOOLEAN);
 
-       chat_signals[NAME_CHANGED] =
+       signals[NAME_CHANGED] =
                g_signal_new ("name-changed",
                              G_OBJECT_CLASS_TYPE (object_class),
                              G_SIGNAL_RUN_LAST,
@@ -179,7 +192,7 @@ gossip_chat_class_init (GossipChatClass *klass)
                              G_TYPE_NONE,
                              1, G_TYPE_POINTER);
 
-       chat_signals[STATUS_CHANGED] =
+       signals[STATUS_CHANGED] =
                g_signal_new ("status-changed",
                              G_OBJECT_CLASS_TYPE (object_class),
                              G_SIGNAL_RUN_LAST,
@@ -214,11 +227,13 @@ gossip_chat_init (GossipChat *chat)
 
        priv = GET_PRIV (chat);
 
-       priv->tooltips = gtk_tooltips_new ();
-
+       priv->manager = empathy_contact_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;
 
        g_signal_connect (chat->input_text_view,
                          "key_press_event",
@@ -230,7 +245,7 @@ gossip_chat_init (GossipChat *chat)
                          "changed",
                          G_CALLBACK (chat_input_text_buffer_changed_cb),
                          chat);
-       g_signal_connect (GOSSIP_CHAT (chat)->view,
+       g_signal_connect (chat->view,
                          "focus_in_event",
                          G_CALLBACK (chat_text_view_focus_in_event_cb),
                          chat);
@@ -255,6 +270,51 @@ gossip_chat_init (GossipChat *chat)
                                    "misspelled",
                                    "underline", PANGO_UNDERLINE_ERROR,
                                    NULL);
+
+
+
+       /* Turn off scrolling temporarily */
+       gossip_chat_view_scroll (chat->view, FALSE);
+#if 0
+FIXME:
+       /* Add messages from last conversation */
+       log_manager = gossip_session_get_log_manager (gossip_app_get_session ());
+       messages = gossip_log_get_last_for_contact (log_manager, priv->contact);
+       num_messages  = g_list_length (messages);
+
+       for (l = messages, i = 0; l; l = l->next, i++) {
+               message = l->data;
+
+               if (num_messages - i > 10) {
+                       continue;
+               }
+
+               sender = gossip_message_get_sender (message);
+               if (gossip_contact_equal (priv->own_contact, sender)) {
+                       gossip_chat_view_append_message_from_self (view,
+                                                                  message,
+                                                                  priv->own_contact,
+                                                                  priv->own_avatar);
+               } else {
+                       gossip_chat_view_append_message_from_other (view,
+                                                                   message,
+                                                                   sender,
+                                                                   priv->other_avatar);
+               }
+       }
+
+       g_list_foreach (messages, (GFunc) g_object_unref, NULL);
+       g_list_free (messages);
+#endif
+       /* Turn back on scrolling */
+       gossip_chat_view_scroll (chat->view, TRUE);
+
+       /* Scroll to the most recent messages, we reference the chat
+        * for the duration of the scroll func.
+        */
+       priv->scroll_idle_id = g_idle_add ((GSourceFunc) chat_scroll_down_idle_func, 
+                                          g_object_ref (chat));
+
 }
 
 static void
@@ -268,13 +328,25 @@ chat_finalize (GObject *object)
 
        gossip_debug (DEBUG_DOMAIN, "Finalized: %p", object);
 
+       g_slist_foreach (priv->sent_messages, (GFunc) g_free, NULL);
+       g_slist_free (priv->sent_messages);
+
+       g_list_foreach (priv->compositors, (GFunc) g_object_unref, NULL);
+       g_list_free (priv->compositors);
+
        chat_composing_remove_timeout (chat);
-       g_object_unref (GOSSIP_CHAT (object)->account);
+       g_object_unref (chat->account);
+       g_object_unref (priv->manager);
+       g_object_unref (priv->tooltips);
 
        if (priv->tp_chat) {
                g_object_unref (priv->tp_chat);
        }
 
+       if (priv->scroll_idle_id) {
+               g_source_remove (priv->scroll_idle_id);
+       }
+
        g_free (priv->id);
 
        G_OBJECT_CLASS (gossip_chat_parent_class)->finalize (object);
@@ -312,18 +384,23 @@ chat_send (GossipChat  *chat,
 
        priv = GET_PRIV (chat);
 
-       if (msg == NULL || msg[0] == '\0') {
+       if (G_STR_EMPTY (msg)) {
                return;
        }
 
+       chat_sent_message_add (chat, msg);
+
        if (g_str_has_prefix (msg, "/clear")) {
                gossip_chat_view_clear (chat->view);
                return;
        }
 
+       /* FIXME: add here something to let group/privrate chat handle
+        *        some special messages */
+
        /* FIXME: gossip_app_set_not_away ();*/
 
-       own_contact = gossip_chat_get_own_contact (chat);
+       own_contact = empathy_contact_manager_get_user (priv->manager, chat->account);
        message = gossip_message_new (msg);
        gossip_message_set_sender (message, own_contact);
 
@@ -385,7 +462,98 @@ chat_message_received_cb (EmpathyTpChat *tp_chat,
                // FIXME: gossip_sound_play (GOSSIP_SOUND_CHAT);
        }
 
-       g_signal_emit_by_name (chat, "new-message", message);
+       g_signal_emit (chat, signals[NEW_MESSAGE], 0, message, FALSE);
+}
+
+void 
+chat_sent_message_add (GossipChat  *chat,
+                      const gchar *str)
+{
+       GossipChatPriv *priv;
+       GSList         *list;
+       GSList         *item;
+
+       priv = GET_PRIV (chat);
+
+       /* Save the sent message in our repeat buffer */
+       list = priv->sent_messages;
+       
+       /* Remove any other occurances of this msg */
+       while ((item = g_slist_find_custom (list, str, (GCompareFunc) strcmp)) != NULL) {
+               list = g_slist_remove_link (list, item);
+               g_free (item->data);
+               g_slist_free1 (item);
+       }
+
+       /* Trim the list to the last 10 items */
+       while (g_slist_length (list) > 10) {
+               item = g_slist_last (list);
+               if (item) {
+                       list = g_slist_remove_link (list, item);
+                       g_free (item->data);
+                       g_slist_free1 (item);
+               }
+       }
+
+       /* Add new message */
+       list = g_slist_prepend (list, g_strdup (str));
+
+       /* Set list and reset the index */
+       priv->sent_messages = list;
+       priv->sent_messages_index = -1;
+}
+
+const gchar *
+chat_sent_message_get_next (GossipChat *chat)
+{
+       GossipChatPriv *priv;
+       gint            max;
+       
+       priv = GET_PRIV (chat);
+
+       if (!priv->sent_messages) {
+               gossip_debug (DEBUG_DOMAIN, 
+                             "No sent messages, next message is NULL");
+               return NULL;
+       }
+
+       max = g_slist_length (priv->sent_messages) - 1;
+
+       if (priv->sent_messages_index < max) {
+               priv->sent_messages_index++;
+       }
+       
+       gossip_debug (DEBUG_DOMAIN, 
+                     "Returning next message index:%d",
+                     priv->sent_messages_index);
+
+       return g_slist_nth_data (priv->sent_messages, priv->sent_messages_index);
+}
+
+const gchar *
+chat_sent_message_get_last (GossipChat *chat)
+{
+       GossipChatPriv *priv;
+
+       g_return_val_if_fail (GOSSIP_IS_CHAT (chat), NULL);
+
+       priv = GET_PRIV (chat);
+       
+       if (!priv->sent_messages) {
+               gossip_debug (DEBUG_DOMAIN, 
+                             "No sent messages, last message is NULL");
+               return NULL;
+       }
+
+       if (priv->sent_messages_index >= 0) {
+               priv->sent_messages_index--;
+       }
+
+       gossip_debug (DEBUG_DOMAIN, 
+                     "Returning last message index:%d",
+                     priv->sent_messages_index);
+
+       return g_slist_nth_data (priv->sent_messages, priv->sent_messages_index);
 }
 
 static gboolean
@@ -404,8 +572,34 @@ chat_input_key_press_event_cb (GtkWidget   *widget,
                return TRUE;
        }
 
+       /* Catch ctrl+up/down so we can traverse messages we sent */
+       if ((event->state & GDK_CONTROL_MASK) && 
+           (event->keyval == GDK_Up || 
+            event->keyval == GDK_Down)) {
+               GtkTextBuffer *buffer;
+               const gchar   *str;
+
+               buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (chat->input_text_view));
+
+               if (event->keyval == GDK_Up) {
+                       str = chat_sent_message_get_next (chat);
+               } else {
+                       str = chat_sent_message_get_last (chat);
+               }
+
+               g_signal_handlers_block_by_func (buffer, 
+                                                chat_input_text_buffer_changed_cb,
+                                                chat);
+               gtk_text_buffer_set_text (buffer, str ? str : "", -1);
+               g_signal_handlers_unblock_by_func (buffer, 
+                                                  chat_input_text_buffer_changed_cb,
+                                                  chat);
+
+               return TRUE;    
+       }
+
        /* Catch enter but not ctrl/shift-enter */
-       if (IS_ENTER (event->keyval) && !(event->state & GDK_SHIFT_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
@@ -425,7 +619,8 @@ 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)) {
+
+       if (IS_ENTER (event->keyval) && (event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))) {
                /* Newline for shift-enter. */
                return FALSE;
        }
@@ -822,10 +1017,8 @@ chat_composing_start (GossipChat *chat)
                /* Just restart the timeout */
                chat_composing_remove_timeout (chat);
        } else {
-       /* FIXME:
-               gossip_session_send_composing (gossip_app_get_session (),
-                                              priv->contact, TRUE);
-                                             */
+               empathy_tp_chat_set_state (priv->tp_chat,
+                                          TP_CHANNEL_CHAT_STATE_COMPOSING);
        }
 
        priv->composing_stop_timeout_id = g_timeout_add (
@@ -842,9 +1035,8 @@ chat_composing_stop (GossipChat *chat)
        priv = GET_PRIV (chat);
 
        chat_composing_remove_timeout (chat);
-       /* FIXME:
-       gossip_session_send_composing (gossip_app_get_session (),
-                                      priv->contact, FALSE);*/
+       empathy_tp_chat_set_state (priv->tp_chat,
+                                  TP_CHANNEL_CHAT_STATE_ACTIVE);
 }
 
 static void
@@ -868,9 +1060,87 @@ chat_composing_stop_timeout_cb (GossipChat *chat)
        priv = GET_PRIV (chat);
 
        priv->composing_stop_timeout_id = 0;
-       /* FIXME:
-       gossip_session_send_composing (gossip_app_get_session (),
-                                      priv->contact, FALSE);*/
+       empathy_tp_chat_set_state (priv->tp_chat,
+                                  TP_CHANNEL_CHAT_STATE_PAUSED);
+
+       return FALSE;
+}
+
+static void
+chat_state_changed_cb (EmpathyTpChat             *tp_chat,
+                      GossipContact             *contact,
+                      TelepathyChannelChatState  state,
+                      GossipChat                *chat)
+{
+       GossipChatPriv *priv;
+       GossipContact  *own_contact;
+       GList          *l;
+       gboolean        was_composing;
+
+       priv = GET_PRIV (chat);
+
+       own_contact = gossip_contact_get_user (contact);
+       if (gossip_contact_equal (own_contact, contact)) {
+               /* We don't care about our own chat state */
+               return;
+       }
+
+       was_composing = (priv->compositors != NULL);
+
+       /* Find the contact in the list. After that l is the list elem or NULL */
+       for (l = priv->compositors; l; l = l->next) {
+               if (gossip_contact_equal (contact, l->data)) {
+                       break;
+               }
+       }
+
+       switch (state) {
+       case TP_CHANNEL_CHAT_STATE_GONE:
+       case TP_CHANNEL_CHAT_STATE_INACTIVE:
+       case TP_CHANNEL_CHAT_STATE_ACTIVE:
+               /* Contact is not composing */
+               if (l) {
+                       priv->compositors = g_list_remove_link (priv->compositors, l);
+                       g_object_unref (l->data);
+                       g_list_free1 (l);
+               }
+               break;
+       case TP_CHANNEL_CHAT_STATE_PAUSED:
+       case TP_CHANNEL_CHAT_STATE_COMPOSING:
+               /* Contact is composing */
+               if (!l) {
+                       priv->compositors = g_list_prepend (priv->compositors,
+                                                           g_object_ref (contact));
+               }
+               break;
+       default:
+               g_assert_not_reached ();
+       }
+
+       gossip_debug (DEBUG_DOMAIN, "Was composing: %s now composing: %s",
+                     was_composing ? "yes" : "no",
+                     priv->compositors ? "yes" : "no");
+
+       if ((was_composing && !priv->compositors) ||
+           (!was_composing && priv->compositors)) {
+               /* Composing state changed */
+               g_signal_emit (chat, signals[COMPOSING], 0,
+                              (gboolean) priv->compositors);
+       }
+}
+
+/* Scroll down after the back-log has been received. */
+static gboolean
+chat_scroll_down_idle_func (GossipChat *chat)
+{
+       GossipChatPriv *priv;
+
+       priv = GET_PRIV (chat);
+
+       gossip_chat_scroll_down (chat);
+       g_object_unref (chat);
+
+       priv->scroll_idle_id = 0;
 
        return FALSE;
 }
@@ -940,40 +1210,17 @@ gossip_chat_get_tooltip (GossipChat *chat)
        return NULL;
 }
 
-GdkPixbuf *
-gossip_chat_get_status_pixbuf (GossipChat *chat)
-{
-       g_return_val_if_fail (GOSSIP_IS_CHAT (chat), NULL);
-
-       if (GOSSIP_CHAT_GET_CLASS (chat)->get_status_pixbuf) {
-               return GOSSIP_CHAT_GET_CLASS (chat)->get_status_pixbuf (chat);
-       }
-
-       return NULL;
-}
-
-GossipContact *
-gossip_chat_get_contact (GossipChat *chat)
+const gchar *
+gossip_chat_get_status_icon_name (GossipChat *chat)
 {
        g_return_val_if_fail (GOSSIP_IS_CHAT (chat), NULL);
 
-       if (GOSSIP_CHAT_GET_CLASS (chat)->get_contact) {
-               return GOSSIP_CHAT_GET_CLASS (chat)->get_contact (chat);
+       if (GOSSIP_CHAT_GET_CLASS (chat)->get_status_icon_name) {
+               return GOSSIP_CHAT_GET_CLASS (chat)->get_status_icon_name (chat);
        }
 
        return NULL;
 }
-GossipContact *
-gossip_chat_get_own_contact (GossipChat *chat)
-{
-       EmpathyContactManager *manager;
-
-       g_return_val_if_fail (GOSSIP_IS_CHAT (chat), NULL);
-
-       manager = empathy_session_get_contact_manager ();
-
-       return empathy_contact_manager_get_own (manager, chat->account);
-}
 
 GtkWidget *
 gossip_chat_get_widget (GossipChat *chat)
@@ -1011,29 +1258,6 @@ gossip_chat_is_connected (GossipChat *chat)
        return (priv->tp_chat != NULL);
 }
 
-gboolean
-gossip_chat_get_show_contacts (GossipChat *chat)
-{
-       g_return_val_if_fail (GOSSIP_IS_CHAT (chat), FALSE);
-
-       if (GOSSIP_CHAT_GET_CLASS (chat)->get_show_contacts) {
-               return GOSSIP_CHAT_GET_CLASS (chat)->get_show_contacts (chat);
-       }
-
-       return FALSE;
-}
-
-void
-gossip_chat_set_show_contacts (GossipChat *chat,
-                              gboolean    show)
-{
-       g_return_if_fail (GOSSIP_IS_CHAT (chat));
-
-       if (GOSSIP_CHAT_GET_CLASS (chat)->set_show_contacts) {
-               GOSSIP_CHAT_GET_CLASS (chat)->set_show_contacts (chat, show);
-       }
-}
-
 void
 gossip_chat_save_geometry (GossipChat *chat,
                           gint        x,
@@ -1087,6 +1311,9 @@ gossip_chat_set_tp_chat (GossipChat    *chat,
        g_signal_connect (tp_chat, "message-received",
                          G_CALLBACK (chat_message_received_cb),
                          chat);
+       g_signal_connect (tp_chat, "chat-state-changed",
+                         G_CALLBACK (chat_state_changed_cb),
+                         chat);
        g_signal_connect (tp_chat, "destroy",
                          G_CALLBACK (chat_destroy_cb),
                          chat);
@@ -1236,7 +1463,7 @@ gossip_chat_should_play_sound (GossipChat *chat)
 
        g_return_val_if_fail (GOSSIP_IS_CHAT (chat), FALSE);
 
-       window = gossip_chat_get_window (GOSSIP_CHAT (chat));
+       window = gossip_chat_get_window (chat);
        if (!window) {
                return TRUE;
        }
@@ -1266,7 +1493,7 @@ gossip_chat_should_highlight_nick (GossipMessage *message)
                return FALSE;
        }
 
-       my_contact = gossip_get_own_contact_from_contact (gossip_message_get_sender (message));
+       my_contact = gossip_contact_get_user (gossip_message_get_sender (message));
        to = gossip_contact_get_name (my_contact);
        if (!to) {
                return FALSE;