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 const gchar *active_name;
458 guint current_unread_msgs;
460 nb_chats = g_list_length (priv->chats);
461 g_assert (nb_chats > 0);
463 active_name = empathy_chat_get_name (priv->current_chat);
465 current_unread_msgs = empathy_chat_get_nb_unread_messages (
470 if (current_unread_msgs == 0)
471 return g_strdup (active_name);
473 return 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 return 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 return 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 return 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 return g_strdup_printf (ngettext (
511 "%s (%d unread from all)",
512 "%s (%d unread from all)",
514 active_name, all_unread_msgs);
520 chat_window_title_update (EmpathyChatWindowPriv *priv)
524 name = get_window_title_name (priv);
525 gtk_window_set_title (GTK_WINDOW (priv->dialog), name);
530 chat_window_icon_update (EmpathyChatWindowPriv *priv, gboolean new_messages)
533 EmpathyContact *remote_contact;
534 gboolean avatar_in_icon;
537 n_chats = g_list_length (priv->chats);
539 /* Update window icon */
541 gtk_window_set_icon_name (GTK_WINDOW (priv->dialog),
542 EMPATHY_IMAGE_MESSAGE);
544 avatar_in_icon = g_settings_get_boolean (priv->gsettings_chat,
545 EMPATHY_PREFS_CHAT_AVATAR_IN_ICON);
547 if (n_chats == 1 && avatar_in_icon) {
548 remote_contact = empathy_chat_get_remote_contact (priv->current_chat);
549 icon = empathy_pixbuf_avatar_from_contact_scaled (remote_contact, 0, 0);
550 gtk_window_set_icon (GTK_WINDOW (priv->dialog), icon);
553 g_object_unref (icon);
556 gtk_window_set_icon_name (GTK_WINDOW (priv->dialog), NULL);
562 chat_window_close_button_update (EmpathyChatWindowPriv *priv,
566 GtkWidget *chat_close_button;
569 if (num_pages == 1) {
570 chat = gtk_notebook_get_nth_page (GTK_NOTEBOOK (priv->notebook), 0);
571 chat_close_button = g_object_get_data (G_OBJECT (chat),
572 "chat-window-tab-close-button");
573 gtk_widget_hide (chat_close_button);
575 for (i=0; i<num_pages; i++) {
576 chat = gtk_notebook_get_nth_page (GTK_NOTEBOOK (priv->notebook), i);
577 chat_close_button = g_object_get_data (G_OBJECT (chat),
578 "chat-window-tab-close-button");
579 gtk_widget_show (chat_close_button);
585 chat_window_update (EmpathyChatWindow *window,
586 gboolean update_contact_menu)
588 EmpathyChatWindowPriv *priv = GET_PRIV (window);
591 num_pages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (priv->notebook));
593 /* Update Tab menu */
594 chat_window_menu_context_update (priv,
597 chat_window_conversation_menu_update (priv, window);
599 /* If this update is due to a focus-in event, we know the menu will be
600 the same as when we last left it, so no work to do. Besides, if we
601 swap out the menu on a focus-in, we may confuse any external global
603 if (update_contact_menu) {
604 chat_window_contact_menu_update (priv,
608 chat_window_title_update (priv);
610 chat_window_icon_update (priv, get_all_unread_messages (priv) > 0);
612 chat_window_close_button_update (priv,
617 append_markup_printf (GString *string,
624 va_start (args, format);
626 tmp = g_markup_vprintf_escaped (format, args);
627 g_string_append (string, tmp);
634 chat_window_update_chat_tab_full (EmpathyChat *chat,
635 gboolean update_contact_menu)
637 EmpathyChatWindow *window;
638 EmpathyChatWindowPriv *priv;
639 EmpathyContact *remote_contact;
643 const gchar *subject;
644 const gchar *status = NULL;
648 const gchar *icon_name;
649 GtkWidget *tab_image;
650 GtkWidget *menu_image;
652 window = chat_window_find_chat (chat);
656 priv = GET_PRIV (window);
658 /* Get information */
659 name = empathy_chat_get_name (chat);
660 account = empathy_chat_get_account (chat);
661 subject = empathy_chat_get_subject (chat);
662 remote_contact = empathy_chat_get_remote_contact (chat);
664 DEBUG ("Updating chat tab, name=%s, account=%s, subject=%s, remote_contact=%p",
665 name, tp_proxy_get_object_path (account), subject, remote_contact);
667 /* Update tab image */
668 if (empathy_chat_get_tp_chat (chat) == NULL) {
669 /* No TpChat, we are disconnected */
672 else if (empathy_chat_get_nb_unread_messages (chat) > 0) {
673 icon_name = EMPATHY_IMAGE_MESSAGE;
675 else if (remote_contact && empathy_chat_is_composing (chat)) {
676 icon_name = EMPATHY_IMAGE_TYPING;
678 else if (empathy_chat_is_sms_channel (chat)) {
679 icon_name = EMPATHY_IMAGE_SMS;
681 else if (remote_contact) {
682 icon_name = empathy_icon_name_for_contact (remote_contact);
684 icon_name = EMPATHY_IMAGE_GROUP_MESSAGE;
687 tab_image = g_object_get_data (G_OBJECT (chat), "chat-window-tab-image");
688 menu_image = g_object_get_data (G_OBJECT (chat), "chat-window-menu-image");
689 if (icon_name != NULL) {
690 gtk_image_set_from_icon_name (GTK_IMAGE (tab_image), icon_name, GTK_ICON_SIZE_MENU);
691 gtk_widget_show (tab_image);
692 gtk_image_set_from_icon_name (GTK_IMAGE (menu_image), icon_name, GTK_ICON_SIZE_MENU);
693 gtk_widget_show (menu_image);
695 gtk_widget_hide (tab_image);
696 gtk_widget_hide (menu_image);
699 /* Update tab tooltip */
700 tooltip = g_string_new (NULL);
702 if (remote_contact) {
703 id = empathy_contact_get_id (remote_contact);
704 status = empathy_contact_get_presence_message (remote_contact);
709 if (empathy_chat_is_sms_channel (chat)) {
710 append_markup_printf (tooltip, "%s ", _("SMS:"));
713 append_markup_printf (tooltip,
714 "<b>%s</b><small> (%s)</small>",
716 tp_account_get_display_name (account));
718 if (!EMP_STR_EMPTY (status)) {
719 append_markup_printf (tooltip, "\n<i>%s</i>", status);
723 append_markup_printf (tooltip, "\n<b>%s</b> %s",
724 _("Topic:"), subject);
727 if (remote_contact && empathy_chat_is_composing (chat)) {
728 append_markup_printf (tooltip, "\n%s", _("Typing a message."));
731 markup = g_string_free (tooltip, FALSE);
732 widget = g_object_get_data (G_OBJECT (chat), "chat-window-tab-tooltip-widget");
733 gtk_widget_set_tooltip_markup (widget, markup);
734 widget = g_object_get_data (G_OBJECT (chat), "chat-window-menu-tooltip-widget");
735 gtk_widget_set_tooltip_markup (widget, markup);
738 /* Update tab and menu label */
739 widget = g_object_get_data (G_OBJECT (chat), "chat-window-tab-label");
740 gtk_label_set_text (GTK_LABEL (widget), name);
741 widget = g_object_get_data (G_OBJECT (chat), "chat-window-menu-label");
742 gtk_label_set_text (GTK_LABEL (widget), name);
744 /* Update the window if it's the current chat */
745 if (priv->current_chat == chat) {
746 chat_window_update (window, update_contact_menu);
751 chat_window_update_chat_tab (EmpathyChat *chat)
753 chat_window_update_chat_tab_full (chat, TRUE);
757 chat_window_chat_notify_cb (EmpathyChat *chat)
759 EmpathyContact *old_remote_contact;
760 EmpathyContact *remote_contact = NULL;
762 old_remote_contact = g_object_get_data (G_OBJECT (chat), "chat-window-remote-contact");
763 remote_contact = empathy_chat_get_remote_contact (chat);
765 if (old_remote_contact != remote_contact) {
766 /* The remote-contact associated with the chat changed, we need
767 * to keep track of any change of that contact and update the
768 * window each time. */
769 if (remote_contact) {
770 g_signal_connect_swapped (remote_contact, "notify",
771 G_CALLBACK (chat_window_update_chat_tab),
774 if (old_remote_contact) {
775 g_signal_handlers_disconnect_by_func (old_remote_contact,
776 chat_window_update_chat_tab,
780 g_object_set_data_full (G_OBJECT (chat), "chat-window-remote-contact",
781 g_object_ref (remote_contact), (GDestroyNotify) g_object_unref);
784 chat_window_update_chat_tab (chat);
788 chat_window_insert_smiley_activate_cb (EmpathySmileyManager *manager,
789 EmpathySmiley *smiley,
792 EmpathyChatWindowPriv *priv = GET_PRIV (window);
794 GtkTextBuffer *buffer;
797 chat = priv->current_chat;
799 buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (chat->input_text_view));
800 gtk_text_buffer_get_end_iter (buffer, &iter);
801 gtk_text_buffer_insert (buffer, &iter, smiley->str, -1);
805 chat_window_conv_activate_cb (GtkAction *action,
806 EmpathyChatWindow *window)
808 EmpathyChatWindowPriv *priv = GET_PRIV (window);
811 EmpathyContact *remote_contact = NULL;
813 /* Favorite room menu */
814 is_room = empathy_chat_is_room (priv->current_chat);
818 gboolean found = FALSE;
819 EmpathyChatroom *chatroom;
821 room = empathy_chat_get_id (priv->current_chat);
822 account = empathy_chat_get_account (priv->current_chat);
823 chatroom = empathy_chatroom_manager_find (priv->chatroom_manager,
825 if (chatroom != NULL)
826 found = empathy_chatroom_is_favorite (chatroom);
828 DEBUG ("This room %s favorite", found ? "is" : "is not");
829 gtk_toggle_action_set_active (
830 GTK_TOGGLE_ACTION (priv->menu_conv_favorite), found);
832 if (chatroom != NULL)
833 found = empathy_chatroom_is_always_urgent (chatroom);
835 gtk_toggle_action_set_active (
836 GTK_TOGGLE_ACTION (priv->menu_conv_always_urgent),
839 gtk_action_set_visible (priv->menu_conv_favorite, is_room);
840 gtk_action_set_visible (priv->menu_conv_always_urgent, is_room);
842 /* Show contacts menu */
843 g_object_get (priv->current_chat,
844 "remote-contact", &remote_contact,
845 "show-contacts", &active,
847 if (remote_contact == NULL) {
848 gtk_toggle_action_set_active (
849 GTK_TOGGLE_ACTION (priv->menu_conv_toggle_contacts),
852 gtk_action_set_visible (priv->menu_conv_toggle_contacts,
853 (remote_contact == NULL));
854 if (remote_contact != NULL) {
855 g_object_unref (remote_contact);
860 chat_window_clear_activate_cb (GtkAction *action,
861 EmpathyChatWindow *window)
863 EmpathyChatWindowPriv *priv = GET_PRIV (window);
865 empathy_chat_clear (priv->current_chat);
869 chat_window_favorite_toggled_cb (GtkToggleAction *toggle_action,
870 EmpathyChatWindow *window)
872 EmpathyChatWindowPriv *priv = GET_PRIV (window);
876 EmpathyChatroom *chatroom;
878 active = gtk_toggle_action_get_active (toggle_action);
879 account = empathy_chat_get_account (priv->current_chat);
880 room = empathy_chat_get_id (priv->current_chat);
882 chatroom = empathy_chatroom_manager_ensure_chatroom (
883 priv->chatroom_manager,
886 empathy_chat_get_name (priv->current_chat));
888 empathy_chatroom_set_favorite (chatroom, active);
889 g_object_unref (chatroom);
893 chat_window_always_urgent_toggled_cb (GtkToggleAction *toggle_action,
894 EmpathyChatWindow *window)
896 EmpathyChatWindowPriv *priv = GET_PRIV (window);
900 EmpathyChatroom *chatroom;
902 active = gtk_toggle_action_get_active (toggle_action);
903 account = empathy_chat_get_account (priv->current_chat);
904 room = empathy_chat_get_id (priv->current_chat);
906 chatroom = empathy_chatroom_manager_ensure_chatroom (
907 priv->chatroom_manager,
910 empathy_chat_get_name (priv->current_chat));
912 empathy_chatroom_set_always_urgent (chatroom, active);
913 g_object_unref (chatroom);
917 chat_window_contacts_toggled_cb (GtkToggleAction *toggle_action,
918 EmpathyChatWindow *window)
920 EmpathyChatWindowPriv *priv = GET_PRIV (window);
923 active = gtk_toggle_action_get_active (toggle_action);
925 empathy_chat_set_show_contacts (priv->current_chat, active);
929 got_contact_cb (TpConnection *connection,
930 EmpathyContact *contact,
935 EmpathyTpChat *tp_chat = EMPATHY_TP_CHAT (user_data);
938 DEBUG ("Failed: %s", error->message);
941 empathy_contact_list_add (EMPATHY_CONTACT_LIST (tp_chat),
942 contact, _("Inviting you to this room"));
947 chat_window_invite_participant_activate_cb (GtkAction *action,
948 EmpathyChatWindow *window)
950 EmpathyChatWindowPriv *priv;
952 EmpathyTpChat *tp_chat;
957 priv = GET_PRIV (window);
959 g_return_if_fail (priv->current_chat != NULL);
961 tp_chat = empathy_chat_get_tp_chat (priv->current_chat);
962 channel = empathy_tp_chat_get_channel (tp_chat);
963 account = empathy_chat_get_account (priv->current_chat);
965 dialog = empathy_invite_participant_dialog_new (
966 GTK_WINDOW (priv->dialog), account);
967 gtk_widget_show (dialog);
969 response = gtk_dialog_run (GTK_DIALOG (dialog));
971 if (response == GTK_RESPONSE_ACCEPT) {
972 TpConnection *connection;
975 id = empathy_contact_selector_dialog_get_selected (
976 EMPATHY_CONTACT_SELECTOR_DIALOG (dialog), NULL, NULL);
977 if (EMP_STR_EMPTY (id)) goto out;
979 connection = tp_channel_borrow_connection (channel);
980 empathy_tp_contact_factory_get_from_id (connection, id,
981 got_contact_cb, tp_chat, NULL, NULL);
985 gtk_widget_destroy (dialog);
989 chat_window_close_activate_cb (GtkAction *action,
990 EmpathyChatWindow *window)
992 EmpathyChatWindowPriv *priv;
994 priv = GET_PRIV (window);
996 g_return_if_fail (priv->current_chat != NULL);
998 empathy_chat_window_remove_chat (window, priv->current_chat);
1002 chat_window_edit_activate_cb (GtkAction *action,
1003 EmpathyChatWindow *window)
1005 EmpathyChatWindowPriv *priv;
1006 GtkClipboard *clipboard;
1007 GtkTextBuffer *buffer;
1008 gboolean text_available;
1010 priv = GET_PRIV (window);
1012 g_return_if_fail (priv->current_chat != NULL);
1014 if (!empathy_chat_get_tp_chat (priv->current_chat)) {
1015 gtk_action_set_sensitive (priv->menu_edit_copy, FALSE);
1016 gtk_action_set_sensitive (priv->menu_edit_cut, FALSE);
1017 gtk_action_set_sensitive (priv->menu_edit_paste, FALSE);
1021 buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (priv->current_chat->input_text_view));
1022 if (gtk_text_buffer_get_has_selection (buffer)) {
1023 gtk_action_set_sensitive (priv->menu_edit_copy, TRUE);
1024 gtk_action_set_sensitive (priv->menu_edit_cut, TRUE);
1028 selection = empathy_chat_view_get_has_selection (priv->current_chat->view);
1030 gtk_action_set_sensitive (priv->menu_edit_cut, FALSE);
1031 gtk_action_set_sensitive (priv->menu_edit_copy, selection);
1034 clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
1035 text_available = gtk_clipboard_wait_is_text_available (clipboard);
1036 gtk_action_set_sensitive (priv->menu_edit_paste, text_available);
1040 chat_window_cut_activate_cb (GtkAction *action,
1041 EmpathyChatWindow *window)
1043 EmpathyChatWindowPriv *priv;
1045 g_return_if_fail (EMPATHY_IS_CHAT_WINDOW (window));
1047 priv = GET_PRIV (window);
1049 empathy_chat_cut (priv->current_chat);
1053 chat_window_copy_activate_cb (GtkAction *action,
1054 EmpathyChatWindow *window)
1056 EmpathyChatWindowPriv *priv;
1058 g_return_if_fail (EMPATHY_IS_CHAT_WINDOW (window));
1060 priv = GET_PRIV (window);
1062 empathy_chat_copy (priv->current_chat);
1066 chat_window_paste_activate_cb (GtkAction *action,
1067 EmpathyChatWindow *window)
1069 EmpathyChatWindowPriv *priv;
1071 g_return_if_fail (EMPATHY_IS_CHAT_WINDOW (window));
1073 priv = GET_PRIV (window);
1075 empathy_chat_paste (priv->current_chat);
1079 chat_window_find_activate_cb (GtkAction *action,
1080 EmpathyChatWindow *window)
1082 EmpathyChatWindowPriv *priv;
1084 g_return_if_fail (EMPATHY_IS_CHAT_WINDOW (window));
1086 priv = GET_PRIV (window);
1088 empathy_chat_find (priv->current_chat);
1092 chat_window_tabs_next_activate_cb (GtkAction *action,
1093 EmpathyChatWindow *window)
1095 EmpathyChatWindowPriv *priv;
1097 gint index_, numPages;
1098 gboolean wrap_around;
1100 priv = GET_PRIV (window);
1102 g_object_get (gtk_settings_get_default (), "gtk-keynav-wrap-around",
1103 &wrap_around, NULL);
1105 chat = priv->current_chat;
1106 index_ = gtk_notebook_get_current_page (GTK_NOTEBOOK (priv->notebook));
1107 numPages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (priv->notebook));
1109 if (index_ == (numPages - 1) && wrap_around) {
1110 gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->notebook), 0);
1114 gtk_notebook_next_page (GTK_NOTEBOOK (priv->notebook));
1118 chat_window_tabs_previous_activate_cb (GtkAction *action,
1119 EmpathyChatWindow *window)
1121 EmpathyChatWindowPriv *priv;
1123 gint index_, numPages;
1124 gboolean wrap_around;
1126 priv = GET_PRIV (window);
1128 g_object_get (gtk_settings_get_default (), "gtk-keynav-wrap-around",
1129 &wrap_around, NULL);
1131 chat = priv->current_chat;
1132 index_ = gtk_notebook_get_current_page (GTK_NOTEBOOK (priv->notebook));
1133 numPages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (priv->notebook));
1135 if (index_ <= 0 && wrap_around) {
1136 gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->notebook), numPages - 1);
1140 gtk_notebook_prev_page (GTK_NOTEBOOK (priv->notebook));
1144 chat_window_tabs_undo_close_tab_activate_cb (GtkAction *action,
1145 EmpathyChatWindow *window)
1147 EmpathyChatWindowPriv *priv = GET_PRIV (window);
1148 empathy_chat_manager_undo_closed_chat (priv->chat_manager);
1152 chat_window_tabs_left_activate_cb (GtkAction *action,
1153 EmpathyChatWindow *window)
1155 EmpathyChatWindowPriv *priv;
1157 gint index_, num_pages;
1159 priv = GET_PRIV (window);
1161 chat = priv->current_chat;
1162 index_ = gtk_notebook_get_current_page (GTK_NOTEBOOK (priv->notebook));
1167 gtk_notebook_reorder_child (GTK_NOTEBOOK (priv->notebook),
1171 num_pages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (priv->notebook));
1172 chat_window_menu_context_update (priv, num_pages);
1176 chat_window_tabs_right_activate_cb (GtkAction *action,
1177 EmpathyChatWindow *window)
1179 EmpathyChatWindowPriv *priv;
1181 gint index_, num_pages;
1183 priv = GET_PRIV (window);
1185 chat = priv->current_chat;
1186 index_ = gtk_notebook_get_current_page (GTK_NOTEBOOK (priv->notebook));
1188 gtk_notebook_reorder_child (GTK_NOTEBOOK (priv->notebook),
1192 num_pages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (priv->notebook));
1193 chat_window_menu_context_update (priv, num_pages);
1196 static EmpathyChatWindow *
1197 empathy_chat_window_new (void)
1199 return EMPATHY_CHAT_WINDOW (g_object_new (EMPATHY_TYPE_CHAT_WINDOW, NULL));
1203 chat_window_detach_activate_cb (GtkAction *action,
1204 EmpathyChatWindow *window)
1206 EmpathyChatWindowPriv *priv;
1207 EmpathyChatWindow *new_window;
1210 priv = GET_PRIV (window);
1212 chat = priv->current_chat;
1213 new_window = empathy_chat_window_new ();
1215 empathy_chat_window_move_chat (window, new_window, chat);
1217 priv = GET_PRIV (new_window);
1218 gtk_widget_show (priv->dialog);
1222 chat_window_help_contents_activate_cb (GtkAction *action,
1223 EmpathyChatWindow *window)
1225 EmpathyChatWindowPriv *priv = GET_PRIV (window);
1227 empathy_url_show (priv->dialog, "ghelp:empathy");
1231 chat_window_help_about_activate_cb (GtkAction *action,
1232 EmpathyChatWindow *window)
1234 EmpathyChatWindowPriv *priv = GET_PRIV (window);
1236 empathy_about_dialog_new (GTK_WINDOW (priv->dialog));
1240 chat_window_delete_event_cb (GtkWidget *dialog,
1242 EmpathyChatWindow *window)
1244 EmpathyChatWindowPriv *priv = GET_PRIV (window);
1246 DEBUG ("Delete event received");
1248 g_object_ref (window);
1249 while (priv->chats) {
1250 empathy_chat_window_remove_chat (window, priv->chats->data);
1252 g_object_unref (window);
1258 chat_window_composing_cb (EmpathyChat *chat,
1259 gboolean is_composing,
1260 EmpathyChatWindow *window)
1262 chat_window_update_chat_tab (chat);
1266 chat_window_set_urgency_hint (EmpathyChatWindow *window,
1269 EmpathyChatWindowPriv *priv;
1271 priv = GET_PRIV (window);
1273 gtk_window_set_urgency_hint (GTK_WINDOW (priv->dialog), urgent);
1277 chat_window_notification_closed_cb (NotifyNotification *notify,
1278 EmpathyChatWindow *self)
1280 EmpathyChatWindowPriv *priv = GET_PRIV (self);
1282 g_object_unref (notify);
1283 if (priv->notification == notify) {
1284 priv->notification = NULL;
1289 chat_window_show_or_update_notification (EmpathyChatWindow *window,
1290 EmpathyMessage *message,
1293 EmpathyContact *sender;
1294 const gchar *header;
1298 EmpathyChatWindowPriv *priv = GET_PRIV (window);
1299 gboolean res, has_x_canonical_append;
1300 NotifyNotification *notification = priv->notification;
1302 if (!empathy_notify_manager_notification_is_enabled (priv->notify_mgr)) {
1305 res = g_settings_get_boolean (priv->gsettings_notif,
1306 EMPATHY_PREFS_NOTIFICATIONS_FOCUS);
1313 sender = empathy_message_get_sender (message);
1314 header = empathy_contact_get_alias (sender);
1315 body = empathy_message_get_body (message);
1316 escaped = g_markup_escape_text (body, -1);
1317 has_x_canonical_append = empathy_notify_manager_has_capability (
1318 priv->notify_mgr, EMPATHY_NOTIFY_MANAGER_CAP_X_CANONICAL_APPEND);
1320 if (notification != NULL && !has_x_canonical_append) {
1321 /* if the notification server supports x-canonical-append, it is
1322 better to not use notify_notification_update to avoid
1323 overwriting the current notification message */
1324 notify_notification_update (notification,
1325 header, escaped, NULL);
1327 /* if the notification server supports x-canonical-append,
1328 the hint will be added, so that the message from the
1329 just created notification will be automatically appended
1330 to an existing notification with the same title.
1331 In this way the previous message will not be lost: the new
1332 message will appear below it, in the same notification */
1333 notification = notify_notification_new (header, escaped, NULL);
1335 if (priv->notification == NULL) {
1336 priv->notification = notification;
1339 notify_notification_set_timeout (notification, NOTIFY_EXPIRES_DEFAULT);
1341 tp_g_signal_connect_object (notification, "closed",
1342 G_CALLBACK (chat_window_notification_closed_cb), window, 0);
1344 if (has_x_canonical_append) {
1345 notify_notification_set_hint_string (notification,
1346 EMPATHY_NOTIFY_MANAGER_CAP_X_CANONICAL_APPEND, "");
1350 pixbuf = empathy_notify_manager_get_pixbuf_for_notification (priv->notify_mgr,
1351 sender, EMPATHY_IMAGE_NEW_MESSAGE);
1353 if (pixbuf != NULL) {
1354 notify_notification_set_icon_from_pixbuf (notification, pixbuf);
1355 g_object_unref (pixbuf);
1358 notify_notification_show (notification, NULL);
1364 chat_window_set_highlight_room_labels (EmpathyChat *chat)
1369 if (!empathy_chat_is_room (chat))
1372 markup = g_markup_printf_escaped (
1373 "<span color=\"red\" weight=\"bold\">%s</span>",
1374 empathy_chat_get_name (chat));
1376 widget = g_object_get_data (G_OBJECT (chat), "chat-window-tab-label");
1377 gtk_label_set_markup (GTK_LABEL (widget), markup);
1379 widget = g_object_get_data (G_OBJECT (chat), "chat-window-menu-label");
1380 gtk_label_set_markup (GTK_LABEL (widget), markup);
1386 empathy_chat_window_has_focus (EmpathyChatWindow *window)
1388 EmpathyChatWindowPriv *priv;
1391 g_return_val_if_fail (EMPATHY_IS_CHAT_WINDOW (window), FALSE);
1393 priv = GET_PRIV (window);
1395 g_object_get (priv->dialog, "has-toplevel-focus", &has_focus, NULL);
1401 chat_window_new_message_cb (EmpathyChat *chat,
1402 EmpathyMessage *message,
1404 EmpathyChatWindow *window)
1406 EmpathyChatWindowPriv *priv;
1408 gboolean needs_urgency;
1409 EmpathyContact *sender;
1411 priv = GET_PRIV (window);
1413 has_focus = empathy_chat_window_has_focus (window);
1415 /* - if we're the sender, we play the sound if it's specified in the
1416 * preferences and we're not away.
1417 * - if we receive a message, we play the sound if it's specified in the
1418 * preferences and the window does not have focus on the chat receiving
1422 sender = empathy_message_get_sender (message);
1424 if (empathy_contact_is_user (sender)) {
1425 empathy_sound_manager_play (priv->sound_mgr, GTK_WIDGET (priv->dialog),
1426 EMPATHY_SOUND_MESSAGE_OUTGOING);
1429 if (has_focus && priv->current_chat == chat) {
1430 /* window and tab are focused so consider the message to be read */
1432 /* FIXME: see Bug#610994 and coments about it in EmpathyChatPriv */
1433 empathy_chat_messages_read (chat);
1437 /* Update the chat tab if this is the first unread message */
1438 if (empathy_chat_get_nb_unread_messages (chat) == 1) {
1439 chat_window_update_chat_tab (chat);
1442 /* If empathy_chat_is_room () returns TRUE, that means it's a named MUC.
1443 * If empathy_chat_get_remote_contact () returns NULL, that means it's
1444 * an unamed MUC (msn-like).
1445 * In case of a MUC, we set urgency if either:
1446 * a) the chatroom's always_urgent property is TRUE
1447 * b) the message contains our alias
1449 if (empathy_chat_is_room (chat) ||
1450 empathy_chat_get_remote_contact (chat) == NULL) {
1453 EmpathyChatroom *chatroom;
1455 account = empathy_chat_get_account (chat);
1456 room = empathy_chat_get_id (chat);
1458 chatroom = empathy_chatroom_manager_find (priv->chatroom_manager,
1461 if (chatroom != NULL && empathy_chatroom_is_always_urgent (chatroom)) {
1462 needs_urgency = TRUE;
1464 needs_urgency = empathy_message_should_highlight (message);
1467 needs_urgency = TRUE;
1470 if (needs_urgency) {
1471 chat_window_set_highlight_room_labels (chat);
1474 chat_window_set_urgency_hint (window, TRUE);
1477 /* Pending messages have already been displayed and notified in the
1478 * approver, so we don't display a notification and play a sound for those */
1480 empathy_sound_manager_play (priv->sound_mgr, GTK_WIDGET (priv->dialog),
1481 EMPATHY_SOUND_MESSAGE_INCOMING);
1483 chat_window_show_or_update_notification (window, message, chat);
1487 /* update the number of unread messages and the window icon */
1488 chat_window_title_update (priv);
1489 chat_window_icon_update (priv, TRUE);
1492 static GtkNotebook *
1493 notebook_create_window_cb (GtkNotebook *source,
1499 EmpathyChatWindowPriv *priv;
1500 EmpathyChatWindow *window, *new_window;
1503 chat = EMPATHY_CHAT (page);
1504 window = chat_window_find_chat (chat);
1506 new_window = empathy_chat_window_new ();
1507 priv = GET_PRIV (new_window);
1509 DEBUG ("Detach hook called");
1511 empathy_chat_window_move_chat (window, new_window, chat);
1513 gtk_widget_show (priv->dialog);
1514 gtk_window_move (GTK_WINDOW (priv->dialog), x, y);
1520 chat_window_page_switched_cb (GtkNotebook *notebook,
1523 EmpathyChatWindow *window)
1525 EmpathyChatWindowPriv *priv = GET_PRIV (window);
1526 EmpathyChat *chat = EMPATHY_CHAT (child);
1528 DEBUG ("Page switched");
1530 if (priv->page_added) {
1531 priv->page_added = FALSE;
1532 empathy_chat_scroll_down (chat);
1534 else if (priv->current_chat == chat) {
1538 priv->current_chat = chat;
1539 empathy_chat_messages_read (chat);
1541 chat_window_update_chat_tab (chat);
1545 chat_window_page_added_cb (GtkNotebook *notebook,
1548 EmpathyChatWindow *window)
1550 EmpathyChatWindowPriv *priv;
1553 priv = GET_PRIV (window);
1555 /* If we just received DND to the same window, we don't want
1556 * to do anything here like removing the tab and then readding
1557 * it, so we return here and in "page-added".
1559 if (priv->dnd_same_window) {
1560 DEBUG ("Page added (back to the same window)");
1561 priv->dnd_same_window = FALSE;
1565 DEBUG ("Page added");
1567 /* Get chat object */
1568 chat = EMPATHY_CHAT (child);
1570 /* Connect chat signals for this window */
1571 g_signal_connect (chat, "composing",
1572 G_CALLBACK (chat_window_composing_cb),
1574 g_signal_connect (chat, "new-message",
1575 G_CALLBACK (chat_window_new_message_cb),
1577 g_signal_connect (chat, "notify::tp-chat",
1578 G_CALLBACK (chat_window_update_chat_tab),
1581 /* Set flag so we know to perform some special operations on
1582 * switch page due to the new page being added.
1584 priv->page_added = TRUE;
1586 /* Get list of chats up to date */
1587 priv->chats = g_list_append (priv->chats, chat);
1589 chat_window_update_chat_tab (chat);
1593 chat_window_page_removed_cb (GtkNotebook *notebook,
1596 EmpathyChatWindow *window)
1598 EmpathyChatWindowPriv *priv;
1601 priv = GET_PRIV (window);
1603 /* If we just received DND to the same window, we don't want
1604 * to do anything here like removing the tab and then readding
1605 * it, so we return here and in "page-added".
1607 if (priv->dnd_same_window) {
1608 DEBUG ("Page removed (and will be readded to same window)");
1612 DEBUG ("Page removed");
1614 /* Get chat object */
1615 chat = EMPATHY_CHAT (child);
1617 /* Disconnect all signal handlers for this chat and this window */
1618 g_signal_handlers_disconnect_by_func (chat,
1619 G_CALLBACK (chat_window_composing_cb),
1621 g_signal_handlers_disconnect_by_func (chat,
1622 G_CALLBACK (chat_window_new_message_cb),
1624 g_signal_handlers_disconnect_by_func (chat,
1625 G_CALLBACK (chat_window_update_chat_tab),
1628 /* Keep list of chats up to date */
1629 priv->chats = g_list_remove (priv->chats, chat);
1630 empathy_chat_messages_read (chat);
1632 if (priv->chats == NULL) {
1633 g_object_unref (window);
1635 chat_window_update (window, TRUE);
1640 chat_window_focus_in_event_cb (GtkWidget *widget,
1642 EmpathyChatWindow *window)
1644 EmpathyChatWindowPriv *priv;
1646 priv = GET_PRIV (window);
1648 empathy_chat_messages_read (priv->current_chat);
1650 chat_window_set_urgency_hint (window, FALSE);
1652 /* Update the title, since we now mark all unread messages as read. */
1653 chat_window_update_chat_tab_full (priv->current_chat, FALSE);
1659 chat_window_drag_drop (GtkWidget *widget,
1660 GdkDragContext *context,
1664 EmpathyChatWindow *window)
1667 EmpathyChatWindowPriv *priv;
1669 priv = GET_PRIV (window);
1671 target = gtk_drag_dest_find_target (widget, context, priv->file_targets);
1672 if (target == GDK_NONE)
1673 target = gtk_drag_dest_find_target (widget, context, priv->contact_targets);
1675 if (target != GDK_NONE) {
1676 gtk_drag_get_data (widget, context, target, time_);
1684 chat_window_drag_motion (GtkWidget *widget,
1685 GdkDragContext *context,
1689 EmpathyChatWindow *window)
1692 EmpathyChatWindowPriv *priv;
1694 priv = GET_PRIV (window);
1696 target = gtk_drag_dest_find_target (widget, context, priv->file_targets);
1697 if (target != GDK_NONE) {
1698 /* This is a file drag. Ensure the contact is online and set the
1699 drag type to COPY. Note that it's possible that the tab will
1700 be switched by GTK+ after a timeout from drag_motion without
1701 getting another drag_motion to disable the drop. You have
1702 to hold your mouse really still.
1704 EmpathyContact *contact;
1706 priv = GET_PRIV (window);
1707 contact = empathy_chat_get_remote_contact (priv->current_chat);
1708 /* contact is NULL for multi-user chats. We don't do
1709 * file transfers to MUCs. We also don't send files
1710 * to offline contacts or contacts that don't support
1713 if ((contact == NULL) || !empathy_contact_is_online (contact)) {
1714 gdk_drag_status (context, 0, time_);
1717 if (!(empathy_contact_get_capabilities (contact)
1718 & EMPATHY_CAPABILITIES_FT)) {
1719 gdk_drag_status (context, 0, time_);
1722 gdk_drag_status (context, GDK_ACTION_COPY, time_);
1726 target = gtk_drag_dest_find_target (widget, context, priv->contact_targets);
1727 if (target != GDK_NONE) {
1728 /* This is a drag of a contact from a contact list. Set to COPY.
1729 FIXME: If this drag is to a MUC window, it invites the user.
1730 Otherwise, it opens a chat. Should we use a different drag
1731 type for invites? Should we allow ASK?
1733 gdk_drag_status (context, GDK_ACTION_COPY, time_);
1741 chat_window_drag_data_received (GtkWidget *widget,
1742 GdkDragContext *context,
1745 GtkSelectionData *selection,
1748 EmpathyChatWindow *window)
1750 if (info == DND_DRAG_TYPE_CONTACT_ID) {
1751 EmpathyChat *chat = NULL;
1752 EmpathyChatWindow *old_window;
1753 TpAccount *account = NULL;
1754 TpAccountManager *account_manager;
1757 const gchar *account_id;
1758 const gchar *contact_id;
1760 id = (const gchar*) gtk_selection_data_get_data (selection);
1762 /* FIXME: Perhaps should be sure that the account manager is
1763 * prepared before calling _ensure_account on it. */
1764 account_manager = tp_account_manager_dup ();
1766 DEBUG ("DND contact from roster with id:'%s'", id);
1768 strv = g_strsplit (id, ":", 2);
1769 if (g_strv_length (strv) == 2) {
1770 account_id = strv[0];
1771 contact_id = strv[1];
1773 tp_account_manager_ensure_account (account_manager, account_id);
1774 if (account != NULL)
1775 chat = empathy_chat_window_find_chat (account, contact_id);
1778 if (account == NULL) {
1780 gtk_drag_finish (context, FALSE, FALSE, time_);
1785 empathy_chat_with_contact_id (
1786 account, contact_id, gtk_get_current_event_time ());
1791 g_object_unref (account_manager);
1794 old_window = chat_window_find_chat (chat);
1796 if (old_window == window) {
1797 gtk_drag_finish (context, TRUE, FALSE, time_);
1801 empathy_chat_window_move_chat (old_window, window, chat);
1803 empathy_chat_window_add_chat (window, chat);
1806 /* Added to take care of any outstanding chat events */
1807 empathy_chat_window_present_chat (chat,
1808 TP_USER_ACTION_TIME_NOT_USER_ACTION);
1810 /* We should return TRUE to remove the data when doing
1811 * GDK_ACTION_MOVE, but we don't here otherwise it has
1812 * weird consequences, and we handle that internally
1813 * anyway with add_chat () and remove_chat ().
1815 gtk_drag_finish (context, TRUE, FALSE, time_);
1817 else if (info == DND_DRAG_TYPE_URI_LIST) {
1818 EmpathyChatWindowPriv *priv;
1819 EmpathyContact *contact;
1822 priv = GET_PRIV (window);
1823 contact = empathy_chat_get_remote_contact (priv->current_chat);
1825 /* contact is NULL when current_chat is a multi-user chat.
1826 * We don't do file transfers to MUCs, so just cancel the drag.
1828 if (contact == NULL) {
1829 gtk_drag_finish (context, TRUE, FALSE, time_);
1833 data = (const gchar *) gtk_selection_data_get_data (selection);
1834 empathy_send_file_from_uri_list (contact, data);
1836 gtk_drag_finish (context, TRUE, FALSE, time_);
1838 else if (info == DND_DRAG_TYPE_TAB) {
1840 EmpathyChatWindow *old_window = NULL;
1844 chat = (void *) gtk_selection_data_get_data (selection);
1845 old_window = chat_window_find_chat (*chat);
1848 EmpathyChatWindowPriv *priv;
1850 priv = GET_PRIV (window);
1851 priv->dnd_same_window = (old_window == window);
1852 DEBUG ("DND tab (within same window: %s)",
1853 priv->dnd_same_window ? "Yes" : "No");
1856 DEBUG ("DND from unknown source");
1857 gtk_drag_finish (context, FALSE, FALSE, time_);
1862 chat_window_chat_manager_chats_changed_cb (EmpathyChatManager *chat_manager,
1863 guint num_chats_in_manager,
1864 EmpathyChatWindow *window)
1866 EmpathyChatWindowPriv *priv = GET_PRIV (window);
1868 gtk_action_set_sensitive (priv->menu_tabs_undo_close_tab,
1869 num_chats_in_manager > 0);
1873 chat_window_finalize (GObject *object)
1875 EmpathyChatWindow *window;
1876 EmpathyChatWindowPriv *priv;
1878 window = EMPATHY_CHAT_WINDOW (object);
1879 priv = GET_PRIV (window);
1881 DEBUG ("Finalized: %p", object);
1883 g_object_unref (priv->ui_manager);
1884 g_object_unref (priv->chatroom_manager);
1885 g_object_unref (priv->notify_mgr);
1886 g_object_unref (priv->gsettings_chat);
1887 g_object_unref (priv->gsettings_notif);
1888 g_object_unref (priv->gsettings_ui);
1889 g_object_unref (priv->sound_mgr);
1891 if (priv->notification != NULL) {
1892 notify_notification_close (priv->notification, NULL);
1893 priv->notification = NULL;
1896 if (priv->contact_targets) {
1897 gtk_target_list_unref (priv->contact_targets);
1899 if (priv->file_targets) {
1900 gtk_target_list_unref (priv->file_targets);
1903 if (priv->chat_manager) {
1904 g_signal_handler_disconnect (priv->chat_manager,
1905 priv->chat_manager_chats_changed_id);
1906 g_object_unref (priv->chat_manager);
1907 priv->chat_manager = NULL;
1910 chat_windows = g_list_remove (chat_windows, window);
1911 gtk_widget_destroy (priv->dialog);
1913 G_OBJECT_CLASS (empathy_chat_window_parent_class)->finalize (object);
1917 empathy_chat_window_class_init (EmpathyChatWindowClass *klass)
1919 GObjectClass *object_class = G_OBJECT_CLASS (klass);
1921 object_class->finalize = chat_window_finalize;
1923 g_type_class_add_private (object_class, sizeof (EmpathyChatWindowPriv));
1927 empathy_chat_window_init (EmpathyChatWindow *window)
1930 GtkAccelGroup *accel_group;
1935 GtkWidget *chat_vbox;
1937 EmpathySmileyManager *smiley_manager;
1938 EmpathyChatWindowPriv *priv = G_TYPE_INSTANCE_GET_PRIVATE (window,
1939 EMPATHY_TYPE_CHAT_WINDOW, EmpathyChatWindowPriv);
1941 window->priv = priv;
1942 filename = empathy_file_lookup ("empathy-chat-window.ui", "src");
1943 gui = empathy_builder_get_file (filename,
1944 "chat_window", &priv->dialog,
1945 "chat_vbox", &chat_vbox,
1946 "ui_manager", &priv->ui_manager,
1947 "menu_conv_insert_smiley", &priv->menu_conv_insert_smiley,
1948 "menu_conv_favorite", &priv->menu_conv_favorite,
1949 "menu_conv_always_urgent", &priv->menu_conv_always_urgent,
1950 "menu_conv_toggle_contacts", &priv->menu_conv_toggle_contacts,
1951 "menu_edit_cut", &priv->menu_edit_cut,
1952 "menu_edit_copy", &priv->menu_edit_copy,
1953 "menu_edit_paste", &priv->menu_edit_paste,
1954 "menu_edit_find", &priv->menu_edit_find,
1955 "menu_tabs_next", &priv->menu_tabs_next,
1956 "menu_tabs_prev", &priv->menu_tabs_prev,
1957 "menu_tabs_undo_close_tab", &priv->menu_tabs_undo_close_tab,
1958 "menu_tabs_left", &priv->menu_tabs_left,
1959 "menu_tabs_right", &priv->menu_tabs_right,
1960 "menu_tabs_detach", &priv->menu_tabs_detach,
1964 empathy_builder_connect (gui, window,
1965 "menu_conv", "activate", chat_window_conv_activate_cb,
1966 "menu_conv_clear", "activate", chat_window_clear_activate_cb,
1967 "menu_conv_favorite", "toggled", chat_window_favorite_toggled_cb,
1968 "menu_conv_always_urgent", "toggled", chat_window_always_urgent_toggled_cb,
1969 "menu_conv_toggle_contacts", "toggled", chat_window_contacts_toggled_cb,
1970 "menu_conv_invite_participant", "activate", chat_window_invite_participant_activate_cb,
1971 "menu_conv_close", "activate", chat_window_close_activate_cb,
1972 "menu_edit", "activate", chat_window_edit_activate_cb,
1973 "menu_edit_cut", "activate", chat_window_cut_activate_cb,
1974 "menu_edit_copy", "activate", chat_window_copy_activate_cb,
1975 "menu_edit_paste", "activate", chat_window_paste_activate_cb,
1976 "menu_edit_find", "activate", chat_window_find_activate_cb,
1977 "menu_tabs_next", "activate", chat_window_tabs_next_activate_cb,
1978 "menu_tabs_prev", "activate", chat_window_tabs_previous_activate_cb,
1979 "menu_tabs_undo_close_tab", "activate", chat_window_tabs_undo_close_tab_activate_cb,
1980 "menu_tabs_left", "activate", chat_window_tabs_left_activate_cb,
1981 "menu_tabs_right", "activate", chat_window_tabs_right_activate_cb,
1982 "menu_tabs_detach", "activate", chat_window_detach_activate_cb,
1983 "menu_help_contents", "activate", chat_window_help_contents_activate_cb,
1984 "menu_help_about", "activate", chat_window_help_about_activate_cb,
1987 g_object_ref (priv->ui_manager);
1988 g_object_unref (gui);
1990 priv->gsettings_chat = g_settings_new (EMPATHY_PREFS_CHAT_SCHEMA);
1991 priv->gsettings_notif = g_settings_new (EMPATHY_PREFS_NOTIFICATIONS_SCHEMA);
1992 priv->gsettings_ui = g_settings_new (EMPATHY_PREFS_UI_SCHEMA);
1993 priv->chatroom_manager = empathy_chatroom_manager_dup_singleton (NULL);
1995 priv->sound_mgr = empathy_sound_manager_dup_singleton ();
1997 priv->notebook = gtk_notebook_new ();
1999 g_signal_connect (priv->notebook, "create-window",
2000 G_CALLBACK (notebook_create_window_cb), window);
2002 gtk_notebook_set_group_name (GTK_NOTEBOOK (priv->notebook),
2003 "EmpathyChatWindow");
2004 gtk_notebook_set_scrollable (GTK_NOTEBOOK (priv->notebook), TRUE);
2005 gtk_notebook_popup_enable (GTK_NOTEBOOK (priv->notebook));
2006 gtk_box_pack_start (GTK_BOX (chat_vbox), priv->notebook, TRUE, TRUE, 0);
2007 gtk_widget_show (priv->notebook);
2010 accel_group = gtk_accel_group_new ();
2011 gtk_window_add_accel_group (GTK_WINDOW (priv->dialog), accel_group);
2013 for (i = 0; i < G_N_ELEMENTS (tab_accel_keys); i++) {
2014 closure = g_cclosure_new (G_CALLBACK (chat_window_accel_cb),
2017 gtk_accel_group_connect (accel_group,
2024 g_object_unref (accel_group);
2026 /* Set up drag target lists */
2027 priv->contact_targets = gtk_target_list_new (drag_types_dest_contact,
2028 G_N_ELEMENTS (drag_types_dest_contact));
2029 priv->file_targets = gtk_target_list_new (drag_types_dest_file,
2030 G_N_ELEMENTS (drag_types_dest_file));
2032 /* Set up smiley menu */
2033 smiley_manager = empathy_smiley_manager_dup_singleton ();
2034 submenu = empathy_smiley_menu_new (smiley_manager,
2035 chat_window_insert_smiley_activate_cb,
2037 menu = gtk_ui_manager_get_widget (priv->ui_manager,
2038 "/chats_menubar/menu_conv/menu_conv_insert_smiley");
2039 gtk_menu_item_set_submenu (GTK_MENU_ITEM (menu), submenu);
2040 g_object_unref (smiley_manager);
2042 /* Set up signals we can't do with ui file since we may need to
2043 * block/unblock them at some later stage.
2046 g_signal_connect (priv->dialog,
2048 G_CALLBACK (chat_window_delete_event_cb),
2050 g_signal_connect (priv->dialog,
2052 G_CALLBACK (chat_window_focus_in_event_cb),
2054 g_signal_connect_after (priv->notebook,
2056 G_CALLBACK (chat_window_page_switched_cb),
2058 g_signal_connect (priv->notebook,
2060 G_CALLBACK (chat_window_page_added_cb),
2062 g_signal_connect (priv->notebook,
2064 G_CALLBACK (chat_window_page_removed_cb),
2067 /* Set up drag and drop */
2068 gtk_drag_dest_set (GTK_WIDGET (priv->notebook),
2069 GTK_DEST_DEFAULT_HIGHLIGHT,
2071 G_N_ELEMENTS (drag_types_dest),
2072 GDK_ACTION_MOVE | GDK_ACTION_COPY);
2074 /* connect_after to allow GtkNotebook's built-in tab switching */
2075 g_signal_connect_after (priv->notebook,
2077 G_CALLBACK (chat_window_drag_motion),
2079 g_signal_connect (priv->notebook,
2080 "drag-data-received",
2081 G_CALLBACK (chat_window_drag_data_received),
2083 g_signal_connect (priv->notebook,
2085 G_CALLBACK (chat_window_drag_drop),
2088 chat_windows = g_list_prepend (chat_windows, window);
2090 /* Set up private details */
2092 priv->current_chat = NULL;
2093 priv->notification = NULL;
2095 priv->notify_mgr = empathy_notify_manager_dup_singleton ();
2097 priv->chat_manager = empathy_chat_manager_dup_singleton ();
2098 priv->chat_manager_chats_changed_id =
2099 g_signal_connect (priv->chat_manager, "closed-chats-changed",
2100 G_CALLBACK (chat_window_chat_manager_chats_changed_cb),
2103 chat_window_chat_manager_chats_changed_cb (priv->chat_manager,
2104 empathy_chat_manager_get_num_closed_chats (priv->chat_manager),
2109 empathy_chat_window_get_dialog (EmpathyChatWindow *window)
2111 EmpathyChatWindowPriv *priv;
2113 g_return_val_if_fail (window != NULL, NULL);
2115 priv = GET_PRIV (window);
2117 return priv->dialog;
2120 /* Returns the window to open a new tab in if there is a suitable window,
2121 * otherwise, returns NULL indicating that a new window should be added.
2123 static EmpathyChatWindow *
2124 empathy_chat_window_get_default (gboolean room)
2126 GSettings *gsettings = g_settings_new (EMPATHY_PREFS_UI_SCHEMA);
2128 gboolean separate_windows = TRUE;
2130 separate_windows = g_settings_get_boolean (gsettings,
2131 EMPATHY_PREFS_UI_SEPARATE_CHAT_WINDOWS);
2133 g_object_unref (gsettings);
2135 if (separate_windows) {
2136 /* Always create a new window */
2140 for (l = chat_windows; l; l = l->next) {
2141 EmpathyChatWindowPriv *priv;
2142 EmpathyChatWindow *chat_window;
2144 guint nb_rooms, nb_private;
2146 chat_window = l->data;
2147 priv = GET_PRIV (chat_window);
2149 dialog = empathy_chat_window_get_dialog (chat_window);
2151 empathy_chat_window_get_nb_chats (chat_window, &nb_rooms, &nb_private);
2153 /* Skip the window if there aren't any rooms in it */
2154 if (room && nb_rooms == 0)
2157 /* Skip the window if there aren't any 1-1 chats in it */
2158 if (!room && nb_private == 0)
2161 /* Found a window on this desktop, make it visible if necessary */
2162 if (!empathy_window_get_is_visible (GTK_WINDOW (dialog)))
2163 empathy_window_present (GTK_WINDOW (dialog));
2171 empathy_chat_window_add_chat (EmpathyChatWindow *window,
2174 EmpathyChatWindowPriv *priv;
2176 GtkWidget *popup_label;
2178 GValue value = { 0, };
2180 g_return_if_fail (window != NULL);
2181 g_return_if_fail (EMPATHY_IS_CHAT (chat));
2183 priv = GET_PRIV (window);
2185 /* Reference the chat object */
2186 g_object_ref (chat);
2188 /* If this window has just been created, position it */
2189 if (priv->chats == NULL) {
2190 const gchar *name = "chat-window";
2191 gboolean separate_windows;
2193 separate_windows = g_settings_get_boolean (priv->gsettings_ui,
2194 EMPATHY_PREFS_UI_SEPARATE_CHAT_WINDOWS);
2196 if (empathy_chat_is_room (chat))
2197 name = "room-window";
2199 if (separate_windows) {
2202 /* Save current position of the window */
2203 gtk_window_get_position (GTK_WINDOW (priv->dialog), &x, &y);
2205 /* First bind to the 'generic' name. So new window for which we didn't
2206 * save a geometry yet will have the geometry of the last saved
2207 * window (bgo #601191). */
2208 empathy_geometry_bind (GTK_WINDOW (priv->dialog), name);
2210 /* Restore previous position of the window so the newly created window
2211 * won't be in the same position as the latest saved window and so
2212 * completely hide it. */
2213 gtk_window_move (GTK_WINDOW (priv->dialog), x, y);
2215 /* Then bind it to the name of the contact/room so we'll save the
2216 * geometry specific to this window */
2217 name = empathy_chat_get_id (chat);
2220 empathy_geometry_bind (GTK_WINDOW (priv->dialog), name);
2223 child = GTK_WIDGET (chat);
2224 label = chat_window_create_label (window, chat, TRUE);
2225 popup_label = chat_window_create_label (window, chat, FALSE);
2226 gtk_widget_show (child);
2228 g_signal_connect (chat, "notify::name",
2229 G_CALLBACK (chat_window_chat_notify_cb),
2231 g_signal_connect (chat, "notify::subject",
2232 G_CALLBACK (chat_window_chat_notify_cb),
2234 g_signal_connect (chat, "notify::remote-contact",
2235 G_CALLBACK (chat_window_chat_notify_cb),
2237 g_signal_connect (chat, "notify::sms-channel",
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 ();
2391 empathy_chat_window_add_chat (window, chat);
2394 /* Don't force the window to show itself when it wasn't
2395 * an action by the user
2397 if (!tp_user_action_time_should_present (timestamp, &x_timestamp))
2400 priv = GET_PRIV (window);
2402 if (x_timestamp != GDK_CURRENT_TIME) {
2403 /* Don't present or switch tab if the action was earlier than the
2404 * last actions X time, accounting for overflow and the first ever
2407 if (priv->x_user_action_time != 0
2408 && X_EARLIER_OR_EQL (x_timestamp, priv->x_user_action_time))
2411 priv->x_user_action_time = x_timestamp;
2414 empathy_chat_window_switch_to_chat (window, chat);
2415 empathy_window_present_with_time (GTK_WINDOW (priv->dialog),
2418 gtk_widget_grab_focus (chat->input_text_view);
2422 empathy_chat_window_get_nb_chats (EmpathyChatWindow *self,
2426 EmpathyChatWindowPriv *priv = GET_PRIV (self);
2428 guint _nb_rooms = 0, _nb_private = 0;
2430 for (l = priv->chats; l != NULL; l = g_list_next (l)) {
2431 if (empathy_chat_is_room (EMPATHY_CHAT (l->data)))
2437 if (nb_rooms != NULL)
2438 *nb_rooms = _nb_rooms;
2439 if (nb_private != NULL)
2440 *nb_private = _nb_private;