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-2010 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-manager.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;
79 gboolean dnd_same_window;
80 EmpathyChatroomManager *chatroom_manager;
81 EmpathyNotifyManager *notify_mgr;
84 NotifyNotification *notification;
86 GtkTargetList *contact_targets;
87 GtkTargetList *file_targets;
89 EmpathyChatManager *chat_manager;
90 gulong chat_manager_chats_changed_id;
93 GtkUIManager *ui_manager;
94 GtkAction *menu_conv_insert_smiley;
95 GtkAction *menu_conv_favorite;
96 GtkAction *menu_conv_always_urgent;
97 GtkAction *menu_conv_toggle_contacts;
99 GtkAction *menu_edit_cut;
100 GtkAction *menu_edit_copy;
101 GtkAction *menu_edit_paste;
102 GtkAction *menu_edit_find;
104 GtkAction *menu_tabs_next;
105 GtkAction *menu_tabs_prev;
106 GtkAction *menu_tabs_undo_close_tab;
107 GtkAction *menu_tabs_left;
108 GtkAction *menu_tabs_right;
109 GtkAction *menu_tabs_detach;
111 /* Last user action time we acted upon to show a tab */
112 guint32 x_user_action_time;
114 GSettings *gsettings_chat;
115 GSettings *gsettings_notif;
116 GSettings *gsettings_ui;
118 EmpathySoundManager *sound_mgr;
119 } EmpathyChatWindowPriv;
121 static GList *chat_windows = NULL;
123 static const guint tab_accel_keys[] = {
124 GDK_KEY_1, GDK_KEY_2, GDK_KEY_3, GDK_KEY_4, GDK_KEY_5,
125 GDK_KEY_6, GDK_KEY_7, GDK_KEY_8, GDK_KEY_9, GDK_KEY_0
129 DND_DRAG_TYPE_CONTACT_ID,
130 DND_DRAG_TYPE_URI_LIST,
134 static const GtkTargetEntry drag_types_dest[] = {
135 { "text/contact-id", 0, DND_DRAG_TYPE_CONTACT_ID },
136 { "GTK_NOTEBOOK_TAB", GTK_TARGET_SAME_APP, DND_DRAG_TYPE_TAB },
137 { "text/uri-list", 0, DND_DRAG_TYPE_URI_LIST },
138 { "text/path-list", 0, DND_DRAG_TYPE_URI_LIST },
141 static const GtkTargetEntry drag_types_dest_contact[] = {
142 { "text/contact-id", 0, DND_DRAG_TYPE_CONTACT_ID },
145 static const GtkTargetEntry drag_types_dest_file[] = {
146 /* must be first to be prioritized, in order to receive the
147 * note's file path from Tomboy instead of an URI */
148 { "text/path-list", 0, DND_DRAG_TYPE_URI_LIST },
149 { "text/uri-list", 0, DND_DRAG_TYPE_URI_LIST },
152 static void chat_window_update (EmpathyChatWindow *window);
154 static void empathy_chat_window_add_chat (EmpathyChatWindow *window,
157 static void empathy_chat_window_remove_chat (EmpathyChatWindow *window,
160 static void empathy_chat_window_move_chat (EmpathyChatWindow *old_window,
161 EmpathyChatWindow *new_window,
164 static void empathy_chat_window_get_nb_chats (EmpathyChatWindow *self,
168 G_DEFINE_TYPE (EmpathyChatWindow, empathy_chat_window, G_TYPE_OBJECT);
171 chat_window_accel_cb (GtkAccelGroup *accelgroup,
175 EmpathyChatWindow *window)
177 EmpathyChatWindowPriv *priv;
181 priv = GET_PRIV (window);
183 for (i = 0; i < G_N_ELEMENTS (tab_accel_keys); i++) {
184 if (tab_accel_keys[i] == key) {
191 gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->notebook), num);
195 static EmpathyChatWindow *
196 chat_window_find_chat (EmpathyChat *chat)
198 EmpathyChatWindowPriv *priv;
201 for (l = chat_windows; l; l = l->next) {
202 priv = GET_PRIV (l->data);
203 ll = g_list_find (priv->chats, chat);
213 chat_window_close_clicked_cb (GtkAction *action,
216 EmpathyChatWindow *window;
218 window = chat_window_find_chat (chat);
219 empathy_chat_window_remove_chat (window, chat);
223 chat_tab_style_set_cb (GtkWidget *hbox,
224 GtkStyle *previous_style,
228 int char_width, h, w;
229 PangoContext *context;
230 PangoFontMetrics *metrics;
232 button = g_object_get_data (G_OBJECT (user_data),
233 "chat-window-tab-close-button");
234 context = gtk_widget_get_pango_context (hbox);
236 metrics = pango_context_get_metrics (context, gtk_widget_get_style (hbox)->font_desc,
237 pango_context_get_language (context));
238 char_width = pango_font_metrics_get_approximate_char_width (metrics);
239 pango_font_metrics_unref (metrics);
241 gtk_icon_size_lookup_for_settings (gtk_widget_get_settings (button),
242 GTK_ICON_SIZE_MENU, &w, &h);
244 /* Request at least about 12 chars width plus at least space for the status
245 * image and the close button */
246 gtk_widget_set_size_request (hbox,
247 12 * PANGO_PIXELS (char_width) + 2 * w, -1);
249 gtk_widget_set_size_request (button, w, h);
253 chat_window_create_label (EmpathyChatWindow *window,
255 gboolean is_tab_label)
257 EmpathyChatWindowPriv *priv;
259 GtkWidget *name_label;
260 GtkWidget *status_image;
261 GtkWidget *close_button;
262 GtkWidget *close_image;
263 GtkWidget *event_box;
264 GtkWidget *event_box_hbox;
265 PangoAttrList *attr_list;
266 PangoAttribute *attr;
268 priv = GET_PRIV (window);
270 /* The spacing between the button and the label. */
271 hbox = gtk_hbox_new (FALSE, 0);
273 event_box = gtk_event_box_new ();
274 gtk_event_box_set_visible_window (GTK_EVENT_BOX (event_box), FALSE);
276 name_label = gtk_label_new (NULL);
278 gtk_label_set_ellipsize (GTK_LABEL (name_label), PANGO_ELLIPSIZE_END);
280 attr_list = pango_attr_list_new ();
281 attr = pango_attr_scale_new (1/1.2);
282 attr->start_index = 0;
283 attr->end_index = -1;
284 pango_attr_list_insert (attr_list, attr);
285 gtk_label_set_attributes (GTK_LABEL (name_label), attr_list);
286 pango_attr_list_unref (attr_list);
288 gtk_misc_set_padding (GTK_MISC (name_label), 2, 0);
289 gtk_misc_set_alignment (GTK_MISC (name_label), 0.0, 0.5);
290 g_object_set_data (G_OBJECT (chat),
291 is_tab_label ? "chat-window-tab-label" : "chat-window-menu-label",
294 status_image = gtk_image_new ();
296 /* Spacing between the icon and label. */
297 event_box_hbox = gtk_hbox_new (FALSE, 0);
299 gtk_box_pack_start (GTK_BOX (event_box_hbox), status_image, FALSE, FALSE, 0);
300 gtk_box_pack_start (GTK_BOX (event_box_hbox), name_label, TRUE, TRUE, 0);
302 g_object_set_data (G_OBJECT (chat),
303 is_tab_label ? "chat-window-tab-image" : "chat-window-menu-image",
305 g_object_set_data (G_OBJECT (chat),
306 is_tab_label ? "chat-window-tab-tooltip-widget" : "chat-window-menu-tooltip-widget",
309 gtk_container_add (GTK_CONTAINER (event_box), event_box_hbox);
310 gtk_box_pack_start (GTK_BOX (hbox), event_box, TRUE, TRUE, 0);
313 close_button = gtk_button_new ();
314 gtk_button_set_relief (GTK_BUTTON (close_button), GTK_RELIEF_NONE);
315 g_object_set_data (G_OBJECT (chat), "chat-window-tab-close-button", close_button);
317 /* We don't want focus/keynav for the button to avoid clutter, and
318 * Ctrl-W works anyway.
320 gtk_widget_set_can_focus (close_button, FALSE);
321 gtk_widget_set_can_default (close_button, FALSE);
323 /* Set the name to make the special rc style match. */
324 gtk_widget_set_name (close_button, "empathy-close-button");
326 close_image = gtk_image_new_from_stock (GTK_STOCK_CLOSE, GTK_ICON_SIZE_MENU);
328 gtk_container_add (GTK_CONTAINER (close_button), close_image);
330 gtk_box_pack_end (GTK_BOX (hbox), close_button, FALSE, FALSE, 0);
332 g_signal_connect (close_button,
334 G_CALLBACK (chat_window_close_clicked_cb),
337 /* React to theme changes and also setup the size correctly. */
338 g_signal_connect (hbox,
340 G_CALLBACK (chat_tab_style_set_cb),
344 gtk_widget_show_all (hbox);
350 _submenu_notify_visible_changed_cb (GObject *object,
354 g_signal_handlers_disconnect_by_func (object,
355 _submenu_notify_visible_changed_cb,
357 chat_window_update (EMPATHY_CHAT_WINDOW (userdata));
361 chat_window_menu_context_update (EmpathyChatWindowPriv *priv,
366 gboolean wrap_around;
367 gboolean is_connected;
370 page_num = gtk_notebook_get_current_page (GTK_NOTEBOOK (priv->notebook));
371 first_page = (page_num == 0);
372 last_page = (page_num == (num_pages - 1));
373 g_object_get (gtk_settings_get_default (), "gtk-keynav-wrap-around",
375 is_connected = empathy_chat_get_tp_chat (priv->current_chat) != NULL;
377 gtk_action_set_sensitive (priv->menu_tabs_next, (!last_page ||
379 gtk_action_set_sensitive (priv->menu_tabs_prev, (!first_page ||
381 gtk_action_set_sensitive (priv->menu_tabs_detach, num_pages > 1);
382 gtk_action_set_sensitive (priv->menu_tabs_left, !first_page);
383 gtk_action_set_sensitive (priv->menu_tabs_right, !last_page);
384 gtk_action_set_sensitive (priv->menu_conv_insert_smiley, is_connected);
388 chat_window_conversation_menu_update (EmpathyChatWindowPriv *priv,
389 EmpathyChatWindow *self)
391 EmpathyTpChat *tp_chat;
392 TpConnection *connection;
394 gboolean sensitive = FALSE;
396 g_return_if_fail (priv->current_chat != NULL);
398 action = gtk_ui_manager_get_action (priv->ui_manager,
399 "/chats_menubar/menu_conv/menu_conv_invite_participant");
400 tp_chat = empathy_chat_get_tp_chat (priv->current_chat);
402 if (tp_chat != NULL) {
403 connection = empathy_tp_chat_get_connection (tp_chat);
405 sensitive = empathy_tp_chat_can_add_contact (tp_chat) &&
406 (tp_connection_get_status (connection, NULL) ==
407 TP_CONNECTION_STATUS_CONNECTED);
410 gtk_action_set_sensitive (action, sensitive);
414 chat_window_contact_menu_update (EmpathyChatWindowPriv *priv,
415 EmpathyChatWindow *window)
417 GtkWidget *menu, *submenu, *orig_submenu;
419 menu = gtk_ui_manager_get_widget (priv->ui_manager,
420 "/chats_menubar/menu_contact");
421 orig_submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (menu));
423 if (orig_submenu == NULL || !gtk_widget_get_visible (orig_submenu)) {
424 submenu = empathy_chat_get_contact_menu (priv->current_chat);
425 if (submenu != NULL) {
426 gtk_menu_item_set_submenu (GTK_MENU_ITEM (menu), submenu);
427 gtk_widget_show (menu);
428 gtk_widget_set_sensitive (menu, TRUE);
431 gtk_widget_set_sensitive (menu, FALSE);
434 tp_g_signal_connect_object (orig_submenu,
436 (GCallback)_submenu_notify_visible_changed_cb,
442 get_all_unread_messages (EmpathyChatWindowPriv *priv)
447 for (l = priv->chats_new_msg; l != NULL; l = g_list_next (l)) {
448 EmpathyChat *chat = l->data;
450 nb += empathy_chat_get_nb_unread_messages (chat);
457 get_window_title_name (EmpathyChatWindowPriv *priv)
459 const gchar *active_name;
461 guint current_unread_msgs;
463 nb_chats = g_list_length (priv->chats);
464 g_assert (nb_chats > 0);
466 active_name = empathy_chat_get_name (priv->current_chat);
468 current_unread_msgs = empathy_chat_get_nb_unread_messages (
473 if (current_unread_msgs == 0)
474 return g_strdup (active_name);
476 return g_strdup_printf (ngettext (
478 "%s (%d unread)", current_unread_msgs),
479 active_name, current_unread_msgs);
481 guint nb_others = nb_chats - 1;
482 guint all_unread_msgs;
484 all_unread_msgs = get_all_unread_messages (priv);
486 if (all_unread_msgs == 0) {
487 /* no unread message */
488 return g_strdup_printf (ngettext (
490 "%s (and %u others)", nb_others),
491 active_name, nb_others);
494 else if (all_unread_msgs == current_unread_msgs) {
495 /* unread messages are in the current tab */
496 return g_strdup_printf (ngettext (
498 "%s (%d unread)", current_unread_msgs),
499 active_name, current_unread_msgs);
502 else if (current_unread_msgs == 0) {
503 /* unread messages are in other tabs */
504 return g_strdup_printf (ngettext (
505 "%s (%d unread from others)",
506 "%s (%d unread from others)",
508 active_name, all_unread_msgs);
512 /* unread messages are in all the tabs */
513 return g_strdup_printf (ngettext (
514 "%s (%d unread from all)",
515 "%s (%d unread from all)",
517 active_name, all_unread_msgs);
523 chat_window_title_update (EmpathyChatWindowPriv *priv)
527 name = get_window_title_name (priv);
528 gtk_window_set_title (GTK_WINDOW (priv->dialog), name);
533 chat_window_icon_update (EmpathyChatWindowPriv *priv)
536 EmpathyContact *remote_contact;
537 gboolean avatar_in_icon;
540 n_chats = g_list_length (priv->chats);
542 /* Update window icon */
543 if (priv->chats_new_msg) {
544 gtk_window_set_icon_name (GTK_WINDOW (priv->dialog),
545 EMPATHY_IMAGE_MESSAGE);
547 avatar_in_icon = g_settings_get_boolean (priv->gsettings_chat,
548 EMPATHY_PREFS_CHAT_AVATAR_IN_ICON);
550 if (n_chats == 1 && avatar_in_icon) {
551 remote_contact = empathy_chat_get_remote_contact (priv->current_chat);
552 icon = empathy_pixbuf_avatar_from_contact_scaled (remote_contact, 0, 0);
553 gtk_window_set_icon (GTK_WINDOW (priv->dialog), icon);
556 g_object_unref (icon);
559 gtk_window_set_icon_name (GTK_WINDOW (priv->dialog), NULL);
565 chat_window_close_button_update (EmpathyChatWindowPriv *priv,
569 GtkWidget *chat_close_button;
572 if (num_pages == 1) {
573 chat = gtk_notebook_get_nth_page (GTK_NOTEBOOK (priv->notebook), 0);
574 chat_close_button = g_object_get_data (G_OBJECT (chat),
575 "chat-window-tab-close-button");
576 gtk_widget_hide (chat_close_button);
578 for (i=0; i<num_pages; i++) {
579 chat = gtk_notebook_get_nth_page (GTK_NOTEBOOK (priv->notebook), i);
580 chat_close_button = g_object_get_data (G_OBJECT (chat),
581 "chat-window-tab-close-button");
582 gtk_widget_show (chat_close_button);
588 chat_window_update (EmpathyChatWindow *window)
590 EmpathyChatWindowPriv *priv = GET_PRIV (window);
593 num_pages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (priv->notebook));
595 /* Update Tab menu */
596 chat_window_menu_context_update (priv,
599 chat_window_conversation_menu_update (priv, window);
601 chat_window_contact_menu_update (priv,
604 chat_window_title_update (priv);
606 chat_window_icon_update (priv);
608 chat_window_close_button_update (priv,
613 append_markup_printf (GString *string,
620 va_start (args, format);
622 tmp = g_markup_vprintf_escaped (format, args);
623 g_string_append (string, tmp);
630 chat_window_update_chat_tab (EmpathyChat *chat)
632 EmpathyChatWindow *window;
633 EmpathyChatWindowPriv *priv;
634 EmpathyContact *remote_contact;
638 const gchar *subject;
639 const gchar *status = NULL;
643 const gchar *icon_name;
644 GtkWidget *tab_image;
645 GtkWidget *menu_image;
647 window = chat_window_find_chat (chat);
651 priv = GET_PRIV (window);
653 /* Get information */
654 name = empathy_chat_get_name (chat);
655 account = empathy_chat_get_account (chat);
656 subject = empathy_chat_get_subject (chat);
657 remote_contact = empathy_chat_get_remote_contact (chat);
659 DEBUG ("Updating chat tab, name=%s, account=%s, subject=%s, remote_contact=%p",
660 name, tp_proxy_get_object_path (account), subject, remote_contact);
662 /* Update tab image */
663 if (empathy_chat_get_tp_chat (chat) == NULL) {
664 /* No TpChat, we are disconnected */
667 else if (g_list_find (priv->chats_new_msg, chat)) {
668 icon_name = EMPATHY_IMAGE_MESSAGE;
670 else if (empathy_chat_is_composing (chat)) {
671 icon_name = EMPATHY_IMAGE_TYPING;
673 else if (remote_contact) {
674 icon_name = empathy_icon_name_for_contact (remote_contact);
676 icon_name = EMPATHY_IMAGE_GROUP_MESSAGE;
679 tab_image = g_object_get_data (G_OBJECT (chat), "chat-window-tab-image");
680 menu_image = g_object_get_data (G_OBJECT (chat), "chat-window-menu-image");
681 if (icon_name != NULL) {
682 gtk_image_set_from_icon_name (GTK_IMAGE (tab_image), icon_name, GTK_ICON_SIZE_MENU);
683 gtk_widget_show (tab_image);
684 gtk_image_set_from_icon_name (GTK_IMAGE (menu_image), icon_name, GTK_ICON_SIZE_MENU);
685 gtk_widget_show (menu_image);
687 gtk_widget_hide (tab_image);
688 gtk_widget_hide (menu_image);
691 /* Update tab tooltip */
692 tooltip = g_string_new (NULL);
694 if (remote_contact) {
695 id = empathy_contact_get_id (remote_contact);
696 status = empathy_contact_get_presence_message (remote_contact);
701 append_markup_printf (tooltip,
702 "<b>%s</b><small> (%s)</small>",
704 tp_account_get_display_name (account));
706 if (!EMP_STR_EMPTY (status)) {
707 append_markup_printf (tooltip, "\n<i>%s</i>", status);
711 append_markup_printf (tooltip, "\n<b>%s</b> %s",
712 _("Topic:"), subject);
715 if (empathy_chat_is_composing (chat)) {
716 append_markup_printf (tooltip, "\n%s", _("Typing a message."));
719 markup = g_string_free (tooltip, FALSE);
720 widget = g_object_get_data (G_OBJECT (chat), "chat-window-tab-tooltip-widget");
721 gtk_widget_set_tooltip_markup (widget, markup);
722 widget = g_object_get_data (G_OBJECT (chat), "chat-window-menu-tooltip-widget");
723 gtk_widget_set_tooltip_markup (widget, markup);
726 /* Update tab and menu label */
727 widget = g_object_get_data (G_OBJECT (chat), "chat-window-tab-label");
728 gtk_label_set_text (GTK_LABEL (widget), name);
729 widget = g_object_get_data (G_OBJECT (chat), "chat-window-menu-label");
730 gtk_label_set_text (GTK_LABEL (widget), name);
732 /* Update the window if it's the current chat */
733 if (priv->current_chat == chat) {
734 chat_window_update (window);
739 chat_window_chat_notify_cb (EmpathyChat *chat)
741 EmpathyContact *old_remote_contact;
742 EmpathyContact *remote_contact = NULL;
744 old_remote_contact = g_object_get_data (G_OBJECT (chat), "chat-window-remote-contact");
745 remote_contact = empathy_chat_get_remote_contact (chat);
747 if (old_remote_contact != remote_contact) {
748 /* The remote-contact associated with the chat changed, we need
749 * to keep track of any change of that contact and update the
750 * window each time. */
751 if (remote_contact) {
752 g_signal_connect_swapped (remote_contact, "notify",
753 G_CALLBACK (chat_window_update_chat_tab),
756 if (old_remote_contact) {
757 g_signal_handlers_disconnect_by_func (old_remote_contact,
758 chat_window_update_chat_tab,
762 g_object_set_data_full (G_OBJECT (chat), "chat-window-remote-contact",
763 g_object_ref (remote_contact), (GDestroyNotify) g_object_unref);
766 chat_window_update_chat_tab (chat);
770 chat_window_insert_smiley_activate_cb (EmpathySmileyManager *manager,
771 EmpathySmiley *smiley,
774 EmpathyChatWindowPriv *priv = GET_PRIV (window);
776 GtkTextBuffer *buffer;
779 chat = priv->current_chat;
781 buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (chat->input_text_view));
782 gtk_text_buffer_get_end_iter (buffer, &iter);
783 gtk_text_buffer_insert (buffer, &iter, smiley->str, -1);
787 chat_window_conv_activate_cb (GtkAction *action,
788 EmpathyChatWindow *window)
790 EmpathyChatWindowPriv *priv = GET_PRIV (window);
793 EmpathyContact *remote_contact = NULL;
795 /* Favorite room menu */
796 is_room = empathy_chat_is_room (priv->current_chat);
800 gboolean found = FALSE;
801 EmpathyChatroom *chatroom;
803 room = empathy_chat_get_id (priv->current_chat);
804 account = empathy_chat_get_account (priv->current_chat);
805 chatroom = empathy_chatroom_manager_find (priv->chatroom_manager,
807 if (chatroom != NULL)
808 found = empathy_chatroom_is_favorite (chatroom);
810 DEBUG ("This room %s favorite", found ? "is" : "is not");
811 gtk_toggle_action_set_active (
812 GTK_TOGGLE_ACTION (priv->menu_conv_favorite), found);
814 if (chatroom != NULL)
815 found = empathy_chatroom_is_always_urgent (chatroom);
817 gtk_toggle_action_set_active (
818 GTK_TOGGLE_ACTION (priv->menu_conv_always_urgent),
821 gtk_action_set_visible (priv->menu_conv_favorite, is_room);
822 gtk_action_set_visible (priv->menu_conv_always_urgent, is_room);
824 /* Show contacts menu */
825 g_object_get (priv->current_chat,
826 "remote-contact", &remote_contact,
827 "show-contacts", &active,
829 if (remote_contact == NULL) {
830 gtk_toggle_action_set_active (
831 GTK_TOGGLE_ACTION (priv->menu_conv_toggle_contacts),
834 gtk_action_set_visible (priv->menu_conv_toggle_contacts,
835 (remote_contact == NULL));
836 if (remote_contact != NULL) {
837 g_object_unref (remote_contact);
842 chat_window_clear_activate_cb (GtkAction *action,
843 EmpathyChatWindow *window)
845 EmpathyChatWindowPriv *priv = GET_PRIV (window);
847 empathy_chat_clear (priv->current_chat);
851 chat_window_favorite_toggled_cb (GtkToggleAction *toggle_action,
852 EmpathyChatWindow *window)
854 EmpathyChatWindowPriv *priv = GET_PRIV (window);
858 EmpathyChatroom *chatroom;
860 active = gtk_toggle_action_get_active (toggle_action);
861 account = empathy_chat_get_account (priv->current_chat);
862 room = empathy_chat_get_id (priv->current_chat);
864 chatroom = empathy_chatroom_manager_ensure_chatroom (
865 priv->chatroom_manager,
868 empathy_chat_get_name (priv->current_chat));
870 empathy_chatroom_set_favorite (chatroom, active);
871 g_object_unref (chatroom);
875 chat_window_always_urgent_toggled_cb (GtkToggleAction *toggle_action,
876 EmpathyChatWindow *window)
878 EmpathyChatWindowPriv *priv = GET_PRIV (window);
882 EmpathyChatroom *chatroom;
884 active = gtk_toggle_action_get_active (toggle_action);
885 account = empathy_chat_get_account (priv->current_chat);
886 room = empathy_chat_get_id (priv->current_chat);
888 chatroom = empathy_chatroom_manager_ensure_chatroom (
889 priv->chatroom_manager,
892 empathy_chat_get_name (priv->current_chat));
894 empathy_chatroom_set_always_urgent (chatroom, active);
895 g_object_unref (chatroom);
899 chat_window_contacts_toggled_cb (GtkToggleAction *toggle_action,
900 EmpathyChatWindow *window)
902 EmpathyChatWindowPriv *priv = GET_PRIV (window);
905 active = gtk_toggle_action_get_active (toggle_action);
907 empathy_chat_set_show_contacts (priv->current_chat, active);
911 got_contact_cb (TpConnection *connection,
912 EmpathyContact *contact,
917 EmpathyTpChat *tp_chat = EMPATHY_TP_CHAT (user_data);
920 DEBUG ("Failed: %s", error->message);
923 empathy_contact_list_add (EMPATHY_CONTACT_LIST (tp_chat),
924 contact, _("Inviting you to this room"));
929 chat_window_invite_participant_activate_cb (GtkAction *action,
930 EmpathyChatWindow *window)
932 EmpathyChatWindowPriv *priv;
934 EmpathyTpChat *tp_chat;
939 priv = GET_PRIV (window);
941 g_return_if_fail (priv->current_chat != NULL);
943 tp_chat = empathy_chat_get_tp_chat (priv->current_chat);
944 channel = empathy_tp_chat_get_channel (tp_chat);
945 account = empathy_chat_get_account (priv->current_chat);
947 dialog = empathy_invite_participant_dialog_new (
948 GTK_WINDOW (priv->dialog), account);
949 gtk_widget_show (dialog);
951 response = gtk_dialog_run (GTK_DIALOG (dialog));
953 if (response == GTK_RESPONSE_ACCEPT) {
954 TpConnection *connection;
957 id = empathy_contact_selector_dialog_get_selected (
958 EMPATHY_CONTACT_SELECTOR_DIALOG (dialog), NULL, NULL);
959 if (EMP_STR_EMPTY (id)) goto out;
961 connection = tp_channel_borrow_connection (channel);
962 empathy_tp_contact_factory_get_from_id (connection, id,
963 got_contact_cb, tp_chat, NULL, NULL);
967 gtk_widget_destroy (dialog);
971 chat_window_close_activate_cb (GtkAction *action,
972 EmpathyChatWindow *window)
974 EmpathyChatWindowPriv *priv;
976 priv = GET_PRIV (window);
978 g_return_if_fail (priv->current_chat != NULL);
980 empathy_chat_window_remove_chat (window, priv->current_chat);
984 chat_window_edit_activate_cb (GtkAction *action,
985 EmpathyChatWindow *window)
987 EmpathyChatWindowPriv *priv;
988 GtkClipboard *clipboard;
989 GtkTextBuffer *buffer;
990 gboolean text_available;
992 priv = GET_PRIV (window);
994 g_return_if_fail (priv->current_chat != NULL);
996 if (!empathy_chat_get_tp_chat (priv->current_chat)) {
997 gtk_action_set_sensitive (priv->menu_edit_copy, FALSE);
998 gtk_action_set_sensitive (priv->menu_edit_cut, FALSE);
999 gtk_action_set_sensitive (priv->menu_edit_paste, FALSE);
1003 buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (priv->current_chat->input_text_view));
1004 if (gtk_text_buffer_get_has_selection (buffer)) {
1005 gtk_action_set_sensitive (priv->menu_edit_copy, TRUE);
1006 gtk_action_set_sensitive (priv->menu_edit_cut, TRUE);
1010 selection = empathy_chat_view_get_has_selection (priv->current_chat->view);
1012 gtk_action_set_sensitive (priv->menu_edit_cut, FALSE);
1013 gtk_action_set_sensitive (priv->menu_edit_copy, selection);
1016 clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
1017 text_available = gtk_clipboard_wait_is_text_available (clipboard);
1018 gtk_action_set_sensitive (priv->menu_edit_paste, text_available);
1022 chat_window_cut_activate_cb (GtkAction *action,
1023 EmpathyChatWindow *window)
1025 EmpathyChatWindowPriv *priv;
1027 g_return_if_fail (EMPATHY_IS_CHAT_WINDOW (window));
1029 priv = GET_PRIV (window);
1031 empathy_chat_cut (priv->current_chat);
1035 chat_window_copy_activate_cb (GtkAction *action,
1036 EmpathyChatWindow *window)
1038 EmpathyChatWindowPriv *priv;
1040 g_return_if_fail (EMPATHY_IS_CHAT_WINDOW (window));
1042 priv = GET_PRIV (window);
1044 empathy_chat_copy (priv->current_chat);
1048 chat_window_paste_activate_cb (GtkAction *action,
1049 EmpathyChatWindow *window)
1051 EmpathyChatWindowPriv *priv;
1053 g_return_if_fail (EMPATHY_IS_CHAT_WINDOW (window));
1055 priv = GET_PRIV (window);
1057 empathy_chat_paste (priv->current_chat);
1061 chat_window_find_activate_cb (GtkAction *action,
1062 EmpathyChatWindow *window)
1064 EmpathyChatWindowPriv *priv;
1066 g_return_if_fail (EMPATHY_IS_CHAT_WINDOW (window));
1068 priv = GET_PRIV (window);
1070 empathy_chat_find (priv->current_chat);
1074 chat_window_tabs_next_activate_cb (GtkAction *action,
1075 EmpathyChatWindow *window)
1077 EmpathyChatWindowPriv *priv;
1079 gint index_, numPages;
1080 gboolean wrap_around;
1082 priv = GET_PRIV (window);
1084 g_object_get (gtk_settings_get_default (), "gtk-keynav-wrap-around",
1085 &wrap_around, NULL);
1087 chat = priv->current_chat;
1088 index_ = gtk_notebook_get_current_page (GTK_NOTEBOOK (priv->notebook));
1089 numPages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (priv->notebook));
1091 if (index_ == (numPages - 1) && wrap_around) {
1092 gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->notebook), 0);
1096 gtk_notebook_next_page (GTK_NOTEBOOK (priv->notebook));
1100 chat_window_tabs_previous_activate_cb (GtkAction *action,
1101 EmpathyChatWindow *window)
1103 EmpathyChatWindowPriv *priv;
1105 gint index_, numPages;
1106 gboolean wrap_around;
1108 priv = GET_PRIV (window);
1110 g_object_get (gtk_settings_get_default (), "gtk-keynav-wrap-around",
1111 &wrap_around, NULL);
1113 chat = priv->current_chat;
1114 index_ = gtk_notebook_get_current_page (GTK_NOTEBOOK (priv->notebook));
1115 numPages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (priv->notebook));
1117 if (index_ <= 0 && wrap_around) {
1118 gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->notebook), numPages - 1);
1122 gtk_notebook_prev_page (GTK_NOTEBOOK (priv->notebook));
1126 chat_window_tabs_undo_close_tab_activate_cb (GtkAction *action,
1127 EmpathyChatWindow *window)
1129 EmpathyChatWindowPriv *priv = GET_PRIV (window);
1130 empathy_chat_manager_undo_closed_chat (priv->chat_manager);
1134 chat_window_tabs_left_activate_cb (GtkAction *action,
1135 EmpathyChatWindow *window)
1137 EmpathyChatWindowPriv *priv;
1139 gint index_, num_pages;
1141 priv = GET_PRIV (window);
1143 chat = priv->current_chat;
1144 index_ = gtk_notebook_get_current_page (GTK_NOTEBOOK (priv->notebook));
1149 gtk_notebook_reorder_child (GTK_NOTEBOOK (priv->notebook),
1153 num_pages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (priv->notebook));
1154 chat_window_menu_context_update (priv, num_pages);
1158 chat_window_tabs_right_activate_cb (GtkAction *action,
1159 EmpathyChatWindow *window)
1161 EmpathyChatWindowPriv *priv;
1163 gint index_, num_pages;
1165 priv = GET_PRIV (window);
1167 chat = priv->current_chat;
1168 index_ = gtk_notebook_get_current_page (GTK_NOTEBOOK (priv->notebook));
1170 gtk_notebook_reorder_child (GTK_NOTEBOOK (priv->notebook),
1174 num_pages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (priv->notebook));
1175 chat_window_menu_context_update (priv, num_pages);
1178 static EmpathyChatWindow *
1179 empathy_chat_window_new (void)
1181 return EMPATHY_CHAT_WINDOW (g_object_new (EMPATHY_TYPE_CHAT_WINDOW, NULL));
1185 chat_window_detach_activate_cb (GtkAction *action,
1186 EmpathyChatWindow *window)
1188 EmpathyChatWindowPriv *priv;
1189 EmpathyChatWindow *new_window;
1192 priv = GET_PRIV (window);
1194 chat = priv->current_chat;
1195 new_window = empathy_chat_window_new ();
1197 empathy_chat_window_move_chat (window, new_window, chat);
1199 priv = GET_PRIV (new_window);
1200 gtk_widget_show (priv->dialog);
1204 chat_window_help_contents_activate_cb (GtkAction *action,
1205 EmpathyChatWindow *window)
1207 EmpathyChatWindowPriv *priv = GET_PRIV (window);
1209 empathy_url_show (priv->dialog, "ghelp:empathy");
1213 chat_window_help_about_activate_cb (GtkAction *action,
1214 EmpathyChatWindow *window)
1216 EmpathyChatWindowPriv *priv = GET_PRIV (window);
1218 empathy_about_dialog_new (GTK_WINDOW (priv->dialog));
1222 chat_window_delete_event_cb (GtkWidget *dialog,
1224 EmpathyChatWindow *window)
1226 EmpathyChatWindowPriv *priv = GET_PRIV (window);
1228 DEBUG ("Delete event received");
1230 g_object_ref (window);
1231 while (priv->chats) {
1232 empathy_chat_window_remove_chat (window, priv->chats->data);
1234 g_object_unref (window);
1240 chat_window_composing_cb (EmpathyChat *chat,
1241 gboolean is_composing,
1242 EmpathyChatWindow *window)
1244 chat_window_update_chat_tab (chat);
1248 chat_window_set_urgency_hint (EmpathyChatWindow *window,
1251 EmpathyChatWindowPriv *priv;
1253 priv = GET_PRIV (window);
1255 gtk_window_set_urgency_hint (GTK_WINDOW (priv->dialog), urgent);
1259 chat_window_notification_closed_cb (NotifyNotification *notify,
1260 EmpathyChatWindow *self)
1262 EmpathyChatWindowPriv *priv = GET_PRIV (self);
1264 g_object_unref (notify);
1265 if (priv->notification == notify) {
1266 priv->notification = NULL;
1271 chat_window_show_or_update_notification (EmpathyChatWindow *window,
1272 EmpathyMessage *message,
1275 EmpathyContact *sender;
1276 const gchar *header;
1280 EmpathyChatWindowPriv *priv = GET_PRIV (window);
1281 gboolean res, has_x_canonical_append;
1282 NotifyNotification *notification = priv->notification;
1284 if (!empathy_notify_manager_notification_is_enabled (priv->notify_mgr)) {
1287 res = g_settings_get_boolean (priv->gsettings_notif,
1288 EMPATHY_PREFS_NOTIFICATIONS_FOCUS);
1295 sender = empathy_message_get_sender (message);
1296 header = empathy_contact_get_alias (sender);
1297 body = empathy_message_get_body (message);
1298 escaped = g_markup_escape_text (body, -1);
1299 has_x_canonical_append = empathy_notify_manager_has_capability (
1300 priv->notify_mgr, EMPATHY_NOTIFY_MANAGER_CAP_X_CANONICAL_APPEND);
1302 if (notification != NULL && !has_x_canonical_append) {
1303 /* if the notification server supports x-canonical-append, it is
1304 better to not use notify_notification_update to avoid
1305 overwriting the current notification message */
1306 notify_notification_update (notification,
1307 header, escaped, NULL);
1309 /* if the notification server supports x-canonical-append,
1310 the hint will be added, so that the message from the
1311 just created notification will be automatically appended
1312 to an existing notification with the same title.
1313 In this way the previous message will not be lost: the new
1314 message will appear below it, in the same notification */
1315 notification = notify_notification_new (header, escaped, NULL);
1317 if (priv->notification == NULL) {
1318 priv->notification = notification;
1321 notify_notification_set_timeout (notification, NOTIFY_EXPIRES_DEFAULT);
1323 tp_g_signal_connect_object (notification, "closed",
1324 G_CALLBACK (chat_window_notification_closed_cb), window, 0);
1326 if (has_x_canonical_append) {
1327 notify_notification_set_hint_string (notification,
1328 EMPATHY_NOTIFY_MANAGER_CAP_X_CANONICAL_APPEND, "");
1332 pixbuf = empathy_notify_manager_get_pixbuf_for_notification (priv->notify_mgr,
1333 sender, EMPATHY_IMAGE_NEW_MESSAGE);
1335 if (pixbuf != NULL) {
1336 notify_notification_set_icon_from_pixbuf (notification, pixbuf);
1337 g_object_unref (pixbuf);
1340 notify_notification_show (notification, NULL);
1346 chat_window_set_highlight_room_labels (EmpathyChat *chat)
1351 if (!empathy_chat_is_room (chat))
1354 markup = g_markup_printf_escaped (
1355 "<span color=\"red\" weight=\"bold\">%s</span>",
1356 empathy_chat_get_name (chat));
1358 widget = g_object_get_data (G_OBJECT (chat), "chat-window-tab-label");
1359 gtk_label_set_markup (GTK_LABEL (widget), markup);
1361 widget = g_object_get_data (G_OBJECT (chat), "chat-window-menu-label");
1362 gtk_label_set_markup (GTK_LABEL (widget), markup);
1368 empathy_chat_window_has_focus (EmpathyChatWindow *window)
1370 EmpathyChatWindowPriv *priv;
1373 g_return_val_if_fail (EMPATHY_IS_CHAT_WINDOW (window), FALSE);
1375 priv = GET_PRIV (window);
1377 g_object_get (priv->dialog, "has-toplevel-focus", &has_focus, NULL);
1383 chat_window_new_message_cb (EmpathyChat *chat,
1384 EmpathyMessage *message,
1386 EmpathyChatWindow *window)
1388 EmpathyChatWindowPriv *priv;
1390 gboolean needs_urgency;
1391 EmpathyContact *sender;
1393 priv = GET_PRIV (window);
1395 has_focus = empathy_chat_window_has_focus (window);
1397 /* - if we're the sender, we play the sound if it's specified in the
1398 * preferences and we're not away.
1399 * - if we receive a message, we play the sound if it's specified in the
1400 * preferences and the window does not have focus on the chat receiving
1404 sender = empathy_message_get_sender (message);
1406 if (empathy_contact_is_user (sender)) {
1407 empathy_sound_manager_play (priv->sound_mgr, GTK_WIDGET (priv->dialog),
1408 EMPATHY_SOUND_MESSAGE_OUTGOING);
1411 if (has_focus && priv->current_chat == chat) {
1412 /* window and tab are focused so consider the message to be read */
1414 /* FIXME: see Bug#610994 and coments about it in EmpathyChatPriv */
1415 empathy_chat_messages_read (chat);
1419 if (!g_list_find (priv->chats_new_msg, chat)) {
1420 priv->chats_new_msg = g_list_prepend (priv->chats_new_msg, chat);
1421 chat_window_update_chat_tab (chat);
1424 /* If empathy_chat_is_room () returns TRUE, that means it's a named MUC.
1425 * If empathy_chat_get_remote_contact () returns NULL, that means it's
1426 * an unamed MUC (msn-like).
1427 * In case of a MUC, we set urgency if either:
1428 * a) the chatroom's always_urgent property is TRUE
1429 * b) the message contains our alias
1431 if (empathy_chat_is_room (chat) ||
1432 empathy_chat_get_remote_contact (chat) == NULL) {
1435 EmpathyChatroom *chatroom;
1437 account = empathy_chat_get_account (chat);
1438 room = empathy_chat_get_id (chat);
1440 chatroom = empathy_chatroom_manager_find (priv->chatroom_manager,
1443 if (empathy_chatroom_is_always_urgent (chatroom)) {
1444 needs_urgency = TRUE;
1446 needs_urgency = empathy_message_should_highlight (message);
1449 needs_urgency = TRUE;
1452 if (needs_urgency) {
1454 chat_window_set_urgency_hint (window, TRUE);
1455 chat_window_set_highlight_room_labels (chat);
1458 empathy_sound_manager_play (priv->sound_mgr, GTK_WIDGET (priv->dialog),
1459 EMPATHY_SOUND_MESSAGE_INCOMING);
1461 /* Pending messages have already been displayed in the approver, so we don't
1462 * display a notification for those. */
1464 chat_window_show_or_update_notification (window, message, chat);
1467 /* update the number of unread messages and the window icon */
1468 chat_window_title_update (priv);
1469 chat_window_icon_update (priv);
1472 static GtkNotebook *
1473 notebook_create_window_cb (GtkNotebook *source,
1479 EmpathyChatWindowPriv *priv;
1480 EmpathyChatWindow *window, *new_window;
1483 chat = EMPATHY_CHAT (page);
1484 window = chat_window_find_chat (chat);
1486 new_window = empathy_chat_window_new ();
1487 priv = GET_PRIV (new_window);
1489 DEBUG ("Detach hook called");
1491 empathy_chat_window_move_chat (window, new_window, chat);
1493 gtk_widget_show (priv->dialog);
1494 gtk_window_move (GTK_WINDOW (priv->dialog), x, y);
1500 chat_window_page_switched_cb (GtkNotebook *notebook,
1501 gpointer ignore, /* see note below */
1503 EmpathyChatWindow *window)
1505 EmpathyChatWindowPriv *priv;
1509 DEBUG ("Page switched");
1511 priv = GET_PRIV (window);
1513 /* N.B. in GTK+ 3 child is passed as the first argument to the signal,
1514 * but we can't use that while trying to retain GTK+ 2.x compatibility.
1516 child = gtk_notebook_get_nth_page (notebook, page_num);
1517 chat = EMPATHY_CHAT (child);
1519 if (priv->page_added) {
1520 priv->page_added = FALSE;
1521 empathy_chat_scroll_down (chat);
1523 else if (priv->current_chat == chat) {
1527 priv->current_chat = chat;
1528 priv->chats_new_msg = g_list_remove (priv->chats_new_msg, chat);
1529 empathy_chat_messages_read (chat);
1531 chat_window_update_chat_tab (chat);
1535 chat_window_page_added_cb (GtkNotebook *notebook,
1538 EmpathyChatWindow *window)
1540 EmpathyChatWindowPriv *priv;
1543 priv = GET_PRIV (window);
1545 /* If we just received DND to the same window, we don't want
1546 * to do anything here like removing the tab and then readding
1547 * it, so we return here and in "page-added".
1549 if (priv->dnd_same_window) {
1550 DEBUG ("Page added (back to the same window)");
1551 priv->dnd_same_window = FALSE;
1555 DEBUG ("Page added");
1557 /* Get chat object */
1558 chat = EMPATHY_CHAT (child);
1560 /* Connect chat signals for this window */
1561 g_signal_connect (chat, "composing",
1562 G_CALLBACK (chat_window_composing_cb),
1564 g_signal_connect (chat, "new-message",
1565 G_CALLBACK (chat_window_new_message_cb),
1567 g_signal_connect (chat, "notify::tp-chat",
1568 G_CALLBACK (chat_window_update_chat_tab),
1571 /* Set flag so we know to perform some special operations on
1572 * switch page due to the new page being added.
1574 priv->page_added = TRUE;
1576 /* Get list of chats up to date */
1577 priv->chats = g_list_append (priv->chats, chat);
1579 chat_window_update_chat_tab (chat);
1583 chat_window_page_removed_cb (GtkNotebook *notebook,
1586 EmpathyChatWindow *window)
1588 EmpathyChatWindowPriv *priv;
1591 priv = GET_PRIV (window);
1593 /* If we just received DND to the same window, we don't want
1594 * to do anything here like removing the tab and then readding
1595 * it, so we return here and in "page-added".
1597 if (priv->dnd_same_window) {
1598 DEBUG ("Page removed (and will be readded to same window)");
1602 DEBUG ("Page removed");
1604 /* Get chat object */
1605 chat = EMPATHY_CHAT (child);
1607 /* Disconnect all signal handlers for this chat and this window */
1608 g_signal_handlers_disconnect_by_func (chat,
1609 G_CALLBACK (chat_window_composing_cb),
1611 g_signal_handlers_disconnect_by_func (chat,
1612 G_CALLBACK (chat_window_new_message_cb),
1614 g_signal_handlers_disconnect_by_func (chat,
1615 G_CALLBACK (chat_window_update_chat_tab),
1618 /* Keep list of chats up to date */
1619 priv->chats = g_list_remove (priv->chats, chat);
1620 priv->chats_new_msg = g_list_remove (priv->chats_new_msg, chat);
1621 empathy_chat_messages_read (chat);
1623 if (priv->chats == NULL) {
1624 g_object_unref (window);
1626 chat_window_update (window);
1631 chat_window_focus_in_event_cb (GtkWidget *widget,
1633 EmpathyChatWindow *window)
1635 EmpathyChatWindowPriv *priv;
1637 priv = GET_PRIV (window);
1639 priv->chats_new_msg = g_list_remove (priv->chats_new_msg, priv->current_chat);
1640 empathy_chat_messages_read (priv->current_chat);
1642 chat_window_set_urgency_hint (window, FALSE);
1644 /* Update the title, since we now mark all unread messages as read. */
1645 chat_window_update_chat_tab (priv->current_chat);
1651 chat_window_drag_drop (GtkWidget *widget,
1652 GdkDragContext *context,
1656 EmpathyChatWindow *window)
1659 EmpathyChatWindowPriv *priv;
1661 priv = GET_PRIV (window);
1663 target = gtk_drag_dest_find_target (widget, context, priv->file_targets);
1664 if (target == GDK_NONE)
1665 target = gtk_drag_dest_find_target (widget, context, priv->contact_targets);
1667 if (target != GDK_NONE) {
1668 gtk_drag_get_data (widget, context, target, time_);
1676 chat_window_drag_motion (GtkWidget *widget,
1677 GdkDragContext *context,
1681 EmpathyChatWindow *window)
1684 EmpathyChatWindowPriv *priv;
1686 priv = GET_PRIV (window);
1688 target = gtk_drag_dest_find_target (widget, context, priv->file_targets);
1689 if (target != GDK_NONE) {
1690 /* This is a file drag. Ensure the contact is online and set the
1691 drag type to COPY. Note that it's possible that the tab will
1692 be switched by GTK+ after a timeout from drag_motion without
1693 getting another drag_motion to disable the drop. You have
1694 to hold your mouse really still.
1696 EmpathyContact *contact;
1698 priv = GET_PRIV (window);
1699 contact = empathy_chat_get_remote_contact (priv->current_chat);
1700 /* contact is NULL for multi-user chats. We don't do
1701 * file transfers to MUCs. We also don't send files
1702 * to offline contacts or contacts that don't support
1705 if ((contact == NULL) || !empathy_contact_is_online (contact)) {
1706 gdk_drag_status (context, 0, time_);
1709 if (!(empathy_contact_get_capabilities (contact)
1710 & EMPATHY_CAPABILITIES_FT)) {
1711 gdk_drag_status (context, 0, time_);
1714 gdk_drag_status (context, GDK_ACTION_COPY, time_);
1718 target = gtk_drag_dest_find_target (widget, context, priv->contact_targets);
1719 if (target != GDK_NONE) {
1720 /* This is a drag of a contact from a contact list. Set to COPY.
1721 FIXME: If this drag is to a MUC window, it invites the user.
1722 Otherwise, it opens a chat. Should we use a different drag
1723 type for invites? Should we allow ASK?
1725 gdk_drag_status (context, GDK_ACTION_COPY, time_);
1733 chat_window_drag_data_received (GtkWidget *widget,
1734 GdkDragContext *context,
1737 GtkSelectionData *selection,
1740 EmpathyChatWindow *window)
1742 if (info == DND_DRAG_TYPE_CONTACT_ID) {
1743 EmpathyChat *chat = NULL;
1744 EmpathyChatWindow *old_window;
1745 TpAccount *account = NULL;
1746 TpAccountManager *account_manager;
1749 const gchar *account_id;
1750 const gchar *contact_id;
1752 id = (const gchar*) gtk_selection_data_get_data (selection);
1754 /* FIXME: Perhaps should be sure that the account manager is
1755 * prepared before calling _ensure_account on it. */
1756 account_manager = tp_account_manager_dup ();
1758 DEBUG ("DND contact from roster with id:'%s'", id);
1760 strv = g_strsplit (id, ":", 2);
1761 if (g_strv_length (strv) == 2) {
1762 account_id = strv[0];
1763 contact_id = strv[1];
1765 tp_account_manager_ensure_account (account_manager, account_id);
1766 if (account != NULL)
1767 chat = empathy_chat_window_find_chat (account, contact_id);
1770 if (account == NULL) {
1772 gtk_drag_finish (context, FALSE, FALSE, time_);
1777 empathy_dispatcher_chat_with_contact_id (
1778 account, contact_id, gtk_get_current_event_time ());
1783 g_object_unref (account_manager);
1786 old_window = chat_window_find_chat (chat);
1788 if (old_window == window) {
1789 gtk_drag_finish (context, TRUE, FALSE, time_);
1793 empathy_chat_window_move_chat (old_window, window, chat);
1795 empathy_chat_window_add_chat (window, chat);
1798 /* Added to take care of any outstanding chat events */
1799 empathy_chat_window_present_chat (chat,
1800 TP_USER_ACTION_TIME_NOT_USER_ACTION);
1802 /* We should return TRUE to remove the data when doing
1803 * GDK_ACTION_MOVE, but we don't here otherwise it has
1804 * weird consequences, and we handle that internally
1805 * anyway with add_chat () and remove_chat ().
1807 gtk_drag_finish (context, TRUE, FALSE, time_);
1809 else if (info == DND_DRAG_TYPE_URI_LIST) {
1810 EmpathyChatWindowPriv *priv;
1811 EmpathyContact *contact;
1814 priv = GET_PRIV (window);
1815 contact = empathy_chat_get_remote_contact (priv->current_chat);
1817 /* contact is NULL when current_chat is a multi-user chat.
1818 * We don't do file transfers to MUCs, so just cancel the drag.
1820 if (contact == NULL) {
1821 gtk_drag_finish (context, TRUE, FALSE, time_);
1825 data = (const gchar *) gtk_selection_data_get_data (selection);
1826 empathy_send_file_from_uri_list (contact, data);
1828 gtk_drag_finish (context, TRUE, FALSE, time_);
1830 else if (info == DND_DRAG_TYPE_TAB) {
1832 EmpathyChatWindow *old_window = NULL;
1836 chat = (void *) gtk_selection_data_get_data (selection);
1837 old_window = chat_window_find_chat (*chat);
1840 EmpathyChatWindowPriv *priv;
1842 priv = GET_PRIV (window);
1843 priv->dnd_same_window = (old_window == window);
1844 DEBUG ("DND tab (within same window: %s)",
1845 priv->dnd_same_window ? "Yes" : "No");
1848 DEBUG ("DND from unknown source");
1849 gtk_drag_finish (context, FALSE, FALSE, time_);
1854 chat_window_chat_manager_chats_changed_cb (EmpathyChatManager *chat_manager,
1855 guint num_chats_in_manager,
1856 EmpathyChatWindow *window)
1858 EmpathyChatWindowPriv *priv = GET_PRIV (window);
1860 gtk_action_set_sensitive (priv->menu_tabs_undo_close_tab,
1861 num_chats_in_manager > 0);
1865 chat_window_finalize (GObject *object)
1867 EmpathyChatWindow *window;
1868 EmpathyChatWindowPriv *priv;
1870 window = EMPATHY_CHAT_WINDOW (object);
1871 priv = GET_PRIV (window);
1873 DEBUG ("Finalized: %p", object);
1875 g_object_unref (priv->ui_manager);
1876 g_object_unref (priv->chatroom_manager);
1877 g_object_unref (priv->notify_mgr);
1878 g_object_unref (priv->gsettings_chat);
1879 g_object_unref (priv->gsettings_notif);
1880 g_object_unref (priv->gsettings_ui);
1881 g_object_unref (priv->sound_mgr);
1883 if (priv->notification != NULL) {
1884 notify_notification_close (priv->notification, NULL);
1885 priv->notification = NULL;
1888 if (priv->contact_targets) {
1889 gtk_target_list_unref (priv->contact_targets);
1891 if (priv->file_targets) {
1892 gtk_target_list_unref (priv->file_targets);
1895 if (priv->chat_manager) {
1896 g_signal_handler_disconnect (priv->chat_manager,
1897 priv->chat_manager_chats_changed_id);
1898 g_object_unref (priv->chat_manager);
1899 priv->chat_manager = NULL;
1902 chat_windows = g_list_remove (chat_windows, window);
1903 gtk_widget_destroy (priv->dialog);
1905 G_OBJECT_CLASS (empathy_chat_window_parent_class)->finalize (object);
1909 empathy_chat_window_class_init (EmpathyChatWindowClass *klass)
1911 GObjectClass *object_class = G_OBJECT_CLASS (klass);
1913 object_class->finalize = chat_window_finalize;
1915 g_type_class_add_private (object_class, sizeof (EmpathyChatWindowPriv));
1917 /* Set up a style for the close button with no focus padding. */
1918 gtk_rc_parse_string (
1919 "style \"empathy-close-button-style\"\n"
1921 " GtkWidget::focus-padding = 0\n"
1925 "widget \"*.empathy-close-button\" style \"empathy-close-button-style\"");
1929 empathy_chat_window_init (EmpathyChatWindow *window)
1932 GtkAccelGroup *accel_group;
1937 GtkWidget *chat_vbox;
1939 EmpathySmileyManager *smiley_manager;
1940 EmpathyChatWindowPriv *priv = G_TYPE_INSTANCE_GET_PRIVATE (window,
1941 EMPATHY_TYPE_CHAT_WINDOW, EmpathyChatWindowPriv);
1943 window->priv = priv;
1944 filename = empathy_file_lookup ("empathy-chat-window.ui", "src");
1945 gui = empathy_builder_get_file (filename,
1946 "chat_window", &priv->dialog,
1947 "chat_vbox", &chat_vbox,
1948 "ui_manager", &priv->ui_manager,
1949 "menu_conv_insert_smiley", &priv->menu_conv_insert_smiley,
1950 "menu_conv_favorite", &priv->menu_conv_favorite,
1951 "menu_conv_always_urgent", &priv->menu_conv_always_urgent,
1952 "menu_conv_toggle_contacts", &priv->menu_conv_toggle_contacts,
1953 "menu_edit_cut", &priv->menu_edit_cut,
1954 "menu_edit_copy", &priv->menu_edit_copy,
1955 "menu_edit_paste", &priv->menu_edit_paste,
1956 "menu_edit_find", &priv->menu_edit_find,
1957 "menu_tabs_next", &priv->menu_tabs_next,
1958 "menu_tabs_prev", &priv->menu_tabs_prev,
1959 "menu_tabs_undo_close_tab", &priv->menu_tabs_undo_close_tab,
1960 "menu_tabs_left", &priv->menu_tabs_left,
1961 "menu_tabs_right", &priv->menu_tabs_right,
1962 "menu_tabs_detach", &priv->menu_tabs_detach,
1966 empathy_builder_connect (gui, window,
1967 "menu_conv", "activate", chat_window_conv_activate_cb,
1968 "menu_conv_clear", "activate", chat_window_clear_activate_cb,
1969 "menu_conv_favorite", "toggled", chat_window_favorite_toggled_cb,
1970 "menu_conv_always_urgent", "toggled", chat_window_always_urgent_toggled_cb,
1971 "menu_conv_toggle_contacts", "toggled", chat_window_contacts_toggled_cb,
1972 "menu_conv_invite_participant", "activate", chat_window_invite_participant_activate_cb,
1973 "menu_conv_close", "activate", chat_window_close_activate_cb,
1974 "menu_edit", "activate", chat_window_edit_activate_cb,
1975 "menu_edit_cut", "activate", chat_window_cut_activate_cb,
1976 "menu_edit_copy", "activate", chat_window_copy_activate_cb,
1977 "menu_edit_paste", "activate", chat_window_paste_activate_cb,
1978 "menu_edit_find", "activate", chat_window_find_activate_cb,
1979 "menu_tabs_next", "activate", chat_window_tabs_next_activate_cb,
1980 "menu_tabs_prev", "activate", chat_window_tabs_previous_activate_cb,
1981 "menu_tabs_undo_close_tab", "activate", chat_window_tabs_undo_close_tab_activate_cb,
1982 "menu_tabs_left", "activate", chat_window_tabs_left_activate_cb,
1983 "menu_tabs_right", "activate", chat_window_tabs_right_activate_cb,
1984 "menu_tabs_detach", "activate", chat_window_detach_activate_cb,
1985 "menu_help_contents", "activate", chat_window_help_contents_activate_cb,
1986 "menu_help_about", "activate", chat_window_help_about_activate_cb,
1989 g_object_ref (priv->ui_manager);
1990 g_object_unref (gui);
1992 priv->gsettings_chat = g_settings_new (EMPATHY_PREFS_CHAT_SCHEMA);
1993 priv->gsettings_notif = g_settings_new (EMPATHY_PREFS_NOTIFICATIONS_SCHEMA);
1994 priv->gsettings_ui = g_settings_new (EMPATHY_PREFS_UI_SCHEMA);
1995 priv->chatroom_manager = empathy_chatroom_manager_dup_singleton (NULL);
1997 priv->sound_mgr = empathy_sound_manager_dup_singleton ();
1999 priv->notebook = gtk_notebook_new ();
2001 g_signal_connect (priv->notebook, "create-window",
2002 G_CALLBACK (notebook_create_window_cb), window);
2004 gtk_notebook_set_group_name (GTK_NOTEBOOK (priv->notebook),
2005 "EmpathyChatWindow");
2006 gtk_notebook_set_scrollable (GTK_NOTEBOOK (priv->notebook), TRUE);
2007 gtk_notebook_popup_enable (GTK_NOTEBOOK (priv->notebook));
2008 gtk_box_pack_start (GTK_BOX (chat_vbox), priv->notebook, TRUE, TRUE, 0);
2009 gtk_widget_show (priv->notebook);
2012 accel_group = gtk_accel_group_new ();
2013 gtk_window_add_accel_group (GTK_WINDOW (priv->dialog), accel_group);
2015 for (i = 0; i < G_N_ELEMENTS (tab_accel_keys); i++) {
2016 closure = g_cclosure_new (G_CALLBACK (chat_window_accel_cb),
2019 gtk_accel_group_connect (accel_group,
2026 g_object_unref (accel_group);
2028 /* Set up drag target lists */
2029 priv->contact_targets = gtk_target_list_new (drag_types_dest_contact,
2030 G_N_ELEMENTS (drag_types_dest_contact));
2031 priv->file_targets = gtk_target_list_new (drag_types_dest_file,
2032 G_N_ELEMENTS (drag_types_dest_file));
2034 /* Set up smiley menu */
2035 smiley_manager = empathy_smiley_manager_dup_singleton ();
2036 submenu = empathy_smiley_menu_new (smiley_manager,
2037 chat_window_insert_smiley_activate_cb,
2039 menu = gtk_ui_manager_get_widget (priv->ui_manager,
2040 "/chats_menubar/menu_conv/menu_conv_insert_smiley");
2041 gtk_menu_item_set_submenu (GTK_MENU_ITEM (menu), submenu);
2042 g_object_unref (smiley_manager);
2044 /* Set up signals we can't do with ui file since we may need to
2045 * block/unblock them at some later stage.
2048 g_signal_connect (priv->dialog,
2050 G_CALLBACK (chat_window_delete_event_cb),
2052 g_signal_connect (priv->dialog,
2054 G_CALLBACK (chat_window_focus_in_event_cb),
2056 g_signal_connect_after (priv->notebook,
2058 G_CALLBACK (chat_window_page_switched_cb),
2060 g_signal_connect (priv->notebook,
2062 G_CALLBACK (chat_window_page_added_cb),
2064 g_signal_connect (priv->notebook,
2066 G_CALLBACK (chat_window_page_removed_cb),
2069 /* Set up drag and drop */
2070 gtk_drag_dest_set (GTK_WIDGET (priv->notebook),
2071 GTK_DEST_DEFAULT_HIGHLIGHT,
2073 G_N_ELEMENTS (drag_types_dest),
2074 GDK_ACTION_MOVE | GDK_ACTION_COPY);
2076 /* connect_after to allow GtkNotebook's built-in tab switching */
2077 g_signal_connect_after (priv->notebook,
2079 G_CALLBACK (chat_window_drag_motion),
2081 g_signal_connect (priv->notebook,
2082 "drag-data-received",
2083 G_CALLBACK (chat_window_drag_data_received),
2085 g_signal_connect (priv->notebook,
2087 G_CALLBACK (chat_window_drag_drop),
2090 chat_windows = g_list_prepend (chat_windows, window);
2092 /* Set up private details */
2094 priv->chats_new_msg = NULL;
2095 priv->current_chat = NULL;
2096 priv->notification = NULL;
2098 priv->notify_mgr = empathy_notify_manager_dup_singleton ();
2100 priv->chat_manager = empathy_chat_manager_dup_singleton ();
2101 priv->chat_manager_chats_changed_id =
2102 g_signal_connect (priv->chat_manager, "closed-chats-changed",
2103 G_CALLBACK (chat_window_chat_manager_chats_changed_cb),
2106 chat_window_chat_manager_chats_changed_cb (priv->chat_manager,
2107 empathy_chat_manager_get_num_closed_chats (priv->chat_manager),
2112 empathy_chat_window_get_dialog (EmpathyChatWindow *window)
2114 EmpathyChatWindowPriv *priv;
2116 g_return_val_if_fail (window != NULL, NULL);
2118 priv = GET_PRIV (window);
2120 return priv->dialog;
2123 /* Returns the window to open a new tab in if there is a suitable window,
2124 * otherwise, returns NULL indicating that a new window should be added.
2126 static EmpathyChatWindow *
2127 empathy_chat_window_get_default (gboolean room)
2129 GSettings *gsettings = g_settings_new (EMPATHY_PREFS_UI_SCHEMA);
2131 gboolean separate_windows = TRUE;
2133 separate_windows = g_settings_get_boolean (gsettings,
2134 EMPATHY_PREFS_UI_SEPARATE_CHAT_WINDOWS);
2136 g_object_unref (gsettings);
2138 if (separate_windows) {
2139 /* Always create a new window */
2143 for (l = chat_windows; l; l = l->next) {
2144 EmpathyChatWindowPriv *priv;
2145 EmpathyChatWindow *chat_window;
2147 guint nb_rooms, nb_private;
2149 chat_window = l->data;
2150 priv = GET_PRIV (chat_window);
2152 dialog = empathy_chat_window_get_dialog (chat_window);
2154 empathy_chat_window_get_nb_chats (chat_window, &nb_rooms, &nb_private);
2156 /* Skip the window if there aren't any rooms in it */
2157 if (room && nb_rooms == 0)
2160 /* Skip the window if there aren't any 1-1 chats in it */
2161 if (!room && nb_private == 0)
2164 /* Found a window on this desktop, make it visible if necessary */
2165 if (!empathy_window_get_is_visible (GTK_WINDOW (dialog)))
2166 empathy_window_present (GTK_WINDOW (dialog));
2174 empathy_chat_window_add_chat (EmpathyChatWindow *window,
2177 EmpathyChatWindowPriv *priv;
2179 GtkWidget *popup_label;
2181 GValue value = { 0, };
2183 g_return_if_fail (window != NULL);
2184 g_return_if_fail (EMPATHY_IS_CHAT (chat));
2186 priv = GET_PRIV (window);
2188 /* Reference the chat object */
2189 g_object_ref (chat);
2191 /* If this window has just been created, position it */
2192 if (priv->chats == NULL) {
2193 const gchar *name = "chat-window";
2194 gboolean separate_windows;
2196 separate_windows = g_settings_get_boolean (priv->gsettings_ui,
2197 EMPATHY_PREFS_UI_SEPARATE_CHAT_WINDOWS);
2199 if (empathy_chat_is_room (chat))
2200 name = "room-window";
2202 if (separate_windows) {
2205 /* Save current position of the window */
2206 gtk_window_get_position (GTK_WINDOW (priv->dialog), &x, &y);
2208 /* First bind to the 'generic' name. So new window for which we didn't
2209 * save a geometry yet will have the geometry of the last saved
2210 * window (bgo #601191). */
2211 empathy_geometry_bind (GTK_WINDOW (priv->dialog), name);
2213 /* Restore previous position of the window so the newly created window
2214 * won't be in the same position as the latest saved window and so
2215 * completely hide it. */
2216 gtk_window_move (GTK_WINDOW (priv->dialog), x, y);
2218 /* Then bind it to the name of the contact/room so we'll save the
2219 * geometry specific to this window */
2220 name = empathy_chat_get_id (chat);
2223 empathy_geometry_bind (GTK_WINDOW (priv->dialog), name);
2226 child = GTK_WIDGET (chat);
2227 label = chat_window_create_label (window, chat, TRUE);
2228 popup_label = chat_window_create_label (window, chat, FALSE);
2229 gtk_widget_show (child);
2231 g_signal_connect (chat, "notify::name",
2232 G_CALLBACK (chat_window_chat_notify_cb),
2234 g_signal_connect (chat, "notify::subject",
2235 G_CALLBACK (chat_window_chat_notify_cb),
2237 g_signal_connect (chat, "notify::remote-contact",
2238 G_CALLBACK (chat_window_chat_notify_cb),
2240 chat_window_chat_notify_cb (chat);
2242 gtk_notebook_append_page_menu (GTK_NOTEBOOK (priv->notebook), child, label, popup_label);
2243 gtk_notebook_set_tab_reorderable (GTK_NOTEBOOK (priv->notebook), child, TRUE);
2244 gtk_notebook_set_tab_detachable (GTK_NOTEBOOK (priv->notebook), child, TRUE);
2245 g_value_init (&value, G_TYPE_BOOLEAN);
2246 g_value_set_boolean (&value, TRUE);
2247 gtk_container_child_set_property (GTK_CONTAINER (priv->notebook),
2248 child, "tab-expand" , &value);
2249 gtk_container_child_set_property (GTK_CONTAINER (priv->notebook),
2250 child, "tab-fill" , &value);
2251 g_value_unset (&value);
2253 DEBUG ("Chat added (%d references)", G_OBJECT (chat)->ref_count);
2257 empathy_chat_window_remove_chat (EmpathyChatWindow *window,
2260 EmpathyChatWindowPriv *priv;
2262 EmpathyContact *remote_contact;
2263 EmpathyChatManager *chat_manager;
2265 g_return_if_fail (window != NULL);
2266 g_return_if_fail (EMPATHY_IS_CHAT (chat));
2268 priv = GET_PRIV (window);
2270 g_signal_handlers_disconnect_by_func (chat,
2271 chat_window_chat_notify_cb,
2273 remote_contact = g_object_get_data (G_OBJECT (chat),
2274 "chat-window-remote-contact");
2275 if (remote_contact) {
2276 g_signal_handlers_disconnect_by_func (remote_contact,
2277 chat_window_update_chat_tab,
2281 chat_manager = empathy_chat_manager_dup_singleton ();
2282 empathy_chat_manager_closed_chat (chat_manager, chat);
2283 g_object_unref (chat_manager);
2285 position = gtk_notebook_page_num (GTK_NOTEBOOK (priv->notebook),
2287 gtk_notebook_remove_page (GTK_NOTEBOOK (priv->notebook), position);
2289 DEBUG ("Chat removed (%d references)", G_OBJECT (chat)->ref_count - 1);
2291 g_object_unref (chat);
2295 empathy_chat_window_move_chat (EmpathyChatWindow *old_window,
2296 EmpathyChatWindow *new_window,
2301 g_return_if_fail (EMPATHY_IS_CHAT_WINDOW (old_window));
2302 g_return_if_fail (EMPATHY_IS_CHAT_WINDOW (new_window));
2303 g_return_if_fail (EMPATHY_IS_CHAT (chat));
2305 widget = GTK_WIDGET (chat);
2307 DEBUG ("Chat moving with widget:%p (%d references)", widget,
2308 G_OBJECT (widget)->ref_count);
2310 /* We reference here to make sure we don't loose the widget
2311 * and the EmpathyChat object during the move.
2313 g_object_ref (chat);
2314 g_object_ref (widget);
2316 empathy_chat_window_remove_chat (old_window, chat);
2317 empathy_chat_window_add_chat (new_window, chat);
2319 g_object_unref (widget);
2320 g_object_unref (chat);
2324 empathy_chat_window_switch_to_chat (EmpathyChatWindow *window,
2327 EmpathyChatWindowPriv *priv;
2330 g_return_if_fail (window != NULL);
2331 g_return_if_fail (EMPATHY_IS_CHAT (chat));
2333 priv = GET_PRIV (window);
2335 page_num = gtk_notebook_page_num (GTK_NOTEBOOK (priv->notebook),
2337 gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->notebook),
2342 empathy_chat_window_find_chat (TpAccount *account,
2347 g_return_val_if_fail (!EMP_STR_EMPTY (id), NULL);
2349 for (l = chat_windows; l; l = l->next) {
2350 EmpathyChatWindowPriv *priv;
2351 EmpathyChatWindow *window;
2355 priv = GET_PRIV (window);
2357 for (ll = priv->chats; ll; ll = ll->next) {
2362 if (account == empathy_chat_get_account (chat) &&
2363 !tp_strdiff (id, empathy_chat_get_id (chat))) {
2373 empathy_chat_window_present_chat (EmpathyChat *chat,
2376 EmpathyChatWindow *window;
2377 EmpathyChatWindowPriv *priv;
2378 guint32 x_timestamp;
2380 g_return_if_fail (EMPATHY_IS_CHAT (chat));
2382 window = chat_window_find_chat (chat);
2384 /* If the chat has no window, create one */
2385 if (window == NULL) {
2386 window = empathy_chat_window_get_default (empathy_chat_is_room (chat));
2388 window = empathy_chat_window_new ();
2389 gtk_widget_show_all (GET_PRIV (window)->dialog);
2392 empathy_chat_window_add_chat (window, chat);
2395 /* Don't force the window to show itself when it wasn't
2396 * an action by the user
2398 if (!tp_user_action_time_should_present (timestamp, &x_timestamp))
2401 priv = GET_PRIV (window);
2403 if (x_timestamp != GDK_CURRENT_TIME) {
2404 /* Don't present or switch tab if the action was earlier than the
2405 * last actions X time, accounting for overflow and the first ever
2408 if (priv->x_user_action_time != 0
2409 && X_EARLIER_OR_EQL (x_timestamp, priv->x_user_action_time))
2412 priv->x_user_action_time = x_timestamp;
2415 empathy_chat_window_switch_to_chat (window, chat);
2416 empathy_window_present_with_time (GTK_WINDOW (priv->dialog),
2419 gtk_widget_grab_focus (chat->input_text_view);
2423 empathy_chat_window_get_nb_chats (EmpathyChatWindow *self,
2427 EmpathyChatWindowPriv *priv = GET_PRIV (self);
2429 guint _nb_rooms = 0, _nb_private = 0;
2431 for (l = priv->chats; l != NULL; l = g_list_next (l)) {
2432 if (empathy_chat_is_room (EMPATHY_CHAT (l->data)))
2438 if (nb_rooms != NULL)
2439 *nb_rooms = _nb_rooms;
2440 if (nb_private != NULL)
2441 *nb_private = _nb_private;