]> git.0d.be Git - empathy.git/blobdiff - libempathy-gtk/empathy-chat.c
Always set urgency hint on p2p chat windows when receiving a message.
[empathy.git] / libempathy-gtk / empathy-chat.c
index 7079775cf0eead5049e3a31e2b5479f074a9e537..3810e209b19354575c807a140b67cf74d1a45e04 100644 (file)
@@ -46,6 +46,8 @@
 #include "empathy-conf.h"
 #include "empathy-spell.h"
 #include "empathy-spell-dialog.h"
+#include "empathy-contact-list-store.h"
+#include "empathy-contact-list-view.h"
 #include "empathy-ui-utils.h"
 
 #define GET_PRIV(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), EMPATHY_TYPE_CHAT, EmpathyChatPriv))
@@ -64,6 +66,7 @@ struct _EmpathyChatPriv {
        gchar             *id;
        gchar             *name;
        gchar             *subject;
+       EmpathyContact    *remote_contact;
 
        EmpathyLogManager *log_manager;
        MissionControl    *mc;
@@ -75,6 +78,8 @@ struct _EmpathyChatPriv {
        guint              composing_stop_timeout_id;
        guint              block_events_timeout_id;
        TpHandleType       handle_type;
+       gpointer           token;
+       gint               contacts_width;
 
        GtkWidget         *widget;
        GtkWidget         *hpaned;
@@ -84,6 +89,8 @@ struct _EmpathyChatPriv {
        GtkWidget         *scrolled_window_contacts;
        GtkWidget         *hbox_topic;
        GtkWidget         *label_topic;
+       EmpathyContactListView *view;
+       EmpathyContactListStore *store;
 
        /* Used to automatically shrink a window that has temporarily
         * grown due to long input. 
@@ -111,6 +118,7 @@ enum {
        PROP_ID,
        PROP_NAME,
        PROP_SUBJECT,
+       PROP_REMOTE_CONTACT,
 };
 
 static guint signals[LAST_SIGNAL] = { 0 };
@@ -141,6 +149,9 @@ chat_get_property (GObject    *object,
        case PROP_SUBJECT:
                g_value_set_string (value, priv->subject);
                break;
+       case PROP_REMOTE_CONTACT:
+               g_value_set_object (value, priv->remote_contact);
+               break;
        default:
                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
                break;
@@ -265,6 +276,7 @@ chat_destroy_cb (EmpathyTpChat *tp_chat,
        if (priv->tp_chat) {
                g_object_unref (priv->tp_chat);
                priv->tp_chat = NULL;
+               g_object_notify (G_OBJECT (chat), "tp-chat");
        }
 
        empathy_chat_view_append_event (chat->view, _("Disconnected"));
@@ -581,6 +593,24 @@ chat_property_changed_cb (EmpathyTpChat *tp_chat,
                g_free (priv->subject);
                priv->subject = g_value_dup_string (value);
                g_object_notify (G_OBJECT (chat), "subject");
+
+               if (G_STR_EMPTY (priv->subject)) {
+                       gtk_widget_hide (priv->hbox_topic);
+               } else {
+                       gtk_label_set_text (GTK_LABEL (priv->label_topic), priv->subject);
+                       gtk_widget_show (priv->hbox_topic);
+               }
+               if (priv->block_events_timeout_id == 0) {
+                       gchar *str;
+
+                       if (!G_STR_EMPTY (priv->subject)) {
+                               str = g_strdup_printf (_("Topic set to: %s"), priv->subject);
+                       } else {
+                               str = g_strdup (_("No topic defined"));
+                       }
+                       empathy_chat_view_append_event (EMPATHY_CHAT (chat)->view, str);
+                       g_free (str);
+               }
        }
        else if (!tp_strdiff (name, "name")) {
                g_free (priv->name);
@@ -1181,6 +1211,13 @@ chat_contacts_completion_func (const gchar *s1,
        gchar *tmp, *nick1, *nick2;
        gint   ret;
 
+       if (s1 == s2) {
+               return 0;
+       }
+       if (!s1 || !s2) {
+               return s1 ? -1 : +1;
+       }
+
        tmp = g_utf8_normalize (s1, -1, G_NORMALIZE_DEFAULT);
        nick1 = g_utf8_casefold (tmp, -1);
        g_free (tmp);
@@ -1197,6 +1234,76 @@ chat_contacts_completion_func (const gchar *s1,
        return ret;
 }
 
+static void
+chat_members_changed_cb (EmpathyTpChat  *tp_chat,
+                        EmpathyContact *contact,
+                        EmpathyContact *actor,
+                        guint           reason,
+                        gchar          *message,
+                        gboolean        is_member,
+                        EmpathyChat    *chat)
+{
+       EmpathyChatPriv *priv = GET_PRIV (chat);
+
+       if (priv->block_events_timeout_id == 0) {
+               gchar *str;
+
+               empathy_contact_run_until_ready (contact,
+                                                EMPATHY_CONTACT_READY_NAME,
+                                                NULL);
+
+               if (is_member) {
+                       str = g_strdup_printf (_("%s has joined the room"),
+                                              empathy_contact_get_name (contact));
+               } else {
+                       str = g_strdup_printf (_("%s has left the room"),
+                                              empathy_contact_get_name (contact));
+               }
+               empathy_chat_view_append_event (chat->view, str);
+               g_free (str);
+       }
+}
+
+static void
+chat_set_show_contacts (EmpathyChat *chat, gboolean show)
+{
+       EmpathyChatPriv *priv = GET_PRIV (chat);
+
+       if (!priv->scrolled_window_contacts ||
+           GTK_WIDGET_VISIBLE (priv->scrolled_window_contacts) == show) {
+               return;
+       }
+
+       if (show) {
+               gtk_widget_show (priv->scrolled_window_contacts);
+               gtk_paned_set_position (GTK_PANED (priv->hpaned),
+                                       priv->contacts_width);
+       } else {
+               priv->contacts_width = gtk_paned_get_position (GTK_PANED (priv->hpaned));
+               gtk_widget_hide (priv->scrolled_window_contacts);
+       }
+}
+
+static void
+chat_remote_contact_changed_cb (EmpathyChat *chat)
+{
+       EmpathyChatPriv *priv = GET_PRIV (chat);
+
+       if (priv->remote_contact) {
+               g_object_unref (priv->remote_contact);
+               priv->remote_contact = NULL;
+       }
+
+       priv->remote_contact = empathy_tp_chat_get_remote_contact (priv->tp_chat);
+       if (priv->remote_contact) {
+               g_object_ref (priv->remote_contact);
+       }
+
+       chat_set_show_contacts (chat, priv->remote_contact == NULL);
+
+       g_object_notify (G_OBJECT (chat), "remote-contact");
+}
+
 static void
 chat_create_ui (EmpathyChat *chat)
 {
@@ -1204,6 +1311,7 @@ chat_create_ui (EmpathyChat *chat)
        GladeXML        *glade;
        GList           *list = NULL; 
        gchar           *filename;
+       GtkTextBuffer   *buffer;
 
        filename = empathy_file_lookup ("empathy-chat.glade",
                                        "libempathy-gtk");
@@ -1224,28 +1332,63 @@ chat_create_ui (EmpathyChat *chat)
 
        /* Add message GtkTextView. */
        chat->view = empathy_chat_view_new ();
+       g_signal_connect (chat->view, "focus_in_event",
+                         G_CALLBACK (chat_text_view_focus_in_event_cb),
+                         chat);
        gtk_container_add (GTK_CONTAINER (priv->scrolled_window_chat),
                           GTK_WIDGET (chat->view));
        gtk_widget_show (GTK_WIDGET (chat->view));
 
        /* Add input GtkTextView */
-       chat->input_text_view = gtk_text_view_new ();
-       g_object_set (chat->input_text_view,
-                     "pixels-above-lines", 2,
-                     "pixels-below-lines", 2,
-                     "pixels-inside-wrap", 1,
-                     "right-margin", 2,
-                     "left-margin", 2,
-                     "wrap-mode", GTK_WRAP_WORD_CHAR,
-                     NULL);
+       chat->input_text_view = g_object_new (GTK_TYPE_TEXT_VIEW,
+                                             "pixels-above-lines", 2,
+                                             "pixels-below-lines", 2,
+                                             "pixels-inside-wrap", 1,
+                                             "right-margin", 2,
+                                             "left-margin", 2,
+                                             "wrap-mode", GTK_WRAP_WORD_CHAR,
+                                             NULL);
+       g_signal_connect (chat->input_text_view, "key_press_event",
+                         G_CALLBACK (chat_input_key_press_event_cb),
+                         chat);
+       g_signal_connect (chat->input_text_view, "size_allocate",
+                         G_CALLBACK (chat_text_view_size_allocate_cb),
+                         chat);
+       g_signal_connect (chat->input_text_view, "realize",
+                         G_CALLBACK (chat_text_view_realize_cb),
+                         chat);
+       g_signal_connect (chat->input_text_view, "populate_popup",
+                         G_CALLBACK (chat_text_populate_popup_cb),
+                         chat);
+       buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (chat->input_text_view));
+       g_signal_connect (buffer, "changed",
+                         G_CALLBACK (chat_input_text_buffer_changed_cb),
+                         chat);
+       gtk_text_buffer_create_tag (buffer, "misspelled",
+                                   "underline", PANGO_UNDERLINE_ERROR,
+                                   NULL);
        gtk_container_add (GTK_CONTAINER (priv->scrolled_window_input),
                           chat->input_text_view);
        gtk_widget_show (chat->input_text_view);
 
-       /* Add nick name completion */
-       priv->completion = g_completion_new ((GCompletionFunc) empathy_contact_get_name);
-       g_completion_set_compare (priv->completion,
-                                 chat_contacts_completion_func);
+       /* Create contact list */
+       priv->store = empathy_contact_list_store_new (EMPATHY_CONTACT_LIST (priv->tp_chat));
+       priv->view = empathy_contact_list_view_new (priv->store,
+                                                   EMPATHY_CONTACT_LIST_FEATURE_CONTACT_CHAT |
+                                                   EMPATHY_CONTACT_LIST_FEATURE_CONTACT_CALL |
+                                                   EMPATHY_CONTACT_LIST_FEATURE_CONTACT_LOG |
+                                                   EMPATHY_CONTACT_LIST_FEATURE_CONTACT_FT |
+                                                   EMPATHY_CONTACT_LIST_FEATURE_CONTACT_INVITE |
+                                                   EMPATHY_CONTACT_LIST_FEATURE_CONTACT_INFO);
+       gtk_container_add (GTK_CONTAINER (priv->scrolled_window_contacts),
+                          GTK_WIDGET (priv->view));
+       gtk_widget_show (GTK_WIDGET (priv->view));
+
+       /* Initialy hide the topic, will be shown if not empty */
+       gtk_widget_hide (priv->hbox_topic);
+
+       /* Show/Hide contact list */
+       chat_set_show_contacts (chat, priv->remote_contact == NULL);
 
        /* Set widget focus order */
        list = g_list_append (NULL, priv->scrolled_window_input);
@@ -1266,6 +1409,46 @@ chat_create_ui (EmpathyChat *chat)
        gtk_container_add (GTK_CONTAINER (chat), priv->widget);
 }
 
+static void
+chat_size_request (GtkWidget      *widget,
+                  GtkRequisition *requisition)
+{
+  GtkBin *bin = GTK_BIN (widget);
+
+  requisition->width = GTK_CONTAINER (widget)->border_width * 2;
+  requisition->height = GTK_CONTAINER (widget)->border_width * 2;
+
+  if (bin->child && GTK_WIDGET_VISIBLE (bin->child))
+    {
+      GtkRequisition child_requisition;
+      
+      gtk_widget_size_request (bin->child, &child_requisition);
+
+      requisition->width += child_requisition.width;
+      requisition->height += child_requisition.height;
+    }
+}
+
+static void
+chat_size_allocate (GtkWidget     *widget,
+                   GtkAllocation *allocation)
+{
+  GtkBin *bin = GTK_BIN (widget);
+  GtkAllocation child_allocation;
+  
+  widget->allocation = *allocation;
+
+  if (bin->child && GTK_WIDGET_VISIBLE (bin->child))
+    {
+      child_allocation.x = allocation->x + GTK_CONTAINER (widget)->border_width;
+      child_allocation.y = allocation->y + GTK_CONTAINER (widget)->border_width;
+      child_allocation.width = MAX (allocation->width - GTK_CONTAINER (widget)->border_width * 2, 0);
+      child_allocation.height = MAX (allocation->height - GTK_CONTAINER (widget)->border_width * 2, 0);
+
+      gtk_widget_size_allocate (bin->child, &child_allocation);
+    }
+}
+
 static void
 chat_finalize (GObject *object)
 {
@@ -1288,20 +1471,20 @@ chat_finalize (GObject *object)
 
        chat_composing_remove_timeout (chat);
 
-       dbus_g_proxy_disconnect_signal (DBUS_G_PROXY (priv->mc), "AccountStatusChanged",
-                                       G_CALLBACK (chat_status_changed_cb),
-                                       chat);
+       empathy_disconnect_account_status_changed (priv->token);
        g_object_unref (priv->mc);
        g_object_unref (priv->log_manager);
-
+       g_object_unref (priv->store);
 
        if (priv->tp_chat) {
                g_object_unref (priv->tp_chat);
        }
-
        if (priv->account) {
                g_object_unref (priv->account);
        }
+       if (priv->remote_contact) {
+               g_object_unref (priv->remote_contact);
+       }
 
        if (priv->block_events_timeout_id) {
                g_source_remove (priv->block_events_timeout_id);
@@ -1317,21 +1500,26 @@ chat_finalize (GObject *object)
 static void
 chat_constructed (GObject *object)
 {
-       chat_add_logs (EMPATHY_CHAT (object));
+       EmpathyChat *chat = EMPATHY_CHAT (object);
+
+       chat_create_ui (chat);
+       chat_add_logs (chat);
 }
 
 static void
 empathy_chat_class_init (EmpathyChatClass *klass)
 {
-       GObjectClass *object_class;
-
-       object_class = G_OBJECT_CLASS (klass);
+       GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+       GObjectClass   *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;
        object_class->constructed = chat_constructed;
 
+       widget_class->size_request = chat_size_request;
+       widget_class->size_allocate = chat_size_allocate;
+
        g_object_class_install_property (object_class,
                                         PROP_TP_CHAT,
                                         g_param_spec_object ("tp-chat",
@@ -1368,6 +1556,13 @@ empathy_chat_class_init (EmpathyChatClass *klass)
                                                              "The subject or topic of the chat",
                                                              NULL,
                                                              G_PARAM_READABLE));
+       g_object_class_install_property (object_class,
+                                        PROP_REMOTE_CONTACT,
+                                        g_param_spec_object ("remote-contact",
+                                                             "The remote contact",
+                                                             "The remote contact is any",
+                                                             EMPATHY_TYPE_CONTACT,
+                                                             G_PARAM_READABLE));
 
        signals[COMPOSING] =
                g_signal_new ("composing",
@@ -1392,13 +1587,20 @@ empathy_chat_class_init (EmpathyChatClass *klass)
        g_type_class_add_private (object_class, sizeof (EmpathyChatPriv));
 }
 
+static gboolean
+chat_block_events_timeout_cb (gpointer data)
+{
+       EmpathyChatPriv *priv = GET_PRIV (data);
+
+       priv->block_events_timeout_id = 0;
+
+       return FALSE;
+}
+
 static void
 empathy_chat_init (EmpathyChat *chat)
 {
        EmpathyChatPriv *priv = GET_PRIV (chat);
-       GtkTextBuffer  *buffer;
-
-       chat_create_ui (chat);
 
        priv->is_first_char = TRUE;
        priv->log_manager = empathy_log_manager_new ();
@@ -1408,45 +1610,18 @@ empathy_chat_init (EmpathyChat *chat)
        priv->sent_messages_index = -1;
        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);
+       priv->token = empathy_connect_to_account_status_changed (priv->mc,
+                                                  G_CALLBACK (chat_status_changed_cb),
+                                                  chat, NULL);
 
-       g_signal_connect (chat->input_text_view,
-                         "key_press_event",
-                         G_CALLBACK (chat_input_key_press_event_cb),
-                         chat);
-
-       buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (chat->input_text_view));
-       g_signal_connect (buffer,
-                         "changed",
-                         G_CALLBACK (chat_input_text_buffer_changed_cb),
-                         chat);
-       g_signal_connect (chat->view,
-                         "focus_in_event",
-                         G_CALLBACK (chat_text_view_focus_in_event_cb),
-                         chat);
-
-       g_signal_connect (chat->input_text_view,
-                         "size_allocate",
-                         G_CALLBACK (chat_text_view_size_allocate_cb),
-                         chat);
-
-       g_signal_connect (chat->input_text_view,
-                         "realize",
-                         G_CALLBACK (chat_text_view_realize_cb),
-                         chat);
-
-       g_signal_connect (GTK_TEXT_VIEW (chat->input_text_view),
-                         "populate_popup",
-                         G_CALLBACK (chat_text_populate_popup_cb),
-                         chat);
+       /* Block events for some time to avoid having "has come online" or
+        * "joined" messages. */
+       priv->block_events_timeout_id =
+               g_timeout_add_seconds (1, chat_block_events_timeout_cb, chat);
 
-       /* create misspelt words identification tag */
-       gtk_text_buffer_create_tag (buffer,
-                                   "misspelled",
-                                   "underline", PANGO_UNDERLINE_ERROR,
-                                   NULL);
+       /* Add nick name completion */
+       priv->completion = g_completion_new ((GCompletionFunc) empathy_contact_get_name);
+       g_completion_set_compare (priv->completion, chat_contacts_completion_func);
 }
 
 EmpathyChat *
@@ -1465,25 +1640,15 @@ empathy_chat_get_tp_chat (EmpathyChat *chat)
        return priv->tp_chat;
 }
 
-static gboolean
-chat_block_events_timeout_cb (gpointer data)
-{
-       EmpathyChatPriv *priv = GET_PRIV (data);
-
-       priv->block_events_timeout_id = 0;
-
-       return FALSE;
-}
-
 void
 empathy_chat_set_tp_chat (EmpathyChat   *chat,
                          EmpathyTpChat *tp_chat)
 {
        EmpathyChatPriv *priv;
-       TpChan          *tp_chan;
 
        g_return_if_fail (EMPATHY_IS_CHAT (chat));
        g_return_if_fail (EMPATHY_IS_TP_CHAT (tp_chat));
+       g_return_if_fail (empathy_tp_chat_is_ready (tp_chat));
 
        priv = GET_PRIV (chat);
 
@@ -1502,8 +1667,6 @@ empathy_chat_set_tp_chat (EmpathyChat   *chat,
        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));
-       tp_chan = empathy_tp_chat_get_channel (tp_chat);
-       priv->handle_type = tp_chan->handle_type;
 
        g_signal_connect (tp_chat, "message-received",
                          G_CALLBACK (chat_message_received_cb),
@@ -1517,19 +1680,24 @@ empathy_chat_set_tp_chat (EmpathyChat   *chat,
        g_signal_connect (tp_chat, "property-changed",
                          G_CALLBACK (chat_property_changed_cb),
                          chat);
+       g_signal_connect (tp_chat, "members-changed",
+                         G_CALLBACK (chat_members_changed_cb),
+                         chat);
+       g_signal_connect_swapped (tp_chat, "notify::remote-contact",
+                                 G_CALLBACK (chat_remote_contact_changed_cb),
+                                 chat);
        g_signal_connect (tp_chat, "destroy",
                          G_CALLBACK (chat_destroy_cb),
                          chat);
 
-       /* Block events for some time to avoid having "has come online" or
-        * "joined" messages. */
-       if (priv->block_events_timeout_id == 0) {
-               priv->block_events_timeout_id =
-                       g_timeout_add_seconds (1, chat_block_events_timeout_cb, chat);
-       }
+       chat_remote_contact_changed_cb (chat);
 
-       gtk_widget_set_sensitive (chat->input_text_view, TRUE);
-       empathy_chat_view_append_event (chat->view, _("Connected"));
+       if (chat->input_text_view) {
+               gtk_widget_set_sensitive (chat->input_text_view, TRUE);
+               if (priv->block_events_timeout_id == 0) {
+                       empathy_chat_view_append_event (chat->view, _("Connected"));
+               }
+       }
 
        g_object_notify (G_OBJECT (chat), "tp-chat");
        g_object_notify (G_OBJECT (chat), "id");
@@ -1576,6 +1744,30 @@ empathy_chat_get_subject (EmpathyChat *chat)
        return priv->subject;
 }
 
+EmpathyContact *
+empathy_chat_get_remote_contact (EmpathyChat *chat)
+{
+       EmpathyChatPriv *priv = GET_PRIV (chat);
+
+       g_return_val_if_fail (EMPATHY_IS_CHAT (chat), NULL);
+
+       return priv->remote_contact;
+}
+
+guint
+empathy_chat_get_members_count (EmpathyChat *chat)
+{
+       EmpathyChatPriv *priv = GET_PRIV (chat);
+
+       g_return_val_if_fail (EMPATHY_IS_CHAT (chat), 0);
+
+       if (priv->tp_chat) {
+               return empathy_tp_chat_get_members_count (priv->tp_chat);
+       }
+
+       return 0;
+}
+
 void
 empathy_chat_clear (EmpathyChat *chat)
 {