1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * Copyright (C) 2003-2007 Imendio AB
4 * Copyright (C) 2007-2008 Collabora Ltd.
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License as
8 * published by the Free Software Foundation; either version 2 of the
9 * License, or (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
16 * You should have received a copy of the GNU General Public
17 * License along with this program; if not, write to the
18 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
19 * Boston, MA 02110-1301 USA
21 * Authors: Mikael Hallendal <micke@imendio.com>
22 * Richard Hult <richard@imendio.com>
23 * Martyn Russell <martyn@imendio.com>
24 * Geert-Jan Van den Bogaerde <geertjan@gnome.org>
25 * Xavier Claessens <xclaesse@gmail.com>
26 * RĂ´mulo Fernandes Machado <romulo@castorgroup.net>
34 #include <gdk/gdkkeysyms.h>
36 #include <glib/gi18n.h>
37 #include <libnotify/notification.h>
39 #include <telepathy-glib/telepathy-glib.h>
41 #include <libempathy/empathy-contact.h>
42 #include <libempathy/empathy-message.h>
43 #include <libempathy/empathy-chatroom-manager.h>
44 #include <libempathy/empathy-gsettings.h>
45 #include <libempathy/empathy-utils.h>
46 #include <libempathy/empathy-tp-contact-factory.h>
47 #include <libempathy/empathy-contact-list.h>
49 #include <libempathy-gtk/empathy-images.h>
50 #include <libempathy-gtk/empathy-contact-dialogs.h>
51 #include <libempathy-gtk/empathy-log-window.h>
52 #include <libempathy-gtk/empathy-geometry.h>
53 #include <libempathy-gtk/empathy-smiley-manager.h>
54 #include <libempathy-gtk/empathy-sound.h>
55 #include <libempathy-gtk/empathy-ui-utils.h>
56 #include <libempathy-gtk/empathy-notify-manager.h>
58 #include "empathy-chat-manager.h"
59 #include "empathy-chat-window.h"
60 #include "empathy-about-dialog.h"
61 #include "empathy-invite-participant-dialog.h"
63 #define DEBUG_FLAG EMPATHY_DEBUG_CHAT
64 #include <libempathy/empathy-debug.h>
66 /* Macro to compare guint32 X timestamps, while accounting for wrapping around
68 #define X_EARLIER_OR_EQL(t1, t2) \
69 ((t1 <= t2 && ((t2 - t1) < G_MAXUINT32/2)) \
70 || (t1 >= t2 && (t1 - t2) > (G_MAXUINT32/2)) \
73 #define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyChatWindow)
75 EmpathyChat *current_chat;
78 GList *chats_composing;
80 gboolean dnd_same_window;
81 EmpathyChatroomManager *chatroom_manager;
82 EmpathyNotifyManager *notify_mgr;
85 NotifyNotification *notification;
87 GtkTargetList *contact_targets;
88 GtkTargetList *file_targets;
90 EmpathyChatManager *chat_manager;
91 gulong chat_manager_chats_changed_id;
94 GtkUIManager *ui_manager;
95 GtkAction *menu_conv_insert_smiley;
96 GtkAction *menu_conv_favorite;
97 GtkAction *menu_conv_always_urgent;
98 GtkAction *menu_conv_toggle_contacts;
100 GtkAction *menu_edit_cut;
101 GtkAction *menu_edit_copy;
102 GtkAction *menu_edit_paste;
103 GtkAction *menu_edit_find;
105 GtkAction *menu_tabs_next;
106 GtkAction *menu_tabs_prev;
107 GtkAction *menu_tabs_undo_close_tab;
108 GtkAction *menu_tabs_left;
109 GtkAction *menu_tabs_right;
110 GtkAction *menu_tabs_detach;
112 /* Last user action time we acted upon to show a tab */
113 guint32 x_user_action_time;
115 GSettings *gsettings_chat;
116 } EmpathyChatWindowPriv;
118 static GList *chat_windows = NULL;
120 static const guint tab_accel_keys[] = {
121 GDK_KEY_1, GDK_KEY_2, GDK_KEY_3, GDK_KEY_4, GDK_KEY_5,
122 GDK_KEY_6, GDK_KEY_7, GDK_KEY_8, GDK_KEY_9, GDK_KEY_0
126 DND_DRAG_TYPE_CONTACT_ID,
127 DND_DRAG_TYPE_URI_LIST,
131 static const GtkTargetEntry drag_types_dest[] = {
132 { "text/contact-id", 0, DND_DRAG_TYPE_CONTACT_ID },
133 { "GTK_NOTEBOOK_TAB", GTK_TARGET_SAME_APP, DND_DRAG_TYPE_TAB },
134 { "text/uri-list", 0, DND_DRAG_TYPE_URI_LIST },
135 { "text/path-list", 0, DND_DRAG_TYPE_URI_LIST },
138 static const GtkTargetEntry drag_types_dest_contact[] = {
139 { "text/contact-id", 0, DND_DRAG_TYPE_CONTACT_ID },
142 static const GtkTargetEntry drag_types_dest_file[] = {
143 /* must be first to be prioritized, in order to receive the
144 * note's file path from Tomboy instead of an URI */
145 { "text/path-list", 0, DND_DRAG_TYPE_URI_LIST },
146 { "text/uri-list", 0, DND_DRAG_TYPE_URI_LIST },
149 static void chat_window_update (EmpathyChatWindow *window);
151 static void empathy_chat_window_add_chat (EmpathyChatWindow *window,
154 static void empathy_chat_window_remove_chat (EmpathyChatWindow *window,
157 static void empathy_chat_window_move_chat (EmpathyChatWindow *old_window,
158 EmpathyChatWindow *new_window,
161 static void empathy_chat_window_get_nb_chats (EmpathyChatWindow *self,
165 G_DEFINE_TYPE (EmpathyChatWindow, empathy_chat_window, G_TYPE_OBJECT);
168 chat_window_accel_cb (GtkAccelGroup *accelgroup,
172 EmpathyChatWindow *window)
174 EmpathyChatWindowPriv *priv;
178 priv = GET_PRIV (window);
180 for (i = 0; i < G_N_ELEMENTS (tab_accel_keys); i++) {
181 if (tab_accel_keys[i] == key) {
188 gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->notebook), num);
192 static EmpathyChatWindow *
193 chat_window_find_chat (EmpathyChat *chat)
195 EmpathyChatWindowPriv *priv;
198 for (l = chat_windows; l; l = l->next) {
199 priv = GET_PRIV (l->data);
200 ll = g_list_find (priv->chats, chat);
210 chat_window_close_clicked_cb (GtkAction *action,
213 EmpathyChatWindow *window;
215 window = chat_window_find_chat (chat);
216 empathy_chat_window_remove_chat (window, chat);
220 chat_tab_style_set_cb (GtkWidget *hbox,
221 GtkStyle *previous_style,
225 int char_width, h, w;
226 PangoContext *context;
227 PangoFontMetrics *metrics;
229 button = g_object_get_data (G_OBJECT (user_data),
230 "chat-window-tab-close-button");
231 context = gtk_widget_get_pango_context (hbox);
233 metrics = pango_context_get_metrics (context, gtk_widget_get_style (hbox)->font_desc,
234 pango_context_get_language (context));
235 char_width = pango_font_metrics_get_approximate_char_width (metrics);
236 pango_font_metrics_unref (metrics);
238 gtk_icon_size_lookup_for_settings (gtk_widget_get_settings (button),
239 GTK_ICON_SIZE_MENU, &w, &h);
241 /* Request at least about 12 chars width plus at least space for the status
242 * image and the close button */
243 gtk_widget_set_size_request (hbox,
244 12 * PANGO_PIXELS (char_width) + 2 * w, -1);
246 gtk_widget_set_size_request (button, w, h);
250 chat_window_create_label (EmpathyChatWindow *window,
252 gboolean is_tab_label)
254 EmpathyChatWindowPriv *priv;
256 GtkWidget *name_label;
257 GtkWidget *status_image;
258 GtkWidget *close_button;
259 GtkWidget *close_image;
260 GtkWidget *event_box;
261 GtkWidget *event_box_hbox;
262 PangoAttrList *attr_list;
263 PangoAttribute *attr;
265 priv = GET_PRIV (window);
267 /* The spacing between the button and the label. */
268 hbox = gtk_hbox_new (FALSE, 0);
270 event_box = gtk_event_box_new ();
271 gtk_event_box_set_visible_window (GTK_EVENT_BOX (event_box), FALSE);
273 name_label = gtk_label_new (NULL);
275 gtk_label_set_ellipsize (GTK_LABEL (name_label), PANGO_ELLIPSIZE_END);
277 attr_list = pango_attr_list_new ();
278 attr = pango_attr_scale_new (1/1.2);
279 attr->start_index = 0;
280 attr->end_index = -1;
281 pango_attr_list_insert (attr_list, attr);
282 gtk_label_set_attributes (GTK_LABEL (name_label), attr_list);
283 pango_attr_list_unref (attr_list);
285 gtk_misc_set_padding (GTK_MISC (name_label), 2, 0);
286 gtk_misc_set_alignment (GTK_MISC (name_label), 0.0, 0.5);
287 g_object_set_data (G_OBJECT (chat),
288 is_tab_label ? "chat-window-tab-label" : "chat-window-menu-label",
291 status_image = gtk_image_new ();
293 /* Spacing between the icon and label. */
294 event_box_hbox = gtk_hbox_new (FALSE, 0);
296 gtk_box_pack_start (GTK_BOX (event_box_hbox), status_image, FALSE, FALSE, 0);
297 gtk_box_pack_start (GTK_BOX (event_box_hbox), name_label, TRUE, TRUE, 0);
299 g_object_set_data (G_OBJECT (chat),
300 is_tab_label ? "chat-window-tab-image" : "chat-window-menu-image",
302 g_object_set_data (G_OBJECT (chat),
303 is_tab_label ? "chat-window-tab-tooltip-widget" : "chat-window-menu-tooltip-widget",
306 gtk_container_add (GTK_CONTAINER (event_box), event_box_hbox);
307 gtk_box_pack_start (GTK_BOX (hbox), event_box, TRUE, TRUE, 0);
310 close_button = gtk_button_new ();
311 gtk_button_set_relief (GTK_BUTTON (close_button), GTK_RELIEF_NONE);
312 g_object_set_data (G_OBJECT (chat), "chat-window-tab-close-button", close_button);
314 /* We don't want focus/keynav for the button to avoid clutter, and
315 * Ctrl-W works anyway.
317 gtk_widget_set_can_focus (close_button, FALSE);
318 gtk_widget_set_can_default (close_button, FALSE);
320 /* Set the name to make the special rc style match. */
321 gtk_widget_set_name (close_button, "empathy-close-button");
323 close_image = gtk_image_new_from_stock (GTK_STOCK_CLOSE, GTK_ICON_SIZE_MENU);
325 gtk_container_add (GTK_CONTAINER (close_button), close_image);
327 gtk_box_pack_end (GTK_BOX (hbox), close_button, FALSE, FALSE, 0);
329 g_signal_connect (close_button,
331 G_CALLBACK (chat_window_close_clicked_cb),
334 /* React to theme changes and also setup the size correctly. */
335 g_signal_connect (hbox,
337 G_CALLBACK (chat_tab_style_set_cb),
341 gtk_widget_show_all (hbox);
347 _submenu_notify_visible_changed_cb (GObject *object,
351 g_signal_handlers_disconnect_by_func (object,
352 _submenu_notify_visible_changed_cb,
354 chat_window_update (EMPATHY_CHAT_WINDOW (userdata));
358 chat_window_menu_context_update (EmpathyChatWindowPriv *priv,
363 gboolean wrap_around;
364 gboolean is_connected;
367 page_num = gtk_notebook_get_current_page (GTK_NOTEBOOK (priv->notebook));
368 first_page = (page_num == 0);
369 last_page = (page_num == (num_pages - 1));
370 g_object_get (gtk_settings_get_default (), "gtk-keynav-wrap-around",
372 is_connected = empathy_chat_get_tp_chat (priv->current_chat) != NULL;
374 gtk_action_set_sensitive (priv->menu_tabs_next, (!last_page ||
376 gtk_action_set_sensitive (priv->menu_tabs_prev, (!first_page ||
378 gtk_action_set_sensitive (priv->menu_tabs_detach, num_pages > 1);
379 gtk_action_set_sensitive (priv->menu_tabs_left, !first_page);
380 gtk_action_set_sensitive (priv->menu_tabs_right, !last_page);
381 gtk_action_set_sensitive (priv->menu_conv_insert_smiley, is_connected);
385 chat_window_conversation_menu_update (EmpathyChatWindowPriv *priv,
386 EmpathyChatWindow *self)
388 EmpathyTpChat *tp_chat;
389 TpConnection *connection;
391 gboolean sensitive = FALSE;
393 g_return_if_fail (priv->current_chat != NULL);
395 action = gtk_ui_manager_get_action (priv->ui_manager,
396 "/chats_menubar/menu_conv/menu_conv_invite_participant");
397 tp_chat = empathy_chat_get_tp_chat (priv->current_chat);
399 if (tp_chat != NULL) {
400 connection = empathy_tp_chat_get_connection (tp_chat);
402 sensitive = empathy_tp_chat_can_add_contact (tp_chat) &&
403 (tp_connection_get_status (connection, NULL) ==
404 TP_CONNECTION_STATUS_CONNECTED);
407 gtk_action_set_sensitive (action, sensitive);
411 chat_window_contact_menu_update (EmpathyChatWindowPriv *priv,
412 EmpathyChatWindow *window)
414 GtkWidget *menu, *submenu, *orig_submenu;
416 menu = gtk_ui_manager_get_widget (priv->ui_manager,
417 "/chats_menubar/menu_contact");
418 orig_submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (menu));
420 if (orig_submenu == NULL || !gtk_widget_get_visible (orig_submenu)) {
421 submenu = empathy_chat_get_contact_menu (priv->current_chat);
422 gtk_menu_item_set_submenu (GTK_MENU_ITEM (menu), submenu);
423 gtk_widget_show (menu);
425 tp_g_signal_connect_object (orig_submenu,
427 (GCallback)_submenu_notify_visible_changed_cb,
433 get_all_unread_messages (EmpathyChatWindowPriv *priv)
438 for (l = priv->chats_new_msg; l != NULL; l = g_list_next (l)) {
439 EmpathyChat *chat = l->data;
441 nb += empathy_chat_get_nb_unread_messages (chat);
448 get_window_title_name (EmpathyChatWindowPriv *priv)
450 const gchar *active_name;
452 guint current_unread_msgs;
454 nb_chats = g_list_length (priv->chats);
455 g_assert (nb_chats > 0);
457 active_name = empathy_chat_get_name (priv->current_chat);
459 current_unread_msgs = empathy_chat_get_nb_unread_messages (
464 if (current_unread_msgs == 0)
465 return g_strdup (active_name);
467 return g_strdup_printf (ngettext (
469 "%s (%d unread)", current_unread_msgs),
470 active_name, current_unread_msgs);
472 guint nb_others = nb_chats - 1;
473 guint all_unread_msgs;
475 all_unread_msgs = get_all_unread_messages (priv);
477 if (all_unread_msgs == 0) {
478 /* no unread message */
479 return g_strdup_printf (ngettext (
481 "%s (and %u others)", nb_others),
482 active_name, nb_others);
485 else if (all_unread_msgs == current_unread_msgs) {
486 /* unread messages are in the current tab */
487 return g_strdup_printf (ngettext (
489 "%s (%d unread)", current_unread_msgs),
490 active_name, current_unread_msgs);
493 else if (current_unread_msgs == 0) {
494 /* unread messages are in other tabs */
495 return g_strdup_printf (ngettext (
496 "%s (%d unread from others)",
497 "%s (%d unread from others)",
499 active_name, all_unread_msgs);
503 /* unread messages are in all the tabs */
504 return g_strdup_printf (ngettext (
505 "%s (%d unread from all)",
506 "%s (%d unread from all)",
508 active_name, all_unread_msgs);
514 chat_window_title_update (EmpathyChatWindowPriv *priv)
518 name = get_window_title_name (priv);
519 gtk_window_set_title (GTK_WINDOW (priv->dialog), name);
524 chat_window_icon_update (EmpathyChatWindowPriv *priv)
527 EmpathyContact *remote_contact;
528 gboolean avatar_in_icon;
531 n_chats = g_list_length (priv->chats);
533 /* Update window icon */
534 if (priv->chats_new_msg) {
535 gtk_window_set_icon_name (GTK_WINDOW (priv->dialog),
536 EMPATHY_IMAGE_MESSAGE);
538 avatar_in_icon = g_settings_get_boolean (priv->gsettings_chat,
539 EMPATHY_PREFS_CHAT_AVATAR_IN_ICON);
541 if (n_chats == 1 && avatar_in_icon) {
542 remote_contact = empathy_chat_get_remote_contact (priv->current_chat);
543 icon = empathy_pixbuf_avatar_from_contact_scaled (remote_contact, 0, 0);
544 gtk_window_set_icon (GTK_WINDOW (priv->dialog), icon);
547 g_object_unref (icon);
550 gtk_window_set_icon_name (GTK_WINDOW (priv->dialog), NULL);
556 chat_window_close_button_update (EmpathyChatWindowPriv *priv,
560 GtkWidget *chat_close_button;
563 if (num_pages == 1) {
564 chat = gtk_notebook_get_nth_page (GTK_NOTEBOOK (priv->notebook), 0);
565 chat_close_button = g_object_get_data (G_OBJECT (chat),
566 "chat-window-tab-close-button");
567 gtk_widget_hide (chat_close_button);
569 for (i=0; i<num_pages; i++) {
570 chat = gtk_notebook_get_nth_page (GTK_NOTEBOOK (priv->notebook), i);
571 chat_close_button = g_object_get_data (G_OBJECT (chat),
572 "chat-window-tab-close-button");
573 gtk_widget_show (chat_close_button);
579 chat_window_update (EmpathyChatWindow *window)
581 EmpathyChatWindowPriv *priv = GET_PRIV (window);
584 num_pages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (priv->notebook));
586 /* Update Tab menu */
587 chat_window_menu_context_update (priv,
590 chat_window_conversation_menu_update (priv, window);
592 chat_window_contact_menu_update (priv,
595 chat_window_title_update (priv);
597 chat_window_icon_update (priv);
599 chat_window_close_button_update (priv,
604 append_markup_printf (GString *string,
611 va_start (args, format);
613 tmp = g_markup_vprintf_escaped (format, args);
614 g_string_append (string, tmp);
621 chat_window_update_chat_tab (EmpathyChat *chat)
623 EmpathyChatWindow *window;
624 EmpathyChatWindowPriv *priv;
625 EmpathyContact *remote_contact;
629 const gchar *subject;
630 const gchar *status = NULL;
634 const gchar *icon_name;
635 GtkWidget *tab_image;
636 GtkWidget *menu_image;
638 window = chat_window_find_chat (chat);
642 priv = GET_PRIV (window);
644 /* Get information */
645 name = empathy_chat_get_name (chat);
646 account = empathy_chat_get_account (chat);
647 subject = empathy_chat_get_subject (chat);
648 remote_contact = empathy_chat_get_remote_contact (chat);
650 DEBUG ("Updating chat tab, name=%s, account=%s, subject=%s, remote_contact=%p",
651 name, tp_proxy_get_object_path (account), subject, remote_contact);
653 /* Update tab image */
654 if (empathy_chat_get_tp_chat (chat) == NULL) {
655 /* No TpChat, we are disconnected */
658 else if (g_list_find (priv->chats_new_msg, chat)) {
659 icon_name = EMPATHY_IMAGE_MESSAGE;
661 else if (g_list_find (priv->chats_composing, chat)) {
662 icon_name = EMPATHY_IMAGE_TYPING;
664 else if (remote_contact) {
665 icon_name = empathy_icon_name_for_contact (remote_contact);
667 icon_name = EMPATHY_IMAGE_GROUP_MESSAGE;
670 tab_image = g_object_get_data (G_OBJECT (chat), "chat-window-tab-image");
671 menu_image = g_object_get_data (G_OBJECT (chat), "chat-window-menu-image");
672 if (icon_name != NULL) {
673 gtk_image_set_from_icon_name (GTK_IMAGE (tab_image), icon_name, GTK_ICON_SIZE_MENU);
674 gtk_widget_show (tab_image);
675 gtk_image_set_from_icon_name (GTK_IMAGE (menu_image), icon_name, GTK_ICON_SIZE_MENU);
676 gtk_widget_show (menu_image);
678 gtk_widget_hide (tab_image);
679 gtk_widget_hide (menu_image);
682 /* Update tab tooltip */
683 tooltip = g_string_new (NULL);
685 if (remote_contact) {
686 id = empathy_contact_get_id (remote_contact);
687 status = empathy_contact_get_presence_message (remote_contact);
692 append_markup_printf (tooltip,
693 "<b>%s</b><small> (%s)</small>",
695 tp_account_get_display_name (account));
697 if (!EMP_STR_EMPTY (status)) {
698 append_markup_printf (tooltip, "\n<i>%s</i>", status);
702 append_markup_printf (tooltip, "\n<b>%s</b> %s",
703 _("Topic:"), subject);
706 if (g_list_find (priv->chats_composing, chat)) {
707 append_markup_printf (tooltip, "\n%s", _("Typing a message."));
710 markup = g_string_free (tooltip, FALSE);
711 widget = g_object_get_data (G_OBJECT (chat), "chat-window-tab-tooltip-widget");
712 gtk_widget_set_tooltip_markup (widget, markup);
713 widget = g_object_get_data (G_OBJECT (chat), "chat-window-menu-tooltip-widget");
714 gtk_widget_set_tooltip_markup (widget, markup);
717 /* Update tab and menu label */
718 widget = g_object_get_data (G_OBJECT (chat), "chat-window-tab-label");
719 gtk_label_set_text (GTK_LABEL (widget), name);
720 widget = g_object_get_data (G_OBJECT (chat), "chat-window-menu-label");
721 gtk_label_set_text (GTK_LABEL (widget), name);
723 /* Update the window if it's the current chat */
724 if (priv->current_chat == chat) {
725 chat_window_update (window);
730 chat_window_chat_notify_cb (EmpathyChat *chat)
732 EmpathyContact *old_remote_contact;
733 EmpathyContact *remote_contact = NULL;
735 old_remote_contact = g_object_get_data (G_OBJECT (chat), "chat-window-remote-contact");
736 remote_contact = empathy_chat_get_remote_contact (chat);
738 if (old_remote_contact != remote_contact) {
739 /* The remote-contact associated with the chat changed, we need
740 * to keep track of any change of that contact and update the
741 * window each time. */
742 if (remote_contact) {
743 g_signal_connect_swapped (remote_contact, "notify",
744 G_CALLBACK (chat_window_update_chat_tab),
747 if (old_remote_contact) {
748 g_signal_handlers_disconnect_by_func (old_remote_contact,
749 chat_window_update_chat_tab,
753 g_object_set_data_full (G_OBJECT (chat), "chat-window-remote-contact",
754 g_object_ref (remote_contact), (GDestroyNotify) g_object_unref);
757 chat_window_update_chat_tab (chat);
761 chat_window_insert_smiley_activate_cb (EmpathySmileyManager *manager,
762 EmpathySmiley *smiley,
765 EmpathyChatWindowPriv *priv = GET_PRIV (window);
767 GtkTextBuffer *buffer;
770 chat = priv->current_chat;
772 buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (chat->input_text_view));
773 gtk_text_buffer_get_end_iter (buffer, &iter);
774 gtk_text_buffer_insert (buffer, &iter, smiley->str, -1);
778 chat_window_conv_activate_cb (GtkAction *action,
779 EmpathyChatWindow *window)
781 EmpathyChatWindowPriv *priv = GET_PRIV (window);
784 EmpathyContact *remote_contact = NULL;
786 /* Favorite room menu */
787 is_room = empathy_chat_is_room (priv->current_chat);
791 gboolean found = FALSE;
792 EmpathyChatroom *chatroom;
794 room = empathy_chat_get_id (priv->current_chat);
795 account = empathy_chat_get_account (priv->current_chat);
796 chatroom = empathy_chatroom_manager_find (priv->chatroom_manager,
798 if (chatroom != NULL)
799 found = empathy_chatroom_is_favorite (chatroom);
801 DEBUG ("This room %s favorite", found ? "is" : "is not");
802 gtk_toggle_action_set_active (
803 GTK_TOGGLE_ACTION (priv->menu_conv_favorite), found);
805 if (chatroom != NULL)
806 found = empathy_chatroom_is_always_urgent (chatroom);
808 gtk_toggle_action_set_active (
809 GTK_TOGGLE_ACTION (priv->menu_conv_always_urgent),
812 gtk_action_set_visible (priv->menu_conv_favorite, is_room);
813 gtk_action_set_visible (priv->menu_conv_always_urgent, is_room);
815 /* Show contacts menu */
816 g_object_get (priv->current_chat,
817 "remote-contact", &remote_contact,
818 "show-contacts", &active,
820 if (remote_contact == NULL) {
821 gtk_toggle_action_set_active (
822 GTK_TOGGLE_ACTION (priv->menu_conv_toggle_contacts),
825 gtk_action_set_visible (priv->menu_conv_toggle_contacts,
826 (remote_contact == NULL));
827 if (remote_contact != NULL) {
828 g_object_unref (remote_contact);
833 chat_window_clear_activate_cb (GtkAction *action,
834 EmpathyChatWindow *window)
836 EmpathyChatWindowPriv *priv = GET_PRIV (window);
838 empathy_chat_clear (priv->current_chat);
842 chat_window_favorite_toggled_cb (GtkToggleAction *toggle_action,
843 EmpathyChatWindow *window)
845 EmpathyChatWindowPriv *priv = GET_PRIV (window);
849 EmpathyChatroom *chatroom;
851 active = gtk_toggle_action_get_active (toggle_action);
852 account = empathy_chat_get_account (priv->current_chat);
853 room = empathy_chat_get_id (priv->current_chat);
855 chatroom = empathy_chatroom_manager_ensure_chatroom (
856 priv->chatroom_manager,
859 empathy_chat_get_name (priv->current_chat));
861 empathy_chatroom_set_favorite (chatroom, active);
862 g_object_unref (chatroom);
866 chat_window_always_urgent_toggled_cb (GtkToggleAction *toggle_action,
867 EmpathyChatWindow *window)
869 EmpathyChatWindowPriv *priv = GET_PRIV (window);
873 EmpathyChatroom *chatroom;
875 active = gtk_toggle_action_get_active (toggle_action);
876 account = empathy_chat_get_account (priv->current_chat);
877 room = empathy_chat_get_id (priv->current_chat);
879 chatroom = empathy_chatroom_manager_ensure_chatroom (
880 priv->chatroom_manager,
883 empathy_chat_get_name (priv->current_chat));
885 empathy_chatroom_set_always_urgent (chatroom, active);
886 g_object_unref (chatroom);
890 chat_window_contacts_toggled_cb (GtkToggleAction *toggle_action,
891 EmpathyChatWindow *window)
893 EmpathyChatWindowPriv *priv = GET_PRIV (window);
896 active = gtk_toggle_action_get_active (toggle_action);
898 empathy_chat_set_show_contacts (priv->current_chat, active);
902 got_contact_cb (TpConnection *connection,
903 EmpathyContact *contact,
908 EmpathyTpChat *tp_chat = EMPATHY_TP_CHAT (user_data);
911 DEBUG ("Failed: %s", error->message);
914 empathy_contact_list_add (EMPATHY_CONTACT_LIST (tp_chat),
915 contact, _("Inviting you to this room"));
920 chat_window_invite_participant_activate_cb (GtkAction *action,
921 EmpathyChatWindow *window)
923 EmpathyChatWindowPriv *priv;
925 EmpathyTpChat *tp_chat;
930 priv = GET_PRIV (window);
932 g_return_if_fail (priv->current_chat != NULL);
934 tp_chat = empathy_chat_get_tp_chat (priv->current_chat);
935 channel = empathy_tp_chat_get_channel (tp_chat);
936 account = empathy_chat_get_account (priv->current_chat);
938 dialog = empathy_invite_participant_dialog_new (
939 GTK_WINDOW (priv->dialog), account);
940 gtk_widget_show (dialog);
942 response = gtk_dialog_run (GTK_DIALOG (dialog));
944 if (response == GTK_RESPONSE_ACCEPT) {
945 TpConnection *connection;
948 id = empathy_contact_selector_dialog_get_selected (
949 EMPATHY_CONTACT_SELECTOR_DIALOG (dialog), NULL, NULL);
950 if (EMP_STR_EMPTY (id)) goto out;
952 connection = tp_channel_borrow_connection (channel);
953 empathy_tp_contact_factory_get_from_id (connection, id,
954 got_contact_cb, tp_chat, NULL, NULL);
958 gtk_widget_destroy (dialog);
962 chat_window_close_activate_cb (GtkAction *action,
963 EmpathyChatWindow *window)
965 EmpathyChatWindowPriv *priv;
967 priv = GET_PRIV (window);
969 g_return_if_fail (priv->current_chat != NULL);
971 empathy_chat_window_remove_chat (window, priv->current_chat);
975 chat_window_edit_activate_cb (GtkAction *action,
976 EmpathyChatWindow *window)
978 EmpathyChatWindowPriv *priv;
979 GtkClipboard *clipboard;
980 GtkTextBuffer *buffer;
981 gboolean text_available;
983 priv = GET_PRIV (window);
985 g_return_if_fail (priv->current_chat != NULL);
987 if (!empathy_chat_get_tp_chat (priv->current_chat)) {
988 gtk_action_set_sensitive (priv->menu_edit_copy, FALSE);
989 gtk_action_set_sensitive (priv->menu_edit_cut, FALSE);
990 gtk_action_set_sensitive (priv->menu_edit_paste, FALSE);
994 buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (priv->current_chat->input_text_view));
995 if (gtk_text_buffer_get_has_selection (buffer)) {
996 gtk_action_set_sensitive (priv->menu_edit_copy, TRUE);
997 gtk_action_set_sensitive (priv->menu_edit_cut, TRUE);
1001 selection = empathy_chat_view_get_has_selection (priv->current_chat->view);
1003 gtk_action_set_sensitive (priv->menu_edit_cut, FALSE);
1004 gtk_action_set_sensitive (priv->menu_edit_copy, selection);
1007 clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
1008 text_available = gtk_clipboard_wait_is_text_available (clipboard);
1009 gtk_action_set_sensitive (priv->menu_edit_paste, text_available);
1013 chat_window_cut_activate_cb (GtkAction *action,
1014 EmpathyChatWindow *window)
1016 EmpathyChatWindowPriv *priv;
1018 g_return_if_fail (EMPATHY_IS_CHAT_WINDOW (window));
1020 priv = GET_PRIV (window);
1022 empathy_chat_cut (priv->current_chat);
1026 chat_window_copy_activate_cb (GtkAction *action,
1027 EmpathyChatWindow *window)
1029 EmpathyChatWindowPriv *priv;
1031 g_return_if_fail (EMPATHY_IS_CHAT_WINDOW (window));
1033 priv = GET_PRIV (window);
1035 empathy_chat_copy (priv->current_chat);
1039 chat_window_paste_activate_cb (GtkAction *action,
1040 EmpathyChatWindow *window)
1042 EmpathyChatWindowPriv *priv;
1044 g_return_if_fail (EMPATHY_IS_CHAT_WINDOW (window));
1046 priv = GET_PRIV (window);
1048 empathy_chat_paste (priv->current_chat);
1052 chat_window_find_activate_cb (GtkAction *action,
1053 EmpathyChatWindow *window)
1055 EmpathyChatWindowPriv *priv;
1057 g_return_if_fail (EMPATHY_IS_CHAT_WINDOW (window));
1059 priv = GET_PRIV (window);
1061 empathy_chat_find (priv->current_chat);
1065 chat_window_tabs_next_activate_cb (GtkAction *action,
1066 EmpathyChatWindow *window)
1068 EmpathyChatWindowPriv *priv;
1070 gint index_, numPages;
1071 gboolean wrap_around;
1073 priv = GET_PRIV (window);
1075 g_object_get (gtk_settings_get_default (), "gtk-keynav-wrap-around",
1076 &wrap_around, NULL);
1078 chat = priv->current_chat;
1079 index_ = gtk_notebook_get_current_page (GTK_NOTEBOOK (priv->notebook));
1080 numPages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (priv->notebook));
1082 if (index_ == (numPages - 1) && wrap_around) {
1083 gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->notebook), 0);
1087 gtk_notebook_next_page (GTK_NOTEBOOK (priv->notebook));
1091 chat_window_tabs_previous_activate_cb (GtkAction *action,
1092 EmpathyChatWindow *window)
1094 EmpathyChatWindowPriv *priv;
1096 gint index_, numPages;
1097 gboolean wrap_around;
1099 priv = GET_PRIV (window);
1101 g_object_get (gtk_settings_get_default (), "gtk-keynav-wrap-around",
1102 &wrap_around, NULL);
1104 chat = priv->current_chat;
1105 index_ = gtk_notebook_get_current_page (GTK_NOTEBOOK (priv->notebook));
1106 numPages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (priv->notebook));
1108 if (index_ <= 0 && wrap_around) {
1109 gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->notebook), numPages - 1);
1113 gtk_notebook_prev_page (GTK_NOTEBOOK (priv->notebook));
1117 chat_window_tabs_undo_close_tab_activate_cb (GtkAction *action,
1118 EmpathyChatWindow *window)
1120 EmpathyChatWindowPriv *priv = GET_PRIV (window);
1121 empathy_chat_manager_undo_closed_chat (priv->chat_manager);
1125 chat_window_tabs_left_activate_cb (GtkAction *action,
1126 EmpathyChatWindow *window)
1128 EmpathyChatWindowPriv *priv;
1130 gint index_, num_pages;
1132 priv = GET_PRIV (window);
1134 chat = priv->current_chat;
1135 index_ = gtk_notebook_get_current_page (GTK_NOTEBOOK (priv->notebook));
1140 gtk_notebook_reorder_child (GTK_NOTEBOOK (priv->notebook),
1144 num_pages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (priv->notebook));
1145 chat_window_menu_context_update (priv, num_pages);
1149 chat_window_tabs_right_activate_cb (GtkAction *action,
1150 EmpathyChatWindow *window)
1152 EmpathyChatWindowPriv *priv;
1154 gint index_, num_pages;
1156 priv = GET_PRIV (window);
1158 chat = priv->current_chat;
1159 index_ = gtk_notebook_get_current_page (GTK_NOTEBOOK (priv->notebook));
1161 gtk_notebook_reorder_child (GTK_NOTEBOOK (priv->notebook),
1165 num_pages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (priv->notebook));
1166 chat_window_menu_context_update (priv, num_pages);
1169 static EmpathyChatWindow *
1170 empathy_chat_window_new (void)
1172 return EMPATHY_CHAT_WINDOW (g_object_new (EMPATHY_TYPE_CHAT_WINDOW, NULL));
1176 chat_window_detach_activate_cb (GtkAction *action,
1177 EmpathyChatWindow *window)
1179 EmpathyChatWindowPriv *priv;
1180 EmpathyChatWindow *new_window;
1183 priv = GET_PRIV (window);
1185 chat = priv->current_chat;
1186 new_window = empathy_chat_window_new ();
1188 empathy_chat_window_move_chat (window, new_window, chat);
1190 priv = GET_PRIV (new_window);
1191 gtk_widget_show (priv->dialog);
1195 chat_window_help_contents_activate_cb (GtkAction *action,
1196 EmpathyChatWindow *window)
1198 EmpathyChatWindowPriv *priv = GET_PRIV (window);
1200 empathy_url_show (priv->dialog, "ghelp:empathy");
1204 chat_window_help_about_activate_cb (GtkAction *action,
1205 EmpathyChatWindow *window)
1207 EmpathyChatWindowPriv *priv = GET_PRIV (window);
1209 empathy_about_dialog_new (GTK_WINDOW (priv->dialog));
1213 chat_window_delete_event_cb (GtkWidget *dialog,
1215 EmpathyChatWindow *window)
1217 EmpathyChatWindowPriv *priv = GET_PRIV (window);
1219 DEBUG ("Delete event received");
1221 g_object_ref (window);
1222 while (priv->chats) {
1223 empathy_chat_window_remove_chat (window, priv->chats->data);
1225 g_object_unref (window);
1231 chat_window_composing_cb (EmpathyChat *chat,
1232 gboolean is_composing,
1233 EmpathyChatWindow *window)
1235 EmpathyChatWindowPriv *priv;
1237 priv = GET_PRIV (window);
1239 if (is_composing && !g_list_find (priv->chats_composing, chat)) {
1240 priv->chats_composing = g_list_prepend (priv->chats_composing, chat);
1242 priv->chats_composing = g_list_remove (priv->chats_composing, chat);
1245 chat_window_update_chat_tab (chat);
1249 chat_window_set_urgency_hint (EmpathyChatWindow *window,
1252 EmpathyChatWindowPriv *priv;
1254 priv = GET_PRIV (window);
1256 gtk_window_set_urgency_hint (GTK_WINDOW (priv->dialog), urgent);
1260 chat_window_notification_closed_cb (NotifyNotification *notify,
1261 EmpathyChatWindow *self)
1263 EmpathyChatWindowPriv *priv = GET_PRIV (self);
1265 g_object_unref (notify);
1266 if (priv->notification == notify) {
1267 priv->notification = NULL;
1272 chat_window_show_or_update_notification (EmpathyChatWindow *window,
1273 EmpathyMessage *message,
1276 EmpathyContact *sender;
1277 const gchar *header;
1281 EmpathyChatWindowPriv *priv = GET_PRIV (window);
1282 gboolean res, has_x_canonical_append;
1283 NotifyNotification *notification = priv->notification;
1285 if (!empathy_notify_manager_notification_is_enabled (priv->notify_mgr)) {
1288 GSettings *gsettings = g_settings_new (
1289 EMPATHY_PREFS_NOTIFICATIONS_SCHEMA);
1291 res = g_settings_get_boolean (gsettings,
1292 EMPATHY_PREFS_NOTIFICATIONS_FOCUS);
1294 g_object_unref (gsettings);
1301 sender = empathy_message_get_sender (message);
1302 header = empathy_contact_get_alias (sender);
1303 body = empathy_message_get_body (message);
1304 escaped = g_markup_escape_text (body, -1);
1305 has_x_canonical_append = empathy_notify_manager_has_capability (
1306 priv->notify_mgr, EMPATHY_NOTIFY_MANAGER_CAP_X_CANONICAL_APPEND);
1308 if (notification != NULL && !has_x_canonical_append) {
1309 /* if the notification server supports x-canonical-append, it is
1310 better to not use notify_notification_update to avoid
1311 overwriting the current notification message */
1312 notify_notification_update (notification,
1313 header, escaped, NULL);
1315 /* if the notification server supports x-canonical-append,
1316 the hint will be added, so that the message from the
1317 just created notification will be automatically appended
1318 to an existing notification with the same title.
1319 In this way the previous message will not be lost: the new
1320 message will appear below it, in the same notification */
1321 notification = notify_notification_new (header, escaped, NULL);
1323 if (priv->notification == NULL) {
1324 priv->notification = notification;
1327 notify_notification_set_timeout (notification, NOTIFY_EXPIRES_DEFAULT);
1329 tp_g_signal_connect_object (notification, "closed",
1330 G_CALLBACK (chat_window_notification_closed_cb), window, 0);
1332 if (has_x_canonical_append) {
1333 notify_notification_set_hint_string (notification,
1334 EMPATHY_NOTIFY_MANAGER_CAP_X_CANONICAL_APPEND, "");
1338 pixbuf = empathy_notify_manager_get_pixbuf_for_notification (priv->notify_mgr,
1339 sender, EMPATHY_IMAGE_NEW_MESSAGE);
1341 if (pixbuf != NULL) {
1342 notify_notification_set_icon_from_pixbuf (notification, pixbuf);
1343 g_object_unref (pixbuf);
1346 notify_notification_show (notification, NULL);
1352 chat_window_set_highlight_room_tab_label (EmpathyChat *chat)
1357 if (!empathy_chat_is_room (chat))
1360 markup = g_markup_printf_escaped (
1361 "<span color=\"red\" weight=\"bold\">%s</span>",
1362 empathy_chat_get_name (chat));
1364 widget = g_object_get_data (G_OBJECT (chat), "chat-window-tab-label");
1365 gtk_label_set_markup (GTK_LABEL (widget), markup);
1370 empathy_chat_window_has_focus (EmpathyChatWindow *window)
1372 EmpathyChatWindowPriv *priv;
1375 g_return_val_if_fail (EMPATHY_IS_CHAT_WINDOW (window), FALSE);
1377 priv = GET_PRIV (window);
1379 g_object_get (priv->dialog, "has-toplevel-focus", &has_focus, NULL);
1385 chat_window_new_message_cb (EmpathyChat *chat,
1386 EmpathyMessage *message,
1387 EmpathyChatWindow *window)
1389 EmpathyChatWindowPriv *priv;
1391 gboolean needs_urgency;
1392 EmpathyContact *sender;
1394 priv = GET_PRIV (window);
1396 has_focus = empathy_chat_window_has_focus (window);
1398 /* - if we're the sender, we play the sound if it's specified in the
1399 * preferences and we're not away.
1400 * - if we receive a message, we play the sound if it's specified in the
1401 * preferences and the window does not have focus on the chat receiving
1405 sender = empathy_message_get_sender (message);
1407 if (empathy_contact_is_user (sender)) {
1408 empathy_sound_play (GTK_WIDGET (priv->dialog),
1409 EMPATHY_SOUND_MESSAGE_OUTGOING);
1412 if (has_focus && priv->current_chat == chat) {
1413 /* window and tab are focused so consider the message to be read */
1415 /* FIXME: see Bug#610994 and coments about it in EmpathyChatPriv */
1416 empathy_chat_messages_read (chat);
1420 if (!g_list_find (priv->chats_new_msg, chat)) {
1421 priv->chats_new_msg = g_list_prepend (priv->chats_new_msg, chat);
1422 chat_window_update_chat_tab (chat);
1425 /* If empathy_chat_is_room () returns TRUE, that means it's a named MUC.
1426 * If empathy_chat_get_remote_contact () returns NULL, that means it's
1427 * an unamed MUC (msn-like).
1428 * In case of a MUC, we set urgency if either:
1429 * a) the chatroom's always_urgent property is TRUE
1430 * b) the message contains our alias
1432 if (empathy_chat_is_room (chat) ||
1433 empathy_chat_get_remote_contact (chat) == NULL) {
1436 EmpathyChatroom *chatroom;
1438 account = empathy_chat_get_account (chat);
1439 room = empathy_chat_get_id (chat);
1441 chatroom = empathy_chatroom_manager_find (priv->chatroom_manager,
1444 if (empathy_chatroom_is_always_urgent (chatroom)) {
1445 needs_urgency = TRUE;
1447 needs_urgency = empathy_message_should_highlight (message);
1450 needs_urgency = TRUE;
1453 if (needs_urgency) {
1455 chat_window_set_urgency_hint (window, TRUE);
1456 chat_window_set_highlight_room_tab_label (chat);
1459 empathy_sound_play (GTK_WIDGET (priv->dialog),
1460 EMPATHY_SOUND_MESSAGE_INCOMING);
1461 chat_window_show_or_update_notification (window, message, chat);
1464 /* update the number of unread messages and the window icon */
1465 chat_window_title_update (priv);
1466 chat_window_icon_update (priv);
1469 static GtkNotebook *
1470 notebook_create_window_cb (GtkNotebook *source,
1476 EmpathyChatWindowPriv *priv;
1477 EmpathyChatWindow *window, *new_window;
1480 chat = EMPATHY_CHAT (page);
1481 window = chat_window_find_chat (chat);
1483 new_window = empathy_chat_window_new ();
1484 priv = GET_PRIV (new_window);
1486 DEBUG ("Detach hook called");
1488 empathy_chat_window_move_chat (window, new_window, chat);
1490 gtk_widget_show (priv->dialog);
1491 gtk_window_move (GTK_WINDOW (priv->dialog), x, y);
1497 chat_window_page_switched_cb (GtkNotebook *notebook,
1498 gpointer ignore, /* see note below */
1500 EmpathyChatWindow *window)
1502 EmpathyChatWindowPriv *priv;
1506 DEBUG ("Page switched");
1508 priv = GET_PRIV (window);
1510 /* N.B. in GTK+ 3 child is passed as the first argument to the signal,
1511 * but we can't use that while trying to retain GTK+ 2.x compatibility.
1513 child = gtk_notebook_get_nth_page (notebook, page_num);
1514 chat = EMPATHY_CHAT (child);
1516 if (priv->page_added) {
1517 priv->page_added = FALSE;
1518 empathy_chat_scroll_down (chat);
1520 else if (priv->current_chat == chat) {
1524 priv->current_chat = chat;
1525 priv->chats_new_msg = g_list_remove (priv->chats_new_msg, chat);
1526 empathy_chat_messages_read (chat);
1528 chat_window_update_chat_tab (chat);
1532 chat_window_page_added_cb (GtkNotebook *notebook,
1535 EmpathyChatWindow *window)
1537 EmpathyChatWindowPriv *priv;
1540 priv = GET_PRIV (window);
1542 /* If we just received DND to the same window, we don't want
1543 * to do anything here like removing the tab and then readding
1544 * it, so we return here and in "page-added".
1546 if (priv->dnd_same_window) {
1547 DEBUG ("Page added (back to the same window)");
1548 priv->dnd_same_window = FALSE;
1552 DEBUG ("Page added");
1554 /* Get chat object */
1555 chat = EMPATHY_CHAT (child);
1557 /* Connect chat signals for this window */
1558 g_signal_connect (chat, "composing",
1559 G_CALLBACK (chat_window_composing_cb),
1561 g_signal_connect (chat, "new-message",
1562 G_CALLBACK (chat_window_new_message_cb),
1564 g_signal_connect (chat, "notify::tp-chat",
1565 G_CALLBACK (chat_window_update_chat_tab),
1568 /* Set flag so we know to perform some special operations on
1569 * switch page due to the new page being added.
1571 priv->page_added = TRUE;
1573 /* Get list of chats up to date */
1574 priv->chats = g_list_append (priv->chats, chat);
1576 chat_window_update_chat_tab (chat);
1580 chat_window_page_removed_cb (GtkNotebook *notebook,
1583 EmpathyChatWindow *window)
1585 EmpathyChatWindowPriv *priv;
1588 priv = GET_PRIV (window);
1590 /* If we just received DND to the same window, we don't want
1591 * to do anything here like removing the tab and then readding
1592 * it, so we return here and in "page-added".
1594 if (priv->dnd_same_window) {
1595 DEBUG ("Page removed (and will be readded to same window)");
1599 DEBUG ("Page removed");
1601 /* Get chat object */
1602 chat = EMPATHY_CHAT (child);
1604 /* Disconnect all signal handlers for this chat and this window */
1605 g_signal_handlers_disconnect_by_func (chat,
1606 G_CALLBACK (chat_window_composing_cb),
1608 g_signal_handlers_disconnect_by_func (chat,
1609 G_CALLBACK (chat_window_new_message_cb),
1611 g_signal_handlers_disconnect_by_func (chat,
1612 G_CALLBACK (chat_window_update_chat_tab),
1615 /* Keep list of chats up to date */
1616 priv->chats = g_list_remove (priv->chats, chat);
1617 priv->chats_new_msg = g_list_remove (priv->chats_new_msg, chat);
1618 empathy_chat_messages_read (chat);
1619 priv->chats_composing = g_list_remove (priv->chats_composing, chat);
1621 if (priv->chats == NULL) {
1622 g_object_unref (window);
1624 chat_window_update (window);
1629 chat_window_focus_in_event_cb (GtkWidget *widget,
1631 EmpathyChatWindow *window)
1633 EmpathyChatWindowPriv *priv;
1635 priv = GET_PRIV (window);
1637 priv->chats_new_msg = g_list_remove (priv->chats_new_msg, priv->current_chat);
1638 empathy_chat_messages_read (priv->current_chat);
1640 chat_window_set_urgency_hint (window, FALSE);
1642 /* Update the title, since we now mark all unread messages as read. */
1643 chat_window_update_chat_tab (priv->current_chat);
1649 chat_window_drag_drop (GtkWidget *widget,
1650 GdkDragContext *context,
1654 EmpathyChatWindow *window)
1657 EmpathyChatWindowPriv *priv;
1659 priv = GET_PRIV (window);
1661 target = gtk_drag_dest_find_target (widget, context, priv->file_targets);
1662 if (target == GDK_NONE)
1663 target = gtk_drag_dest_find_target (widget, context, priv->contact_targets);
1665 if (target != GDK_NONE) {
1666 gtk_drag_get_data (widget, context, target, time_);
1674 chat_window_drag_motion (GtkWidget *widget,
1675 GdkDragContext *context,
1679 EmpathyChatWindow *window)
1682 EmpathyChatWindowPriv *priv;
1684 priv = GET_PRIV (window);
1686 target = gtk_drag_dest_find_target (widget, context, priv->file_targets);
1687 if (target != GDK_NONE) {
1688 /* This is a file drag. Ensure the contact is online and set the
1689 drag type to COPY. Note that it's possible that the tab will
1690 be switched by GTK+ after a timeout from drag_motion without
1691 getting another drag_motion to disable the drop. You have
1692 to hold your mouse really still.
1694 EmpathyContact *contact;
1696 priv = GET_PRIV (window);
1697 contact = empathy_chat_get_remote_contact (priv->current_chat);
1698 /* contact is NULL for multi-user chats. We don't do
1699 * file transfers to MUCs. We also don't send files
1700 * to offline contacts or contacts that don't support
1703 if ((contact == NULL) || !empathy_contact_is_online (contact)) {
1704 gdk_drag_status (context, 0, time_);
1707 if (!(empathy_contact_get_capabilities (contact)
1708 & EMPATHY_CAPABILITIES_FT)) {
1709 gdk_drag_status (context, 0, time_);
1712 gdk_drag_status (context, GDK_ACTION_COPY, time_);
1716 target = gtk_drag_dest_find_target (widget, context, priv->contact_targets);
1717 if (target != GDK_NONE) {
1718 /* This is a drag of a contact from a contact list. Set to COPY.
1719 FIXME: If this drag is to a MUC window, it invites the user.
1720 Otherwise, it opens a chat. Should we use a different drag
1721 type for invites? Should we allow ASK?
1723 gdk_drag_status (context, GDK_ACTION_COPY, time_);
1731 chat_window_drag_data_received (GtkWidget *widget,
1732 GdkDragContext *context,
1735 GtkSelectionData *selection,
1738 EmpathyChatWindow *window)
1740 if (info == DND_DRAG_TYPE_CONTACT_ID) {
1741 EmpathyChat *chat = NULL;
1742 EmpathyChatWindow *old_window;
1743 TpAccount *account = NULL;
1744 TpAccountManager *account_manager;
1747 const gchar *account_id;
1748 const gchar *contact_id;
1750 id = (const gchar*) gtk_selection_data_get_data (selection);
1752 /* FIXME: Perhaps should be sure that the account manager is
1753 * prepared before calling _ensure_account on it. */
1754 account_manager = tp_account_manager_dup ();
1756 DEBUG ("DND contact from roster with id:'%s'", id);
1758 strv = g_strsplit (id, ":", 2);
1759 if (g_strv_length (strv) == 2) {
1760 account_id = strv[0];
1761 contact_id = strv[1];
1763 tp_account_manager_ensure_account (account_manager, account_id);
1764 if (account != NULL)
1765 chat = empathy_chat_window_find_chat (account, contact_id);
1768 if (account == NULL) {
1770 gtk_drag_finish (context, FALSE, FALSE, time_);
1775 empathy_dispatcher_chat_with_contact_id (
1776 account, contact_id, gtk_get_current_event_time ());
1781 g_object_unref (account_manager);
1784 old_window = chat_window_find_chat (chat);
1786 if (old_window == window) {
1787 gtk_drag_finish (context, TRUE, FALSE, time_);
1791 empathy_chat_window_move_chat (old_window, window, chat);
1793 empathy_chat_window_add_chat (window, chat);
1796 /* Added to take care of any outstanding chat events */
1797 empathy_chat_window_present_chat (chat,
1798 TP_USER_ACTION_TIME_NOT_USER_ACTION);
1800 /* We should return TRUE to remove the data when doing
1801 * GDK_ACTION_MOVE, but we don't here otherwise it has
1802 * weird consequences, and we handle that internally
1803 * anyway with add_chat () and remove_chat ().
1805 gtk_drag_finish (context, TRUE, FALSE, time_);
1807 else if (info == DND_DRAG_TYPE_URI_LIST) {
1808 EmpathyChatWindowPriv *priv;
1809 EmpathyContact *contact;
1812 priv = GET_PRIV (window);
1813 contact = empathy_chat_get_remote_contact (priv->current_chat);
1815 /* contact is NULL when current_chat is a multi-user chat.
1816 * We don't do file transfers to MUCs, so just cancel the drag.
1818 if (contact == NULL) {
1819 gtk_drag_finish (context, TRUE, FALSE, time_);
1823 data = (const gchar *) gtk_selection_data_get_data (selection);
1824 empathy_send_file_from_uri_list (contact, data);
1826 gtk_drag_finish (context, TRUE, FALSE, time_);
1828 else if (info == DND_DRAG_TYPE_TAB) {
1830 EmpathyChatWindow *old_window = NULL;
1834 chat = (void *) gtk_selection_data_get_data (selection);
1835 old_window = chat_window_find_chat (*chat);
1838 EmpathyChatWindowPriv *priv;
1840 priv = GET_PRIV (window);
1841 priv->dnd_same_window = (old_window == window);
1842 DEBUG ("DND tab (within same window: %s)",
1843 priv->dnd_same_window ? "Yes" : "No");
1846 DEBUG ("DND from unknown source");
1847 gtk_drag_finish (context, FALSE, FALSE, time_);
1852 chat_window_chat_manager_chats_changed_cb (EmpathyChatManager *chat_manager,
1853 guint num_chats_in_manager,
1854 EmpathyChatWindow *window)
1856 EmpathyChatWindowPriv *priv = GET_PRIV (window);
1858 gtk_action_set_sensitive (priv->menu_tabs_undo_close_tab,
1859 num_chats_in_manager > 0);
1863 chat_window_finalize (GObject *object)
1865 EmpathyChatWindow *window;
1866 EmpathyChatWindowPriv *priv;
1868 window = EMPATHY_CHAT_WINDOW (object);
1869 priv = GET_PRIV (window);
1871 DEBUG ("Finalized: %p", object);
1873 g_object_unref (priv->ui_manager);
1874 g_object_unref (priv->chatroom_manager);
1875 g_object_unref (priv->notify_mgr);
1876 g_object_unref (priv->gsettings_chat);
1878 if (priv->notification != NULL) {
1879 notify_notification_close (priv->notification, NULL);
1880 priv->notification = NULL;
1883 if (priv->contact_targets) {
1884 gtk_target_list_unref (priv->contact_targets);
1886 if (priv->file_targets) {
1887 gtk_target_list_unref (priv->file_targets);
1890 if (priv->chat_manager) {
1891 g_signal_handler_disconnect (priv->chat_manager,
1892 priv->chat_manager_chats_changed_id);
1893 g_object_unref (priv->chat_manager);
1894 priv->chat_manager = NULL;
1897 chat_windows = g_list_remove (chat_windows, window);
1898 gtk_widget_destroy (priv->dialog);
1900 G_OBJECT_CLASS (empathy_chat_window_parent_class)->finalize (object);
1904 empathy_chat_window_class_init (EmpathyChatWindowClass *klass)
1906 GObjectClass *object_class = G_OBJECT_CLASS (klass);
1908 object_class->finalize = chat_window_finalize;
1910 g_type_class_add_private (object_class, sizeof (EmpathyChatWindowPriv));
1912 /* Set up a style for the close button with no focus padding. */
1913 gtk_rc_parse_string (
1914 "style \"empathy-close-button-style\"\n"
1916 " GtkWidget::focus-padding = 0\n"
1920 "widget \"*.empathy-close-button\" style \"empathy-close-button-style\"");
1924 empathy_chat_window_init (EmpathyChatWindow *window)
1927 GtkAccelGroup *accel_group;
1932 GtkWidget *chat_vbox;
1934 EmpathySmileyManager *smiley_manager;
1935 EmpathyChatWindowPriv *priv = G_TYPE_INSTANCE_GET_PRIVATE (window,
1936 EMPATHY_TYPE_CHAT_WINDOW, EmpathyChatWindowPriv);
1938 window->priv = priv;
1939 filename = empathy_file_lookup ("empathy-chat-window.ui", "src");
1940 gui = empathy_builder_get_file (filename,
1941 "chat_window", &priv->dialog,
1942 "chat_vbox", &chat_vbox,
1943 "ui_manager", &priv->ui_manager,
1944 "menu_conv_insert_smiley", &priv->menu_conv_insert_smiley,
1945 "menu_conv_favorite", &priv->menu_conv_favorite,
1946 "menu_conv_always_urgent", &priv->menu_conv_always_urgent,
1947 "menu_conv_toggle_contacts", &priv->menu_conv_toggle_contacts,
1948 "menu_edit_cut", &priv->menu_edit_cut,
1949 "menu_edit_copy", &priv->menu_edit_copy,
1950 "menu_edit_paste", &priv->menu_edit_paste,
1951 "menu_edit_find", &priv->menu_edit_find,
1952 "menu_tabs_next", &priv->menu_tabs_next,
1953 "menu_tabs_prev", &priv->menu_tabs_prev,
1954 "menu_tabs_undo_close_tab", &priv->menu_tabs_undo_close_tab,
1955 "menu_tabs_left", &priv->menu_tabs_left,
1956 "menu_tabs_right", &priv->menu_tabs_right,
1957 "menu_tabs_detach", &priv->menu_tabs_detach,
1961 empathy_builder_connect (gui, window,
1962 "menu_conv", "activate", chat_window_conv_activate_cb,
1963 "menu_conv_clear", "activate", chat_window_clear_activate_cb,
1964 "menu_conv_favorite", "toggled", chat_window_favorite_toggled_cb,
1965 "menu_conv_always_urgent", "toggled", chat_window_always_urgent_toggled_cb,
1966 "menu_conv_toggle_contacts", "toggled", chat_window_contacts_toggled_cb,
1967 "menu_conv_invite_participant", "activate", chat_window_invite_participant_activate_cb,
1968 "menu_conv_close", "activate", chat_window_close_activate_cb,
1969 "menu_edit", "activate", chat_window_edit_activate_cb,
1970 "menu_edit_cut", "activate", chat_window_cut_activate_cb,
1971 "menu_edit_copy", "activate", chat_window_copy_activate_cb,
1972 "menu_edit_paste", "activate", chat_window_paste_activate_cb,
1973 "menu_edit_find", "activate", chat_window_find_activate_cb,
1974 "menu_tabs_next", "activate", chat_window_tabs_next_activate_cb,
1975 "menu_tabs_prev", "activate", chat_window_tabs_previous_activate_cb,
1976 "menu_tabs_undo_close_tab", "activate", chat_window_tabs_undo_close_tab_activate_cb,
1977 "menu_tabs_left", "activate", chat_window_tabs_left_activate_cb,
1978 "menu_tabs_right", "activate", chat_window_tabs_right_activate_cb,
1979 "menu_tabs_detach", "activate", chat_window_detach_activate_cb,
1980 "menu_help_contents", "activate", chat_window_help_contents_activate_cb,
1981 "menu_help_about", "activate", chat_window_help_about_activate_cb,
1984 g_object_ref (priv->ui_manager);
1985 g_object_unref (gui);
1987 priv->gsettings_chat = g_settings_new (EMPATHY_PREFS_CHAT_SCHEMA);
1988 priv->chatroom_manager = empathy_chatroom_manager_dup_singleton (NULL);
1990 priv->notebook = gtk_notebook_new ();
1992 g_signal_connect (priv->notebook, "create-window",
1993 G_CALLBACK (notebook_create_window_cb), window);
1995 gtk_notebook_set_group_name (GTK_NOTEBOOK (priv->notebook),
1996 "EmpathyChatWindow");
1997 gtk_notebook_set_scrollable (GTK_NOTEBOOK (priv->notebook), TRUE);
1998 gtk_notebook_popup_enable (GTK_NOTEBOOK (priv->notebook));
1999 gtk_box_pack_start (GTK_BOX (chat_vbox), priv->notebook, TRUE, TRUE, 0);
2000 gtk_widget_show (priv->notebook);
2003 accel_group = gtk_accel_group_new ();
2004 gtk_window_add_accel_group (GTK_WINDOW (priv->dialog), accel_group);
2006 for (i = 0; i < G_N_ELEMENTS (tab_accel_keys); i++) {
2007 closure = g_cclosure_new (G_CALLBACK (chat_window_accel_cb),
2010 gtk_accel_group_connect (accel_group,
2017 g_object_unref (accel_group);
2019 /* Set up drag target lists */
2020 priv->contact_targets = gtk_target_list_new (drag_types_dest_contact,
2021 G_N_ELEMENTS (drag_types_dest_contact));
2022 priv->file_targets = gtk_target_list_new (drag_types_dest_file,
2023 G_N_ELEMENTS (drag_types_dest_file));
2025 /* Set up smiley menu */
2026 smiley_manager = empathy_smiley_manager_dup_singleton ();
2027 submenu = empathy_smiley_menu_new (smiley_manager,
2028 chat_window_insert_smiley_activate_cb,
2030 menu = gtk_ui_manager_get_widget (priv->ui_manager,
2031 "/chats_menubar/menu_conv/menu_conv_insert_smiley");
2032 gtk_menu_item_set_submenu (GTK_MENU_ITEM (menu), submenu);
2033 g_object_unref (smiley_manager);
2035 /* Set up signals we can't do with ui file since we may need to
2036 * block/unblock them at some later stage.
2039 g_signal_connect (priv->dialog,
2041 G_CALLBACK (chat_window_delete_event_cb),
2043 g_signal_connect (priv->dialog,
2045 G_CALLBACK (chat_window_focus_in_event_cb),
2047 g_signal_connect_after (priv->notebook,
2049 G_CALLBACK (chat_window_page_switched_cb),
2051 g_signal_connect (priv->notebook,
2053 G_CALLBACK (chat_window_page_added_cb),
2055 g_signal_connect (priv->notebook,
2057 G_CALLBACK (chat_window_page_removed_cb),
2060 /* Set up drag and drop */
2061 gtk_drag_dest_set (GTK_WIDGET (priv->notebook),
2062 GTK_DEST_DEFAULT_HIGHLIGHT,
2064 G_N_ELEMENTS (drag_types_dest),
2065 GDK_ACTION_MOVE | GDK_ACTION_COPY);
2067 /* connect_after to allow GtkNotebook's built-in tab switching */
2068 g_signal_connect_after (priv->notebook,
2070 G_CALLBACK (chat_window_drag_motion),
2072 g_signal_connect (priv->notebook,
2073 "drag-data-received",
2074 G_CALLBACK (chat_window_drag_data_received),
2076 g_signal_connect (priv->notebook,
2078 G_CALLBACK (chat_window_drag_drop),
2081 chat_windows = g_list_prepend (chat_windows, window);
2083 /* Set up private details */
2085 priv->chats_new_msg = NULL;
2086 priv->chats_composing = NULL;
2087 priv->current_chat = NULL;
2088 priv->notification = NULL;
2090 priv->notify_mgr = empathy_notify_manager_dup_singleton ();
2092 priv->chat_manager = empathy_chat_manager_dup_singleton ();
2093 priv->chat_manager_chats_changed_id =
2094 g_signal_connect (priv->chat_manager, "closed-chats-changed",
2095 G_CALLBACK (chat_window_chat_manager_chats_changed_cb),
2098 chat_window_chat_manager_chats_changed_cb (priv->chat_manager,
2099 empathy_chat_manager_get_num_closed_chats (priv->chat_manager),
2104 empathy_chat_window_get_dialog (EmpathyChatWindow *window)
2106 EmpathyChatWindowPriv *priv;
2108 g_return_val_if_fail (window != NULL, NULL);
2110 priv = GET_PRIV (window);
2112 return priv->dialog;
2115 /* Returns the window to open a new tab in if there is a suitable window,
2116 * otherwise, returns NULL indicating that a new window should be added.
2118 static EmpathyChatWindow *
2119 empathy_chat_window_get_default (gboolean room)
2121 GSettings *gsettings = g_settings_new (EMPATHY_PREFS_UI_SCHEMA);
2123 gboolean separate_windows = TRUE;
2125 separate_windows = g_settings_get_boolean (gsettings,
2126 EMPATHY_PREFS_UI_SEPARATE_CHAT_WINDOWS);
2128 g_object_unref (gsettings);
2130 if (separate_windows) {
2131 /* Always create a new window */
2135 for (l = chat_windows; l; l = l->next) {
2136 EmpathyChatWindowPriv *priv;
2137 EmpathyChatWindow *chat_window;
2139 guint nb_rooms, nb_private;
2141 chat_window = l->data;
2142 priv = GET_PRIV (chat_window);
2144 dialog = empathy_chat_window_get_dialog (chat_window);
2146 empathy_chat_window_get_nb_chats (chat_window, &nb_rooms, &nb_private);
2148 /* Skip the window if there aren't any rooms in it */
2149 if (room && nb_rooms == 0)
2152 /* Skip the window if there aren't any 1-1 chats in it */
2153 if (!room && nb_private == 0)
2156 /* Found a window on this desktop, make it visible if necessary */
2157 if (!empathy_window_get_is_visible (GTK_WINDOW (dialog)))
2158 empathy_window_present (GTK_WINDOW (dialog));
2166 empathy_chat_window_add_chat (EmpathyChatWindow *window,
2169 EmpathyChatWindowPriv *priv;
2171 GtkWidget *popup_label;
2173 GValue value = { 0, };
2175 g_return_if_fail (window != NULL);
2176 g_return_if_fail (EMPATHY_IS_CHAT (chat));
2178 priv = GET_PRIV (window);
2180 /* Reference the chat object */
2181 g_object_ref (chat);
2183 /* If this window has just been created, position it */
2184 if (priv->chats == NULL) {
2185 GSettings *gsettings = g_settings_new (EMPATHY_PREFS_UI_SCHEMA);
2186 const gchar *name = "chat-window";
2187 gboolean separate_windows;
2189 separate_windows = g_settings_get_boolean (gsettings,
2190 EMPATHY_PREFS_UI_SEPARATE_CHAT_WINDOWS);
2192 g_object_unref (gsettings);
2194 if (empathy_chat_is_room (chat))
2195 name = "room-window";
2197 if (separate_windows) {
2200 /* Save current position of the window */
2201 gtk_window_get_position (GTK_WINDOW (priv->dialog), &x, &y);
2203 /* First bind to the 'generic' name. So new window for which we didn't
2204 * save a geometry yet will have the geometry of the last saved
2205 * window (bgo #601191). */
2206 empathy_geometry_bind (GTK_WINDOW (priv->dialog), name);
2208 /* Restore previous position of the window so the newly created window
2209 * won't be in the same position as the latest saved window and so
2210 * completely hide it. */
2211 gtk_window_move (GTK_WINDOW (priv->dialog), x, y);
2213 /* Then bind it to the name of the contact/room so we'll save the
2214 * geometry specific to this window */
2215 name = empathy_chat_get_id (chat);
2218 empathy_geometry_bind (GTK_WINDOW (priv->dialog), name);
2221 child = GTK_WIDGET (chat);
2222 label = chat_window_create_label (window, chat, TRUE);
2223 popup_label = chat_window_create_label (window, chat, FALSE);
2224 gtk_widget_show (child);
2226 g_signal_connect (chat, "notify::name",
2227 G_CALLBACK (chat_window_chat_notify_cb),
2229 g_signal_connect (chat, "notify::subject",
2230 G_CALLBACK (chat_window_chat_notify_cb),
2232 g_signal_connect (chat, "notify::remote-contact",
2233 G_CALLBACK (chat_window_chat_notify_cb),
2235 chat_window_chat_notify_cb (chat);
2237 gtk_notebook_append_page_menu (GTK_NOTEBOOK (priv->notebook), child, label, popup_label);
2238 gtk_notebook_set_tab_reorderable (GTK_NOTEBOOK (priv->notebook), child, TRUE);
2239 gtk_notebook_set_tab_detachable (GTK_NOTEBOOK (priv->notebook), child, TRUE);
2240 g_value_init (&value, G_TYPE_BOOLEAN);
2241 g_value_set_boolean (&value, TRUE);
2242 gtk_container_child_set_property (GTK_CONTAINER (priv->notebook),
2243 child, "tab-expand" , &value);
2244 gtk_container_child_set_property (GTK_CONTAINER (priv->notebook),
2245 child, "tab-fill" , &value);
2246 g_value_unset (&value);
2248 DEBUG ("Chat added (%d references)", G_OBJECT (chat)->ref_count);
2252 empathy_chat_window_remove_chat (EmpathyChatWindow *window,
2255 EmpathyChatWindowPriv *priv;
2257 EmpathyContact *remote_contact;
2258 EmpathyChatManager *chat_manager;
2260 g_return_if_fail (window != NULL);
2261 g_return_if_fail (EMPATHY_IS_CHAT (chat));
2263 priv = GET_PRIV (window);
2265 g_signal_handlers_disconnect_by_func (chat,
2266 chat_window_chat_notify_cb,
2268 remote_contact = g_object_get_data (G_OBJECT (chat),
2269 "chat-window-remote-contact");
2270 if (remote_contact) {
2271 g_signal_handlers_disconnect_by_func (remote_contact,
2272 chat_window_update_chat_tab,
2276 chat_manager = empathy_chat_manager_dup_singleton ();
2277 empathy_chat_manager_closed_chat (chat_manager, chat);
2278 g_object_unref (chat_manager);
2280 position = gtk_notebook_page_num (GTK_NOTEBOOK (priv->notebook),
2282 gtk_notebook_remove_page (GTK_NOTEBOOK (priv->notebook), position);
2284 DEBUG ("Chat removed (%d references)", G_OBJECT (chat)->ref_count - 1);
2286 g_object_unref (chat);
2290 empathy_chat_window_move_chat (EmpathyChatWindow *old_window,
2291 EmpathyChatWindow *new_window,
2296 g_return_if_fail (EMPATHY_IS_CHAT_WINDOW (old_window));
2297 g_return_if_fail (EMPATHY_IS_CHAT_WINDOW (new_window));
2298 g_return_if_fail (EMPATHY_IS_CHAT (chat));
2300 widget = GTK_WIDGET (chat);
2302 DEBUG ("Chat moving with widget:%p (%d references)", widget,
2303 G_OBJECT (widget)->ref_count);
2305 /* We reference here to make sure we don't loose the widget
2306 * and the EmpathyChat object during the move.
2308 g_object_ref (chat);
2309 g_object_ref (widget);
2311 empathy_chat_window_remove_chat (old_window, chat);
2312 empathy_chat_window_add_chat (new_window, chat);
2314 g_object_unref (widget);
2315 g_object_unref (chat);
2319 empathy_chat_window_switch_to_chat (EmpathyChatWindow *window,
2322 EmpathyChatWindowPriv *priv;
2325 g_return_if_fail (window != NULL);
2326 g_return_if_fail (EMPATHY_IS_CHAT (chat));
2328 priv = GET_PRIV (window);
2330 page_num = gtk_notebook_page_num (GTK_NOTEBOOK (priv->notebook),
2332 gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->notebook),
2337 empathy_chat_window_find_chat (TpAccount *account,
2342 g_return_val_if_fail (!EMP_STR_EMPTY (id), NULL);
2344 for (l = chat_windows; l; l = l->next) {
2345 EmpathyChatWindowPriv *priv;
2346 EmpathyChatWindow *window;
2350 priv = GET_PRIV (window);
2352 for (ll = priv->chats; ll; ll = ll->next) {
2357 if (account == empathy_chat_get_account (chat) &&
2358 !tp_strdiff (id, empathy_chat_get_id (chat))) {
2368 empathy_chat_window_present_chat (EmpathyChat *chat,
2371 EmpathyChatWindow *window;
2372 EmpathyChatWindowPriv *priv;
2373 guint32 x_timestamp;
2375 g_return_if_fail (EMPATHY_IS_CHAT (chat));
2377 window = chat_window_find_chat (chat);
2379 /* If the chat has no window, create one */
2380 if (window == NULL) {
2381 window = empathy_chat_window_get_default (empathy_chat_is_room (chat));
2383 window = empathy_chat_window_new ();
2384 gtk_widget_show_all (GET_PRIV (window)->dialog);
2387 empathy_chat_window_add_chat (window, chat);
2390 /* Don't force the window to show itself when it wasn't
2391 * an action by the user
2393 if (!tp_user_action_time_should_present (timestamp, &x_timestamp))
2396 priv = GET_PRIV (window);
2398 if (x_timestamp != GDK_CURRENT_TIME) {
2399 /* Don't present or switch tab if the action was earlier than the
2400 * last actions X time, accounting for overflow and the first ever
2403 if (priv->x_user_action_time != 0
2404 && X_EARLIER_OR_EQL (x_timestamp, priv->x_user_action_time))
2407 priv->x_user_action_time = x_timestamp;
2410 empathy_chat_window_switch_to_chat (window, chat);
2411 empathy_window_present_with_time (GTK_WINDOW (priv->dialog),
2414 gtk_widget_grab_focus (chat->input_text_view);
2418 empathy_chat_window_get_nb_chats (EmpathyChatWindow *self,
2422 EmpathyChatWindowPriv *priv = GET_PRIV (self);
2424 guint _nb_rooms = 0, _nb_private = 0;
2426 for (l = priv->chats; l != NULL; l = g_list_next (l)) {
2427 if (empathy_chat_is_room (EMPATHY_CHAT (l->data)))
2433 if (nb_rooms != NULL)
2434 *nb_rooms = _nb_rooms;
2435 if (nb_private != NULL)
2436 *nb_private = _nb_private;