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>
48 #include <libempathy/empathy-request-util.h>
50 #include <libempathy-gtk/empathy-images.h>
51 #include <libempathy-gtk/empathy-contact-dialogs.h>
52 #include <libempathy-gtk/empathy-log-window.h>
53 #include <libempathy-gtk/empathy-geometry.h>
54 #include <libempathy-gtk/empathy-smiley-manager.h>
55 #include <libempathy-gtk/empathy-sound-manager.h>
56 #include <libempathy-gtk/empathy-ui-utils.h>
57 #include <libempathy-gtk/empathy-notify-manager.h>
59 #include "empathy-chat-manager.h"
60 #include "empathy-chat-window.h"
61 #include "empathy-about-dialog.h"
62 #include "empathy-invite-participant-dialog.h"
63 #include "gedit-close-button.h"
65 #define DEBUG_FLAG EMPATHY_DEBUG_CHAT
66 #include <libempathy/empathy-debug.h>
68 /* Macro to compare guint32 X timestamps, while accounting for wrapping around
70 #define X_EARLIER_OR_EQL(t1, t2) \
71 ((t1 <= t2 && ((t2 - t1) < G_MAXUINT32/2)) \
72 || (t1 >= t2 && (t1 - t2) > (G_MAXUINT32/2)) \
75 #define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyChatWindow)
77 EmpathyChat *current_chat;
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 GSettings *gsettings_notif;
117 GSettings *gsettings_ui;
119 EmpathySoundManager *sound_mgr;
120 } EmpathyChatWindowPriv;
122 static GList *chat_windows = NULL;
124 static const guint tab_accel_keys[] = {
125 GDK_KEY_1, GDK_KEY_2, GDK_KEY_3, GDK_KEY_4, GDK_KEY_5,
126 GDK_KEY_6, GDK_KEY_7, GDK_KEY_8, GDK_KEY_9, GDK_KEY_0
130 DND_DRAG_TYPE_CONTACT_ID,
131 DND_DRAG_TYPE_URI_LIST,
135 static const GtkTargetEntry drag_types_dest[] = {
136 { "text/contact-id", 0, DND_DRAG_TYPE_CONTACT_ID },
137 { "GTK_NOTEBOOK_TAB", GTK_TARGET_SAME_APP, DND_DRAG_TYPE_TAB },
138 { "text/uri-list", 0, DND_DRAG_TYPE_URI_LIST },
139 { "text/path-list", 0, DND_DRAG_TYPE_URI_LIST },
142 static const GtkTargetEntry drag_types_dest_contact[] = {
143 { "text/contact-id", 0, DND_DRAG_TYPE_CONTACT_ID },
146 static const GtkTargetEntry drag_types_dest_file[] = {
147 /* must be first to be prioritized, in order to receive the
148 * note's file path from Tomboy instead of an URI */
149 { "text/path-list", 0, DND_DRAG_TYPE_URI_LIST },
150 { "text/uri-list", 0, DND_DRAG_TYPE_URI_LIST },
153 static void chat_window_update (EmpathyChatWindow *window,
154 gboolean update_contact_menu);
156 static void empathy_chat_window_add_chat (EmpathyChatWindow *window,
159 static void empathy_chat_window_remove_chat (EmpathyChatWindow *window,
162 static void empathy_chat_window_move_chat (EmpathyChatWindow *old_window,
163 EmpathyChatWindow *new_window,
166 static void empathy_chat_window_get_nb_chats (EmpathyChatWindow *self,
170 G_DEFINE_TYPE (EmpathyChatWindow, empathy_chat_window, G_TYPE_OBJECT);
173 chat_window_accel_cb (GtkAccelGroup *accelgroup,
177 EmpathyChatWindow *window)
179 EmpathyChatWindowPriv *priv;
183 priv = GET_PRIV (window);
185 for (i = 0; i < G_N_ELEMENTS (tab_accel_keys); i++) {
186 if (tab_accel_keys[i] == key) {
193 gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->notebook), num);
197 static EmpathyChatWindow *
198 chat_window_find_chat (EmpathyChat *chat)
200 EmpathyChatWindowPriv *priv;
203 for (l = chat_windows; l; l = l->next) {
204 priv = GET_PRIV (l->data);
205 ll = g_list_find (priv->chats, chat);
215 chat_window_close_clicked_cb (GtkAction *action,
218 EmpathyChatWindow *window;
220 window = chat_window_find_chat (chat);
221 empathy_chat_window_remove_chat (window, chat);
225 chat_tab_style_updated_cb (GtkWidget *hbox,
229 int char_width, h, w;
230 PangoContext *context;
231 const PangoFontDescription *font_desc;
232 PangoFontMetrics *metrics;
234 button = g_object_get_data (G_OBJECT (user_data),
235 "chat-window-tab-close-button");
236 context = gtk_widget_get_pango_context (hbox);
238 font_desc = gtk_style_context_get_font (gtk_widget_get_style_context (hbox),
239 GTK_STATE_FLAG_NORMAL);
241 metrics = pango_context_get_metrics (context, font_desc,
242 pango_context_get_language (context));
243 char_width = pango_font_metrics_get_approximate_char_width (metrics);
244 pango_font_metrics_unref (metrics);
246 gtk_icon_size_lookup_for_settings (gtk_widget_get_settings (button),
247 GTK_ICON_SIZE_MENU, &w, &h);
249 /* Request at least about 12 chars width plus at least space for the status
250 * image and the close button */
251 gtk_widget_set_size_request (hbox,
252 12 * PANGO_PIXELS (char_width) + 2 * w, -1);
254 gtk_widget_set_size_request (button, w, h);
258 chat_window_create_label (EmpathyChatWindow *window,
260 gboolean is_tab_label)
262 EmpathyChatWindowPriv *priv;
264 GtkWidget *name_label;
265 GtkWidget *status_image;
266 GtkWidget *event_box;
267 GtkWidget *event_box_hbox;
268 PangoAttrList *attr_list;
269 PangoAttribute *attr;
271 priv = GET_PRIV (window);
273 /* The spacing between the button and the label. */
274 hbox = gtk_hbox_new (FALSE, 0);
276 event_box = gtk_event_box_new ();
277 gtk_event_box_set_visible_window (GTK_EVENT_BOX (event_box), FALSE);
279 name_label = gtk_label_new (NULL);
281 gtk_label_set_ellipsize (GTK_LABEL (name_label), PANGO_ELLIPSIZE_END);
283 attr_list = pango_attr_list_new ();
284 attr = pango_attr_scale_new (1/1.2);
285 attr->start_index = 0;
286 attr->end_index = -1;
287 pango_attr_list_insert (attr_list, attr);
288 gtk_label_set_attributes (GTK_LABEL (name_label), attr_list);
289 pango_attr_list_unref (attr_list);
291 gtk_misc_set_padding (GTK_MISC (name_label), 2, 0);
292 gtk_misc_set_alignment (GTK_MISC (name_label), 0.0, 0.5);
293 g_object_set_data (G_OBJECT (chat),
294 is_tab_label ? "chat-window-tab-label" : "chat-window-menu-label",
297 status_image = gtk_image_new ();
299 /* Spacing between the icon and label. */
300 event_box_hbox = gtk_hbox_new (FALSE, 0);
302 gtk_box_pack_start (GTK_BOX (event_box_hbox), status_image, FALSE, FALSE, 0);
303 gtk_box_pack_start (GTK_BOX (event_box_hbox), name_label, TRUE, TRUE, 0);
305 g_object_set_data (G_OBJECT (chat),
306 is_tab_label ? "chat-window-tab-image" : "chat-window-menu-image",
308 g_object_set_data (G_OBJECT (chat),
309 is_tab_label ? "chat-window-tab-tooltip-widget" : "chat-window-menu-tooltip-widget",
312 gtk_container_add (GTK_CONTAINER (event_box), event_box_hbox);
313 gtk_box_pack_start (GTK_BOX (hbox), event_box, TRUE, TRUE, 0);
316 GtkWidget *close_button;
318 close_button = gedit_close_button_new ();
319 g_object_set_data (G_OBJECT (chat), "chat-window-tab-close-button", close_button);
321 /* We don't want focus/keynav for the button to avoid clutter, and
322 * Ctrl-W works anyway.
324 gtk_widget_set_can_focus (close_button, FALSE);
325 gtk_widget_set_can_default (close_button, FALSE);
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_updated_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), TRUE);
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);
423 if (submenu != NULL) {
424 /* gtk_menu_attach_to_widget () doesn't behave nicely here */
425 g_object_set_data (G_OBJECT (submenu), "window", priv->dialog);
427 gtk_menu_item_set_submenu (GTK_MENU_ITEM (menu), submenu);
428 gtk_widget_show (menu);
429 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; l != NULL; l = g_list_next (l))
448 nb += empathy_chat_get_nb_unread_messages (EMPATHY_CHAT (l->data));
454 get_window_title_name (EmpathyChatWindowPriv *priv)
456 gchar *active_name, *ret;
458 guint current_unread_msgs;
460 nb_chats = g_list_length (priv->chats);
461 g_assert (nb_chats > 0);
463 active_name = empathy_chat_dup_name (priv->current_chat);
465 current_unread_msgs = empathy_chat_get_nb_unread_messages (
470 if (current_unread_msgs == 0)
471 ret = g_strdup (active_name);
473 ret = g_strdup_printf (ngettext (
475 "%s (%d unread)", current_unread_msgs),
476 active_name, current_unread_msgs);
478 guint nb_others = nb_chats - 1;
479 guint all_unread_msgs;
481 all_unread_msgs = get_all_unread_messages (priv);
483 if (all_unread_msgs == 0) {
484 /* no unread message */
485 ret = g_strdup_printf (ngettext (
487 "%s (and %u others)", nb_others),
488 active_name, nb_others);
491 else if (all_unread_msgs == current_unread_msgs) {
492 /* unread messages are in the current tab */
493 ret = g_strdup_printf (ngettext (
495 "%s (%d unread)", current_unread_msgs),
496 active_name, current_unread_msgs);
499 else if (current_unread_msgs == 0) {
500 /* unread messages are in other tabs */
501 ret = g_strdup_printf (ngettext (
502 "%s (%d unread from others)",
503 "%s (%d unread from others)",
505 active_name, all_unread_msgs);
509 /* unread messages are in all the tabs */
510 ret = g_strdup_printf (ngettext (
511 "%s (%d unread from all)",
512 "%s (%d unread from all)",
514 active_name, all_unread_msgs);
518 g_free (active_name);
524 chat_window_title_update (EmpathyChatWindowPriv *priv)
528 name = get_window_title_name (priv);
529 gtk_window_set_title (GTK_WINDOW (priv->dialog), name);
534 chat_window_icon_update (EmpathyChatWindowPriv *priv, gboolean new_messages)
537 EmpathyContact *remote_contact;
538 gboolean avatar_in_icon;
541 n_chats = g_list_length (priv->chats);
543 /* Update window icon */
545 gtk_window_set_icon_name (GTK_WINDOW (priv->dialog),
546 EMPATHY_IMAGE_MESSAGE);
548 avatar_in_icon = g_settings_get_boolean (priv->gsettings_chat,
549 EMPATHY_PREFS_CHAT_AVATAR_IN_ICON);
551 if (n_chats == 1 && avatar_in_icon) {
552 remote_contact = empathy_chat_get_remote_contact (priv->current_chat);
553 icon = empathy_pixbuf_avatar_from_contact_scaled (remote_contact, 0, 0);
554 gtk_window_set_icon (GTK_WINDOW (priv->dialog), icon);
557 g_object_unref (icon);
560 gtk_window_set_icon_name (GTK_WINDOW (priv->dialog), NULL);
566 chat_window_close_button_update (EmpathyChatWindowPriv *priv,
570 GtkWidget *chat_close_button;
573 if (num_pages == 1) {
574 chat = gtk_notebook_get_nth_page (GTK_NOTEBOOK (priv->notebook), 0);
575 chat_close_button = g_object_get_data (G_OBJECT (chat),
576 "chat-window-tab-close-button");
577 gtk_widget_hide (chat_close_button);
579 for (i=0; i<num_pages; i++) {
580 chat = gtk_notebook_get_nth_page (GTK_NOTEBOOK (priv->notebook), i);
581 chat_close_button = g_object_get_data (G_OBJECT (chat),
582 "chat-window-tab-close-button");
583 gtk_widget_show (chat_close_button);
589 chat_window_update (EmpathyChatWindow *window,
590 gboolean update_contact_menu)
592 EmpathyChatWindowPriv *priv = GET_PRIV (window);
595 num_pages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (priv->notebook));
597 /* Update Tab menu */
598 chat_window_menu_context_update (priv,
601 chat_window_conversation_menu_update (priv, window);
603 /* If this update is due to a focus-in event, we know the menu will be
604 the same as when we last left it, so no work to do. Besides, if we
605 swap out the menu on a focus-in, we may confuse any external global
607 if (update_contact_menu) {
608 chat_window_contact_menu_update (priv,
612 chat_window_title_update (priv);
614 chat_window_icon_update (priv, get_all_unread_messages (priv) > 0);
616 chat_window_close_button_update (priv,
621 append_markup_printf (GString *string,
628 va_start (args, format);
630 tmp = g_markup_vprintf_escaped (format, args);
631 g_string_append (string, tmp);
638 chat_window_update_chat_tab_full (EmpathyChat *chat,
639 gboolean update_contact_menu)
641 EmpathyChatWindow *window;
642 EmpathyChatWindowPriv *priv;
643 EmpathyContact *remote_contact;
647 const gchar *subject;
648 const gchar *status = NULL;
652 const gchar *icon_name;
653 GtkWidget *tab_image;
654 GtkWidget *menu_image;
656 window = chat_window_find_chat (chat);
660 priv = GET_PRIV (window);
662 /* Get information */
663 name = empathy_chat_dup_name (chat);
664 account = empathy_chat_get_account (chat);
665 subject = empathy_chat_get_subject (chat);
666 remote_contact = empathy_chat_get_remote_contact (chat);
668 DEBUG ("Updating chat tab, name=%s, account=%s, subject=%s, remote_contact=%p",
669 name, tp_proxy_get_object_path (account), subject, remote_contact);
671 /* Update tab image */
672 if (empathy_chat_get_tp_chat (chat) == NULL) {
673 /* No TpChat, we are disconnected */
676 else if (empathy_chat_get_nb_unread_messages (chat) > 0) {
677 icon_name = EMPATHY_IMAGE_MESSAGE;
679 else if (remote_contact && empathy_chat_is_composing (chat)) {
680 icon_name = EMPATHY_IMAGE_TYPING;
682 else if (empathy_chat_is_sms_channel (chat)) {
683 icon_name = EMPATHY_IMAGE_SMS;
685 else if (remote_contact) {
686 icon_name = empathy_icon_name_for_contact (remote_contact);
688 icon_name = EMPATHY_IMAGE_GROUP_MESSAGE;
691 tab_image = g_object_get_data (G_OBJECT (chat), "chat-window-tab-image");
692 menu_image = g_object_get_data (G_OBJECT (chat), "chat-window-menu-image");
693 if (icon_name != NULL) {
694 gtk_image_set_from_icon_name (GTK_IMAGE (tab_image), icon_name, GTK_ICON_SIZE_MENU);
695 gtk_widget_show (tab_image);
696 gtk_image_set_from_icon_name (GTK_IMAGE (menu_image), icon_name, GTK_ICON_SIZE_MENU);
697 gtk_widget_show (menu_image);
699 gtk_widget_hide (tab_image);
700 gtk_widget_hide (menu_image);
703 /* Update tab tooltip */
704 tooltip = g_string_new (NULL);
706 if (remote_contact) {
707 id = empathy_contact_get_id (remote_contact);
708 status = empathy_contact_get_presence_message (remote_contact);
713 if (empathy_chat_is_sms_channel (chat)) {
714 append_markup_printf (tooltip, "%s ", _("SMS:"));
717 append_markup_printf (tooltip,
718 "<b>%s</b><small> (%s)</small>",
720 tp_account_get_display_name (account));
722 if (!EMP_STR_EMPTY (status)) {
723 append_markup_printf (tooltip, "\n<i>%s</i>", status);
727 append_markup_printf (tooltip, "\n<b>%s</b> %s",
728 _("Topic:"), subject);
731 if (remote_contact && empathy_chat_is_composing (chat)) {
732 append_markup_printf (tooltip, "\n%s", _("Typing a message."));
735 markup = g_string_free (tooltip, FALSE);
736 widget = g_object_get_data (G_OBJECT (chat), "chat-window-tab-tooltip-widget");
737 gtk_widget_set_tooltip_markup (widget, markup);
738 widget = g_object_get_data (G_OBJECT (chat), "chat-window-menu-tooltip-widget");
739 gtk_widget_set_tooltip_markup (widget, markup);
742 /* Update tab and menu label */
743 widget = g_object_get_data (G_OBJECT (chat), "chat-window-tab-label");
744 gtk_label_set_text (GTK_LABEL (widget), name);
745 widget = g_object_get_data (G_OBJECT (chat), "chat-window-menu-label");
746 gtk_label_set_text (GTK_LABEL (widget), name);
748 /* Update the window if it's the current chat */
749 if (priv->current_chat == chat) {
750 chat_window_update (window, update_contact_menu);
757 chat_window_update_chat_tab (EmpathyChat *chat)
759 chat_window_update_chat_tab_full (chat, TRUE);
763 chat_window_chat_notify_cb (EmpathyChat *chat)
765 EmpathyContact *old_remote_contact;
766 EmpathyContact *remote_contact = NULL;
768 old_remote_contact = g_object_get_data (G_OBJECT (chat), "chat-window-remote-contact");
769 remote_contact = empathy_chat_get_remote_contact (chat);
771 if (old_remote_contact != remote_contact) {
772 /* The remote-contact associated with the chat changed, we need
773 * to keep track of any change of that contact and update the
774 * window each time. */
775 if (remote_contact) {
776 g_signal_connect_swapped (remote_contact, "notify",
777 G_CALLBACK (chat_window_update_chat_tab),
780 if (old_remote_contact) {
781 g_signal_handlers_disconnect_by_func (old_remote_contact,
782 chat_window_update_chat_tab,
786 g_object_set_data_full (G_OBJECT (chat), "chat-window-remote-contact",
787 g_object_ref (remote_contact), (GDestroyNotify) g_object_unref);
790 chat_window_update_chat_tab (chat);
794 chat_window_insert_smiley_activate_cb (EmpathySmileyManager *manager,
795 EmpathySmiley *smiley,
798 EmpathyChatWindowPriv *priv = GET_PRIV (window);
800 GtkTextBuffer *buffer;
803 chat = priv->current_chat;
805 buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (chat->input_text_view));
806 gtk_text_buffer_get_end_iter (buffer, &iter);
807 gtk_text_buffer_insert (buffer, &iter, smiley->str, -1);
811 chat_window_conv_activate_cb (GtkAction *action,
812 EmpathyChatWindow *window)
814 EmpathyChatWindowPriv *priv = GET_PRIV (window);
817 EmpathyContact *remote_contact = NULL;
819 /* Favorite room menu */
820 is_room = empathy_chat_is_room (priv->current_chat);
824 gboolean found = FALSE;
825 EmpathyChatroom *chatroom;
827 room = empathy_chat_get_id (priv->current_chat);
828 account = empathy_chat_get_account (priv->current_chat);
829 chatroom = empathy_chatroom_manager_find (priv->chatroom_manager,
831 if (chatroom != NULL)
832 found = empathy_chatroom_is_favorite (chatroom);
834 DEBUG ("This room %s favorite", found ? "is" : "is not");
835 gtk_toggle_action_set_active (
836 GTK_TOGGLE_ACTION (priv->menu_conv_favorite), found);
838 if (chatroom != NULL)
839 found = empathy_chatroom_is_always_urgent (chatroom);
841 gtk_toggle_action_set_active (
842 GTK_TOGGLE_ACTION (priv->menu_conv_always_urgent),
845 gtk_action_set_visible (priv->menu_conv_favorite, is_room);
846 gtk_action_set_visible (priv->menu_conv_always_urgent, is_room);
848 /* Show contacts menu */
849 g_object_get (priv->current_chat,
850 "remote-contact", &remote_contact,
851 "show-contacts", &active,
853 if (remote_contact == NULL) {
854 gtk_toggle_action_set_active (
855 GTK_TOGGLE_ACTION (priv->menu_conv_toggle_contacts),
858 gtk_action_set_visible (priv->menu_conv_toggle_contacts,
859 (remote_contact == NULL));
860 if (remote_contact != NULL) {
861 g_object_unref (remote_contact);
866 chat_window_clear_activate_cb (GtkAction *action,
867 EmpathyChatWindow *window)
869 EmpathyChatWindowPriv *priv = GET_PRIV (window);
871 empathy_chat_clear (priv->current_chat);
875 chat_window_favorite_toggled_cb (GtkToggleAction *toggle_action,
876 EmpathyChatWindow *window)
878 EmpathyChatWindowPriv *priv = GET_PRIV (window);
883 EmpathyChatroom *chatroom;
885 active = gtk_toggle_action_get_active (toggle_action);
886 account = empathy_chat_get_account (priv->current_chat);
887 room = empathy_chat_get_id (priv->current_chat);
888 name = empathy_chat_dup_name (priv->current_chat);
890 chatroom = empathy_chatroom_manager_ensure_chatroom (
891 priv->chatroom_manager,
896 empathy_chatroom_set_favorite (chatroom, active);
897 g_object_unref (chatroom);
902 chat_window_always_urgent_toggled_cb (GtkToggleAction *toggle_action,
903 EmpathyChatWindow *window)
905 EmpathyChatWindowPriv *priv = GET_PRIV (window);
910 EmpathyChatroom *chatroom;
912 active = gtk_toggle_action_get_active (toggle_action);
913 account = empathy_chat_get_account (priv->current_chat);
914 room = empathy_chat_get_id (priv->current_chat);
915 name = empathy_chat_dup_name (priv->current_chat);
917 chatroom = empathy_chatroom_manager_ensure_chatroom (
918 priv->chatroom_manager,
923 empathy_chatroom_set_always_urgent (chatroom, active);
924 g_object_unref (chatroom);
929 chat_window_contacts_toggled_cb (GtkToggleAction *toggle_action,
930 EmpathyChatWindow *window)
932 EmpathyChatWindowPriv *priv = GET_PRIV (window);
935 active = gtk_toggle_action_get_active (toggle_action);
937 empathy_chat_set_show_contacts (priv->current_chat, active);
941 got_contact_cb (TpConnection *connection,
942 EmpathyContact *contact,
947 EmpathyTpChat *tp_chat = EMPATHY_TP_CHAT (user_data);
950 DEBUG ("Failed: %s", error->message);
953 empathy_contact_list_add (EMPATHY_CONTACT_LIST (tp_chat),
954 contact, _("Inviting you to this room"));
959 chat_window_invite_participant_activate_cb (GtkAction *action,
960 EmpathyChatWindow *window)
962 EmpathyChatWindowPriv *priv;
964 EmpathyTpChat *tp_chat;
969 priv = GET_PRIV (window);
971 g_return_if_fail (priv->current_chat != NULL);
973 tp_chat = empathy_chat_get_tp_chat (priv->current_chat);
974 channel = empathy_tp_chat_get_channel (tp_chat);
975 account = empathy_chat_get_account (priv->current_chat);
977 dialog = empathy_invite_participant_dialog_new (
978 GTK_WINDOW (priv->dialog), account);
979 gtk_widget_show (dialog);
981 response = gtk_dialog_run (GTK_DIALOG (dialog));
983 if (response == GTK_RESPONSE_ACCEPT) {
984 TpConnection *connection;
987 id = empathy_contact_selector_dialog_get_selected (
988 EMPATHY_CONTACT_SELECTOR_DIALOG (dialog), NULL, NULL);
989 if (EMP_STR_EMPTY (id)) goto out;
991 connection = tp_channel_borrow_connection (channel);
992 empathy_tp_contact_factory_get_from_id (connection, id,
993 got_contact_cb, tp_chat, NULL, NULL);
997 gtk_widget_destroy (dialog);
1001 chat_window_close_activate_cb (GtkAction *action,
1002 EmpathyChatWindow *window)
1004 EmpathyChatWindowPriv *priv;
1006 priv = GET_PRIV (window);
1008 g_return_if_fail (priv->current_chat != NULL);
1010 empathy_chat_window_remove_chat (window, priv->current_chat);
1014 chat_window_edit_activate_cb (GtkAction *action,
1015 EmpathyChatWindow *window)
1017 EmpathyChatWindowPriv *priv;
1018 GtkClipboard *clipboard;
1019 GtkTextBuffer *buffer;
1020 gboolean text_available;
1022 priv = GET_PRIV (window);
1024 g_return_if_fail (priv->current_chat != NULL);
1026 if (!empathy_chat_get_tp_chat (priv->current_chat)) {
1027 gtk_action_set_sensitive (priv->menu_edit_copy, FALSE);
1028 gtk_action_set_sensitive (priv->menu_edit_cut, FALSE);
1029 gtk_action_set_sensitive (priv->menu_edit_paste, FALSE);
1033 buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (priv->current_chat->input_text_view));
1034 if (gtk_text_buffer_get_has_selection (buffer)) {
1035 gtk_action_set_sensitive (priv->menu_edit_copy, TRUE);
1036 gtk_action_set_sensitive (priv->menu_edit_cut, TRUE);
1040 selection = empathy_chat_view_get_has_selection (priv->current_chat->view);
1042 gtk_action_set_sensitive (priv->menu_edit_cut, FALSE);
1043 gtk_action_set_sensitive (priv->menu_edit_copy, selection);
1046 clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
1047 text_available = gtk_clipboard_wait_is_text_available (clipboard);
1048 gtk_action_set_sensitive (priv->menu_edit_paste, text_available);
1052 chat_window_cut_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_cut (priv->current_chat);
1065 chat_window_copy_activate_cb (GtkAction *action,
1066 EmpathyChatWindow *window)
1068 EmpathyChatWindowPriv *priv;
1070 g_return_if_fail (EMPATHY_IS_CHAT_WINDOW (window));
1072 priv = GET_PRIV (window);
1074 empathy_chat_copy (priv->current_chat);
1078 chat_window_paste_activate_cb (GtkAction *action,
1079 EmpathyChatWindow *window)
1081 EmpathyChatWindowPriv *priv;
1083 g_return_if_fail (EMPATHY_IS_CHAT_WINDOW (window));
1085 priv = GET_PRIV (window);
1087 empathy_chat_paste (priv->current_chat);
1091 chat_window_find_activate_cb (GtkAction *action,
1092 EmpathyChatWindow *window)
1094 EmpathyChatWindowPriv *priv;
1096 g_return_if_fail (EMPATHY_IS_CHAT_WINDOW (window));
1098 priv = GET_PRIV (window);
1100 empathy_chat_find (priv->current_chat);
1104 chat_window_tabs_next_activate_cb (GtkAction *action,
1105 EmpathyChatWindow *window)
1107 EmpathyChatWindowPriv *priv;
1109 gint index_, numPages;
1110 gboolean wrap_around;
1112 priv = GET_PRIV (window);
1114 g_object_get (gtk_settings_get_default (), "gtk-keynav-wrap-around",
1115 &wrap_around, NULL);
1117 chat = priv->current_chat;
1118 index_ = gtk_notebook_get_current_page (GTK_NOTEBOOK (priv->notebook));
1119 numPages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (priv->notebook));
1121 if (index_ == (numPages - 1) && wrap_around) {
1122 gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->notebook), 0);
1126 gtk_notebook_next_page (GTK_NOTEBOOK (priv->notebook));
1130 chat_window_tabs_previous_activate_cb (GtkAction *action,
1131 EmpathyChatWindow *window)
1133 EmpathyChatWindowPriv *priv;
1135 gint index_, numPages;
1136 gboolean wrap_around;
1138 priv = GET_PRIV (window);
1140 g_object_get (gtk_settings_get_default (), "gtk-keynav-wrap-around",
1141 &wrap_around, NULL);
1143 chat = priv->current_chat;
1144 index_ = gtk_notebook_get_current_page (GTK_NOTEBOOK (priv->notebook));
1145 numPages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (priv->notebook));
1147 if (index_ <= 0 && wrap_around) {
1148 gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->notebook), numPages - 1);
1152 gtk_notebook_prev_page (GTK_NOTEBOOK (priv->notebook));
1156 chat_window_tabs_undo_close_tab_activate_cb (GtkAction *action,
1157 EmpathyChatWindow *window)
1159 EmpathyChatWindowPriv *priv = GET_PRIV (window);
1160 empathy_chat_manager_undo_closed_chat (priv->chat_manager);
1164 chat_window_tabs_left_activate_cb (GtkAction *action,
1165 EmpathyChatWindow *window)
1167 EmpathyChatWindowPriv *priv;
1169 gint index_, num_pages;
1171 priv = GET_PRIV (window);
1173 chat = priv->current_chat;
1174 index_ = gtk_notebook_get_current_page (GTK_NOTEBOOK (priv->notebook));
1179 gtk_notebook_reorder_child (GTK_NOTEBOOK (priv->notebook),
1183 num_pages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (priv->notebook));
1184 chat_window_menu_context_update (priv, num_pages);
1188 chat_window_tabs_right_activate_cb (GtkAction *action,
1189 EmpathyChatWindow *window)
1191 EmpathyChatWindowPriv *priv;
1193 gint index_, num_pages;
1195 priv = GET_PRIV (window);
1197 chat = priv->current_chat;
1198 index_ = gtk_notebook_get_current_page (GTK_NOTEBOOK (priv->notebook));
1200 gtk_notebook_reorder_child (GTK_NOTEBOOK (priv->notebook),
1204 num_pages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (priv->notebook));
1205 chat_window_menu_context_update (priv, num_pages);
1208 static EmpathyChatWindow *
1209 empathy_chat_window_new (void)
1211 return EMPATHY_CHAT_WINDOW (g_object_new (EMPATHY_TYPE_CHAT_WINDOW, NULL));
1215 chat_window_detach_activate_cb (GtkAction *action,
1216 EmpathyChatWindow *window)
1218 EmpathyChatWindowPriv *priv;
1219 EmpathyChatWindow *new_window;
1222 priv = GET_PRIV (window);
1224 chat = priv->current_chat;
1225 new_window = empathy_chat_window_new ();
1227 empathy_chat_window_move_chat (window, new_window, chat);
1229 priv = GET_PRIV (new_window);
1230 gtk_widget_show (priv->dialog);
1234 chat_window_help_contents_activate_cb (GtkAction *action,
1235 EmpathyChatWindow *window)
1237 EmpathyChatWindowPriv *priv = GET_PRIV (window);
1239 empathy_url_show (priv->dialog, "ghelp:empathy");
1243 chat_window_help_about_activate_cb (GtkAction *action,
1244 EmpathyChatWindow *window)
1246 EmpathyChatWindowPriv *priv = GET_PRIV (window);
1248 empathy_about_dialog_new (GTK_WINDOW (priv->dialog));
1252 chat_window_delete_event_cb (GtkWidget *dialog,
1254 EmpathyChatWindow *window)
1256 EmpathyChatWindowPriv *priv = GET_PRIV (window);
1258 DEBUG ("Delete event received");
1260 g_object_ref (window);
1261 while (priv->chats) {
1262 empathy_chat_window_remove_chat (window, priv->chats->data);
1264 g_object_unref (window);
1270 chat_window_composing_cb (EmpathyChat *chat,
1271 gboolean is_composing,
1272 EmpathyChatWindow *window)
1274 chat_window_update_chat_tab (chat);
1278 chat_window_set_urgency_hint (EmpathyChatWindow *window,
1281 EmpathyChatWindowPriv *priv;
1283 priv = GET_PRIV (window);
1285 gtk_window_set_urgency_hint (GTK_WINDOW (priv->dialog), urgent);
1289 chat_window_notification_closed_cb (NotifyNotification *notify,
1290 EmpathyChatWindow *self)
1292 EmpathyChatWindowPriv *priv = GET_PRIV (self);
1294 g_object_unref (notify);
1295 if (priv->notification == notify) {
1296 priv->notification = NULL;
1301 chat_window_show_or_update_notification (EmpathyChatWindow *window,
1302 EmpathyMessage *message,
1305 EmpathyContact *sender;
1306 const gchar *header;
1310 EmpathyChatWindowPriv *priv = GET_PRIV (window);
1311 gboolean res, has_x_canonical_append;
1312 NotifyNotification *notification = priv->notification;
1314 if (!empathy_notify_manager_notification_is_enabled (priv->notify_mgr)) {
1317 res = g_settings_get_boolean (priv->gsettings_notif,
1318 EMPATHY_PREFS_NOTIFICATIONS_FOCUS);
1325 sender = empathy_message_get_sender (message);
1326 header = empathy_contact_get_alias (sender);
1327 body = empathy_message_get_body (message);
1328 escaped = g_markup_escape_text (body, -1);
1329 has_x_canonical_append = empathy_notify_manager_has_capability (
1330 priv->notify_mgr, EMPATHY_NOTIFY_MANAGER_CAP_X_CANONICAL_APPEND);
1332 if (notification != NULL && !has_x_canonical_append) {
1333 /* if the notification server supports x-canonical-append, it is
1334 better to not use notify_notification_update to avoid
1335 overwriting the current notification message */
1336 notify_notification_update (notification,
1337 header, escaped, NULL);
1339 /* if the notification server supports x-canonical-append,
1340 the hint will be added, so that the message from the
1341 just created notification will be automatically appended
1342 to an existing notification with the same title.
1343 In this way the previous message will not be lost: the new
1344 message will appear below it, in the same notification */
1345 notification = notify_notification_new (header, escaped, NULL);
1347 if (priv->notification == NULL) {
1348 priv->notification = notification;
1351 notify_notification_set_timeout (notification, NOTIFY_EXPIRES_DEFAULT);
1353 tp_g_signal_connect_object (notification, "closed",
1354 G_CALLBACK (chat_window_notification_closed_cb), window, 0);
1356 if (has_x_canonical_append) {
1357 notify_notification_set_hint_string (notification,
1358 EMPATHY_NOTIFY_MANAGER_CAP_X_CANONICAL_APPEND, "");
1362 pixbuf = empathy_notify_manager_get_pixbuf_for_notification (priv->notify_mgr,
1363 sender, EMPATHY_IMAGE_NEW_MESSAGE);
1365 if (pixbuf != NULL) {
1366 notify_notification_set_icon_from_pixbuf (notification, pixbuf);
1367 g_object_unref (pixbuf);
1370 notify_notification_show (notification, NULL);
1376 chat_window_set_highlight_room_labels (EmpathyChat *chat)
1378 gchar *markup, *name;
1381 if (!empathy_chat_is_room (chat))
1384 name = empathy_chat_dup_name (chat);
1385 markup = g_markup_printf_escaped (
1386 "<span color=\"red\" weight=\"bold\">%s</span>",
1389 widget = g_object_get_data (G_OBJECT (chat), "chat-window-tab-label");
1390 gtk_label_set_markup (GTK_LABEL (widget), markup);
1392 widget = g_object_get_data (G_OBJECT (chat), "chat-window-menu-label");
1393 gtk_label_set_markup (GTK_LABEL (widget), markup);
1400 empathy_chat_window_has_focus (EmpathyChatWindow *window)
1402 EmpathyChatWindowPriv *priv;
1405 g_return_val_if_fail (EMPATHY_IS_CHAT_WINDOW (window), FALSE);
1407 priv = GET_PRIV (window);
1409 g_object_get (priv->dialog, "has-toplevel-focus", &has_focus, NULL);
1415 chat_window_new_message_cb (EmpathyChat *chat,
1416 EmpathyMessage *message,
1418 EmpathyChatWindow *window)
1420 EmpathyChatWindowPriv *priv;
1422 gboolean needs_urgency;
1423 EmpathyContact *sender;
1425 priv = GET_PRIV (window);
1427 has_focus = empathy_chat_window_has_focus (window);
1429 /* - if we're the sender, we play the sound if it's specified in the
1430 * preferences and we're not away.
1431 * - if we receive a message, we play the sound if it's specified in the
1432 * preferences and the window does not have focus on the chat receiving
1436 sender = empathy_message_get_sender (message);
1438 if (empathy_contact_is_user (sender)) {
1439 empathy_sound_manager_play (priv->sound_mgr, GTK_WIDGET (priv->dialog),
1440 EMPATHY_SOUND_MESSAGE_OUTGOING);
1443 if (has_focus && priv->current_chat == chat) {
1444 /* window and tab are focused so consider the message to be read */
1446 /* FIXME: see Bug#610994 and coments about it in EmpathyChatPriv */
1447 empathy_chat_messages_read (chat);
1451 /* Update the chat tab if this is the first unread message */
1452 if (empathy_chat_get_nb_unread_messages (chat) == 1) {
1453 chat_window_update_chat_tab (chat);
1456 /* If empathy_chat_is_room () returns TRUE, that means it's a named MUC.
1457 * If empathy_chat_get_remote_contact () returns NULL, that means it's
1458 * an unamed MUC (msn-like).
1459 * In case of a MUC, we set urgency if either:
1460 * a) the chatroom's always_urgent property is TRUE
1461 * b) the message contains our alias
1463 if (empathy_chat_is_room (chat) ||
1464 empathy_chat_get_remote_contact (chat) == NULL) {
1467 EmpathyChatroom *chatroom;
1469 account = empathy_chat_get_account (chat);
1470 room = empathy_chat_get_id (chat);
1472 chatroom = empathy_chatroom_manager_find (priv->chatroom_manager,
1475 if (chatroom != NULL && empathy_chatroom_is_always_urgent (chatroom)) {
1476 needs_urgency = TRUE;
1478 needs_urgency = empathy_message_should_highlight (message);
1481 needs_urgency = TRUE;
1484 if (needs_urgency) {
1485 chat_window_set_highlight_room_labels (chat);
1488 chat_window_set_urgency_hint (window, TRUE);
1491 /* Pending messages have already been displayed and notified in the
1492 * approver, so we don't display a notification and play a sound for those */
1494 empathy_sound_manager_play (priv->sound_mgr, GTK_WIDGET (priv->dialog),
1495 EMPATHY_SOUND_MESSAGE_INCOMING);
1497 chat_window_show_or_update_notification (window, message, chat);
1501 /* update the number of unread messages and the window icon */
1502 chat_window_title_update (priv);
1503 chat_window_icon_update (priv, TRUE);
1506 static GtkNotebook *
1507 notebook_create_window_cb (GtkNotebook *source,
1513 EmpathyChatWindowPriv *priv;
1514 EmpathyChatWindow *window, *new_window;
1517 chat = EMPATHY_CHAT (page);
1518 window = chat_window_find_chat (chat);
1520 new_window = empathy_chat_window_new ();
1521 priv = GET_PRIV (new_window);
1523 DEBUG ("Detach hook called");
1525 empathy_chat_window_move_chat (window, new_window, chat);
1527 gtk_widget_show (priv->dialog);
1528 gtk_window_move (GTK_WINDOW (priv->dialog), x, y);
1534 chat_window_page_switched_cb (GtkNotebook *notebook,
1537 EmpathyChatWindow *window)
1539 EmpathyChatWindowPriv *priv = GET_PRIV (window);
1540 EmpathyChat *chat = EMPATHY_CHAT (child);
1542 DEBUG ("Page switched");
1544 if (priv->page_added) {
1545 priv->page_added = FALSE;
1546 empathy_chat_scroll_down (chat);
1548 else if (priv->current_chat == chat) {
1552 priv->current_chat = chat;
1553 empathy_chat_messages_read (chat);
1555 chat_window_update_chat_tab (chat);
1559 chat_window_page_added_cb (GtkNotebook *notebook,
1562 EmpathyChatWindow *window)
1564 EmpathyChatWindowPriv *priv;
1567 priv = GET_PRIV (window);
1569 /* If we just received DND to the same window, we don't want
1570 * to do anything here like removing the tab and then readding
1571 * it, so we return here and in "page-added".
1573 if (priv->dnd_same_window) {
1574 DEBUG ("Page added (back to the same window)");
1575 priv->dnd_same_window = FALSE;
1579 DEBUG ("Page added");
1581 /* Get chat object */
1582 chat = EMPATHY_CHAT (child);
1584 /* Connect chat signals for this window */
1585 g_signal_connect (chat, "composing",
1586 G_CALLBACK (chat_window_composing_cb),
1588 g_signal_connect (chat, "new-message",
1589 G_CALLBACK (chat_window_new_message_cb),
1591 g_signal_connect (chat, "notify::tp-chat",
1592 G_CALLBACK (chat_window_update_chat_tab),
1595 /* Set flag so we know to perform some special operations on
1596 * switch page due to the new page being added.
1598 priv->page_added = TRUE;
1600 /* Get list of chats up to date */
1601 priv->chats = g_list_append (priv->chats, chat);
1603 chat_window_update_chat_tab (chat);
1607 chat_window_page_removed_cb (GtkNotebook *notebook,
1610 EmpathyChatWindow *window)
1612 EmpathyChatWindowPriv *priv;
1615 priv = GET_PRIV (window);
1617 /* If we just received DND to the same window, we don't want
1618 * to do anything here like removing the tab and then readding
1619 * it, so we return here and in "page-added".
1621 if (priv->dnd_same_window) {
1622 DEBUG ("Page removed (and will be readded to same window)");
1626 DEBUG ("Page removed");
1628 /* Get chat object */
1629 chat = EMPATHY_CHAT (child);
1631 /* Disconnect all signal handlers for this chat and this window */
1632 g_signal_handlers_disconnect_by_func (chat,
1633 G_CALLBACK (chat_window_composing_cb),
1635 g_signal_handlers_disconnect_by_func (chat,
1636 G_CALLBACK (chat_window_new_message_cb),
1638 g_signal_handlers_disconnect_by_func (chat,
1639 G_CALLBACK (chat_window_update_chat_tab),
1642 /* Keep list of chats up to date */
1643 priv->chats = g_list_remove (priv->chats, chat);
1644 empathy_chat_messages_read (chat);
1646 if (priv->chats == NULL) {
1647 g_object_unref (window);
1649 chat_window_update (window, TRUE);
1654 chat_window_focus_in_event_cb (GtkWidget *widget,
1656 EmpathyChatWindow *window)
1658 EmpathyChatWindowPriv *priv;
1660 priv = GET_PRIV (window);
1662 empathy_chat_messages_read (priv->current_chat);
1664 chat_window_set_urgency_hint (window, FALSE);
1666 /* Update the title, since we now mark all unread messages as read. */
1667 chat_window_update_chat_tab_full (priv->current_chat, FALSE);
1673 chat_window_drag_drop (GtkWidget *widget,
1674 GdkDragContext *context,
1678 EmpathyChatWindow *window)
1681 EmpathyChatWindowPriv *priv;
1683 priv = GET_PRIV (window);
1685 target = gtk_drag_dest_find_target (widget, context, priv->file_targets);
1686 if (target == GDK_NONE)
1687 target = gtk_drag_dest_find_target (widget, context, priv->contact_targets);
1689 if (target != GDK_NONE) {
1690 gtk_drag_get_data (widget, context, target, time_);
1698 chat_window_drag_motion (GtkWidget *widget,
1699 GdkDragContext *context,
1703 EmpathyChatWindow *window)
1706 EmpathyChatWindowPriv *priv;
1708 priv = GET_PRIV (window);
1710 target = gtk_drag_dest_find_target (widget, context, priv->file_targets);
1711 if (target != GDK_NONE) {
1712 /* This is a file drag. Ensure the contact is online and set the
1713 drag type to COPY. Note that it's possible that the tab will
1714 be switched by GTK+ after a timeout from drag_motion without
1715 getting another drag_motion to disable the drop. You have
1716 to hold your mouse really still.
1718 EmpathyContact *contact;
1720 priv = GET_PRIV (window);
1721 contact = empathy_chat_get_remote_contact (priv->current_chat);
1722 /* contact is NULL for multi-user chats. We don't do
1723 * file transfers to MUCs. We also don't send files
1724 * to offline contacts or contacts that don't support
1727 if ((contact == NULL) || !empathy_contact_is_online (contact)) {
1728 gdk_drag_status (context, 0, time_);
1731 if (!(empathy_contact_get_capabilities (contact)
1732 & EMPATHY_CAPABILITIES_FT)) {
1733 gdk_drag_status (context, 0, time_);
1736 gdk_drag_status (context, GDK_ACTION_COPY, time_);
1740 target = gtk_drag_dest_find_target (widget, context, priv->contact_targets);
1741 if (target != GDK_NONE) {
1742 /* This is a drag of a contact from a contact list. Set to COPY.
1743 FIXME: If this drag is to a MUC window, it invites the user.
1744 Otherwise, it opens a chat. Should we use a different drag
1745 type for invites? Should we allow ASK?
1747 gdk_drag_status (context, GDK_ACTION_COPY, time_);
1755 chat_window_drag_data_received (GtkWidget *widget,
1756 GdkDragContext *context,
1759 GtkSelectionData *selection,
1762 EmpathyChatWindow *window)
1764 if (info == DND_DRAG_TYPE_CONTACT_ID) {
1765 EmpathyChat *chat = NULL;
1766 EmpathyChatWindow *old_window;
1767 TpAccount *account = NULL;
1768 TpAccountManager *account_manager;
1771 const gchar *account_id;
1772 const gchar *contact_id;
1774 id = (const gchar*) gtk_selection_data_get_data (selection);
1776 /* FIXME: Perhaps should be sure that the account manager is
1777 * prepared before calling _ensure_account on it. */
1778 account_manager = tp_account_manager_dup ();
1780 DEBUG ("DND contact from roster with id:'%s'", id);
1782 strv = g_strsplit (id, ":", 2);
1783 if (g_strv_length (strv) == 2) {
1784 account_id = strv[0];
1785 contact_id = strv[1];
1787 tp_account_manager_ensure_account (account_manager, account_id);
1788 if (account != NULL)
1789 chat = empathy_chat_window_find_chat (account, contact_id);
1792 if (account == NULL) {
1794 gtk_drag_finish (context, FALSE, FALSE, time_);
1799 empathy_chat_with_contact_id (
1800 account, contact_id, gtk_get_current_event_time ());
1805 g_object_unref (account_manager);
1808 old_window = chat_window_find_chat (chat);
1810 if (old_window == window) {
1811 gtk_drag_finish (context, TRUE, FALSE, time_);
1815 empathy_chat_window_move_chat (old_window, window, chat);
1817 empathy_chat_window_add_chat (window, chat);
1820 /* Added to take care of any outstanding chat events */
1821 empathy_chat_window_present_chat (chat,
1822 TP_USER_ACTION_TIME_NOT_USER_ACTION);
1824 /* We should return TRUE to remove the data when doing
1825 * GDK_ACTION_MOVE, but we don't here otherwise it has
1826 * weird consequences, and we handle that internally
1827 * anyway with add_chat () and remove_chat ().
1829 gtk_drag_finish (context, TRUE, FALSE, time_);
1831 else if (info == DND_DRAG_TYPE_URI_LIST) {
1832 EmpathyChatWindowPriv *priv;
1833 EmpathyContact *contact;
1836 priv = GET_PRIV (window);
1837 contact = empathy_chat_get_remote_contact (priv->current_chat);
1839 /* contact is NULL when current_chat is a multi-user chat.
1840 * We don't do file transfers to MUCs, so just cancel the drag.
1842 if (contact == NULL) {
1843 gtk_drag_finish (context, TRUE, FALSE, time_);
1847 data = (const gchar *) gtk_selection_data_get_data (selection);
1848 empathy_send_file_from_uri_list (contact, data);
1850 gtk_drag_finish (context, TRUE, FALSE, time_);
1852 else if (info == DND_DRAG_TYPE_TAB) {
1854 EmpathyChatWindow *old_window = NULL;
1858 chat = (void *) gtk_selection_data_get_data (selection);
1859 old_window = chat_window_find_chat (*chat);
1862 EmpathyChatWindowPriv *priv;
1864 priv = GET_PRIV (window);
1865 priv->dnd_same_window = (old_window == window);
1866 DEBUG ("DND tab (within same window: %s)",
1867 priv->dnd_same_window ? "Yes" : "No");
1870 DEBUG ("DND from unknown source");
1871 gtk_drag_finish (context, FALSE, FALSE, time_);
1876 chat_window_chat_manager_chats_changed_cb (EmpathyChatManager *chat_manager,
1877 guint num_chats_in_manager,
1878 EmpathyChatWindow *window)
1880 EmpathyChatWindowPriv *priv = GET_PRIV (window);
1882 gtk_action_set_sensitive (priv->menu_tabs_undo_close_tab,
1883 num_chats_in_manager > 0);
1887 chat_window_finalize (GObject *object)
1889 EmpathyChatWindow *window;
1890 EmpathyChatWindowPriv *priv;
1892 window = EMPATHY_CHAT_WINDOW (object);
1893 priv = GET_PRIV (window);
1895 DEBUG ("Finalized: %p", object);
1897 g_object_unref (priv->ui_manager);
1898 g_object_unref (priv->chatroom_manager);
1899 g_object_unref (priv->notify_mgr);
1900 g_object_unref (priv->gsettings_chat);
1901 g_object_unref (priv->gsettings_notif);
1902 g_object_unref (priv->gsettings_ui);
1903 g_object_unref (priv->sound_mgr);
1905 if (priv->notification != NULL) {
1906 notify_notification_close (priv->notification, NULL);
1907 priv->notification = NULL;
1910 if (priv->contact_targets) {
1911 gtk_target_list_unref (priv->contact_targets);
1913 if (priv->file_targets) {
1914 gtk_target_list_unref (priv->file_targets);
1917 if (priv->chat_manager) {
1918 g_signal_handler_disconnect (priv->chat_manager,
1919 priv->chat_manager_chats_changed_id);
1920 g_object_unref (priv->chat_manager);
1921 priv->chat_manager = NULL;
1924 chat_windows = g_list_remove (chat_windows, window);
1925 gtk_widget_destroy (priv->dialog);
1927 G_OBJECT_CLASS (empathy_chat_window_parent_class)->finalize (object);
1931 empathy_chat_window_class_init (EmpathyChatWindowClass *klass)
1933 GObjectClass *object_class = G_OBJECT_CLASS (klass);
1935 object_class->finalize = chat_window_finalize;
1937 g_type_class_add_private (object_class, sizeof (EmpathyChatWindowPriv));
1941 empathy_chat_window_init (EmpathyChatWindow *window)
1944 GtkAccelGroup *accel_group;
1949 GtkWidget *chat_vbox;
1951 EmpathySmileyManager *smiley_manager;
1952 EmpathyChatWindowPriv *priv = G_TYPE_INSTANCE_GET_PRIVATE (window,
1953 EMPATHY_TYPE_CHAT_WINDOW, EmpathyChatWindowPriv);
1955 window->priv = priv;
1956 filename = empathy_file_lookup ("empathy-chat-window.ui", "src");
1957 gui = empathy_builder_get_file (filename,
1958 "chat_window", &priv->dialog,
1959 "chat_vbox", &chat_vbox,
1960 "ui_manager", &priv->ui_manager,
1961 "menu_conv_insert_smiley", &priv->menu_conv_insert_smiley,
1962 "menu_conv_favorite", &priv->menu_conv_favorite,
1963 "menu_conv_always_urgent", &priv->menu_conv_always_urgent,
1964 "menu_conv_toggle_contacts", &priv->menu_conv_toggle_contacts,
1965 "menu_edit_cut", &priv->menu_edit_cut,
1966 "menu_edit_copy", &priv->menu_edit_copy,
1967 "menu_edit_paste", &priv->menu_edit_paste,
1968 "menu_edit_find", &priv->menu_edit_find,
1969 "menu_tabs_next", &priv->menu_tabs_next,
1970 "menu_tabs_prev", &priv->menu_tabs_prev,
1971 "menu_tabs_undo_close_tab", &priv->menu_tabs_undo_close_tab,
1972 "menu_tabs_left", &priv->menu_tabs_left,
1973 "menu_tabs_right", &priv->menu_tabs_right,
1974 "menu_tabs_detach", &priv->menu_tabs_detach,
1978 empathy_builder_connect (gui, window,
1979 "menu_conv", "activate", chat_window_conv_activate_cb,
1980 "menu_conv_clear", "activate", chat_window_clear_activate_cb,
1981 "menu_conv_favorite", "toggled", chat_window_favorite_toggled_cb,
1982 "menu_conv_always_urgent", "toggled", chat_window_always_urgent_toggled_cb,
1983 "menu_conv_toggle_contacts", "toggled", chat_window_contacts_toggled_cb,
1984 "menu_conv_invite_participant", "activate", chat_window_invite_participant_activate_cb,
1985 "menu_conv_close", "activate", chat_window_close_activate_cb,
1986 "menu_edit", "activate", chat_window_edit_activate_cb,
1987 "menu_edit_cut", "activate", chat_window_cut_activate_cb,
1988 "menu_edit_copy", "activate", chat_window_copy_activate_cb,
1989 "menu_edit_paste", "activate", chat_window_paste_activate_cb,
1990 "menu_edit_find", "activate", chat_window_find_activate_cb,
1991 "menu_tabs_next", "activate", chat_window_tabs_next_activate_cb,
1992 "menu_tabs_prev", "activate", chat_window_tabs_previous_activate_cb,
1993 "menu_tabs_undo_close_tab", "activate", chat_window_tabs_undo_close_tab_activate_cb,
1994 "menu_tabs_left", "activate", chat_window_tabs_left_activate_cb,
1995 "menu_tabs_right", "activate", chat_window_tabs_right_activate_cb,
1996 "menu_tabs_detach", "activate", chat_window_detach_activate_cb,
1997 "menu_help_contents", "activate", chat_window_help_contents_activate_cb,
1998 "menu_help_about", "activate", chat_window_help_about_activate_cb,
2001 g_object_ref (priv->ui_manager);
2002 g_object_unref (gui);
2004 priv->gsettings_chat = g_settings_new (EMPATHY_PREFS_CHAT_SCHEMA);
2005 priv->gsettings_notif = g_settings_new (EMPATHY_PREFS_NOTIFICATIONS_SCHEMA);
2006 priv->gsettings_ui = g_settings_new (EMPATHY_PREFS_UI_SCHEMA);
2007 priv->chatroom_manager = empathy_chatroom_manager_dup_singleton (NULL);
2009 priv->sound_mgr = empathy_sound_manager_dup_singleton ();
2011 priv->notebook = gtk_notebook_new ();
2013 g_signal_connect (priv->notebook, "create-window",
2014 G_CALLBACK (notebook_create_window_cb), window);
2016 gtk_notebook_set_group_name (GTK_NOTEBOOK (priv->notebook),
2017 "EmpathyChatWindow");
2018 gtk_notebook_set_scrollable (GTK_NOTEBOOK (priv->notebook), TRUE);
2019 gtk_notebook_popup_enable (GTK_NOTEBOOK (priv->notebook));
2020 gtk_box_pack_start (GTK_BOX (chat_vbox), priv->notebook, TRUE, TRUE, 0);
2021 gtk_widget_show (priv->notebook);
2024 accel_group = gtk_accel_group_new ();
2025 gtk_window_add_accel_group (GTK_WINDOW (priv->dialog), accel_group);
2027 for (i = 0; i < G_N_ELEMENTS (tab_accel_keys); i++) {
2028 closure = g_cclosure_new (G_CALLBACK (chat_window_accel_cb),
2031 gtk_accel_group_connect (accel_group,
2038 g_object_unref (accel_group);
2040 /* Set up drag target lists */
2041 priv->contact_targets = gtk_target_list_new (drag_types_dest_contact,
2042 G_N_ELEMENTS (drag_types_dest_contact));
2043 priv->file_targets = gtk_target_list_new (drag_types_dest_file,
2044 G_N_ELEMENTS (drag_types_dest_file));
2046 /* Set up smiley menu */
2047 smiley_manager = empathy_smiley_manager_dup_singleton ();
2048 submenu = empathy_smiley_menu_new (smiley_manager,
2049 chat_window_insert_smiley_activate_cb,
2051 menu = gtk_ui_manager_get_widget (priv->ui_manager,
2052 "/chats_menubar/menu_conv/menu_conv_insert_smiley");
2053 gtk_menu_item_set_submenu (GTK_MENU_ITEM (menu), submenu);
2054 g_object_unref (smiley_manager);
2056 /* Set up signals we can't do with ui file since we may need to
2057 * block/unblock them at some later stage.
2060 g_signal_connect (priv->dialog,
2062 G_CALLBACK (chat_window_delete_event_cb),
2064 g_signal_connect (priv->dialog,
2066 G_CALLBACK (chat_window_focus_in_event_cb),
2068 g_signal_connect_after (priv->notebook,
2070 G_CALLBACK (chat_window_page_switched_cb),
2072 g_signal_connect (priv->notebook,
2074 G_CALLBACK (chat_window_page_added_cb),
2076 g_signal_connect (priv->notebook,
2078 G_CALLBACK (chat_window_page_removed_cb),
2081 /* Set up drag and drop */
2082 gtk_drag_dest_set (GTK_WIDGET (priv->notebook),
2083 GTK_DEST_DEFAULT_HIGHLIGHT,
2085 G_N_ELEMENTS (drag_types_dest),
2086 GDK_ACTION_MOVE | GDK_ACTION_COPY);
2088 /* connect_after to allow GtkNotebook's built-in tab switching */
2089 g_signal_connect_after (priv->notebook,
2091 G_CALLBACK (chat_window_drag_motion),
2093 g_signal_connect (priv->notebook,
2094 "drag-data-received",
2095 G_CALLBACK (chat_window_drag_data_received),
2097 g_signal_connect (priv->notebook,
2099 G_CALLBACK (chat_window_drag_drop),
2102 chat_windows = g_list_prepend (chat_windows, window);
2104 /* Set up private details */
2106 priv->current_chat = NULL;
2107 priv->notification = NULL;
2109 priv->notify_mgr = empathy_notify_manager_dup_singleton ();
2111 priv->chat_manager = empathy_chat_manager_dup_singleton ();
2112 priv->chat_manager_chats_changed_id =
2113 g_signal_connect (priv->chat_manager, "closed-chats-changed",
2114 G_CALLBACK (chat_window_chat_manager_chats_changed_cb),
2117 chat_window_chat_manager_chats_changed_cb (priv->chat_manager,
2118 empathy_chat_manager_get_num_closed_chats (priv->chat_manager),
2123 empathy_chat_window_get_dialog (EmpathyChatWindow *window)
2125 EmpathyChatWindowPriv *priv;
2127 g_return_val_if_fail (window != NULL, NULL);
2129 priv = GET_PRIV (window);
2131 return priv->dialog;
2134 /* Returns the window to open a new tab in if there is a suitable window,
2135 * otherwise, returns NULL indicating that a new window should be added.
2137 static EmpathyChatWindow *
2138 empathy_chat_window_get_default (gboolean room)
2140 GSettings *gsettings = g_settings_new (EMPATHY_PREFS_UI_SCHEMA);
2142 gboolean separate_windows = TRUE;
2144 separate_windows = g_settings_get_boolean (gsettings,
2145 EMPATHY_PREFS_UI_SEPARATE_CHAT_WINDOWS);
2147 g_object_unref (gsettings);
2149 if (separate_windows) {
2150 /* Always create a new window */
2154 for (l = chat_windows; l; l = l->next) {
2155 EmpathyChatWindowPriv *priv;
2156 EmpathyChatWindow *chat_window;
2158 guint nb_rooms, nb_private;
2160 chat_window = l->data;
2161 priv = GET_PRIV (chat_window);
2163 dialog = empathy_chat_window_get_dialog (chat_window);
2165 empathy_chat_window_get_nb_chats (chat_window, &nb_rooms, &nb_private);
2167 /* Skip the window if there aren't any rooms in it */
2168 if (room && nb_rooms == 0)
2171 /* Skip the window if there aren't any 1-1 chats in it */
2172 if (!room && nb_private == 0)
2175 /* Found a window on this desktop, make it visible if necessary */
2176 if (!empathy_window_get_is_visible (GTK_WINDOW (dialog)))
2177 empathy_window_present (GTK_WINDOW (dialog));
2185 empathy_chat_window_add_chat (EmpathyChatWindow *window,
2188 EmpathyChatWindowPriv *priv;
2190 GtkWidget *popup_label;
2192 GValue value = { 0, };
2194 g_return_if_fail (window != NULL);
2195 g_return_if_fail (EMPATHY_IS_CHAT (chat));
2197 priv = GET_PRIV (window);
2199 /* Reference the chat object */
2200 g_object_ref (chat);
2202 /* If this window has just been created, position it */
2203 if (priv->chats == NULL) {
2204 const gchar *name = "chat-window";
2205 gboolean separate_windows;
2207 separate_windows = g_settings_get_boolean (priv->gsettings_ui,
2208 EMPATHY_PREFS_UI_SEPARATE_CHAT_WINDOWS);
2210 if (empathy_chat_is_room (chat))
2211 name = "room-window";
2213 if (separate_windows) {
2216 /* Save current position of the window */
2217 gtk_window_get_position (GTK_WINDOW (priv->dialog), &x, &y);
2219 /* First bind to the 'generic' name. So new window for which we didn't
2220 * save a geometry yet will have the geometry of the last saved
2221 * window (bgo #601191). */
2222 empathy_geometry_bind (GTK_WINDOW (priv->dialog), name);
2224 /* Restore previous position of the window so the newly created window
2225 * won't be in the same position as the latest saved window and so
2226 * completely hide it. */
2227 gtk_window_move (GTK_WINDOW (priv->dialog), x, y);
2229 /* Then bind it to the name of the contact/room so we'll save the
2230 * geometry specific to this window */
2231 name = empathy_chat_get_id (chat);
2234 empathy_geometry_bind (GTK_WINDOW (priv->dialog), name);
2237 child = GTK_WIDGET (chat);
2238 label = chat_window_create_label (window, chat, TRUE);
2239 popup_label = chat_window_create_label (window, chat, FALSE);
2240 gtk_widget_show (child);
2242 g_signal_connect (chat, "notify::name",
2243 G_CALLBACK (chat_window_chat_notify_cb),
2245 g_signal_connect (chat, "notify::subject",
2246 G_CALLBACK (chat_window_chat_notify_cb),
2248 g_signal_connect (chat, "notify::remote-contact",
2249 G_CALLBACK (chat_window_chat_notify_cb),
2251 g_signal_connect (chat, "notify::sms-channel",
2252 G_CALLBACK (chat_window_chat_notify_cb),
2254 chat_window_chat_notify_cb (chat);
2256 gtk_notebook_append_page_menu (GTK_NOTEBOOK (priv->notebook), child, label, popup_label);
2257 gtk_notebook_set_tab_reorderable (GTK_NOTEBOOK (priv->notebook), child, TRUE);
2258 gtk_notebook_set_tab_detachable (GTK_NOTEBOOK (priv->notebook), child, TRUE);
2259 g_value_init (&value, G_TYPE_BOOLEAN);
2260 g_value_set_boolean (&value, TRUE);
2261 gtk_container_child_set_property (GTK_CONTAINER (priv->notebook),
2262 child, "tab-expand" , &value);
2263 gtk_container_child_set_property (GTK_CONTAINER (priv->notebook),
2264 child, "tab-fill" , &value);
2265 g_value_unset (&value);
2267 DEBUG ("Chat added (%d references)", G_OBJECT (chat)->ref_count);
2271 empathy_chat_window_remove_chat (EmpathyChatWindow *window,
2274 EmpathyChatWindowPriv *priv;
2276 EmpathyContact *remote_contact;
2277 EmpathyChatManager *chat_manager;
2279 g_return_if_fail (window != NULL);
2280 g_return_if_fail (EMPATHY_IS_CHAT (chat));
2282 priv = GET_PRIV (window);
2284 g_signal_handlers_disconnect_by_func (chat,
2285 chat_window_chat_notify_cb,
2287 remote_contact = g_object_get_data (G_OBJECT (chat),
2288 "chat-window-remote-contact");
2289 if (remote_contact) {
2290 g_signal_handlers_disconnect_by_func (remote_contact,
2291 chat_window_update_chat_tab,
2295 chat_manager = empathy_chat_manager_dup_singleton ();
2296 empathy_chat_manager_closed_chat (chat_manager, chat);
2297 g_object_unref (chat_manager);
2299 position = gtk_notebook_page_num (GTK_NOTEBOOK (priv->notebook),
2301 gtk_notebook_remove_page (GTK_NOTEBOOK (priv->notebook), position);
2303 DEBUG ("Chat removed (%d references)", G_OBJECT (chat)->ref_count - 1);
2305 g_object_unref (chat);
2309 empathy_chat_window_move_chat (EmpathyChatWindow *old_window,
2310 EmpathyChatWindow *new_window,
2315 g_return_if_fail (EMPATHY_IS_CHAT_WINDOW (old_window));
2316 g_return_if_fail (EMPATHY_IS_CHAT_WINDOW (new_window));
2317 g_return_if_fail (EMPATHY_IS_CHAT (chat));
2319 widget = GTK_WIDGET (chat);
2321 DEBUG ("Chat moving with widget:%p (%d references)", widget,
2322 G_OBJECT (widget)->ref_count);
2324 /* We reference here to make sure we don't loose the widget
2325 * and the EmpathyChat object during the move.
2327 g_object_ref (chat);
2328 g_object_ref (widget);
2330 empathy_chat_window_remove_chat (old_window, chat);
2331 empathy_chat_window_add_chat (new_window, chat);
2333 g_object_unref (widget);
2334 g_object_unref (chat);
2338 empathy_chat_window_switch_to_chat (EmpathyChatWindow *window,
2341 EmpathyChatWindowPriv *priv;
2344 g_return_if_fail (window != NULL);
2345 g_return_if_fail (EMPATHY_IS_CHAT (chat));
2347 priv = GET_PRIV (window);
2349 page_num = gtk_notebook_page_num (GTK_NOTEBOOK (priv->notebook),
2351 gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->notebook),
2356 empathy_chat_window_find_chat (TpAccount *account,
2361 g_return_val_if_fail (!EMP_STR_EMPTY (id), NULL);
2363 for (l = chat_windows; l; l = l->next) {
2364 EmpathyChatWindowPriv *priv;
2365 EmpathyChatWindow *window;
2369 priv = GET_PRIV (window);
2371 for (ll = priv->chats; ll; ll = ll->next) {
2376 if (account == empathy_chat_get_account (chat) &&
2377 !tp_strdiff (id, empathy_chat_get_id (chat))) {
2387 empathy_chat_window_present_chat (EmpathyChat *chat,
2390 EmpathyChatWindow *window;
2391 EmpathyChatWindowPriv *priv;
2392 guint32 x_timestamp;
2394 g_return_if_fail (EMPATHY_IS_CHAT (chat));
2396 window = chat_window_find_chat (chat);
2398 /* If the chat has no window, create one */
2399 if (window == NULL) {
2400 window = empathy_chat_window_get_default (empathy_chat_is_room (chat));
2402 window = empathy_chat_window_new ();
2405 empathy_chat_window_add_chat (window, chat);
2408 /* Don't force the window to show itself when it wasn't
2409 * an action by the user
2411 if (!tp_user_action_time_should_present (timestamp, &x_timestamp))
2414 priv = GET_PRIV (window);
2416 if (x_timestamp != GDK_CURRENT_TIME) {
2417 /* Don't present or switch tab if the action was earlier than the
2418 * last actions X time, accounting for overflow and the first ever
2421 if (priv->x_user_action_time != 0
2422 && X_EARLIER_OR_EQL (x_timestamp, priv->x_user_action_time))
2425 priv->x_user_action_time = x_timestamp;
2428 empathy_chat_window_switch_to_chat (window, chat);
2429 empathy_window_present_with_time (GTK_WINDOW (priv->dialog),
2432 gtk_widget_grab_focus (chat->input_text_view);
2436 empathy_chat_window_get_nb_chats (EmpathyChatWindow *self,
2440 EmpathyChatWindowPriv *priv = GET_PRIV (self);
2442 guint _nb_rooms = 0, _nb_private = 0;
2444 for (l = priv->chats; l != NULL; l = g_list_next (l)) {
2445 if (empathy_chat_is_room (EMPATHY_CHAT (l->data)))
2451 if (nb_rooms != NULL)
2452 *nb_rooms = _nb_rooms;
2453 if (nb_private != NULL)
2454 *nb_private = _nb_private;