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 append_markup_printf (tooltip,
710 "<b>%s</b><small> (%s)</small>",
712 tp_account_get_display_name (account));
714 if (!EMP_STR_EMPTY (status)) {
715 append_markup_printf (tooltip, "\n<i>%s</i>", status);
719 append_markup_printf (tooltip, "\n<b>%s</b> %s",
720 _("Topic:"), subject);
723 if (remote_contact && empathy_chat_is_composing (chat)) {
724 append_markup_printf (tooltip, "\n%s", _("Typing a message."));
727 markup = g_string_free (tooltip, FALSE);
728 widget = g_object_get_data (G_OBJECT (chat), "chat-window-tab-tooltip-widget");
729 gtk_widget_set_tooltip_markup (widget, markup);
730 widget = g_object_get_data (G_OBJECT (chat), "chat-window-menu-tooltip-widget");
731 gtk_widget_set_tooltip_markup (widget, markup);
734 /* Update tab and menu label */
735 widget = g_object_get_data (G_OBJECT (chat), "chat-window-tab-label");
736 gtk_label_set_text (GTK_LABEL (widget), name);
737 widget = g_object_get_data (G_OBJECT (chat), "chat-window-menu-label");
738 gtk_label_set_text (GTK_LABEL (widget), name);
740 /* Update the window if it's the current chat */
741 if (priv->current_chat == chat) {
742 chat_window_update (window, update_contact_menu);
747 chat_window_update_chat_tab (EmpathyChat *chat)
749 chat_window_update_chat_tab_full (chat, TRUE);
753 chat_window_chat_notify_cb (EmpathyChat *chat)
755 EmpathyContact *old_remote_contact;
756 EmpathyContact *remote_contact = NULL;
758 old_remote_contact = g_object_get_data (G_OBJECT (chat), "chat-window-remote-contact");
759 remote_contact = empathy_chat_get_remote_contact (chat);
761 if (old_remote_contact != remote_contact) {
762 /* The remote-contact associated with the chat changed, we need
763 * to keep track of any change of that contact and update the
764 * window each time. */
765 if (remote_contact) {
766 g_signal_connect_swapped (remote_contact, "notify",
767 G_CALLBACK (chat_window_update_chat_tab),
770 if (old_remote_contact) {
771 g_signal_handlers_disconnect_by_func (old_remote_contact,
772 chat_window_update_chat_tab,
776 g_object_set_data_full (G_OBJECT (chat), "chat-window-remote-contact",
777 g_object_ref (remote_contact), (GDestroyNotify) g_object_unref);
780 chat_window_update_chat_tab (chat);
784 chat_window_insert_smiley_activate_cb (EmpathySmileyManager *manager,
785 EmpathySmiley *smiley,
788 EmpathyChatWindowPriv *priv = GET_PRIV (window);
790 GtkTextBuffer *buffer;
793 chat = priv->current_chat;
795 buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (chat->input_text_view));
796 gtk_text_buffer_get_end_iter (buffer, &iter);
797 gtk_text_buffer_insert (buffer, &iter, smiley->str, -1);
801 chat_window_conv_activate_cb (GtkAction *action,
802 EmpathyChatWindow *window)
804 EmpathyChatWindowPriv *priv = GET_PRIV (window);
807 EmpathyContact *remote_contact = NULL;
809 /* Favorite room menu */
810 is_room = empathy_chat_is_room (priv->current_chat);
814 gboolean found = FALSE;
815 EmpathyChatroom *chatroom;
817 room = empathy_chat_get_id (priv->current_chat);
818 account = empathy_chat_get_account (priv->current_chat);
819 chatroom = empathy_chatroom_manager_find (priv->chatroom_manager,
821 if (chatroom != NULL)
822 found = empathy_chatroom_is_favorite (chatroom);
824 DEBUG ("This room %s favorite", found ? "is" : "is not");
825 gtk_toggle_action_set_active (
826 GTK_TOGGLE_ACTION (priv->menu_conv_favorite), found);
828 if (chatroom != NULL)
829 found = empathy_chatroom_is_always_urgent (chatroom);
831 gtk_toggle_action_set_active (
832 GTK_TOGGLE_ACTION (priv->menu_conv_always_urgent),
835 gtk_action_set_visible (priv->menu_conv_favorite, is_room);
836 gtk_action_set_visible (priv->menu_conv_always_urgent, is_room);
838 /* Show contacts menu */
839 g_object_get (priv->current_chat,
840 "remote-contact", &remote_contact,
841 "show-contacts", &active,
843 if (remote_contact == NULL) {
844 gtk_toggle_action_set_active (
845 GTK_TOGGLE_ACTION (priv->menu_conv_toggle_contacts),
848 gtk_action_set_visible (priv->menu_conv_toggle_contacts,
849 (remote_contact == NULL));
850 if (remote_contact != NULL) {
851 g_object_unref (remote_contact);
856 chat_window_clear_activate_cb (GtkAction *action,
857 EmpathyChatWindow *window)
859 EmpathyChatWindowPriv *priv = GET_PRIV (window);
861 empathy_chat_clear (priv->current_chat);
865 chat_window_favorite_toggled_cb (GtkToggleAction *toggle_action,
866 EmpathyChatWindow *window)
868 EmpathyChatWindowPriv *priv = GET_PRIV (window);
872 EmpathyChatroom *chatroom;
874 active = gtk_toggle_action_get_active (toggle_action);
875 account = empathy_chat_get_account (priv->current_chat);
876 room = empathy_chat_get_id (priv->current_chat);
878 chatroom = empathy_chatroom_manager_ensure_chatroom (
879 priv->chatroom_manager,
882 empathy_chat_get_name (priv->current_chat));
884 empathy_chatroom_set_favorite (chatroom, active);
885 g_object_unref (chatroom);
889 chat_window_always_urgent_toggled_cb (GtkToggleAction *toggle_action,
890 EmpathyChatWindow *window)
892 EmpathyChatWindowPriv *priv = GET_PRIV (window);
896 EmpathyChatroom *chatroom;
898 active = gtk_toggle_action_get_active (toggle_action);
899 account = empathy_chat_get_account (priv->current_chat);
900 room = empathy_chat_get_id (priv->current_chat);
902 chatroom = empathy_chatroom_manager_ensure_chatroom (
903 priv->chatroom_manager,
906 empathy_chat_get_name (priv->current_chat));
908 empathy_chatroom_set_always_urgent (chatroom, active);
909 g_object_unref (chatroom);
913 chat_window_contacts_toggled_cb (GtkToggleAction *toggle_action,
914 EmpathyChatWindow *window)
916 EmpathyChatWindowPriv *priv = GET_PRIV (window);
919 active = gtk_toggle_action_get_active (toggle_action);
921 empathy_chat_set_show_contacts (priv->current_chat, active);
925 got_contact_cb (TpConnection *connection,
926 EmpathyContact *contact,
931 EmpathyTpChat *tp_chat = EMPATHY_TP_CHAT (user_data);
934 DEBUG ("Failed: %s", error->message);
937 empathy_contact_list_add (EMPATHY_CONTACT_LIST (tp_chat),
938 contact, _("Inviting you to this room"));
943 chat_window_invite_participant_activate_cb (GtkAction *action,
944 EmpathyChatWindow *window)
946 EmpathyChatWindowPriv *priv;
948 EmpathyTpChat *tp_chat;
953 priv = GET_PRIV (window);
955 g_return_if_fail (priv->current_chat != NULL);
957 tp_chat = empathy_chat_get_tp_chat (priv->current_chat);
958 channel = empathy_tp_chat_get_channel (tp_chat);
959 account = empathy_chat_get_account (priv->current_chat);
961 dialog = empathy_invite_participant_dialog_new (
962 GTK_WINDOW (priv->dialog), account);
963 gtk_widget_show (dialog);
965 response = gtk_dialog_run (GTK_DIALOG (dialog));
967 if (response == GTK_RESPONSE_ACCEPT) {
968 TpConnection *connection;
971 id = empathy_contact_selector_dialog_get_selected (
972 EMPATHY_CONTACT_SELECTOR_DIALOG (dialog), NULL, NULL);
973 if (EMP_STR_EMPTY (id)) goto out;
975 connection = tp_channel_borrow_connection (channel);
976 empathy_tp_contact_factory_get_from_id (connection, id,
977 got_contact_cb, tp_chat, NULL, NULL);
981 gtk_widget_destroy (dialog);
985 chat_window_close_activate_cb (GtkAction *action,
986 EmpathyChatWindow *window)
988 EmpathyChatWindowPriv *priv;
990 priv = GET_PRIV (window);
992 g_return_if_fail (priv->current_chat != NULL);
994 empathy_chat_window_remove_chat (window, priv->current_chat);
998 chat_window_edit_activate_cb (GtkAction *action,
999 EmpathyChatWindow *window)
1001 EmpathyChatWindowPriv *priv;
1002 GtkClipboard *clipboard;
1003 GtkTextBuffer *buffer;
1004 gboolean text_available;
1006 priv = GET_PRIV (window);
1008 g_return_if_fail (priv->current_chat != NULL);
1010 if (!empathy_chat_get_tp_chat (priv->current_chat)) {
1011 gtk_action_set_sensitive (priv->menu_edit_copy, FALSE);
1012 gtk_action_set_sensitive (priv->menu_edit_cut, FALSE);
1013 gtk_action_set_sensitive (priv->menu_edit_paste, FALSE);
1017 buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (priv->current_chat->input_text_view));
1018 if (gtk_text_buffer_get_has_selection (buffer)) {
1019 gtk_action_set_sensitive (priv->menu_edit_copy, TRUE);
1020 gtk_action_set_sensitive (priv->menu_edit_cut, TRUE);
1024 selection = empathy_chat_view_get_has_selection (priv->current_chat->view);
1026 gtk_action_set_sensitive (priv->menu_edit_cut, FALSE);
1027 gtk_action_set_sensitive (priv->menu_edit_copy, selection);
1030 clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
1031 text_available = gtk_clipboard_wait_is_text_available (clipboard);
1032 gtk_action_set_sensitive (priv->menu_edit_paste, text_available);
1036 chat_window_cut_activate_cb (GtkAction *action,
1037 EmpathyChatWindow *window)
1039 EmpathyChatWindowPriv *priv;
1041 g_return_if_fail (EMPATHY_IS_CHAT_WINDOW (window));
1043 priv = GET_PRIV (window);
1045 empathy_chat_cut (priv->current_chat);
1049 chat_window_copy_activate_cb (GtkAction *action,
1050 EmpathyChatWindow *window)
1052 EmpathyChatWindowPriv *priv;
1054 g_return_if_fail (EMPATHY_IS_CHAT_WINDOW (window));
1056 priv = GET_PRIV (window);
1058 empathy_chat_copy (priv->current_chat);
1062 chat_window_paste_activate_cb (GtkAction *action,
1063 EmpathyChatWindow *window)
1065 EmpathyChatWindowPriv *priv;
1067 g_return_if_fail (EMPATHY_IS_CHAT_WINDOW (window));
1069 priv = GET_PRIV (window);
1071 empathy_chat_paste (priv->current_chat);
1075 chat_window_find_activate_cb (GtkAction *action,
1076 EmpathyChatWindow *window)
1078 EmpathyChatWindowPriv *priv;
1080 g_return_if_fail (EMPATHY_IS_CHAT_WINDOW (window));
1082 priv = GET_PRIV (window);
1084 empathy_chat_find (priv->current_chat);
1088 chat_window_tabs_next_activate_cb (GtkAction *action,
1089 EmpathyChatWindow *window)
1091 EmpathyChatWindowPriv *priv;
1093 gint index_, numPages;
1094 gboolean wrap_around;
1096 priv = GET_PRIV (window);
1098 g_object_get (gtk_settings_get_default (), "gtk-keynav-wrap-around",
1099 &wrap_around, NULL);
1101 chat = priv->current_chat;
1102 index_ = gtk_notebook_get_current_page (GTK_NOTEBOOK (priv->notebook));
1103 numPages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (priv->notebook));
1105 if (index_ == (numPages - 1) && wrap_around) {
1106 gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->notebook), 0);
1110 gtk_notebook_next_page (GTK_NOTEBOOK (priv->notebook));
1114 chat_window_tabs_previous_activate_cb (GtkAction *action,
1115 EmpathyChatWindow *window)
1117 EmpathyChatWindowPriv *priv;
1119 gint index_, numPages;
1120 gboolean wrap_around;
1122 priv = GET_PRIV (window);
1124 g_object_get (gtk_settings_get_default (), "gtk-keynav-wrap-around",
1125 &wrap_around, NULL);
1127 chat = priv->current_chat;
1128 index_ = gtk_notebook_get_current_page (GTK_NOTEBOOK (priv->notebook));
1129 numPages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (priv->notebook));
1131 if (index_ <= 0 && wrap_around) {
1132 gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->notebook), numPages - 1);
1136 gtk_notebook_prev_page (GTK_NOTEBOOK (priv->notebook));
1140 chat_window_tabs_undo_close_tab_activate_cb (GtkAction *action,
1141 EmpathyChatWindow *window)
1143 EmpathyChatWindowPriv *priv = GET_PRIV (window);
1144 empathy_chat_manager_undo_closed_chat (priv->chat_manager);
1148 chat_window_tabs_left_activate_cb (GtkAction *action,
1149 EmpathyChatWindow *window)
1151 EmpathyChatWindowPriv *priv;
1153 gint index_, num_pages;
1155 priv = GET_PRIV (window);
1157 chat = priv->current_chat;
1158 index_ = gtk_notebook_get_current_page (GTK_NOTEBOOK (priv->notebook));
1163 gtk_notebook_reorder_child (GTK_NOTEBOOK (priv->notebook),
1167 num_pages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (priv->notebook));
1168 chat_window_menu_context_update (priv, num_pages);
1172 chat_window_tabs_right_activate_cb (GtkAction *action,
1173 EmpathyChatWindow *window)
1175 EmpathyChatWindowPriv *priv;
1177 gint index_, num_pages;
1179 priv = GET_PRIV (window);
1181 chat = priv->current_chat;
1182 index_ = gtk_notebook_get_current_page (GTK_NOTEBOOK (priv->notebook));
1184 gtk_notebook_reorder_child (GTK_NOTEBOOK (priv->notebook),
1188 num_pages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (priv->notebook));
1189 chat_window_menu_context_update (priv, num_pages);
1192 static EmpathyChatWindow *
1193 empathy_chat_window_new (void)
1195 return EMPATHY_CHAT_WINDOW (g_object_new (EMPATHY_TYPE_CHAT_WINDOW, NULL));
1199 chat_window_detach_activate_cb (GtkAction *action,
1200 EmpathyChatWindow *window)
1202 EmpathyChatWindowPriv *priv;
1203 EmpathyChatWindow *new_window;
1206 priv = GET_PRIV (window);
1208 chat = priv->current_chat;
1209 new_window = empathy_chat_window_new ();
1211 empathy_chat_window_move_chat (window, new_window, chat);
1213 priv = GET_PRIV (new_window);
1214 gtk_widget_show (priv->dialog);
1218 chat_window_help_contents_activate_cb (GtkAction *action,
1219 EmpathyChatWindow *window)
1221 EmpathyChatWindowPriv *priv = GET_PRIV (window);
1223 empathy_url_show (priv->dialog, "ghelp:empathy");
1227 chat_window_help_about_activate_cb (GtkAction *action,
1228 EmpathyChatWindow *window)
1230 EmpathyChatWindowPriv *priv = GET_PRIV (window);
1232 empathy_about_dialog_new (GTK_WINDOW (priv->dialog));
1236 chat_window_delete_event_cb (GtkWidget *dialog,
1238 EmpathyChatWindow *window)
1240 EmpathyChatWindowPriv *priv = GET_PRIV (window);
1242 DEBUG ("Delete event received");
1244 g_object_ref (window);
1245 while (priv->chats) {
1246 empathy_chat_window_remove_chat (window, priv->chats->data);
1248 g_object_unref (window);
1254 chat_window_composing_cb (EmpathyChat *chat,
1255 gboolean is_composing,
1256 EmpathyChatWindow *window)
1258 chat_window_update_chat_tab (chat);
1262 chat_window_set_urgency_hint (EmpathyChatWindow *window,
1265 EmpathyChatWindowPriv *priv;
1267 priv = GET_PRIV (window);
1269 gtk_window_set_urgency_hint (GTK_WINDOW (priv->dialog), urgent);
1273 chat_window_notification_closed_cb (NotifyNotification *notify,
1274 EmpathyChatWindow *self)
1276 EmpathyChatWindowPriv *priv = GET_PRIV (self);
1278 g_object_unref (notify);
1279 if (priv->notification == notify) {
1280 priv->notification = NULL;
1285 chat_window_show_or_update_notification (EmpathyChatWindow *window,
1286 EmpathyMessage *message,
1289 EmpathyContact *sender;
1290 const gchar *header;
1294 EmpathyChatWindowPriv *priv = GET_PRIV (window);
1295 gboolean res, has_x_canonical_append;
1296 NotifyNotification *notification = priv->notification;
1298 if (!empathy_notify_manager_notification_is_enabled (priv->notify_mgr)) {
1301 res = g_settings_get_boolean (priv->gsettings_notif,
1302 EMPATHY_PREFS_NOTIFICATIONS_FOCUS);
1309 sender = empathy_message_get_sender (message);
1310 header = empathy_contact_get_alias (sender);
1311 body = empathy_message_get_body (message);
1312 escaped = g_markup_escape_text (body, -1);
1313 has_x_canonical_append = empathy_notify_manager_has_capability (
1314 priv->notify_mgr, EMPATHY_NOTIFY_MANAGER_CAP_X_CANONICAL_APPEND);
1316 if (notification != NULL && !has_x_canonical_append) {
1317 /* if the notification server supports x-canonical-append, it is
1318 better to not use notify_notification_update to avoid
1319 overwriting the current notification message */
1320 notify_notification_update (notification,
1321 header, escaped, NULL);
1323 /* if the notification server supports x-canonical-append,
1324 the hint will be added, so that the message from the
1325 just created notification will be automatically appended
1326 to an existing notification with the same title.
1327 In this way the previous message will not be lost: the new
1328 message will appear below it, in the same notification */
1329 notification = notify_notification_new (header, escaped, NULL);
1331 if (priv->notification == NULL) {
1332 priv->notification = notification;
1335 notify_notification_set_timeout (notification, NOTIFY_EXPIRES_DEFAULT);
1337 tp_g_signal_connect_object (notification, "closed",
1338 G_CALLBACK (chat_window_notification_closed_cb), window, 0);
1340 if (has_x_canonical_append) {
1341 notify_notification_set_hint_string (notification,
1342 EMPATHY_NOTIFY_MANAGER_CAP_X_CANONICAL_APPEND, "");
1346 pixbuf = empathy_notify_manager_get_pixbuf_for_notification (priv->notify_mgr,
1347 sender, EMPATHY_IMAGE_NEW_MESSAGE);
1349 if (pixbuf != NULL) {
1350 notify_notification_set_icon_from_pixbuf (notification, pixbuf);
1351 g_object_unref (pixbuf);
1354 notify_notification_show (notification, NULL);
1360 chat_window_set_highlight_room_labels (EmpathyChat *chat)
1365 if (!empathy_chat_is_room (chat))
1368 markup = g_markup_printf_escaped (
1369 "<span color=\"red\" weight=\"bold\">%s</span>",
1370 empathy_chat_get_name (chat));
1372 widget = g_object_get_data (G_OBJECT (chat), "chat-window-tab-label");
1373 gtk_label_set_markup (GTK_LABEL (widget), markup);
1375 widget = g_object_get_data (G_OBJECT (chat), "chat-window-menu-label");
1376 gtk_label_set_markup (GTK_LABEL (widget), markup);
1382 empathy_chat_window_has_focus (EmpathyChatWindow *window)
1384 EmpathyChatWindowPriv *priv;
1387 g_return_val_if_fail (EMPATHY_IS_CHAT_WINDOW (window), FALSE);
1389 priv = GET_PRIV (window);
1391 g_object_get (priv->dialog, "has-toplevel-focus", &has_focus, NULL);
1397 chat_window_new_message_cb (EmpathyChat *chat,
1398 EmpathyMessage *message,
1400 EmpathyChatWindow *window)
1402 EmpathyChatWindowPriv *priv;
1404 gboolean needs_urgency;
1405 EmpathyContact *sender;
1407 priv = GET_PRIV (window);
1409 has_focus = empathy_chat_window_has_focus (window);
1411 /* - if we're the sender, we play the sound if it's specified in the
1412 * preferences and we're not away.
1413 * - if we receive a message, we play the sound if it's specified in the
1414 * preferences and the window does not have focus on the chat receiving
1418 sender = empathy_message_get_sender (message);
1420 if (empathy_contact_is_user (sender)) {
1421 empathy_sound_manager_play (priv->sound_mgr, GTK_WIDGET (priv->dialog),
1422 EMPATHY_SOUND_MESSAGE_OUTGOING);
1425 if (has_focus && priv->current_chat == chat) {
1426 /* window and tab are focused so consider the message to be read */
1428 /* FIXME: see Bug#610994 and coments about it in EmpathyChatPriv */
1429 empathy_chat_messages_read (chat);
1433 /* Update the chat tab if this is the first unread message */
1434 if (empathy_chat_get_nb_unread_messages (chat) == 1) {
1435 chat_window_update_chat_tab (chat);
1438 /* If empathy_chat_is_room () returns TRUE, that means it's a named MUC.
1439 * If empathy_chat_get_remote_contact () returns NULL, that means it's
1440 * an unamed MUC (msn-like).
1441 * In case of a MUC, we set urgency if either:
1442 * a) the chatroom's always_urgent property is TRUE
1443 * b) the message contains our alias
1445 if (empathy_chat_is_room (chat) ||
1446 empathy_chat_get_remote_contact (chat) == NULL) {
1449 EmpathyChatroom *chatroom;
1451 account = empathy_chat_get_account (chat);
1452 room = empathy_chat_get_id (chat);
1454 chatroom = empathy_chatroom_manager_find (priv->chatroom_manager,
1457 if (chatroom != NULL && empathy_chatroom_is_always_urgent (chatroom)) {
1458 needs_urgency = TRUE;
1460 needs_urgency = empathy_message_should_highlight (message);
1463 needs_urgency = TRUE;
1466 if (needs_urgency) {
1467 chat_window_set_highlight_room_labels (chat);
1470 chat_window_set_urgency_hint (window, TRUE);
1473 /* Pending messages have already been displayed and notified in the
1474 * approver, so we don't display a notification and play a sound for those */
1476 empathy_sound_manager_play (priv->sound_mgr, GTK_WIDGET (priv->dialog),
1477 EMPATHY_SOUND_MESSAGE_INCOMING);
1479 chat_window_show_or_update_notification (window, message, chat);
1483 /* update the number of unread messages and the window icon */
1484 chat_window_title_update (priv);
1485 chat_window_icon_update (priv, TRUE);
1488 static GtkNotebook *
1489 notebook_create_window_cb (GtkNotebook *source,
1495 EmpathyChatWindowPriv *priv;
1496 EmpathyChatWindow *window, *new_window;
1499 chat = EMPATHY_CHAT (page);
1500 window = chat_window_find_chat (chat);
1502 new_window = empathy_chat_window_new ();
1503 priv = GET_PRIV (new_window);
1505 DEBUG ("Detach hook called");
1507 empathy_chat_window_move_chat (window, new_window, chat);
1509 gtk_widget_show (priv->dialog);
1510 gtk_window_move (GTK_WINDOW (priv->dialog), x, y);
1516 chat_window_page_switched_cb (GtkNotebook *notebook,
1519 EmpathyChatWindow *window)
1521 EmpathyChatWindowPriv *priv = GET_PRIV (window);
1522 EmpathyChat *chat = EMPATHY_CHAT (child);
1524 DEBUG ("Page switched");
1526 if (priv->page_added) {
1527 priv->page_added = FALSE;
1528 empathy_chat_scroll_down (chat);
1530 else if (priv->current_chat == chat) {
1534 priv->current_chat = chat;
1535 empathy_chat_messages_read (chat);
1537 chat_window_update_chat_tab (chat);
1541 chat_window_page_added_cb (GtkNotebook *notebook,
1544 EmpathyChatWindow *window)
1546 EmpathyChatWindowPriv *priv;
1549 priv = GET_PRIV (window);
1551 /* If we just received DND to the same window, we don't want
1552 * to do anything here like removing the tab and then readding
1553 * it, so we return here and in "page-added".
1555 if (priv->dnd_same_window) {
1556 DEBUG ("Page added (back to the same window)");
1557 priv->dnd_same_window = FALSE;
1561 DEBUG ("Page added");
1563 /* Get chat object */
1564 chat = EMPATHY_CHAT (child);
1566 /* Connect chat signals for this window */
1567 g_signal_connect (chat, "composing",
1568 G_CALLBACK (chat_window_composing_cb),
1570 g_signal_connect (chat, "new-message",
1571 G_CALLBACK (chat_window_new_message_cb),
1573 g_signal_connect (chat, "notify::tp-chat",
1574 G_CALLBACK (chat_window_update_chat_tab),
1577 /* Set flag so we know to perform some special operations on
1578 * switch page due to the new page being added.
1580 priv->page_added = TRUE;
1582 /* Get list of chats up to date */
1583 priv->chats = g_list_append (priv->chats, chat);
1585 chat_window_update_chat_tab (chat);
1589 chat_window_page_removed_cb (GtkNotebook *notebook,
1592 EmpathyChatWindow *window)
1594 EmpathyChatWindowPriv *priv;
1597 priv = GET_PRIV (window);
1599 /* If we just received DND to the same window, we don't want
1600 * to do anything here like removing the tab and then readding
1601 * it, so we return here and in "page-added".
1603 if (priv->dnd_same_window) {
1604 DEBUG ("Page removed (and will be readded to same window)");
1608 DEBUG ("Page removed");
1610 /* Get chat object */
1611 chat = EMPATHY_CHAT (child);
1613 /* Disconnect all signal handlers for this chat and this window */
1614 g_signal_handlers_disconnect_by_func (chat,
1615 G_CALLBACK (chat_window_composing_cb),
1617 g_signal_handlers_disconnect_by_func (chat,
1618 G_CALLBACK (chat_window_new_message_cb),
1620 g_signal_handlers_disconnect_by_func (chat,
1621 G_CALLBACK (chat_window_update_chat_tab),
1624 /* Keep list of chats up to date */
1625 priv->chats = g_list_remove (priv->chats, chat);
1626 empathy_chat_messages_read (chat);
1628 if (priv->chats == NULL) {
1629 g_object_unref (window);
1631 chat_window_update (window, TRUE);
1636 chat_window_focus_in_event_cb (GtkWidget *widget,
1638 EmpathyChatWindow *window)
1640 EmpathyChatWindowPriv *priv;
1642 priv = GET_PRIV (window);
1644 empathy_chat_messages_read (priv->current_chat);
1646 chat_window_set_urgency_hint (window, FALSE);
1648 /* Update the title, since we now mark all unread messages as read. */
1649 chat_window_update_chat_tab_full (priv->current_chat, FALSE);
1655 chat_window_drag_drop (GtkWidget *widget,
1656 GdkDragContext *context,
1660 EmpathyChatWindow *window)
1663 EmpathyChatWindowPriv *priv;
1665 priv = GET_PRIV (window);
1667 target = gtk_drag_dest_find_target (widget, context, priv->file_targets);
1668 if (target == GDK_NONE)
1669 target = gtk_drag_dest_find_target (widget, context, priv->contact_targets);
1671 if (target != GDK_NONE) {
1672 gtk_drag_get_data (widget, context, target, time_);
1680 chat_window_drag_motion (GtkWidget *widget,
1681 GdkDragContext *context,
1685 EmpathyChatWindow *window)
1688 EmpathyChatWindowPriv *priv;
1690 priv = GET_PRIV (window);
1692 target = gtk_drag_dest_find_target (widget, context, priv->file_targets);
1693 if (target != GDK_NONE) {
1694 /* This is a file drag. Ensure the contact is online and set the
1695 drag type to COPY. Note that it's possible that the tab will
1696 be switched by GTK+ after a timeout from drag_motion without
1697 getting another drag_motion to disable the drop. You have
1698 to hold your mouse really still.
1700 EmpathyContact *contact;
1702 priv = GET_PRIV (window);
1703 contact = empathy_chat_get_remote_contact (priv->current_chat);
1704 /* contact is NULL for multi-user chats. We don't do
1705 * file transfers to MUCs. We also don't send files
1706 * to offline contacts or contacts that don't support
1709 if ((contact == NULL) || !empathy_contact_is_online (contact)) {
1710 gdk_drag_status (context, 0, time_);
1713 if (!(empathy_contact_get_capabilities (contact)
1714 & EMPATHY_CAPABILITIES_FT)) {
1715 gdk_drag_status (context, 0, time_);
1718 gdk_drag_status (context, GDK_ACTION_COPY, time_);
1722 target = gtk_drag_dest_find_target (widget, context, priv->contact_targets);
1723 if (target != GDK_NONE) {
1724 /* This is a drag of a contact from a contact list. Set to COPY.
1725 FIXME: If this drag is to a MUC window, it invites the user.
1726 Otherwise, it opens a chat. Should we use a different drag
1727 type for invites? Should we allow ASK?
1729 gdk_drag_status (context, GDK_ACTION_COPY, time_);
1737 chat_window_drag_data_received (GtkWidget *widget,
1738 GdkDragContext *context,
1741 GtkSelectionData *selection,
1744 EmpathyChatWindow *window)
1746 if (info == DND_DRAG_TYPE_CONTACT_ID) {
1747 EmpathyChat *chat = NULL;
1748 EmpathyChatWindow *old_window;
1749 TpAccount *account = NULL;
1750 TpAccountManager *account_manager;
1753 const gchar *account_id;
1754 const gchar *contact_id;
1756 id = (const gchar*) gtk_selection_data_get_data (selection);
1758 /* FIXME: Perhaps should be sure that the account manager is
1759 * prepared before calling _ensure_account on it. */
1760 account_manager = tp_account_manager_dup ();
1762 DEBUG ("DND contact from roster with id:'%s'", id);
1764 strv = g_strsplit (id, ":", 2);
1765 if (g_strv_length (strv) == 2) {
1766 account_id = strv[0];
1767 contact_id = strv[1];
1769 tp_account_manager_ensure_account (account_manager, account_id);
1770 if (account != NULL)
1771 chat = empathy_chat_window_find_chat (account, contact_id);
1774 if (account == NULL) {
1776 gtk_drag_finish (context, FALSE, FALSE, time_);
1781 empathy_chat_with_contact_id (
1782 account, contact_id, gtk_get_current_event_time ());
1787 g_object_unref (account_manager);
1790 old_window = chat_window_find_chat (chat);
1792 if (old_window == window) {
1793 gtk_drag_finish (context, TRUE, FALSE, time_);
1797 empathy_chat_window_move_chat (old_window, window, chat);
1799 empathy_chat_window_add_chat (window, chat);
1802 /* Added to take care of any outstanding chat events */
1803 empathy_chat_window_present_chat (chat,
1804 TP_USER_ACTION_TIME_NOT_USER_ACTION);
1806 /* We should return TRUE to remove the data when doing
1807 * GDK_ACTION_MOVE, but we don't here otherwise it has
1808 * weird consequences, and we handle that internally
1809 * anyway with add_chat () and remove_chat ().
1811 gtk_drag_finish (context, TRUE, FALSE, time_);
1813 else if (info == DND_DRAG_TYPE_URI_LIST) {
1814 EmpathyChatWindowPriv *priv;
1815 EmpathyContact *contact;
1818 priv = GET_PRIV (window);
1819 contact = empathy_chat_get_remote_contact (priv->current_chat);
1821 /* contact is NULL when current_chat is a multi-user chat.
1822 * We don't do file transfers to MUCs, so just cancel the drag.
1824 if (contact == NULL) {
1825 gtk_drag_finish (context, TRUE, FALSE, time_);
1829 data = (const gchar *) gtk_selection_data_get_data (selection);
1830 empathy_send_file_from_uri_list (contact, data);
1832 gtk_drag_finish (context, TRUE, FALSE, time_);
1834 else if (info == DND_DRAG_TYPE_TAB) {
1836 EmpathyChatWindow *old_window = NULL;
1840 chat = (void *) gtk_selection_data_get_data (selection);
1841 old_window = chat_window_find_chat (*chat);
1844 EmpathyChatWindowPriv *priv;
1846 priv = GET_PRIV (window);
1847 priv->dnd_same_window = (old_window == window);
1848 DEBUG ("DND tab (within same window: %s)",
1849 priv->dnd_same_window ? "Yes" : "No");
1852 DEBUG ("DND from unknown source");
1853 gtk_drag_finish (context, FALSE, FALSE, time_);
1858 chat_window_chat_manager_chats_changed_cb (EmpathyChatManager *chat_manager,
1859 guint num_chats_in_manager,
1860 EmpathyChatWindow *window)
1862 EmpathyChatWindowPriv *priv = GET_PRIV (window);
1864 gtk_action_set_sensitive (priv->menu_tabs_undo_close_tab,
1865 num_chats_in_manager > 0);
1869 chat_window_finalize (GObject *object)
1871 EmpathyChatWindow *window;
1872 EmpathyChatWindowPriv *priv;
1874 window = EMPATHY_CHAT_WINDOW (object);
1875 priv = GET_PRIV (window);
1877 DEBUG ("Finalized: %p", object);
1879 g_object_unref (priv->ui_manager);
1880 g_object_unref (priv->chatroom_manager);
1881 g_object_unref (priv->notify_mgr);
1882 g_object_unref (priv->gsettings_chat);
1883 g_object_unref (priv->gsettings_notif);
1884 g_object_unref (priv->gsettings_ui);
1885 g_object_unref (priv->sound_mgr);
1887 if (priv->notification != NULL) {
1888 notify_notification_close (priv->notification, NULL);
1889 priv->notification = NULL;
1892 if (priv->contact_targets) {
1893 gtk_target_list_unref (priv->contact_targets);
1895 if (priv->file_targets) {
1896 gtk_target_list_unref (priv->file_targets);
1899 if (priv->chat_manager) {
1900 g_signal_handler_disconnect (priv->chat_manager,
1901 priv->chat_manager_chats_changed_id);
1902 g_object_unref (priv->chat_manager);
1903 priv->chat_manager = NULL;
1906 chat_windows = g_list_remove (chat_windows, window);
1907 gtk_widget_destroy (priv->dialog);
1909 G_OBJECT_CLASS (empathy_chat_window_parent_class)->finalize (object);
1913 empathy_chat_window_class_init (EmpathyChatWindowClass *klass)
1915 GObjectClass *object_class = G_OBJECT_CLASS (klass);
1917 object_class->finalize = chat_window_finalize;
1919 g_type_class_add_private (object_class, sizeof (EmpathyChatWindowPriv));
1923 empathy_chat_window_init (EmpathyChatWindow *window)
1926 GtkAccelGroup *accel_group;
1931 GtkWidget *chat_vbox;
1933 EmpathySmileyManager *smiley_manager;
1934 EmpathyChatWindowPriv *priv = G_TYPE_INSTANCE_GET_PRIVATE (window,
1935 EMPATHY_TYPE_CHAT_WINDOW, EmpathyChatWindowPriv);
1937 window->priv = priv;
1938 filename = empathy_file_lookup ("empathy-chat-window.ui", "src");
1939 gui = empathy_builder_get_file (filename,
1940 "chat_window", &priv->dialog,
1941 "chat_vbox", &chat_vbox,
1942 "ui_manager", &priv->ui_manager,
1943 "menu_conv_insert_smiley", &priv->menu_conv_insert_smiley,
1944 "menu_conv_favorite", &priv->menu_conv_favorite,
1945 "menu_conv_always_urgent", &priv->menu_conv_always_urgent,
1946 "menu_conv_toggle_contacts", &priv->menu_conv_toggle_contacts,
1947 "menu_edit_cut", &priv->menu_edit_cut,
1948 "menu_edit_copy", &priv->menu_edit_copy,
1949 "menu_edit_paste", &priv->menu_edit_paste,
1950 "menu_edit_find", &priv->menu_edit_find,
1951 "menu_tabs_next", &priv->menu_tabs_next,
1952 "menu_tabs_prev", &priv->menu_tabs_prev,
1953 "menu_tabs_undo_close_tab", &priv->menu_tabs_undo_close_tab,
1954 "menu_tabs_left", &priv->menu_tabs_left,
1955 "menu_tabs_right", &priv->menu_tabs_right,
1956 "menu_tabs_detach", &priv->menu_tabs_detach,
1960 empathy_builder_connect (gui, window,
1961 "menu_conv", "activate", chat_window_conv_activate_cb,
1962 "menu_conv_clear", "activate", chat_window_clear_activate_cb,
1963 "menu_conv_favorite", "toggled", chat_window_favorite_toggled_cb,
1964 "menu_conv_always_urgent", "toggled", chat_window_always_urgent_toggled_cb,
1965 "menu_conv_toggle_contacts", "toggled", chat_window_contacts_toggled_cb,
1966 "menu_conv_invite_participant", "activate", chat_window_invite_participant_activate_cb,
1967 "menu_conv_close", "activate", chat_window_close_activate_cb,
1968 "menu_edit", "activate", chat_window_edit_activate_cb,
1969 "menu_edit_cut", "activate", chat_window_cut_activate_cb,
1970 "menu_edit_copy", "activate", chat_window_copy_activate_cb,
1971 "menu_edit_paste", "activate", chat_window_paste_activate_cb,
1972 "menu_edit_find", "activate", chat_window_find_activate_cb,
1973 "menu_tabs_next", "activate", chat_window_tabs_next_activate_cb,
1974 "menu_tabs_prev", "activate", chat_window_tabs_previous_activate_cb,
1975 "menu_tabs_undo_close_tab", "activate", chat_window_tabs_undo_close_tab_activate_cb,
1976 "menu_tabs_left", "activate", chat_window_tabs_left_activate_cb,
1977 "menu_tabs_right", "activate", chat_window_tabs_right_activate_cb,
1978 "menu_tabs_detach", "activate", chat_window_detach_activate_cb,
1979 "menu_help_contents", "activate", chat_window_help_contents_activate_cb,
1980 "menu_help_about", "activate", chat_window_help_about_activate_cb,
1983 g_object_ref (priv->ui_manager);
1984 g_object_unref (gui);
1986 priv->gsettings_chat = g_settings_new (EMPATHY_PREFS_CHAT_SCHEMA);
1987 priv->gsettings_notif = g_settings_new (EMPATHY_PREFS_NOTIFICATIONS_SCHEMA);
1988 priv->gsettings_ui = g_settings_new (EMPATHY_PREFS_UI_SCHEMA);
1989 priv->chatroom_manager = empathy_chatroom_manager_dup_singleton (NULL);
1991 priv->sound_mgr = empathy_sound_manager_dup_singleton ();
1993 priv->notebook = gtk_notebook_new ();
1995 g_signal_connect (priv->notebook, "create-window",
1996 G_CALLBACK (notebook_create_window_cb), window);
1998 gtk_notebook_set_group_name (GTK_NOTEBOOK (priv->notebook),
1999 "EmpathyChatWindow");
2000 gtk_notebook_set_scrollable (GTK_NOTEBOOK (priv->notebook), TRUE);
2001 gtk_notebook_popup_enable (GTK_NOTEBOOK (priv->notebook));
2002 gtk_box_pack_start (GTK_BOX (chat_vbox), priv->notebook, TRUE, TRUE, 0);
2003 gtk_widget_show (priv->notebook);
2006 accel_group = gtk_accel_group_new ();
2007 gtk_window_add_accel_group (GTK_WINDOW (priv->dialog), accel_group);
2009 for (i = 0; i < G_N_ELEMENTS (tab_accel_keys); i++) {
2010 closure = g_cclosure_new (G_CALLBACK (chat_window_accel_cb),
2013 gtk_accel_group_connect (accel_group,
2020 g_object_unref (accel_group);
2022 /* Set up drag target lists */
2023 priv->contact_targets = gtk_target_list_new (drag_types_dest_contact,
2024 G_N_ELEMENTS (drag_types_dest_contact));
2025 priv->file_targets = gtk_target_list_new (drag_types_dest_file,
2026 G_N_ELEMENTS (drag_types_dest_file));
2028 /* Set up smiley menu */
2029 smiley_manager = empathy_smiley_manager_dup_singleton ();
2030 submenu = empathy_smiley_menu_new (smiley_manager,
2031 chat_window_insert_smiley_activate_cb,
2033 menu = gtk_ui_manager_get_widget (priv->ui_manager,
2034 "/chats_menubar/menu_conv/menu_conv_insert_smiley");
2035 gtk_menu_item_set_submenu (GTK_MENU_ITEM (menu), submenu);
2036 g_object_unref (smiley_manager);
2038 /* Set up signals we can't do with ui file since we may need to
2039 * block/unblock them at some later stage.
2042 g_signal_connect (priv->dialog,
2044 G_CALLBACK (chat_window_delete_event_cb),
2046 g_signal_connect (priv->dialog,
2048 G_CALLBACK (chat_window_focus_in_event_cb),
2050 g_signal_connect_after (priv->notebook,
2052 G_CALLBACK (chat_window_page_switched_cb),
2054 g_signal_connect (priv->notebook,
2056 G_CALLBACK (chat_window_page_added_cb),
2058 g_signal_connect (priv->notebook,
2060 G_CALLBACK (chat_window_page_removed_cb),
2063 /* Set up drag and drop */
2064 gtk_drag_dest_set (GTK_WIDGET (priv->notebook),
2065 GTK_DEST_DEFAULT_HIGHLIGHT,
2067 G_N_ELEMENTS (drag_types_dest),
2068 GDK_ACTION_MOVE | GDK_ACTION_COPY);
2070 /* connect_after to allow GtkNotebook's built-in tab switching */
2071 g_signal_connect_after (priv->notebook,
2073 G_CALLBACK (chat_window_drag_motion),
2075 g_signal_connect (priv->notebook,
2076 "drag-data-received",
2077 G_CALLBACK (chat_window_drag_data_received),
2079 g_signal_connect (priv->notebook,
2081 G_CALLBACK (chat_window_drag_drop),
2084 chat_windows = g_list_prepend (chat_windows, window);
2086 /* Set up private details */
2088 priv->current_chat = NULL;
2089 priv->notification = NULL;
2091 priv->notify_mgr = empathy_notify_manager_dup_singleton ();
2093 priv->chat_manager = empathy_chat_manager_dup_singleton ();
2094 priv->chat_manager_chats_changed_id =
2095 g_signal_connect (priv->chat_manager, "closed-chats-changed",
2096 G_CALLBACK (chat_window_chat_manager_chats_changed_cb),
2099 chat_window_chat_manager_chats_changed_cb (priv->chat_manager,
2100 empathy_chat_manager_get_num_closed_chats (priv->chat_manager),
2105 empathy_chat_window_get_dialog (EmpathyChatWindow *window)
2107 EmpathyChatWindowPriv *priv;
2109 g_return_val_if_fail (window != NULL, NULL);
2111 priv = GET_PRIV (window);
2113 return priv->dialog;
2116 /* Returns the window to open a new tab in if there is a suitable window,
2117 * otherwise, returns NULL indicating that a new window should be added.
2119 static EmpathyChatWindow *
2120 empathy_chat_window_get_default (gboolean room)
2122 GSettings *gsettings = g_settings_new (EMPATHY_PREFS_UI_SCHEMA);
2124 gboolean separate_windows = TRUE;
2126 separate_windows = g_settings_get_boolean (gsettings,
2127 EMPATHY_PREFS_UI_SEPARATE_CHAT_WINDOWS);
2129 g_object_unref (gsettings);
2131 if (separate_windows) {
2132 /* Always create a new window */
2136 for (l = chat_windows; l; l = l->next) {
2137 EmpathyChatWindowPriv *priv;
2138 EmpathyChatWindow *chat_window;
2140 guint nb_rooms, nb_private;
2142 chat_window = l->data;
2143 priv = GET_PRIV (chat_window);
2145 dialog = empathy_chat_window_get_dialog (chat_window);
2147 empathy_chat_window_get_nb_chats (chat_window, &nb_rooms, &nb_private);
2149 /* Skip the window if there aren't any rooms in it */
2150 if (room && nb_rooms == 0)
2153 /* Skip the window if there aren't any 1-1 chats in it */
2154 if (!room && nb_private == 0)
2157 /* Found a window on this desktop, make it visible if necessary */
2158 if (!empathy_window_get_is_visible (GTK_WINDOW (dialog)))
2159 empathy_window_present (GTK_WINDOW (dialog));
2167 empathy_chat_window_add_chat (EmpathyChatWindow *window,
2170 EmpathyChatWindowPriv *priv;
2172 GtkWidget *popup_label;
2174 GValue value = { 0, };
2176 g_return_if_fail (window != NULL);
2177 g_return_if_fail (EMPATHY_IS_CHAT (chat));
2179 priv = GET_PRIV (window);
2181 /* Reference the chat object */
2182 g_object_ref (chat);
2184 /* If this window has just been created, position it */
2185 if (priv->chats == NULL) {
2186 const gchar *name = "chat-window";
2187 gboolean separate_windows;
2189 separate_windows = g_settings_get_boolean (priv->gsettings_ui,
2190 EMPATHY_PREFS_UI_SEPARATE_CHAT_WINDOWS);
2192 if (empathy_chat_is_room (chat))
2193 name = "room-window";
2195 if (separate_windows) {
2198 /* Save current position of the window */
2199 gtk_window_get_position (GTK_WINDOW (priv->dialog), &x, &y);
2201 /* First bind to the 'generic' name. So new window for which we didn't
2202 * save a geometry yet will have the geometry of the last saved
2203 * window (bgo #601191). */
2204 empathy_geometry_bind (GTK_WINDOW (priv->dialog), name);
2206 /* Restore previous position of the window so the newly created window
2207 * won't be in the same position as the latest saved window and so
2208 * completely hide it. */
2209 gtk_window_move (GTK_WINDOW (priv->dialog), x, y);
2211 /* Then bind it to the name of the contact/room so we'll save the
2212 * geometry specific to this window */
2213 name = empathy_chat_get_id (chat);
2216 empathy_geometry_bind (GTK_WINDOW (priv->dialog), name);
2219 child = GTK_WIDGET (chat);
2220 label = chat_window_create_label (window, chat, TRUE);
2221 popup_label = chat_window_create_label (window, chat, FALSE);
2222 gtk_widget_show (child);
2224 g_signal_connect (chat, "notify::name",
2225 G_CALLBACK (chat_window_chat_notify_cb),
2227 g_signal_connect (chat, "notify::subject",
2228 G_CALLBACK (chat_window_chat_notify_cb),
2230 g_signal_connect (chat, "notify::remote-contact",
2231 G_CALLBACK (chat_window_chat_notify_cb),
2233 g_signal_connect (chat, "notify::sms-channel",
2234 G_CALLBACK (chat_window_chat_notify_cb),
2236 chat_window_chat_notify_cb (chat);
2238 gtk_notebook_append_page_menu (GTK_NOTEBOOK (priv->notebook), child, label, popup_label);
2239 gtk_notebook_set_tab_reorderable (GTK_NOTEBOOK (priv->notebook), child, TRUE);
2240 gtk_notebook_set_tab_detachable (GTK_NOTEBOOK (priv->notebook), child, TRUE);
2241 g_value_init (&value, G_TYPE_BOOLEAN);
2242 g_value_set_boolean (&value, TRUE);
2243 gtk_container_child_set_property (GTK_CONTAINER (priv->notebook),
2244 child, "tab-expand" , &value);
2245 gtk_container_child_set_property (GTK_CONTAINER (priv->notebook),
2246 child, "tab-fill" , &value);
2247 g_value_unset (&value);
2249 DEBUG ("Chat added (%d references)", G_OBJECT (chat)->ref_count);
2253 empathy_chat_window_remove_chat (EmpathyChatWindow *window,
2256 EmpathyChatWindowPriv *priv;
2258 EmpathyContact *remote_contact;
2259 EmpathyChatManager *chat_manager;
2261 g_return_if_fail (window != NULL);
2262 g_return_if_fail (EMPATHY_IS_CHAT (chat));
2264 priv = GET_PRIV (window);
2266 g_signal_handlers_disconnect_by_func (chat,
2267 chat_window_chat_notify_cb,
2269 remote_contact = g_object_get_data (G_OBJECT (chat),
2270 "chat-window-remote-contact");
2271 if (remote_contact) {
2272 g_signal_handlers_disconnect_by_func (remote_contact,
2273 chat_window_update_chat_tab,
2277 chat_manager = empathy_chat_manager_dup_singleton ();
2278 empathy_chat_manager_closed_chat (chat_manager, chat);
2279 g_object_unref (chat_manager);
2281 position = gtk_notebook_page_num (GTK_NOTEBOOK (priv->notebook),
2283 gtk_notebook_remove_page (GTK_NOTEBOOK (priv->notebook), position);
2285 DEBUG ("Chat removed (%d references)", G_OBJECT (chat)->ref_count - 1);
2287 g_object_unref (chat);
2291 empathy_chat_window_move_chat (EmpathyChatWindow *old_window,
2292 EmpathyChatWindow *new_window,
2297 g_return_if_fail (EMPATHY_IS_CHAT_WINDOW (old_window));
2298 g_return_if_fail (EMPATHY_IS_CHAT_WINDOW (new_window));
2299 g_return_if_fail (EMPATHY_IS_CHAT (chat));
2301 widget = GTK_WIDGET (chat);
2303 DEBUG ("Chat moving with widget:%p (%d references)", widget,
2304 G_OBJECT (widget)->ref_count);
2306 /* We reference here to make sure we don't loose the widget
2307 * and the EmpathyChat object during the move.
2309 g_object_ref (chat);
2310 g_object_ref (widget);
2312 empathy_chat_window_remove_chat (old_window, chat);
2313 empathy_chat_window_add_chat (new_window, chat);
2315 g_object_unref (widget);
2316 g_object_unref (chat);
2320 empathy_chat_window_switch_to_chat (EmpathyChatWindow *window,
2323 EmpathyChatWindowPriv *priv;
2326 g_return_if_fail (window != NULL);
2327 g_return_if_fail (EMPATHY_IS_CHAT (chat));
2329 priv = GET_PRIV (window);
2331 page_num = gtk_notebook_page_num (GTK_NOTEBOOK (priv->notebook),
2333 gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->notebook),
2338 empathy_chat_window_find_chat (TpAccount *account,
2343 g_return_val_if_fail (!EMP_STR_EMPTY (id), NULL);
2345 for (l = chat_windows; l; l = l->next) {
2346 EmpathyChatWindowPriv *priv;
2347 EmpathyChatWindow *window;
2351 priv = GET_PRIV (window);
2353 for (ll = priv->chats; ll; ll = ll->next) {
2358 if (account == empathy_chat_get_account (chat) &&
2359 !tp_strdiff (id, empathy_chat_get_id (chat))) {
2369 empathy_chat_window_present_chat (EmpathyChat *chat,
2372 EmpathyChatWindow *window;
2373 EmpathyChatWindowPriv *priv;
2374 guint32 x_timestamp;
2376 g_return_if_fail (EMPATHY_IS_CHAT (chat));
2378 window = chat_window_find_chat (chat);
2380 /* If the chat has no window, create one */
2381 if (window == NULL) {
2382 window = empathy_chat_window_get_default (empathy_chat_is_room (chat));
2384 window = empathy_chat_window_new ();
2387 empathy_chat_window_add_chat (window, chat);
2390 /* Don't force the window to show itself when it wasn't
2391 * an action by the user
2393 if (!tp_user_action_time_should_present (timestamp, &x_timestamp))
2396 priv = GET_PRIV (window);
2398 if (x_timestamp != GDK_CURRENT_TIME) {
2399 /* Don't present or switch tab if the action was earlier than the
2400 * last actions X time, accounting for overflow and the first ever
2403 if (priv->x_user_action_time != 0
2404 && X_EARLIER_OR_EQL (x_timestamp, priv->x_user_action_time))
2407 priv->x_user_action_time = x_timestamp;
2410 empathy_chat_window_switch_to_chat (window, chat);
2411 empathy_window_present_with_time (GTK_WINDOW (priv->dialog),
2414 gtk_widget_grab_focus (chat->input_text_view);
2418 empathy_chat_window_get_nb_chats (EmpathyChatWindow *self,
2422 EmpathyChatWindowPriv *priv = GET_PRIV (self);
2424 guint _nb_rooms = 0, _nb_private = 0;
2426 for (l = priv->chats; l != NULL; l = g_list_next (l)) {
2427 if (empathy_chat_is_room (EMPATHY_CHAT (l->data)))
2433 if (nb_rooms != NULL)
2434 *nb_rooms = _nb_rooms;
2435 if (nb_private != NULL)
2436 *nb_private = _nb_private;