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 (remote_contact) {
679 icon_name = empathy_icon_name_for_contact (remote_contact);
681 icon_name = EMPATHY_IMAGE_GROUP_MESSAGE;
684 tab_image = g_object_get_data (G_OBJECT (chat), "chat-window-tab-image");
685 menu_image = g_object_get_data (G_OBJECT (chat), "chat-window-menu-image");
686 if (icon_name != NULL) {
687 gtk_image_set_from_icon_name (GTK_IMAGE (tab_image), icon_name, GTK_ICON_SIZE_MENU);
688 gtk_widget_show (tab_image);
689 gtk_image_set_from_icon_name (GTK_IMAGE (menu_image), icon_name, GTK_ICON_SIZE_MENU);
690 gtk_widget_show (menu_image);
692 gtk_widget_hide (tab_image);
693 gtk_widget_hide (menu_image);
696 /* Update tab tooltip */
697 tooltip = g_string_new (NULL);
699 if (remote_contact) {
700 id = empathy_contact_get_id (remote_contact);
701 status = empathy_contact_get_presence_message (remote_contact);
706 append_markup_printf (tooltip,
707 "<b>%s</b><small> (%s)</small>",
709 tp_account_get_display_name (account));
711 if (!EMP_STR_EMPTY (status)) {
712 append_markup_printf (tooltip, "\n<i>%s</i>", status);
716 append_markup_printf (tooltip, "\n<b>%s</b> %s",
717 _("Topic:"), subject);
720 if (remote_contact && empathy_chat_is_composing (chat)) {
721 append_markup_printf (tooltip, "\n%s", _("Typing a message."));
724 markup = g_string_free (tooltip, FALSE);
725 widget = g_object_get_data (G_OBJECT (chat), "chat-window-tab-tooltip-widget");
726 gtk_widget_set_tooltip_markup (widget, markup);
727 widget = g_object_get_data (G_OBJECT (chat), "chat-window-menu-tooltip-widget");
728 gtk_widget_set_tooltip_markup (widget, markup);
731 /* Update tab and menu label */
732 widget = g_object_get_data (G_OBJECT (chat), "chat-window-tab-label");
733 gtk_label_set_text (GTK_LABEL (widget), name);
734 widget = g_object_get_data (G_OBJECT (chat), "chat-window-menu-label");
735 gtk_label_set_text (GTK_LABEL (widget), name);
737 /* Update the window if it's the current chat */
738 if (priv->current_chat == chat) {
739 chat_window_update (window, update_contact_menu);
744 chat_window_update_chat_tab (EmpathyChat *chat)
746 chat_window_update_chat_tab_full (chat, TRUE);
750 chat_window_chat_notify_cb (EmpathyChat *chat)
752 EmpathyContact *old_remote_contact;
753 EmpathyContact *remote_contact = NULL;
755 old_remote_contact = g_object_get_data (G_OBJECT (chat), "chat-window-remote-contact");
756 remote_contact = empathy_chat_get_remote_contact (chat);
758 if (old_remote_contact != remote_contact) {
759 /* The remote-contact associated with the chat changed, we need
760 * to keep track of any change of that contact and update the
761 * window each time. */
762 if (remote_contact) {
763 g_signal_connect_swapped (remote_contact, "notify",
764 G_CALLBACK (chat_window_update_chat_tab),
767 if (old_remote_contact) {
768 g_signal_handlers_disconnect_by_func (old_remote_contact,
769 chat_window_update_chat_tab,
773 g_object_set_data_full (G_OBJECT (chat), "chat-window-remote-contact",
774 g_object_ref (remote_contact), (GDestroyNotify) g_object_unref);
777 chat_window_update_chat_tab (chat);
781 chat_window_insert_smiley_activate_cb (EmpathySmileyManager *manager,
782 EmpathySmiley *smiley,
785 EmpathyChatWindowPriv *priv = GET_PRIV (window);
787 GtkTextBuffer *buffer;
790 chat = priv->current_chat;
792 buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (chat->input_text_view));
793 gtk_text_buffer_get_end_iter (buffer, &iter);
794 gtk_text_buffer_insert (buffer, &iter, smiley->str, -1);
798 chat_window_conv_activate_cb (GtkAction *action,
799 EmpathyChatWindow *window)
801 EmpathyChatWindowPriv *priv = GET_PRIV (window);
804 EmpathyContact *remote_contact = NULL;
806 /* Favorite room menu */
807 is_room = empathy_chat_is_room (priv->current_chat);
811 gboolean found = FALSE;
812 EmpathyChatroom *chatroom;
814 room = empathy_chat_get_id (priv->current_chat);
815 account = empathy_chat_get_account (priv->current_chat);
816 chatroom = empathy_chatroom_manager_find (priv->chatroom_manager,
818 if (chatroom != NULL)
819 found = empathy_chatroom_is_favorite (chatroom);
821 DEBUG ("This room %s favorite", found ? "is" : "is not");
822 gtk_toggle_action_set_active (
823 GTK_TOGGLE_ACTION (priv->menu_conv_favorite), found);
825 if (chatroom != NULL)
826 found = empathy_chatroom_is_always_urgent (chatroom);
828 gtk_toggle_action_set_active (
829 GTK_TOGGLE_ACTION (priv->menu_conv_always_urgent),
832 gtk_action_set_visible (priv->menu_conv_favorite, is_room);
833 gtk_action_set_visible (priv->menu_conv_always_urgent, is_room);
835 /* Show contacts menu */
836 g_object_get (priv->current_chat,
837 "remote-contact", &remote_contact,
838 "show-contacts", &active,
840 if (remote_contact == NULL) {
841 gtk_toggle_action_set_active (
842 GTK_TOGGLE_ACTION (priv->menu_conv_toggle_contacts),
845 gtk_action_set_visible (priv->menu_conv_toggle_contacts,
846 (remote_contact == NULL));
847 if (remote_contact != NULL) {
848 g_object_unref (remote_contact);
853 chat_window_clear_activate_cb (GtkAction *action,
854 EmpathyChatWindow *window)
856 EmpathyChatWindowPriv *priv = GET_PRIV (window);
858 empathy_chat_clear (priv->current_chat);
862 chat_window_favorite_toggled_cb (GtkToggleAction *toggle_action,
863 EmpathyChatWindow *window)
865 EmpathyChatWindowPriv *priv = GET_PRIV (window);
869 EmpathyChatroom *chatroom;
871 active = gtk_toggle_action_get_active (toggle_action);
872 account = empathy_chat_get_account (priv->current_chat);
873 room = empathy_chat_get_id (priv->current_chat);
875 chatroom = empathy_chatroom_manager_ensure_chatroom (
876 priv->chatroom_manager,
879 empathy_chat_get_name (priv->current_chat));
881 empathy_chatroom_set_favorite (chatroom, active);
882 g_object_unref (chatroom);
886 chat_window_always_urgent_toggled_cb (GtkToggleAction *toggle_action,
887 EmpathyChatWindow *window)
889 EmpathyChatWindowPriv *priv = GET_PRIV (window);
893 EmpathyChatroom *chatroom;
895 active = gtk_toggle_action_get_active (toggle_action);
896 account = empathy_chat_get_account (priv->current_chat);
897 room = empathy_chat_get_id (priv->current_chat);
899 chatroom = empathy_chatroom_manager_ensure_chatroom (
900 priv->chatroom_manager,
903 empathy_chat_get_name (priv->current_chat));
905 empathy_chatroom_set_always_urgent (chatroom, active);
906 g_object_unref (chatroom);
910 chat_window_contacts_toggled_cb (GtkToggleAction *toggle_action,
911 EmpathyChatWindow *window)
913 EmpathyChatWindowPriv *priv = GET_PRIV (window);
916 active = gtk_toggle_action_get_active (toggle_action);
918 empathy_chat_set_show_contacts (priv->current_chat, active);
922 got_contact_cb (TpConnection *connection,
923 EmpathyContact *contact,
928 EmpathyTpChat *tp_chat = EMPATHY_TP_CHAT (user_data);
931 DEBUG ("Failed: %s", error->message);
934 empathy_contact_list_add (EMPATHY_CONTACT_LIST (tp_chat),
935 contact, _("Inviting you to this room"));
940 chat_window_invite_participant_activate_cb (GtkAction *action,
941 EmpathyChatWindow *window)
943 EmpathyChatWindowPriv *priv;
945 EmpathyTpChat *tp_chat;
950 priv = GET_PRIV (window);
952 g_return_if_fail (priv->current_chat != NULL);
954 tp_chat = empathy_chat_get_tp_chat (priv->current_chat);
955 channel = empathy_tp_chat_get_channel (tp_chat);
956 account = empathy_chat_get_account (priv->current_chat);
958 dialog = empathy_invite_participant_dialog_new (
959 GTK_WINDOW (priv->dialog), account);
960 gtk_widget_show (dialog);
962 response = gtk_dialog_run (GTK_DIALOG (dialog));
964 if (response == GTK_RESPONSE_ACCEPT) {
965 TpConnection *connection;
968 id = empathy_contact_selector_dialog_get_selected (
969 EMPATHY_CONTACT_SELECTOR_DIALOG (dialog), NULL, NULL);
970 if (EMP_STR_EMPTY (id)) goto out;
972 connection = tp_channel_borrow_connection (channel);
973 empathy_tp_contact_factory_get_from_id (connection, id,
974 got_contact_cb, tp_chat, NULL, NULL);
978 gtk_widget_destroy (dialog);
982 chat_window_close_activate_cb (GtkAction *action,
983 EmpathyChatWindow *window)
985 EmpathyChatWindowPriv *priv;
987 priv = GET_PRIV (window);
989 g_return_if_fail (priv->current_chat != NULL);
991 empathy_chat_window_remove_chat (window, priv->current_chat);
995 chat_window_edit_activate_cb (GtkAction *action,
996 EmpathyChatWindow *window)
998 EmpathyChatWindowPriv *priv;
999 GtkClipboard *clipboard;
1000 GtkTextBuffer *buffer;
1001 gboolean text_available;
1003 priv = GET_PRIV (window);
1005 g_return_if_fail (priv->current_chat != NULL);
1007 if (!empathy_chat_get_tp_chat (priv->current_chat)) {
1008 gtk_action_set_sensitive (priv->menu_edit_copy, FALSE);
1009 gtk_action_set_sensitive (priv->menu_edit_cut, FALSE);
1010 gtk_action_set_sensitive (priv->menu_edit_paste, FALSE);
1014 buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (priv->current_chat->input_text_view));
1015 if (gtk_text_buffer_get_has_selection (buffer)) {
1016 gtk_action_set_sensitive (priv->menu_edit_copy, TRUE);
1017 gtk_action_set_sensitive (priv->menu_edit_cut, TRUE);
1021 selection = empathy_chat_view_get_has_selection (priv->current_chat->view);
1023 gtk_action_set_sensitive (priv->menu_edit_cut, FALSE);
1024 gtk_action_set_sensitive (priv->menu_edit_copy, selection);
1027 clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
1028 text_available = gtk_clipboard_wait_is_text_available (clipboard);
1029 gtk_action_set_sensitive (priv->menu_edit_paste, text_available);
1033 chat_window_cut_activate_cb (GtkAction *action,
1034 EmpathyChatWindow *window)
1036 EmpathyChatWindowPriv *priv;
1038 g_return_if_fail (EMPATHY_IS_CHAT_WINDOW (window));
1040 priv = GET_PRIV (window);
1042 empathy_chat_cut (priv->current_chat);
1046 chat_window_copy_activate_cb (GtkAction *action,
1047 EmpathyChatWindow *window)
1049 EmpathyChatWindowPriv *priv;
1051 g_return_if_fail (EMPATHY_IS_CHAT_WINDOW (window));
1053 priv = GET_PRIV (window);
1055 empathy_chat_copy (priv->current_chat);
1059 chat_window_paste_activate_cb (GtkAction *action,
1060 EmpathyChatWindow *window)
1062 EmpathyChatWindowPriv *priv;
1064 g_return_if_fail (EMPATHY_IS_CHAT_WINDOW (window));
1066 priv = GET_PRIV (window);
1068 empathy_chat_paste (priv->current_chat);
1072 chat_window_find_activate_cb (GtkAction *action,
1073 EmpathyChatWindow *window)
1075 EmpathyChatWindowPriv *priv;
1077 g_return_if_fail (EMPATHY_IS_CHAT_WINDOW (window));
1079 priv = GET_PRIV (window);
1081 empathy_chat_find (priv->current_chat);
1085 chat_window_tabs_next_activate_cb (GtkAction *action,
1086 EmpathyChatWindow *window)
1088 EmpathyChatWindowPriv *priv;
1090 gint index_, numPages;
1091 gboolean wrap_around;
1093 priv = GET_PRIV (window);
1095 g_object_get (gtk_settings_get_default (), "gtk-keynav-wrap-around",
1096 &wrap_around, NULL);
1098 chat = priv->current_chat;
1099 index_ = gtk_notebook_get_current_page (GTK_NOTEBOOK (priv->notebook));
1100 numPages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (priv->notebook));
1102 if (index_ == (numPages - 1) && wrap_around) {
1103 gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->notebook), 0);
1107 gtk_notebook_next_page (GTK_NOTEBOOK (priv->notebook));
1111 chat_window_tabs_previous_activate_cb (GtkAction *action,
1112 EmpathyChatWindow *window)
1114 EmpathyChatWindowPriv *priv;
1116 gint index_, numPages;
1117 gboolean wrap_around;
1119 priv = GET_PRIV (window);
1121 g_object_get (gtk_settings_get_default (), "gtk-keynav-wrap-around",
1122 &wrap_around, NULL);
1124 chat = priv->current_chat;
1125 index_ = gtk_notebook_get_current_page (GTK_NOTEBOOK (priv->notebook));
1126 numPages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (priv->notebook));
1128 if (index_ <= 0 && wrap_around) {
1129 gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->notebook), numPages - 1);
1133 gtk_notebook_prev_page (GTK_NOTEBOOK (priv->notebook));
1137 chat_window_tabs_undo_close_tab_activate_cb (GtkAction *action,
1138 EmpathyChatWindow *window)
1140 EmpathyChatWindowPriv *priv = GET_PRIV (window);
1141 empathy_chat_manager_undo_closed_chat (priv->chat_manager);
1145 chat_window_tabs_left_activate_cb (GtkAction *action,
1146 EmpathyChatWindow *window)
1148 EmpathyChatWindowPriv *priv;
1150 gint index_, num_pages;
1152 priv = GET_PRIV (window);
1154 chat = priv->current_chat;
1155 index_ = gtk_notebook_get_current_page (GTK_NOTEBOOK (priv->notebook));
1160 gtk_notebook_reorder_child (GTK_NOTEBOOK (priv->notebook),
1164 num_pages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (priv->notebook));
1165 chat_window_menu_context_update (priv, num_pages);
1169 chat_window_tabs_right_activate_cb (GtkAction *action,
1170 EmpathyChatWindow *window)
1172 EmpathyChatWindowPriv *priv;
1174 gint index_, num_pages;
1176 priv = GET_PRIV (window);
1178 chat = priv->current_chat;
1179 index_ = gtk_notebook_get_current_page (GTK_NOTEBOOK (priv->notebook));
1181 gtk_notebook_reorder_child (GTK_NOTEBOOK (priv->notebook),
1185 num_pages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (priv->notebook));
1186 chat_window_menu_context_update (priv, num_pages);
1189 static EmpathyChatWindow *
1190 empathy_chat_window_new (void)
1192 return EMPATHY_CHAT_WINDOW (g_object_new (EMPATHY_TYPE_CHAT_WINDOW, NULL));
1196 chat_window_detach_activate_cb (GtkAction *action,
1197 EmpathyChatWindow *window)
1199 EmpathyChatWindowPriv *priv;
1200 EmpathyChatWindow *new_window;
1203 priv = GET_PRIV (window);
1205 chat = priv->current_chat;
1206 new_window = empathy_chat_window_new ();
1208 empathy_chat_window_move_chat (window, new_window, chat);
1210 priv = GET_PRIV (new_window);
1211 gtk_widget_show (priv->dialog);
1215 chat_window_help_contents_activate_cb (GtkAction *action,
1216 EmpathyChatWindow *window)
1218 EmpathyChatWindowPriv *priv = GET_PRIV (window);
1220 empathy_url_show (priv->dialog, "ghelp:empathy");
1224 chat_window_help_about_activate_cb (GtkAction *action,
1225 EmpathyChatWindow *window)
1227 EmpathyChatWindowPriv *priv = GET_PRIV (window);
1229 empathy_about_dialog_new (GTK_WINDOW (priv->dialog));
1233 chat_window_delete_event_cb (GtkWidget *dialog,
1235 EmpathyChatWindow *window)
1237 EmpathyChatWindowPriv *priv = GET_PRIV (window);
1239 DEBUG ("Delete event received");
1241 g_object_ref (window);
1242 while (priv->chats) {
1243 empathy_chat_window_remove_chat (window, priv->chats->data);
1245 g_object_unref (window);
1251 chat_window_composing_cb (EmpathyChat *chat,
1252 gboolean is_composing,
1253 EmpathyChatWindow *window)
1255 chat_window_update_chat_tab (chat);
1259 chat_window_set_urgency_hint (EmpathyChatWindow *window,
1262 EmpathyChatWindowPriv *priv;
1264 priv = GET_PRIV (window);
1266 gtk_window_set_urgency_hint (GTK_WINDOW (priv->dialog), urgent);
1270 chat_window_notification_closed_cb (NotifyNotification *notify,
1271 EmpathyChatWindow *self)
1273 EmpathyChatWindowPriv *priv = GET_PRIV (self);
1275 g_object_unref (notify);
1276 if (priv->notification == notify) {
1277 priv->notification = NULL;
1282 chat_window_show_or_update_notification (EmpathyChatWindow *window,
1283 EmpathyMessage *message,
1286 EmpathyContact *sender;
1287 const gchar *header;
1291 EmpathyChatWindowPriv *priv = GET_PRIV (window);
1292 gboolean res, has_x_canonical_append;
1293 NotifyNotification *notification = priv->notification;
1295 if (!empathy_notify_manager_notification_is_enabled (priv->notify_mgr)) {
1298 res = g_settings_get_boolean (priv->gsettings_notif,
1299 EMPATHY_PREFS_NOTIFICATIONS_FOCUS);
1306 sender = empathy_message_get_sender (message);
1307 header = empathy_contact_get_alias (sender);
1308 body = empathy_message_get_body (message);
1309 escaped = g_markup_escape_text (body, -1);
1310 has_x_canonical_append = empathy_notify_manager_has_capability (
1311 priv->notify_mgr, EMPATHY_NOTIFY_MANAGER_CAP_X_CANONICAL_APPEND);
1313 if (notification != NULL && !has_x_canonical_append) {
1314 /* if the notification server supports x-canonical-append, it is
1315 better to not use notify_notification_update to avoid
1316 overwriting the current notification message */
1317 notify_notification_update (notification,
1318 header, escaped, NULL);
1320 /* if the notification server supports x-canonical-append,
1321 the hint will be added, so that the message from the
1322 just created notification will be automatically appended
1323 to an existing notification with the same title.
1324 In this way the previous message will not be lost: the new
1325 message will appear below it, in the same notification */
1326 notification = notify_notification_new (header, escaped, NULL);
1328 if (priv->notification == NULL) {
1329 priv->notification = notification;
1332 notify_notification_set_timeout (notification, NOTIFY_EXPIRES_DEFAULT);
1334 tp_g_signal_connect_object (notification, "closed",
1335 G_CALLBACK (chat_window_notification_closed_cb), window, 0);
1337 if (has_x_canonical_append) {
1338 notify_notification_set_hint_string (notification,
1339 EMPATHY_NOTIFY_MANAGER_CAP_X_CANONICAL_APPEND, "");
1343 pixbuf = empathy_notify_manager_get_pixbuf_for_notification (priv->notify_mgr,
1344 sender, EMPATHY_IMAGE_NEW_MESSAGE);
1346 if (pixbuf != NULL) {
1347 notify_notification_set_icon_from_pixbuf (notification, pixbuf);
1348 g_object_unref (pixbuf);
1351 notify_notification_show (notification, NULL);
1357 chat_window_set_highlight_room_labels (EmpathyChat *chat)
1362 if (!empathy_chat_is_room (chat))
1365 markup = g_markup_printf_escaped (
1366 "<span color=\"red\" weight=\"bold\">%s</span>",
1367 empathy_chat_get_name (chat));
1369 widget = g_object_get_data (G_OBJECT (chat), "chat-window-tab-label");
1370 gtk_label_set_markup (GTK_LABEL (widget), markup);
1372 widget = g_object_get_data (G_OBJECT (chat), "chat-window-menu-label");
1373 gtk_label_set_markup (GTK_LABEL (widget), markup);
1379 empathy_chat_window_has_focus (EmpathyChatWindow *window)
1381 EmpathyChatWindowPriv *priv;
1384 g_return_val_if_fail (EMPATHY_IS_CHAT_WINDOW (window), FALSE);
1386 priv = GET_PRIV (window);
1388 g_object_get (priv->dialog, "has-toplevel-focus", &has_focus, NULL);
1394 chat_window_new_message_cb (EmpathyChat *chat,
1395 EmpathyMessage *message,
1397 EmpathyChatWindow *window)
1399 EmpathyChatWindowPriv *priv;
1401 gboolean needs_urgency;
1402 EmpathyContact *sender;
1404 priv = GET_PRIV (window);
1406 has_focus = empathy_chat_window_has_focus (window);
1408 /* - if we're the sender, we play the sound if it's specified in the
1409 * preferences and we're not away.
1410 * - if we receive a message, we play the sound if it's specified in the
1411 * preferences and the window does not have focus on the chat receiving
1415 sender = empathy_message_get_sender (message);
1417 if (empathy_contact_is_user (sender)) {
1418 empathy_sound_manager_play (priv->sound_mgr, GTK_WIDGET (priv->dialog),
1419 EMPATHY_SOUND_MESSAGE_OUTGOING);
1422 if (has_focus && priv->current_chat == chat) {
1423 /* window and tab are focused so consider the message to be read */
1425 /* FIXME: see Bug#610994 and coments about it in EmpathyChatPriv */
1426 empathy_chat_messages_read (chat);
1430 /* Update the chat tab if this is the first unread message */
1431 if (empathy_chat_get_nb_unread_messages (chat) == 1) {
1432 chat_window_update_chat_tab (chat);
1435 /* If empathy_chat_is_room () returns TRUE, that means it's a named MUC.
1436 * If empathy_chat_get_remote_contact () returns NULL, that means it's
1437 * an unamed MUC (msn-like).
1438 * In case of a MUC, we set urgency if either:
1439 * a) the chatroom's always_urgent property is TRUE
1440 * b) the message contains our alias
1442 if (empathy_chat_is_room (chat) ||
1443 empathy_chat_get_remote_contact (chat) == NULL) {
1446 EmpathyChatroom *chatroom;
1448 account = empathy_chat_get_account (chat);
1449 room = empathy_chat_get_id (chat);
1451 chatroom = empathy_chatroom_manager_find (priv->chatroom_manager,
1454 if (empathy_chatroom_is_always_urgent (chatroom)) {
1455 needs_urgency = TRUE;
1457 needs_urgency = empathy_message_should_highlight (message);
1460 needs_urgency = TRUE;
1463 if (needs_urgency) {
1464 chat_window_set_highlight_room_labels (chat);
1467 chat_window_set_urgency_hint (window, TRUE);
1470 /* Pending messages have already been displayed and notified in the
1471 * approver, so we don't display a notification and play a sound for those */
1473 empathy_sound_manager_play (priv->sound_mgr, GTK_WIDGET (priv->dialog),
1474 EMPATHY_SOUND_MESSAGE_INCOMING);
1476 chat_window_show_or_update_notification (window, message, chat);
1480 /* update the number of unread messages and the window icon */
1481 chat_window_title_update (priv);
1482 chat_window_icon_update (priv, TRUE);
1485 static GtkNotebook *
1486 notebook_create_window_cb (GtkNotebook *source,
1492 EmpathyChatWindowPriv *priv;
1493 EmpathyChatWindow *window, *new_window;
1496 chat = EMPATHY_CHAT (page);
1497 window = chat_window_find_chat (chat);
1499 new_window = empathy_chat_window_new ();
1500 priv = GET_PRIV (new_window);
1502 DEBUG ("Detach hook called");
1504 empathy_chat_window_move_chat (window, new_window, chat);
1506 gtk_widget_show (priv->dialog);
1507 gtk_window_move (GTK_WINDOW (priv->dialog), x, y);
1513 chat_window_page_switched_cb (GtkNotebook *notebook,
1514 gpointer ignore, /* see note below */
1516 EmpathyChatWindow *window)
1518 EmpathyChatWindowPriv *priv;
1522 DEBUG ("Page switched");
1524 priv = GET_PRIV (window);
1526 /* N.B. in GTK+ 3 child is passed as the first argument to the signal,
1527 * but we can't use that while trying to retain GTK+ 2.x compatibility.
1529 child = gtk_notebook_get_nth_page (notebook, page_num);
1530 chat = EMPATHY_CHAT (child);
1532 if (priv->page_added) {
1533 priv->page_added = FALSE;
1534 empathy_chat_scroll_down (chat);
1536 else if (priv->current_chat == chat) {
1540 priv->current_chat = chat;
1541 empathy_chat_messages_read (chat);
1543 chat_window_update_chat_tab (chat);
1547 chat_window_page_added_cb (GtkNotebook *notebook,
1550 EmpathyChatWindow *window)
1552 EmpathyChatWindowPriv *priv;
1555 priv = GET_PRIV (window);
1557 /* If we just received DND to the same window, we don't want
1558 * to do anything here like removing the tab and then readding
1559 * it, so we return here and in "page-added".
1561 if (priv->dnd_same_window) {
1562 DEBUG ("Page added (back to the same window)");
1563 priv->dnd_same_window = FALSE;
1567 DEBUG ("Page added");
1569 /* Get chat object */
1570 chat = EMPATHY_CHAT (child);
1572 /* Connect chat signals for this window */
1573 g_signal_connect (chat, "composing",
1574 G_CALLBACK (chat_window_composing_cb),
1576 g_signal_connect (chat, "new-message",
1577 G_CALLBACK (chat_window_new_message_cb),
1579 g_signal_connect (chat, "notify::tp-chat",
1580 G_CALLBACK (chat_window_update_chat_tab),
1583 /* Set flag so we know to perform some special operations on
1584 * switch page due to the new page being added.
1586 priv->page_added = TRUE;
1588 /* Get list of chats up to date */
1589 priv->chats = g_list_append (priv->chats, chat);
1591 chat_window_update_chat_tab (chat);
1595 chat_window_page_removed_cb (GtkNotebook *notebook,
1598 EmpathyChatWindow *window)
1600 EmpathyChatWindowPriv *priv;
1603 priv = GET_PRIV (window);
1605 /* If we just received DND to the same window, we don't want
1606 * to do anything here like removing the tab and then readding
1607 * it, so we return here and in "page-added".
1609 if (priv->dnd_same_window) {
1610 DEBUG ("Page removed (and will be readded to same window)");
1614 DEBUG ("Page removed");
1616 /* Get chat object */
1617 chat = EMPATHY_CHAT (child);
1619 /* Disconnect all signal handlers for this chat and this window */
1620 g_signal_handlers_disconnect_by_func (chat,
1621 G_CALLBACK (chat_window_composing_cb),
1623 g_signal_handlers_disconnect_by_func (chat,
1624 G_CALLBACK (chat_window_new_message_cb),
1626 g_signal_handlers_disconnect_by_func (chat,
1627 G_CALLBACK (chat_window_update_chat_tab),
1630 /* Keep list of chats up to date */
1631 priv->chats = g_list_remove (priv->chats, chat);
1632 empathy_chat_messages_read (chat);
1634 if (priv->chats == NULL) {
1635 g_object_unref (window);
1637 chat_window_update (window, TRUE);
1642 chat_window_focus_in_event_cb (GtkWidget *widget,
1644 EmpathyChatWindow *window)
1646 EmpathyChatWindowPriv *priv;
1648 priv = GET_PRIV (window);
1650 empathy_chat_messages_read (priv->current_chat);
1652 chat_window_set_urgency_hint (window, FALSE);
1654 /* Update the title, since we now mark all unread messages as read. */
1655 chat_window_update_chat_tab_full (priv->current_chat, FALSE);
1661 chat_window_drag_drop (GtkWidget *widget,
1662 GdkDragContext *context,
1666 EmpathyChatWindow *window)
1669 EmpathyChatWindowPriv *priv;
1671 priv = GET_PRIV (window);
1673 target = gtk_drag_dest_find_target (widget, context, priv->file_targets);
1674 if (target == GDK_NONE)
1675 target = gtk_drag_dest_find_target (widget, context, priv->contact_targets);
1677 if (target != GDK_NONE) {
1678 gtk_drag_get_data (widget, context, target, time_);
1686 chat_window_drag_motion (GtkWidget *widget,
1687 GdkDragContext *context,
1691 EmpathyChatWindow *window)
1694 EmpathyChatWindowPriv *priv;
1696 priv = GET_PRIV (window);
1698 target = gtk_drag_dest_find_target (widget, context, priv->file_targets);
1699 if (target != GDK_NONE) {
1700 /* This is a file drag. Ensure the contact is online and set the
1701 drag type to COPY. Note that it's possible that the tab will
1702 be switched by GTK+ after a timeout from drag_motion without
1703 getting another drag_motion to disable the drop. You have
1704 to hold your mouse really still.
1706 EmpathyContact *contact;
1708 priv = GET_PRIV (window);
1709 contact = empathy_chat_get_remote_contact (priv->current_chat);
1710 /* contact is NULL for multi-user chats. We don't do
1711 * file transfers to MUCs. We also don't send files
1712 * to offline contacts or contacts that don't support
1715 if ((contact == NULL) || !empathy_contact_is_online (contact)) {
1716 gdk_drag_status (context, 0, time_);
1719 if (!(empathy_contact_get_capabilities (contact)
1720 & EMPATHY_CAPABILITIES_FT)) {
1721 gdk_drag_status (context, 0, time_);
1724 gdk_drag_status (context, GDK_ACTION_COPY, time_);
1728 target = gtk_drag_dest_find_target (widget, context, priv->contact_targets);
1729 if (target != GDK_NONE) {
1730 /* This is a drag of a contact from a contact list. Set to COPY.
1731 FIXME: If this drag is to a MUC window, it invites the user.
1732 Otherwise, it opens a chat. Should we use a different drag
1733 type for invites? Should we allow ASK?
1735 gdk_drag_status (context, GDK_ACTION_COPY, time_);
1743 chat_window_drag_data_received (GtkWidget *widget,
1744 GdkDragContext *context,
1747 GtkSelectionData *selection,
1750 EmpathyChatWindow *window)
1752 if (info == DND_DRAG_TYPE_CONTACT_ID) {
1753 EmpathyChat *chat = NULL;
1754 EmpathyChatWindow *old_window;
1755 TpAccount *account = NULL;
1756 TpAccountManager *account_manager;
1759 const gchar *account_id;
1760 const gchar *contact_id;
1762 id = (const gchar*) gtk_selection_data_get_data (selection);
1764 /* FIXME: Perhaps should be sure that the account manager is
1765 * prepared before calling _ensure_account on it. */
1766 account_manager = tp_account_manager_dup ();
1768 DEBUG ("DND contact from roster with id:'%s'", id);
1770 strv = g_strsplit (id, ":", 2);
1771 if (g_strv_length (strv) == 2) {
1772 account_id = strv[0];
1773 contact_id = strv[1];
1775 tp_account_manager_ensure_account (account_manager, account_id);
1776 if (account != NULL)
1777 chat = empathy_chat_window_find_chat (account, contact_id);
1780 if (account == NULL) {
1782 gtk_drag_finish (context, FALSE, FALSE, time_);
1787 empathy_chat_with_contact_id (
1788 account, contact_id, gtk_get_current_event_time ());
1793 g_object_unref (account_manager);
1796 old_window = chat_window_find_chat (chat);
1798 if (old_window == window) {
1799 gtk_drag_finish (context, TRUE, FALSE, time_);
1803 empathy_chat_window_move_chat (old_window, window, chat);
1805 empathy_chat_window_add_chat (window, chat);
1808 /* Added to take care of any outstanding chat events */
1809 empathy_chat_window_present_chat (chat,
1810 TP_USER_ACTION_TIME_NOT_USER_ACTION);
1812 /* We should return TRUE to remove the data when doing
1813 * GDK_ACTION_MOVE, but we don't here otherwise it has
1814 * weird consequences, and we handle that internally
1815 * anyway with add_chat () and remove_chat ().
1817 gtk_drag_finish (context, TRUE, FALSE, time_);
1819 else if (info == DND_DRAG_TYPE_URI_LIST) {
1820 EmpathyChatWindowPriv *priv;
1821 EmpathyContact *contact;
1824 priv = GET_PRIV (window);
1825 contact = empathy_chat_get_remote_contact (priv->current_chat);
1827 /* contact is NULL when current_chat is a multi-user chat.
1828 * We don't do file transfers to MUCs, so just cancel the drag.
1830 if (contact == NULL) {
1831 gtk_drag_finish (context, TRUE, FALSE, time_);
1835 data = (const gchar *) gtk_selection_data_get_data (selection);
1836 empathy_send_file_from_uri_list (contact, data);
1838 gtk_drag_finish (context, TRUE, FALSE, time_);
1840 else if (info == DND_DRAG_TYPE_TAB) {
1842 EmpathyChatWindow *old_window = NULL;
1846 chat = (void *) gtk_selection_data_get_data (selection);
1847 old_window = chat_window_find_chat (*chat);
1850 EmpathyChatWindowPriv *priv;
1852 priv = GET_PRIV (window);
1853 priv->dnd_same_window = (old_window == window);
1854 DEBUG ("DND tab (within same window: %s)",
1855 priv->dnd_same_window ? "Yes" : "No");
1858 DEBUG ("DND from unknown source");
1859 gtk_drag_finish (context, FALSE, FALSE, time_);
1864 chat_window_chat_manager_chats_changed_cb (EmpathyChatManager *chat_manager,
1865 guint num_chats_in_manager,
1866 EmpathyChatWindow *window)
1868 EmpathyChatWindowPriv *priv = GET_PRIV (window);
1870 gtk_action_set_sensitive (priv->menu_tabs_undo_close_tab,
1871 num_chats_in_manager > 0);
1875 chat_window_finalize (GObject *object)
1877 EmpathyChatWindow *window;
1878 EmpathyChatWindowPriv *priv;
1880 window = EMPATHY_CHAT_WINDOW (object);
1881 priv = GET_PRIV (window);
1883 DEBUG ("Finalized: %p", object);
1885 g_object_unref (priv->ui_manager);
1886 g_object_unref (priv->chatroom_manager);
1887 g_object_unref (priv->notify_mgr);
1888 g_object_unref (priv->gsettings_chat);
1889 g_object_unref (priv->gsettings_notif);
1890 g_object_unref (priv->gsettings_ui);
1891 g_object_unref (priv->sound_mgr);
1893 if (priv->notification != NULL) {
1894 notify_notification_close (priv->notification, NULL);
1895 priv->notification = NULL;
1898 if (priv->contact_targets) {
1899 gtk_target_list_unref (priv->contact_targets);
1901 if (priv->file_targets) {
1902 gtk_target_list_unref (priv->file_targets);
1905 if (priv->chat_manager) {
1906 g_signal_handler_disconnect (priv->chat_manager,
1907 priv->chat_manager_chats_changed_id);
1908 g_object_unref (priv->chat_manager);
1909 priv->chat_manager = NULL;
1912 chat_windows = g_list_remove (chat_windows, window);
1913 gtk_widget_destroy (priv->dialog);
1915 G_OBJECT_CLASS (empathy_chat_window_parent_class)->finalize (object);
1919 empathy_chat_window_class_init (EmpathyChatWindowClass *klass)
1921 GObjectClass *object_class = G_OBJECT_CLASS (klass);
1923 object_class->finalize = chat_window_finalize;
1925 g_type_class_add_private (object_class, sizeof (EmpathyChatWindowPriv));
1929 empathy_chat_window_init (EmpathyChatWindow *window)
1932 GtkAccelGroup *accel_group;
1937 GtkWidget *chat_vbox;
1939 EmpathySmileyManager *smiley_manager;
1940 EmpathyChatWindowPriv *priv = G_TYPE_INSTANCE_GET_PRIVATE (window,
1941 EMPATHY_TYPE_CHAT_WINDOW, EmpathyChatWindowPriv);
1943 window->priv = priv;
1944 filename = empathy_file_lookup ("empathy-chat-window.ui", "src");
1945 gui = empathy_builder_get_file (filename,
1946 "chat_window", &priv->dialog,
1947 "chat_vbox", &chat_vbox,
1948 "ui_manager", &priv->ui_manager,
1949 "menu_conv_insert_smiley", &priv->menu_conv_insert_smiley,
1950 "menu_conv_favorite", &priv->menu_conv_favorite,
1951 "menu_conv_always_urgent", &priv->menu_conv_always_urgent,
1952 "menu_conv_toggle_contacts", &priv->menu_conv_toggle_contacts,
1953 "menu_edit_cut", &priv->menu_edit_cut,
1954 "menu_edit_copy", &priv->menu_edit_copy,
1955 "menu_edit_paste", &priv->menu_edit_paste,
1956 "menu_edit_find", &priv->menu_edit_find,
1957 "menu_tabs_next", &priv->menu_tabs_next,
1958 "menu_tabs_prev", &priv->menu_tabs_prev,
1959 "menu_tabs_undo_close_tab", &priv->menu_tabs_undo_close_tab,
1960 "menu_tabs_left", &priv->menu_tabs_left,
1961 "menu_tabs_right", &priv->menu_tabs_right,
1962 "menu_tabs_detach", &priv->menu_tabs_detach,
1966 empathy_builder_connect (gui, window,
1967 "menu_conv", "activate", chat_window_conv_activate_cb,
1968 "menu_conv_clear", "activate", chat_window_clear_activate_cb,
1969 "menu_conv_favorite", "toggled", chat_window_favorite_toggled_cb,
1970 "menu_conv_always_urgent", "toggled", chat_window_always_urgent_toggled_cb,
1971 "menu_conv_toggle_contacts", "toggled", chat_window_contacts_toggled_cb,
1972 "menu_conv_invite_participant", "activate", chat_window_invite_participant_activate_cb,
1973 "menu_conv_close", "activate", chat_window_close_activate_cb,
1974 "menu_edit", "activate", chat_window_edit_activate_cb,
1975 "menu_edit_cut", "activate", chat_window_cut_activate_cb,
1976 "menu_edit_copy", "activate", chat_window_copy_activate_cb,
1977 "menu_edit_paste", "activate", chat_window_paste_activate_cb,
1978 "menu_edit_find", "activate", chat_window_find_activate_cb,
1979 "menu_tabs_next", "activate", chat_window_tabs_next_activate_cb,
1980 "menu_tabs_prev", "activate", chat_window_tabs_previous_activate_cb,
1981 "menu_tabs_undo_close_tab", "activate", chat_window_tabs_undo_close_tab_activate_cb,
1982 "menu_tabs_left", "activate", chat_window_tabs_left_activate_cb,
1983 "menu_tabs_right", "activate", chat_window_tabs_right_activate_cb,
1984 "menu_tabs_detach", "activate", chat_window_detach_activate_cb,
1985 "menu_help_contents", "activate", chat_window_help_contents_activate_cb,
1986 "menu_help_about", "activate", chat_window_help_about_activate_cb,
1989 g_object_ref (priv->ui_manager);
1990 g_object_unref (gui);
1992 priv->gsettings_chat = g_settings_new (EMPATHY_PREFS_CHAT_SCHEMA);
1993 priv->gsettings_notif = g_settings_new (EMPATHY_PREFS_NOTIFICATIONS_SCHEMA);
1994 priv->gsettings_ui = g_settings_new (EMPATHY_PREFS_UI_SCHEMA);
1995 priv->chatroom_manager = empathy_chatroom_manager_dup_singleton (NULL);
1997 priv->sound_mgr = empathy_sound_manager_dup_singleton ();
1999 priv->notebook = gtk_notebook_new ();
2001 g_signal_connect (priv->notebook, "create-window",
2002 G_CALLBACK (notebook_create_window_cb), window);
2004 gtk_notebook_set_group_name (GTK_NOTEBOOK (priv->notebook),
2005 "EmpathyChatWindow");
2006 gtk_notebook_set_scrollable (GTK_NOTEBOOK (priv->notebook), TRUE);
2007 gtk_notebook_popup_enable (GTK_NOTEBOOK (priv->notebook));
2008 gtk_box_pack_start (GTK_BOX (chat_vbox), priv->notebook, TRUE, TRUE, 0);
2009 gtk_widget_show (priv->notebook);
2012 accel_group = gtk_accel_group_new ();
2013 gtk_window_add_accel_group (GTK_WINDOW (priv->dialog), accel_group);
2015 for (i = 0; i < G_N_ELEMENTS (tab_accel_keys); i++) {
2016 closure = g_cclosure_new (G_CALLBACK (chat_window_accel_cb),
2019 gtk_accel_group_connect (accel_group,
2026 g_object_unref (accel_group);
2028 /* Set up drag target lists */
2029 priv->contact_targets = gtk_target_list_new (drag_types_dest_contact,
2030 G_N_ELEMENTS (drag_types_dest_contact));
2031 priv->file_targets = gtk_target_list_new (drag_types_dest_file,
2032 G_N_ELEMENTS (drag_types_dest_file));
2034 /* Set up smiley menu */
2035 smiley_manager = empathy_smiley_manager_dup_singleton ();
2036 submenu = empathy_smiley_menu_new (smiley_manager,
2037 chat_window_insert_smiley_activate_cb,
2039 menu = gtk_ui_manager_get_widget (priv->ui_manager,
2040 "/chats_menubar/menu_conv/menu_conv_insert_smiley");
2041 gtk_menu_item_set_submenu (GTK_MENU_ITEM (menu), submenu);
2042 g_object_unref (smiley_manager);
2044 /* Set up signals we can't do with ui file since we may need to
2045 * block/unblock them at some later stage.
2048 g_signal_connect (priv->dialog,
2050 G_CALLBACK (chat_window_delete_event_cb),
2052 g_signal_connect (priv->dialog,
2054 G_CALLBACK (chat_window_focus_in_event_cb),
2056 g_signal_connect_after (priv->notebook,
2058 G_CALLBACK (chat_window_page_switched_cb),
2060 g_signal_connect (priv->notebook,
2062 G_CALLBACK (chat_window_page_added_cb),
2064 g_signal_connect (priv->notebook,
2066 G_CALLBACK (chat_window_page_removed_cb),
2069 /* Set up drag and drop */
2070 gtk_drag_dest_set (GTK_WIDGET (priv->notebook),
2071 GTK_DEST_DEFAULT_HIGHLIGHT,
2073 G_N_ELEMENTS (drag_types_dest),
2074 GDK_ACTION_MOVE | GDK_ACTION_COPY);
2076 /* connect_after to allow GtkNotebook's built-in tab switching */
2077 g_signal_connect_after (priv->notebook,
2079 G_CALLBACK (chat_window_drag_motion),
2081 g_signal_connect (priv->notebook,
2082 "drag-data-received",
2083 G_CALLBACK (chat_window_drag_data_received),
2085 g_signal_connect (priv->notebook,
2087 G_CALLBACK (chat_window_drag_drop),
2090 chat_windows = g_list_prepend (chat_windows, window);
2092 /* Set up private details */
2094 priv->current_chat = NULL;
2095 priv->notification = NULL;
2097 priv->notify_mgr = empathy_notify_manager_dup_singleton ();
2099 priv->chat_manager = empathy_chat_manager_dup_singleton ();
2100 priv->chat_manager_chats_changed_id =
2101 g_signal_connect (priv->chat_manager, "closed-chats-changed",
2102 G_CALLBACK (chat_window_chat_manager_chats_changed_cb),
2105 chat_window_chat_manager_chats_changed_cb (priv->chat_manager,
2106 empathy_chat_manager_get_num_closed_chats (priv->chat_manager),
2111 empathy_chat_window_get_dialog (EmpathyChatWindow *window)
2113 EmpathyChatWindowPriv *priv;
2115 g_return_val_if_fail (window != NULL, NULL);
2117 priv = GET_PRIV (window);
2119 return priv->dialog;
2122 /* Returns the window to open a new tab in if there is a suitable window,
2123 * otherwise, returns NULL indicating that a new window should be added.
2125 static EmpathyChatWindow *
2126 empathy_chat_window_get_default (gboolean room)
2128 GSettings *gsettings = g_settings_new (EMPATHY_PREFS_UI_SCHEMA);
2130 gboolean separate_windows = TRUE;
2132 separate_windows = g_settings_get_boolean (gsettings,
2133 EMPATHY_PREFS_UI_SEPARATE_CHAT_WINDOWS);
2135 g_object_unref (gsettings);
2137 if (separate_windows) {
2138 /* Always create a new window */
2142 for (l = chat_windows; l; l = l->next) {
2143 EmpathyChatWindowPriv *priv;
2144 EmpathyChatWindow *chat_window;
2146 guint nb_rooms, nb_private;
2148 chat_window = l->data;
2149 priv = GET_PRIV (chat_window);
2151 dialog = empathy_chat_window_get_dialog (chat_window);
2153 empathy_chat_window_get_nb_chats (chat_window, &nb_rooms, &nb_private);
2155 /* Skip the window if there aren't any rooms in it */
2156 if (room && nb_rooms == 0)
2159 /* Skip the window if there aren't any 1-1 chats in it */
2160 if (!room && nb_private == 0)
2163 /* Found a window on this desktop, make it visible if necessary */
2164 if (!empathy_window_get_is_visible (GTK_WINDOW (dialog)))
2165 empathy_window_present (GTK_WINDOW (dialog));
2173 empathy_chat_window_add_chat (EmpathyChatWindow *window,
2176 EmpathyChatWindowPriv *priv;
2178 GtkWidget *popup_label;
2180 GValue value = { 0, };
2182 g_return_if_fail (window != NULL);
2183 g_return_if_fail (EMPATHY_IS_CHAT (chat));
2185 priv = GET_PRIV (window);
2187 /* Reference the chat object */
2188 g_object_ref (chat);
2190 /* If this window has just been created, position it */
2191 if (priv->chats == NULL) {
2192 const gchar *name = "chat-window";
2193 gboolean separate_windows;
2195 separate_windows = g_settings_get_boolean (priv->gsettings_ui,
2196 EMPATHY_PREFS_UI_SEPARATE_CHAT_WINDOWS);
2198 if (empathy_chat_is_room (chat))
2199 name = "room-window";
2201 if (separate_windows) {
2204 /* Save current position of the window */
2205 gtk_window_get_position (GTK_WINDOW (priv->dialog), &x, &y);
2207 /* First bind to the 'generic' name. So new window for which we didn't
2208 * save a geometry yet will have the geometry of the last saved
2209 * window (bgo #601191). */
2210 empathy_geometry_bind (GTK_WINDOW (priv->dialog), name);
2212 /* Restore previous position of the window so the newly created window
2213 * won't be in the same position as the latest saved window and so
2214 * completely hide it. */
2215 gtk_window_move (GTK_WINDOW (priv->dialog), x, y);
2217 /* Then bind it to the name of the contact/room so we'll save the
2218 * geometry specific to this window */
2219 name = empathy_chat_get_id (chat);
2222 empathy_geometry_bind (GTK_WINDOW (priv->dialog), name);
2225 child = GTK_WIDGET (chat);
2226 label = chat_window_create_label (window, chat, TRUE);
2227 popup_label = chat_window_create_label (window, chat, FALSE);
2228 gtk_widget_show (child);
2230 g_signal_connect (chat, "notify::name",
2231 G_CALLBACK (chat_window_chat_notify_cb),
2233 g_signal_connect (chat, "notify::subject",
2234 G_CALLBACK (chat_window_chat_notify_cb),
2236 g_signal_connect (chat, "notify::remote-contact",
2237 G_CALLBACK (chat_window_chat_notify_cb),
2239 chat_window_chat_notify_cb (chat);
2241 gtk_notebook_append_page_menu (GTK_NOTEBOOK (priv->notebook), child, label, popup_label);
2242 gtk_notebook_set_tab_reorderable (GTK_NOTEBOOK (priv->notebook), child, TRUE);
2243 gtk_notebook_set_tab_detachable (GTK_NOTEBOOK (priv->notebook), child, TRUE);
2244 g_value_init (&value, G_TYPE_BOOLEAN);
2245 g_value_set_boolean (&value, TRUE);
2246 gtk_container_child_set_property (GTK_CONTAINER (priv->notebook),
2247 child, "tab-expand" , &value);
2248 gtk_container_child_set_property (GTK_CONTAINER (priv->notebook),
2249 child, "tab-fill" , &value);
2250 g_value_unset (&value);
2252 DEBUG ("Chat added (%d references)", G_OBJECT (chat)->ref_count);
2256 empathy_chat_window_remove_chat (EmpathyChatWindow *window,
2259 EmpathyChatWindowPriv *priv;
2261 EmpathyContact *remote_contact;
2262 EmpathyChatManager *chat_manager;
2264 g_return_if_fail (window != NULL);
2265 g_return_if_fail (EMPATHY_IS_CHAT (chat));
2267 priv = GET_PRIV (window);
2269 g_signal_handlers_disconnect_by_func (chat,
2270 chat_window_chat_notify_cb,
2272 remote_contact = g_object_get_data (G_OBJECT (chat),
2273 "chat-window-remote-contact");
2274 if (remote_contact) {
2275 g_signal_handlers_disconnect_by_func (remote_contact,
2276 chat_window_update_chat_tab,
2280 chat_manager = empathy_chat_manager_dup_singleton ();
2281 empathy_chat_manager_closed_chat (chat_manager, chat);
2282 g_object_unref (chat_manager);
2284 position = gtk_notebook_page_num (GTK_NOTEBOOK (priv->notebook),
2286 gtk_notebook_remove_page (GTK_NOTEBOOK (priv->notebook), position);
2288 DEBUG ("Chat removed (%d references)", G_OBJECT (chat)->ref_count - 1);
2290 g_object_unref (chat);
2294 empathy_chat_window_move_chat (EmpathyChatWindow *old_window,
2295 EmpathyChatWindow *new_window,
2300 g_return_if_fail (EMPATHY_IS_CHAT_WINDOW (old_window));
2301 g_return_if_fail (EMPATHY_IS_CHAT_WINDOW (new_window));
2302 g_return_if_fail (EMPATHY_IS_CHAT (chat));
2304 widget = GTK_WIDGET (chat);
2306 DEBUG ("Chat moving with widget:%p (%d references)", widget,
2307 G_OBJECT (widget)->ref_count);
2309 /* We reference here to make sure we don't loose the widget
2310 * and the EmpathyChat object during the move.
2312 g_object_ref (chat);
2313 g_object_ref (widget);
2315 empathy_chat_window_remove_chat (old_window, chat);
2316 empathy_chat_window_add_chat (new_window, chat);
2318 g_object_unref (widget);
2319 g_object_unref (chat);
2323 empathy_chat_window_switch_to_chat (EmpathyChatWindow *window,
2326 EmpathyChatWindowPriv *priv;
2329 g_return_if_fail (window != NULL);
2330 g_return_if_fail (EMPATHY_IS_CHAT (chat));
2332 priv = GET_PRIV (window);
2334 page_num = gtk_notebook_page_num (GTK_NOTEBOOK (priv->notebook),
2336 gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->notebook),
2341 empathy_chat_window_find_chat (TpAccount *account,
2346 g_return_val_if_fail (!EMP_STR_EMPTY (id), NULL);
2348 for (l = chat_windows; l; l = l->next) {
2349 EmpathyChatWindowPriv *priv;
2350 EmpathyChatWindow *window;
2354 priv = GET_PRIV (window);
2356 for (ll = priv->chats; ll; ll = ll->next) {
2361 if (account == empathy_chat_get_account (chat) &&
2362 !tp_strdiff (id, empathy_chat_get_id (chat))) {
2372 empathy_chat_window_present_chat (EmpathyChat *chat,
2375 EmpathyChatWindow *window;
2376 EmpathyChatWindowPriv *priv;
2377 guint32 x_timestamp;
2379 g_return_if_fail (EMPATHY_IS_CHAT (chat));
2381 window = chat_window_find_chat (chat);
2383 /* If the chat has no window, create one */
2384 if (window == NULL) {
2385 window = empathy_chat_window_get_default (empathy_chat_is_room (chat));
2387 window = empathy_chat_window_new ();
2388 gtk_widget_show_all (GET_PRIV (window)->dialog);
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;