1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * Copyright (C) 2003-2007 Imendio AB
4 * Copyright (C) 2007-2010 Collabora Ltd.
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License as
8 * published by the Free Software Foundation; either version 2 of the
9 * License, or (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
16 * You should have received a copy of the GNU General Public
17 * License along with this program; if not, write to the
18 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
19 * Boston, MA 02110-1301 USA
21 * Authors: Mikael Hallendal <micke@imendio.com>
22 * Richard Hult <richard@imendio.com>
23 * Martyn Russell <martyn@imendio.com>
24 * Geert-Jan Van den Bogaerde <geertjan@gnome.org>
25 * Xavier Claessens <xclaesse@gmail.com>
26 * RĂ´mulo Fernandes Machado <romulo@castorgroup.net>
34 #include <gdk/gdkkeysyms.h>
36 #include <glib/gi18n.h>
37 #include <libnotify/notification.h>
39 #include <telepathy-glib/telepathy-glib.h>
41 #include <libempathy/empathy-contact.h>
42 #include <libempathy/empathy-message.h>
43 #include <libempathy/empathy-chatroom-manager.h>
44 #include <libempathy/empathy-gsettings.h>
45 #include <libempathy/empathy-utils.h>
46 #include <libempathy/empathy-tp-contact-factory.h>
47 #include <libempathy/empathy-contact-list.h>
49 #include <libempathy-gtk/empathy-images.h>
50 #include <libempathy-gtk/empathy-contact-dialogs.h>
51 #include <libempathy-gtk/empathy-log-window.h>
52 #include <libempathy-gtk/empathy-geometry.h>
53 #include <libempathy-gtk/empathy-smiley-manager.h>
54 #include <libempathy-gtk/empathy-sound-manager.h>
55 #include <libempathy-gtk/empathy-ui-utils.h>
56 #include <libempathy-gtk/empathy-notify-manager.h>
58 #include "empathy-chat-manager.h"
59 #include "empathy-chat-window.h"
60 #include "empathy-about-dialog.h"
61 #include "empathy-invite-participant-dialog.h"
63 #define DEBUG_FLAG EMPATHY_DEBUG_CHAT
64 #include <libempathy/empathy-debug.h>
66 /* Macro to compare guint32 X timestamps, while accounting for wrapping around
68 #define X_EARLIER_OR_EQL(t1, t2) \
69 ((t1 <= t2 && ((t2 - t1) < G_MAXUINT32/2)) \
70 || (t1 >= t2 && (t1 - t2) > (G_MAXUINT32/2)) \
73 #define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyChatWindow)
75 EmpathyChat *current_chat;
78 gboolean dnd_same_window;
79 EmpathyChatroomManager *chatroom_manager;
80 EmpathyNotifyManager *notify_mgr;
83 NotifyNotification *notification;
85 GtkTargetList *contact_targets;
86 GtkTargetList *file_targets;
88 EmpathyChatManager *chat_manager;
89 gulong chat_manager_chats_changed_id;
92 GtkUIManager *ui_manager;
93 GtkAction *menu_conv_insert_smiley;
94 GtkAction *menu_conv_favorite;
95 GtkAction *menu_conv_always_urgent;
96 GtkAction *menu_conv_toggle_contacts;
98 GtkAction *menu_edit_cut;
99 GtkAction *menu_edit_copy;
100 GtkAction *menu_edit_paste;
101 GtkAction *menu_edit_find;
103 GtkAction *menu_tabs_next;
104 GtkAction *menu_tabs_prev;
105 GtkAction *menu_tabs_undo_close_tab;
106 GtkAction *menu_tabs_left;
107 GtkAction *menu_tabs_right;
108 GtkAction *menu_tabs_detach;
110 /* Last user action time we acted upon to show a tab */
111 guint32 x_user_action_time;
113 GSettings *gsettings_chat;
114 GSettings *gsettings_notif;
115 GSettings *gsettings_ui;
117 EmpathySoundManager *sound_mgr;
118 } EmpathyChatWindowPriv;
120 static GList *chat_windows = NULL;
122 static const guint tab_accel_keys[] = {
123 GDK_KEY_1, GDK_KEY_2, GDK_KEY_3, GDK_KEY_4, GDK_KEY_5,
124 GDK_KEY_6, GDK_KEY_7, GDK_KEY_8, GDK_KEY_9, GDK_KEY_0
128 DND_DRAG_TYPE_CONTACT_ID,
129 DND_DRAG_TYPE_URI_LIST,
133 static const GtkTargetEntry drag_types_dest[] = {
134 { "text/contact-id", 0, DND_DRAG_TYPE_CONTACT_ID },
135 { "GTK_NOTEBOOK_TAB", GTK_TARGET_SAME_APP, DND_DRAG_TYPE_TAB },
136 { "text/uri-list", 0, DND_DRAG_TYPE_URI_LIST },
137 { "text/path-list", 0, DND_DRAG_TYPE_URI_LIST },
140 static const GtkTargetEntry drag_types_dest_contact[] = {
141 { "text/contact-id", 0, DND_DRAG_TYPE_CONTACT_ID },
144 static const GtkTargetEntry drag_types_dest_file[] = {
145 /* must be first to be prioritized, in order to receive the
146 * note's file path from Tomboy instead of an URI */
147 { "text/path-list", 0, DND_DRAG_TYPE_URI_LIST },
148 { "text/uri-list", 0, DND_DRAG_TYPE_URI_LIST },
151 static void chat_window_update (EmpathyChatWindow *window);
153 static void empathy_chat_window_add_chat (EmpathyChatWindow *window,
156 static void empathy_chat_window_remove_chat (EmpathyChatWindow *window,
159 static void empathy_chat_window_move_chat (EmpathyChatWindow *old_window,
160 EmpathyChatWindow *new_window,
163 static void empathy_chat_window_get_nb_chats (EmpathyChatWindow *self,
167 G_DEFINE_TYPE (EmpathyChatWindow, empathy_chat_window, G_TYPE_OBJECT);
170 chat_window_accel_cb (GtkAccelGroup *accelgroup,
174 EmpathyChatWindow *window)
176 EmpathyChatWindowPriv *priv;
180 priv = GET_PRIV (window);
182 for (i = 0; i < G_N_ELEMENTS (tab_accel_keys); i++) {
183 if (tab_accel_keys[i] == key) {
190 gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->notebook), num);
194 static EmpathyChatWindow *
195 chat_window_find_chat (EmpathyChat *chat)
197 EmpathyChatWindowPriv *priv;
200 for (l = chat_windows; l; l = l->next) {
201 priv = GET_PRIV (l->data);
202 ll = g_list_find (priv->chats, chat);
212 chat_window_close_clicked_cb (GtkAction *action,
215 EmpathyChatWindow *window;
217 window = chat_window_find_chat (chat);
218 empathy_chat_window_remove_chat (window, chat);
222 chat_tab_style_set_cb (GtkWidget *hbox,
223 GtkStyle *previous_style,
227 int char_width, h, w;
228 PangoContext *context;
229 PangoFontMetrics *metrics;
231 button = g_object_get_data (G_OBJECT (user_data),
232 "chat-window-tab-close-button");
233 context = gtk_widget_get_pango_context (hbox);
235 metrics = pango_context_get_metrics (context, gtk_widget_get_style (hbox)->font_desc,
236 pango_context_get_language (context));
237 char_width = pango_font_metrics_get_approximate_char_width (metrics);
238 pango_font_metrics_unref (metrics);
240 gtk_icon_size_lookup_for_settings (gtk_widget_get_settings (button),
241 GTK_ICON_SIZE_MENU, &w, &h);
243 /* Request at least about 12 chars width plus at least space for the status
244 * image and the close button */
245 gtk_widget_set_size_request (hbox,
246 12 * PANGO_PIXELS (char_width) + 2 * w, -1);
248 gtk_widget_set_size_request (button, w, h);
252 chat_window_create_label (EmpathyChatWindow *window,
254 gboolean is_tab_label)
256 EmpathyChatWindowPriv *priv;
258 GtkWidget *name_label;
259 GtkWidget *status_image;
260 GtkWidget *close_button;
261 GtkWidget *close_image;
262 GtkWidget *event_box;
263 GtkWidget *event_box_hbox;
264 PangoAttrList *attr_list;
265 PangoAttribute *attr;
267 priv = GET_PRIV (window);
269 /* The spacing between the button and the label. */
270 hbox = gtk_hbox_new (FALSE, 0);
272 event_box = gtk_event_box_new ();
273 gtk_event_box_set_visible_window (GTK_EVENT_BOX (event_box), FALSE);
275 name_label = gtk_label_new (NULL);
277 gtk_label_set_ellipsize (GTK_LABEL (name_label), PANGO_ELLIPSIZE_END);
279 attr_list = pango_attr_list_new ();
280 attr = pango_attr_scale_new (1/1.2);
281 attr->start_index = 0;
282 attr->end_index = -1;
283 pango_attr_list_insert (attr_list, attr);
284 gtk_label_set_attributes (GTK_LABEL (name_label), attr_list);
285 pango_attr_list_unref (attr_list);
287 gtk_misc_set_padding (GTK_MISC (name_label), 2, 0);
288 gtk_misc_set_alignment (GTK_MISC (name_label), 0.0, 0.5);
289 g_object_set_data (G_OBJECT (chat),
290 is_tab_label ? "chat-window-tab-label" : "chat-window-menu-label",
293 status_image = gtk_image_new ();
295 /* Spacing between the icon and label. */
296 event_box_hbox = gtk_hbox_new (FALSE, 0);
298 gtk_box_pack_start (GTK_BOX (event_box_hbox), status_image, FALSE, FALSE, 0);
299 gtk_box_pack_start (GTK_BOX (event_box_hbox), name_label, TRUE, TRUE, 0);
301 g_object_set_data (G_OBJECT (chat),
302 is_tab_label ? "chat-window-tab-image" : "chat-window-menu-image",
304 g_object_set_data (G_OBJECT (chat),
305 is_tab_label ? "chat-window-tab-tooltip-widget" : "chat-window-menu-tooltip-widget",
308 gtk_container_add (GTK_CONTAINER (event_box), event_box_hbox);
309 gtk_box_pack_start (GTK_BOX (hbox), event_box, TRUE, TRUE, 0);
312 close_button = gtk_button_new ();
313 gtk_button_set_relief (GTK_BUTTON (close_button), GTK_RELIEF_NONE);
314 g_object_set_data (G_OBJECT (chat), "chat-window-tab-close-button", close_button);
316 /* We don't want focus/keynav for the button to avoid clutter, and
317 * Ctrl-W works anyway.
319 gtk_widget_set_can_focus (close_button, FALSE);
320 gtk_widget_set_can_default (close_button, FALSE);
322 /* Set the name to make the special rc style match. */
323 gtk_widget_set_name (close_button, "empathy-close-button");
325 close_image = gtk_image_new_from_stock (GTK_STOCK_CLOSE, GTK_ICON_SIZE_MENU);
327 gtk_container_add (GTK_CONTAINER (close_button), close_image);
329 gtk_box_pack_end (GTK_BOX (hbox), close_button, FALSE, FALSE, 0);
331 g_signal_connect (close_button,
333 G_CALLBACK (chat_window_close_clicked_cb),
336 /* React to theme changes and also setup the size correctly. */
337 g_signal_connect (hbox,
339 G_CALLBACK (chat_tab_style_set_cb),
343 gtk_widget_show_all (hbox);
349 _submenu_notify_visible_changed_cb (GObject *object,
353 g_signal_handlers_disconnect_by_func (object,
354 _submenu_notify_visible_changed_cb,
356 chat_window_update (EMPATHY_CHAT_WINDOW (userdata));
360 chat_window_menu_context_update (EmpathyChatWindowPriv *priv,
365 gboolean wrap_around;
366 gboolean is_connected;
369 page_num = gtk_notebook_get_current_page (GTK_NOTEBOOK (priv->notebook));
370 first_page = (page_num == 0);
371 last_page = (page_num == (num_pages - 1));
372 g_object_get (gtk_settings_get_default (), "gtk-keynav-wrap-around",
374 is_connected = empathy_chat_get_tp_chat (priv->current_chat) != NULL;
376 gtk_action_set_sensitive (priv->menu_tabs_next, (!last_page ||
378 gtk_action_set_sensitive (priv->menu_tabs_prev, (!first_page ||
380 gtk_action_set_sensitive (priv->menu_tabs_detach, num_pages > 1);
381 gtk_action_set_sensitive (priv->menu_tabs_left, !first_page);
382 gtk_action_set_sensitive (priv->menu_tabs_right, !last_page);
383 gtk_action_set_sensitive (priv->menu_conv_insert_smiley, is_connected);
387 chat_window_conversation_menu_update (EmpathyChatWindowPriv *priv,
388 EmpathyChatWindow *self)
390 EmpathyTpChat *tp_chat;
391 TpConnection *connection;
393 gboolean sensitive = FALSE;
395 g_return_if_fail (priv->current_chat != NULL);
397 action = gtk_ui_manager_get_action (priv->ui_manager,
398 "/chats_menubar/menu_conv/menu_conv_invite_participant");
399 tp_chat = empathy_chat_get_tp_chat (priv->current_chat);
401 if (tp_chat != NULL) {
402 connection = empathy_tp_chat_get_connection (tp_chat);
404 sensitive = empathy_tp_chat_can_add_contact (tp_chat) &&
405 (tp_connection_get_status (connection, NULL) ==
406 TP_CONNECTION_STATUS_CONNECTED);
409 gtk_action_set_sensitive (action, sensitive);
413 chat_window_contact_menu_update (EmpathyChatWindowPriv *priv,
414 EmpathyChatWindow *window)
416 GtkWidget *menu, *submenu, *orig_submenu;
418 menu = gtk_ui_manager_get_widget (priv->ui_manager,
419 "/chats_menubar/menu_contact");
420 orig_submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (menu));
422 if (orig_submenu == NULL || !gtk_widget_get_visible (orig_submenu)) {
423 submenu = empathy_chat_get_contact_menu (priv->current_chat);
424 if (submenu != NULL) {
425 gtk_menu_item_set_submenu (GTK_MENU_ITEM (menu), submenu);
426 gtk_widget_show (menu);
427 gtk_widget_set_sensitive (menu, TRUE);
430 gtk_widget_set_sensitive (menu, FALSE);
433 tp_g_signal_connect_object (orig_submenu,
435 (GCallback)_submenu_notify_visible_changed_cb,
441 get_all_unread_messages (EmpathyChatWindowPriv *priv)
446 for (l = priv->chats; l != NULL; l = g_list_next (l))
447 nb += empathy_chat_get_nb_unread_messages (EMPATHY_CHAT (l->data));
453 get_window_title_name (EmpathyChatWindowPriv *priv)
455 const gchar *active_name;
457 guint current_unread_msgs;
459 nb_chats = g_list_length (priv->chats);
460 g_assert (nb_chats > 0);
462 active_name = empathy_chat_get_name (priv->current_chat);
464 current_unread_msgs = empathy_chat_get_nb_unread_messages (
469 if (current_unread_msgs == 0)
470 return g_strdup (active_name);
472 return g_strdup_printf (ngettext (
474 "%s (%d unread)", current_unread_msgs),
475 active_name, current_unread_msgs);
477 guint nb_others = nb_chats - 1;
478 guint all_unread_msgs;
480 all_unread_msgs = get_all_unread_messages (priv);
482 if (all_unread_msgs == 0) {
483 /* no unread message */
484 return g_strdup_printf (ngettext (
486 "%s (and %u others)", nb_others),
487 active_name, nb_others);
490 else if (all_unread_msgs == current_unread_msgs) {
491 /* unread messages are in the current tab */
492 return g_strdup_printf (ngettext (
494 "%s (%d unread)", current_unread_msgs),
495 active_name, current_unread_msgs);
498 else if (current_unread_msgs == 0) {
499 /* unread messages are in other tabs */
500 return g_strdup_printf (ngettext (
501 "%s (%d unread from others)",
502 "%s (%d unread from others)",
504 active_name, all_unread_msgs);
508 /* unread messages are in all the tabs */
509 return g_strdup_printf (ngettext (
510 "%s (%d unread from all)",
511 "%s (%d unread from all)",
513 active_name, all_unread_msgs);
519 chat_window_title_update (EmpathyChatWindowPriv *priv)
523 name = get_window_title_name (priv);
524 gtk_window_set_title (GTK_WINDOW (priv->dialog), name);
529 chat_window_icon_update (EmpathyChatWindowPriv *priv, gboolean new_messages)
532 EmpathyContact *remote_contact;
533 gboolean avatar_in_icon;
536 n_chats = g_list_length (priv->chats);
538 /* Update window icon */
540 gtk_window_set_icon_name (GTK_WINDOW (priv->dialog),
541 EMPATHY_IMAGE_MESSAGE);
543 avatar_in_icon = g_settings_get_boolean (priv->gsettings_chat,
544 EMPATHY_PREFS_CHAT_AVATAR_IN_ICON);
546 if (n_chats == 1 && avatar_in_icon) {
547 remote_contact = empathy_chat_get_remote_contact (priv->current_chat);
548 icon = empathy_pixbuf_avatar_from_contact_scaled (remote_contact, 0, 0);
549 gtk_window_set_icon (GTK_WINDOW (priv->dialog), icon);
552 g_object_unref (icon);
555 gtk_window_set_icon_name (GTK_WINDOW (priv->dialog), NULL);
561 chat_window_close_button_update (EmpathyChatWindowPriv *priv,
565 GtkWidget *chat_close_button;
568 if (num_pages == 1) {
569 chat = gtk_notebook_get_nth_page (GTK_NOTEBOOK (priv->notebook), 0);
570 chat_close_button = g_object_get_data (G_OBJECT (chat),
571 "chat-window-tab-close-button");
572 gtk_widget_hide (chat_close_button);
574 for (i=0; i<num_pages; i++) {
575 chat = gtk_notebook_get_nth_page (GTK_NOTEBOOK (priv->notebook), i);
576 chat_close_button = g_object_get_data (G_OBJECT (chat),
577 "chat-window-tab-close-button");
578 gtk_widget_show (chat_close_button);
584 chat_window_update (EmpathyChatWindow *window)
586 EmpathyChatWindowPriv *priv = GET_PRIV (window);
589 num_pages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (priv->notebook));
591 /* Update Tab menu */
592 chat_window_menu_context_update (priv,
595 chat_window_conversation_menu_update (priv, window);
597 chat_window_contact_menu_update (priv,
600 chat_window_title_update (priv);
602 chat_window_icon_update (priv, get_all_unread_messages (priv) > 0);
604 chat_window_close_button_update (priv,
609 append_markup_printf (GString *string,
616 va_start (args, format);
618 tmp = g_markup_vprintf_escaped (format, args);
619 g_string_append (string, tmp);
626 chat_window_update_chat_tab (EmpathyChat *chat)
628 EmpathyChatWindow *window;
629 EmpathyChatWindowPriv *priv;
630 EmpathyContact *remote_contact;
634 const gchar *subject;
635 const gchar *status = NULL;
639 const gchar *icon_name;
640 GtkWidget *tab_image;
641 GtkWidget *menu_image;
643 window = chat_window_find_chat (chat);
647 priv = GET_PRIV (window);
649 /* Get information */
650 name = empathy_chat_get_name (chat);
651 account = empathy_chat_get_account (chat);
652 subject = empathy_chat_get_subject (chat);
653 remote_contact = empathy_chat_get_remote_contact (chat);
655 DEBUG ("Updating chat tab, name=%s, account=%s, subject=%s, remote_contact=%p",
656 name, tp_proxy_get_object_path (account), subject, remote_contact);
658 /* Update tab image */
659 if (empathy_chat_get_tp_chat (chat) == NULL) {
660 /* No TpChat, we are disconnected */
663 else if (empathy_chat_get_nb_unread_messages (chat) > 0) {
664 icon_name = EMPATHY_IMAGE_MESSAGE;
666 else if (remote_contact && empathy_chat_is_composing (chat)) {
667 icon_name = EMPATHY_IMAGE_TYPING;
669 else if (remote_contact) {
670 icon_name = empathy_icon_name_for_contact (remote_contact);
672 icon_name = EMPATHY_IMAGE_GROUP_MESSAGE;
675 tab_image = g_object_get_data (G_OBJECT (chat), "chat-window-tab-image");
676 menu_image = g_object_get_data (G_OBJECT (chat), "chat-window-menu-image");
677 if (icon_name != NULL) {
678 gtk_image_set_from_icon_name (GTK_IMAGE (tab_image), icon_name, GTK_ICON_SIZE_MENU);
679 gtk_widget_show (tab_image);
680 gtk_image_set_from_icon_name (GTK_IMAGE (menu_image), icon_name, GTK_ICON_SIZE_MENU);
681 gtk_widget_show (menu_image);
683 gtk_widget_hide (tab_image);
684 gtk_widget_hide (menu_image);
687 /* Update tab tooltip */
688 tooltip = g_string_new (NULL);
690 if (remote_contact) {
691 id = empathy_contact_get_id (remote_contact);
692 status = empathy_contact_get_presence_message (remote_contact);
697 append_markup_printf (tooltip,
698 "<b>%s</b><small> (%s)</small>",
700 tp_account_get_display_name (account));
702 if (!EMP_STR_EMPTY (status)) {
703 append_markup_printf (tooltip, "\n<i>%s</i>", status);
707 append_markup_printf (tooltip, "\n<b>%s</b> %s",
708 _("Topic:"), subject);
711 if (remote_contact && empathy_chat_is_composing (chat)) {
712 append_markup_printf (tooltip, "\n%s", _("Typing a message."));
715 markup = g_string_free (tooltip, FALSE);
716 widget = g_object_get_data (G_OBJECT (chat), "chat-window-tab-tooltip-widget");
717 gtk_widget_set_tooltip_markup (widget, markup);
718 widget = g_object_get_data (G_OBJECT (chat), "chat-window-menu-tooltip-widget");
719 gtk_widget_set_tooltip_markup (widget, markup);
722 /* Update tab and menu label */
723 widget = g_object_get_data (G_OBJECT (chat), "chat-window-tab-label");
724 gtk_label_set_text (GTK_LABEL (widget), name);
725 widget = g_object_get_data (G_OBJECT (chat), "chat-window-menu-label");
726 gtk_label_set_text (GTK_LABEL (widget), name);
728 /* Update the window if it's the current chat */
729 if (priv->current_chat == chat) {
730 chat_window_update (window);
735 chat_window_chat_notify_cb (EmpathyChat *chat)
737 EmpathyContact *old_remote_contact;
738 EmpathyContact *remote_contact = NULL;
740 old_remote_contact = g_object_get_data (G_OBJECT (chat), "chat-window-remote-contact");
741 remote_contact = empathy_chat_get_remote_contact (chat);
743 if (old_remote_contact != remote_contact) {
744 /* The remote-contact associated with the chat changed, we need
745 * to keep track of any change of that contact and update the
746 * window each time. */
747 if (remote_contact) {
748 g_signal_connect_swapped (remote_contact, "notify",
749 G_CALLBACK (chat_window_update_chat_tab),
752 if (old_remote_contact) {
753 g_signal_handlers_disconnect_by_func (old_remote_contact,
754 chat_window_update_chat_tab,
758 g_object_set_data_full (G_OBJECT (chat), "chat-window-remote-contact",
759 g_object_ref (remote_contact), (GDestroyNotify) g_object_unref);
762 chat_window_update_chat_tab (chat);
766 chat_window_insert_smiley_activate_cb (EmpathySmileyManager *manager,
767 EmpathySmiley *smiley,
770 EmpathyChatWindowPriv *priv = GET_PRIV (window);
772 GtkTextBuffer *buffer;
775 chat = priv->current_chat;
777 buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (chat->input_text_view));
778 gtk_text_buffer_get_end_iter (buffer, &iter);
779 gtk_text_buffer_insert (buffer, &iter, smiley->str, -1);
783 chat_window_conv_activate_cb (GtkAction *action,
784 EmpathyChatWindow *window)
786 EmpathyChatWindowPriv *priv = GET_PRIV (window);
789 EmpathyContact *remote_contact = NULL;
791 /* Favorite room menu */
792 is_room = empathy_chat_is_room (priv->current_chat);
796 gboolean found = FALSE;
797 EmpathyChatroom *chatroom;
799 room = empathy_chat_get_id (priv->current_chat);
800 account = empathy_chat_get_account (priv->current_chat);
801 chatroom = empathy_chatroom_manager_find (priv->chatroom_manager,
803 if (chatroom != NULL)
804 found = empathy_chatroom_is_favorite (chatroom);
806 DEBUG ("This room %s favorite", found ? "is" : "is not");
807 gtk_toggle_action_set_active (
808 GTK_TOGGLE_ACTION (priv->menu_conv_favorite), found);
810 if (chatroom != NULL)
811 found = empathy_chatroom_is_always_urgent (chatroom);
813 gtk_toggle_action_set_active (
814 GTK_TOGGLE_ACTION (priv->menu_conv_always_urgent),
817 gtk_action_set_visible (priv->menu_conv_favorite, is_room);
818 gtk_action_set_visible (priv->menu_conv_always_urgent, is_room);
820 /* Show contacts menu */
821 g_object_get (priv->current_chat,
822 "remote-contact", &remote_contact,
823 "show-contacts", &active,
825 if (remote_contact == NULL) {
826 gtk_toggle_action_set_active (
827 GTK_TOGGLE_ACTION (priv->menu_conv_toggle_contacts),
830 gtk_action_set_visible (priv->menu_conv_toggle_contacts,
831 (remote_contact == NULL));
832 if (remote_contact != NULL) {
833 g_object_unref (remote_contact);
838 chat_window_clear_activate_cb (GtkAction *action,
839 EmpathyChatWindow *window)
841 EmpathyChatWindowPriv *priv = GET_PRIV (window);
843 empathy_chat_clear (priv->current_chat);
847 chat_window_favorite_toggled_cb (GtkToggleAction *toggle_action,
848 EmpathyChatWindow *window)
850 EmpathyChatWindowPriv *priv = GET_PRIV (window);
854 EmpathyChatroom *chatroom;
856 active = gtk_toggle_action_get_active (toggle_action);
857 account = empathy_chat_get_account (priv->current_chat);
858 room = empathy_chat_get_id (priv->current_chat);
860 chatroom = empathy_chatroom_manager_ensure_chatroom (
861 priv->chatroom_manager,
864 empathy_chat_get_name (priv->current_chat));
866 empathy_chatroom_set_favorite (chatroom, active);
867 g_object_unref (chatroom);
871 chat_window_always_urgent_toggled_cb (GtkToggleAction *toggle_action,
872 EmpathyChatWindow *window)
874 EmpathyChatWindowPriv *priv = GET_PRIV (window);
878 EmpathyChatroom *chatroom;
880 active = gtk_toggle_action_get_active (toggle_action);
881 account = empathy_chat_get_account (priv->current_chat);
882 room = empathy_chat_get_id (priv->current_chat);
884 chatroom = empathy_chatroom_manager_ensure_chatroom (
885 priv->chatroom_manager,
888 empathy_chat_get_name (priv->current_chat));
890 empathy_chatroom_set_always_urgent (chatroom, active);
891 g_object_unref (chatroom);
895 chat_window_contacts_toggled_cb (GtkToggleAction *toggle_action,
896 EmpathyChatWindow *window)
898 EmpathyChatWindowPriv *priv = GET_PRIV (window);
901 active = gtk_toggle_action_get_active (toggle_action);
903 empathy_chat_set_show_contacts (priv->current_chat, active);
907 got_contact_cb (TpConnection *connection,
908 EmpathyContact *contact,
913 EmpathyTpChat *tp_chat = EMPATHY_TP_CHAT (user_data);
916 DEBUG ("Failed: %s", error->message);
919 empathy_contact_list_add (EMPATHY_CONTACT_LIST (tp_chat),
920 contact, _("Inviting you to this room"));
925 chat_window_invite_participant_activate_cb (GtkAction *action,
926 EmpathyChatWindow *window)
928 EmpathyChatWindowPriv *priv;
930 EmpathyTpChat *tp_chat;
935 priv = GET_PRIV (window);
937 g_return_if_fail (priv->current_chat != NULL);
939 tp_chat = empathy_chat_get_tp_chat (priv->current_chat);
940 channel = empathy_tp_chat_get_channel (tp_chat);
941 account = empathy_chat_get_account (priv->current_chat);
943 dialog = empathy_invite_participant_dialog_new (
944 GTK_WINDOW (priv->dialog), account);
945 gtk_widget_show (dialog);
947 response = gtk_dialog_run (GTK_DIALOG (dialog));
949 if (response == GTK_RESPONSE_ACCEPT) {
950 TpConnection *connection;
953 id = empathy_contact_selector_dialog_get_selected (
954 EMPATHY_CONTACT_SELECTOR_DIALOG (dialog), NULL, NULL);
955 if (EMP_STR_EMPTY (id)) goto out;
957 connection = tp_channel_borrow_connection (channel);
958 empathy_tp_contact_factory_get_from_id (connection, id,
959 got_contact_cb, tp_chat, NULL, NULL);
963 gtk_widget_destroy (dialog);
967 chat_window_close_activate_cb (GtkAction *action,
968 EmpathyChatWindow *window)
970 EmpathyChatWindowPriv *priv;
972 priv = GET_PRIV (window);
974 g_return_if_fail (priv->current_chat != NULL);
976 empathy_chat_window_remove_chat (window, priv->current_chat);
980 chat_window_edit_activate_cb (GtkAction *action,
981 EmpathyChatWindow *window)
983 EmpathyChatWindowPriv *priv;
984 GtkClipboard *clipboard;
985 GtkTextBuffer *buffer;
986 gboolean text_available;
988 priv = GET_PRIV (window);
990 g_return_if_fail (priv->current_chat != NULL);
992 if (!empathy_chat_get_tp_chat (priv->current_chat)) {
993 gtk_action_set_sensitive (priv->menu_edit_copy, FALSE);
994 gtk_action_set_sensitive (priv->menu_edit_cut, FALSE);
995 gtk_action_set_sensitive (priv->menu_edit_paste, FALSE);
999 buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (priv->current_chat->input_text_view));
1000 if (gtk_text_buffer_get_has_selection (buffer)) {
1001 gtk_action_set_sensitive (priv->menu_edit_copy, TRUE);
1002 gtk_action_set_sensitive (priv->menu_edit_cut, TRUE);
1006 selection = empathy_chat_view_get_has_selection (priv->current_chat->view);
1008 gtk_action_set_sensitive (priv->menu_edit_cut, FALSE);
1009 gtk_action_set_sensitive (priv->menu_edit_copy, selection);
1012 clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
1013 text_available = gtk_clipboard_wait_is_text_available (clipboard);
1014 gtk_action_set_sensitive (priv->menu_edit_paste, text_available);
1018 chat_window_cut_activate_cb (GtkAction *action,
1019 EmpathyChatWindow *window)
1021 EmpathyChatWindowPriv *priv;
1023 g_return_if_fail (EMPATHY_IS_CHAT_WINDOW (window));
1025 priv = GET_PRIV (window);
1027 empathy_chat_cut (priv->current_chat);
1031 chat_window_copy_activate_cb (GtkAction *action,
1032 EmpathyChatWindow *window)
1034 EmpathyChatWindowPriv *priv;
1036 g_return_if_fail (EMPATHY_IS_CHAT_WINDOW (window));
1038 priv = GET_PRIV (window);
1040 empathy_chat_copy (priv->current_chat);
1044 chat_window_paste_activate_cb (GtkAction *action,
1045 EmpathyChatWindow *window)
1047 EmpathyChatWindowPriv *priv;
1049 g_return_if_fail (EMPATHY_IS_CHAT_WINDOW (window));
1051 priv = GET_PRIV (window);
1053 empathy_chat_paste (priv->current_chat);
1057 chat_window_find_activate_cb (GtkAction *action,
1058 EmpathyChatWindow *window)
1060 EmpathyChatWindowPriv *priv;
1062 g_return_if_fail (EMPATHY_IS_CHAT_WINDOW (window));
1064 priv = GET_PRIV (window);
1066 empathy_chat_find (priv->current_chat);
1070 chat_window_tabs_next_activate_cb (GtkAction *action,
1071 EmpathyChatWindow *window)
1073 EmpathyChatWindowPriv *priv;
1075 gint index_, numPages;
1076 gboolean wrap_around;
1078 priv = GET_PRIV (window);
1080 g_object_get (gtk_settings_get_default (), "gtk-keynav-wrap-around",
1081 &wrap_around, NULL);
1083 chat = priv->current_chat;
1084 index_ = gtk_notebook_get_current_page (GTK_NOTEBOOK (priv->notebook));
1085 numPages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (priv->notebook));
1087 if (index_ == (numPages - 1) && wrap_around) {
1088 gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->notebook), 0);
1092 gtk_notebook_next_page (GTK_NOTEBOOK (priv->notebook));
1096 chat_window_tabs_previous_activate_cb (GtkAction *action,
1097 EmpathyChatWindow *window)
1099 EmpathyChatWindowPriv *priv;
1101 gint index_, numPages;
1102 gboolean wrap_around;
1104 priv = GET_PRIV (window);
1106 g_object_get (gtk_settings_get_default (), "gtk-keynav-wrap-around",
1107 &wrap_around, NULL);
1109 chat = priv->current_chat;
1110 index_ = gtk_notebook_get_current_page (GTK_NOTEBOOK (priv->notebook));
1111 numPages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (priv->notebook));
1113 if (index_ <= 0 && wrap_around) {
1114 gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->notebook), numPages - 1);
1118 gtk_notebook_prev_page (GTK_NOTEBOOK (priv->notebook));
1122 chat_window_tabs_undo_close_tab_activate_cb (GtkAction *action,
1123 EmpathyChatWindow *window)
1125 EmpathyChatWindowPriv *priv = GET_PRIV (window);
1126 empathy_chat_manager_undo_closed_chat (priv->chat_manager);
1130 chat_window_tabs_left_activate_cb (GtkAction *action,
1131 EmpathyChatWindow *window)
1133 EmpathyChatWindowPriv *priv;
1135 gint index_, num_pages;
1137 priv = GET_PRIV (window);
1139 chat = priv->current_chat;
1140 index_ = gtk_notebook_get_current_page (GTK_NOTEBOOK (priv->notebook));
1145 gtk_notebook_reorder_child (GTK_NOTEBOOK (priv->notebook),
1149 num_pages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (priv->notebook));
1150 chat_window_menu_context_update (priv, num_pages);
1154 chat_window_tabs_right_activate_cb (GtkAction *action,
1155 EmpathyChatWindow *window)
1157 EmpathyChatWindowPriv *priv;
1159 gint index_, num_pages;
1161 priv = GET_PRIV (window);
1163 chat = priv->current_chat;
1164 index_ = gtk_notebook_get_current_page (GTK_NOTEBOOK (priv->notebook));
1166 gtk_notebook_reorder_child (GTK_NOTEBOOK (priv->notebook),
1170 num_pages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (priv->notebook));
1171 chat_window_menu_context_update (priv, num_pages);
1174 static EmpathyChatWindow *
1175 empathy_chat_window_new (void)
1177 return EMPATHY_CHAT_WINDOW (g_object_new (EMPATHY_TYPE_CHAT_WINDOW, NULL));
1181 chat_window_detach_activate_cb (GtkAction *action,
1182 EmpathyChatWindow *window)
1184 EmpathyChatWindowPriv *priv;
1185 EmpathyChatWindow *new_window;
1188 priv = GET_PRIV (window);
1190 chat = priv->current_chat;
1191 new_window = empathy_chat_window_new ();
1193 empathy_chat_window_move_chat (window, new_window, chat);
1195 priv = GET_PRIV (new_window);
1196 gtk_widget_show (priv->dialog);
1200 chat_window_help_contents_activate_cb (GtkAction *action,
1201 EmpathyChatWindow *window)
1203 EmpathyChatWindowPriv *priv = GET_PRIV (window);
1205 empathy_url_show (priv->dialog, "ghelp:empathy");
1209 chat_window_help_about_activate_cb (GtkAction *action,
1210 EmpathyChatWindow *window)
1212 EmpathyChatWindowPriv *priv = GET_PRIV (window);
1214 empathy_about_dialog_new (GTK_WINDOW (priv->dialog));
1218 chat_window_delete_event_cb (GtkWidget *dialog,
1220 EmpathyChatWindow *window)
1222 EmpathyChatWindowPriv *priv = GET_PRIV (window);
1224 DEBUG ("Delete event received");
1226 g_object_ref (window);
1227 while (priv->chats) {
1228 empathy_chat_window_remove_chat (window, priv->chats->data);
1230 g_object_unref (window);
1236 chat_window_composing_cb (EmpathyChat *chat,
1237 gboolean is_composing,
1238 EmpathyChatWindow *window)
1240 chat_window_update_chat_tab (chat);
1244 chat_window_set_urgency_hint (EmpathyChatWindow *window,
1247 EmpathyChatWindowPriv *priv;
1249 priv = GET_PRIV (window);
1251 gtk_window_set_urgency_hint (GTK_WINDOW (priv->dialog), urgent);
1255 chat_window_notification_closed_cb (NotifyNotification *notify,
1256 EmpathyChatWindow *self)
1258 EmpathyChatWindowPriv *priv = GET_PRIV (self);
1260 g_object_unref (notify);
1261 if (priv->notification == notify) {
1262 priv->notification = NULL;
1267 chat_window_show_or_update_notification (EmpathyChatWindow *window,
1268 EmpathyMessage *message,
1271 EmpathyContact *sender;
1272 const gchar *header;
1276 EmpathyChatWindowPriv *priv = GET_PRIV (window);
1277 gboolean res, has_x_canonical_append;
1278 NotifyNotification *notification = priv->notification;
1280 if (!empathy_notify_manager_notification_is_enabled (priv->notify_mgr)) {
1283 res = g_settings_get_boolean (priv->gsettings_notif,
1284 EMPATHY_PREFS_NOTIFICATIONS_FOCUS);
1291 sender = empathy_message_get_sender (message);
1292 header = empathy_contact_get_alias (sender);
1293 body = empathy_message_get_body (message);
1294 escaped = g_markup_escape_text (body, -1);
1295 has_x_canonical_append = empathy_notify_manager_has_capability (
1296 priv->notify_mgr, EMPATHY_NOTIFY_MANAGER_CAP_X_CANONICAL_APPEND);
1298 if (notification != NULL && !has_x_canonical_append) {
1299 /* if the notification server supports x-canonical-append, it is
1300 better to not use notify_notification_update to avoid
1301 overwriting the current notification message */
1302 notify_notification_update (notification,
1303 header, escaped, NULL);
1305 /* if the notification server supports x-canonical-append,
1306 the hint will be added, so that the message from the
1307 just created notification will be automatically appended
1308 to an existing notification with the same title.
1309 In this way the previous message will not be lost: the new
1310 message will appear below it, in the same notification */
1311 notification = notify_notification_new (header, escaped, NULL);
1313 if (priv->notification == NULL) {
1314 priv->notification = notification;
1317 notify_notification_set_timeout (notification, NOTIFY_EXPIRES_DEFAULT);
1319 tp_g_signal_connect_object (notification, "closed",
1320 G_CALLBACK (chat_window_notification_closed_cb), window, 0);
1322 if (has_x_canonical_append) {
1323 notify_notification_set_hint_string (notification,
1324 EMPATHY_NOTIFY_MANAGER_CAP_X_CANONICAL_APPEND, "");
1328 pixbuf = empathy_notify_manager_get_pixbuf_for_notification (priv->notify_mgr,
1329 sender, EMPATHY_IMAGE_NEW_MESSAGE);
1331 if (pixbuf != NULL) {
1332 notify_notification_set_icon_from_pixbuf (notification, pixbuf);
1333 g_object_unref (pixbuf);
1336 notify_notification_show (notification, NULL);
1342 chat_window_set_highlight_room_labels (EmpathyChat *chat)
1347 if (!empathy_chat_is_room (chat))
1350 markup = g_markup_printf_escaped (
1351 "<span color=\"red\" weight=\"bold\">%s</span>",
1352 empathy_chat_get_name (chat));
1354 widget = g_object_get_data (G_OBJECT (chat), "chat-window-tab-label");
1355 gtk_label_set_markup (GTK_LABEL (widget), markup);
1357 widget = g_object_get_data (G_OBJECT (chat), "chat-window-menu-label");
1358 gtk_label_set_markup (GTK_LABEL (widget), markup);
1364 empathy_chat_window_has_focus (EmpathyChatWindow *window)
1366 EmpathyChatWindowPriv *priv;
1369 g_return_val_if_fail (EMPATHY_IS_CHAT_WINDOW (window), FALSE);
1371 priv = GET_PRIV (window);
1373 g_object_get (priv->dialog, "has-toplevel-focus", &has_focus, NULL);
1379 chat_window_new_message_cb (EmpathyChat *chat,
1380 EmpathyMessage *message,
1382 EmpathyChatWindow *window)
1384 EmpathyChatWindowPriv *priv;
1386 gboolean needs_urgency;
1387 EmpathyContact *sender;
1389 priv = GET_PRIV (window);
1391 has_focus = empathy_chat_window_has_focus (window);
1393 /* - if we're the sender, we play the sound if it's specified in the
1394 * preferences and we're not away.
1395 * - if we receive a message, we play the sound if it's specified in the
1396 * preferences and the window does not have focus on the chat receiving
1400 sender = empathy_message_get_sender (message);
1402 if (empathy_contact_is_user (sender)) {
1403 empathy_sound_manager_play (priv->sound_mgr, GTK_WIDGET (priv->dialog),
1404 EMPATHY_SOUND_MESSAGE_OUTGOING);
1407 if (has_focus && priv->current_chat == chat) {
1408 /* window and tab are focused so consider the message to be read */
1410 /* FIXME: see Bug#610994 and coments about it in EmpathyChatPriv */
1411 empathy_chat_messages_read (chat);
1415 /* Update the chat tab if this is the first unread message */
1416 if (empathy_chat_get_nb_unread_messages (chat) == 1) {
1417 chat_window_update_chat_tab (chat);
1420 /* If empathy_chat_is_room () returns TRUE, that means it's a named MUC.
1421 * If empathy_chat_get_remote_contact () returns NULL, that means it's
1422 * an unamed MUC (msn-like).
1423 * In case of a MUC, we set urgency if either:
1424 * a) the chatroom's always_urgent property is TRUE
1425 * b) the message contains our alias
1427 if (empathy_chat_is_room (chat) ||
1428 empathy_chat_get_remote_contact (chat) == NULL) {
1431 EmpathyChatroom *chatroom;
1433 account = empathy_chat_get_account (chat);
1434 room = empathy_chat_get_id (chat);
1436 chatroom = empathy_chatroom_manager_find (priv->chatroom_manager,
1439 if (empathy_chatroom_is_always_urgent (chatroom)) {
1440 needs_urgency = TRUE;
1442 needs_urgency = empathy_message_should_highlight (message);
1445 needs_urgency = TRUE;
1448 if (needs_urgency) {
1450 chat_window_set_urgency_hint (window, TRUE);
1451 chat_window_set_highlight_room_labels (chat);
1454 empathy_sound_manager_play (priv->sound_mgr, GTK_WIDGET (priv->dialog),
1455 EMPATHY_SOUND_MESSAGE_INCOMING);
1457 /* Pending messages have already been displayed in the approver, so we don't
1458 * display a notification for those. */
1460 chat_window_show_or_update_notification (window, message, chat);
1463 /* update the number of unread messages and the window icon */
1464 chat_window_title_update (priv);
1465 chat_window_icon_update (priv, TRUE);
1468 static GtkNotebook *
1469 notebook_create_window_cb (GtkNotebook *source,
1475 EmpathyChatWindowPriv *priv;
1476 EmpathyChatWindow *window, *new_window;
1479 chat = EMPATHY_CHAT (page);
1480 window = chat_window_find_chat (chat);
1482 new_window = empathy_chat_window_new ();
1483 priv = GET_PRIV (new_window);
1485 DEBUG ("Detach hook called");
1487 empathy_chat_window_move_chat (window, new_window, chat);
1489 gtk_widget_show (priv->dialog);
1490 gtk_window_move (GTK_WINDOW (priv->dialog), x, y);
1496 chat_window_page_switched_cb (GtkNotebook *notebook,
1497 gpointer ignore, /* see note below */
1499 EmpathyChatWindow *window)
1501 EmpathyChatWindowPriv *priv;
1505 DEBUG ("Page switched");
1507 priv = GET_PRIV (window);
1509 /* N.B. in GTK+ 3 child is passed as the first argument to the signal,
1510 * but we can't use that while trying to retain GTK+ 2.x compatibility.
1512 child = gtk_notebook_get_nth_page (notebook, page_num);
1513 chat = EMPATHY_CHAT (child);
1515 if (priv->page_added) {
1516 priv->page_added = FALSE;
1517 empathy_chat_scroll_down (chat);
1519 else if (priv->current_chat == chat) {
1523 priv->current_chat = chat;
1524 empathy_chat_messages_read (chat);
1526 chat_window_update_chat_tab (chat);
1530 chat_window_page_added_cb (GtkNotebook *notebook,
1533 EmpathyChatWindow *window)
1535 EmpathyChatWindowPriv *priv;
1538 priv = GET_PRIV (window);
1540 /* If we just received DND to the same window, we don't want
1541 * to do anything here like removing the tab and then readding
1542 * it, so we return here and in "page-added".
1544 if (priv->dnd_same_window) {
1545 DEBUG ("Page added (back to the same window)");
1546 priv->dnd_same_window = FALSE;
1550 DEBUG ("Page added");
1552 /* Get chat object */
1553 chat = EMPATHY_CHAT (child);
1555 /* Connect chat signals for this window */
1556 g_signal_connect (chat, "composing",
1557 G_CALLBACK (chat_window_composing_cb),
1559 g_signal_connect (chat, "new-message",
1560 G_CALLBACK (chat_window_new_message_cb),
1562 g_signal_connect (chat, "notify::tp-chat",
1563 G_CALLBACK (chat_window_update_chat_tab),
1566 /* Set flag so we know to perform some special operations on
1567 * switch page due to the new page being added.
1569 priv->page_added = TRUE;
1571 /* Get list of chats up to date */
1572 priv->chats = g_list_append (priv->chats, chat);
1574 chat_window_update_chat_tab (chat);
1578 chat_window_page_removed_cb (GtkNotebook *notebook,
1581 EmpathyChatWindow *window)
1583 EmpathyChatWindowPriv *priv;
1586 priv = GET_PRIV (window);
1588 /* If we just received DND to the same window, we don't want
1589 * to do anything here like removing the tab and then readding
1590 * it, so we return here and in "page-added".
1592 if (priv->dnd_same_window) {
1593 DEBUG ("Page removed (and will be readded to same window)");
1597 DEBUG ("Page removed");
1599 /* Get chat object */
1600 chat = EMPATHY_CHAT (child);
1602 /* Disconnect all signal handlers for this chat and this window */
1603 g_signal_handlers_disconnect_by_func (chat,
1604 G_CALLBACK (chat_window_composing_cb),
1606 g_signal_handlers_disconnect_by_func (chat,
1607 G_CALLBACK (chat_window_new_message_cb),
1609 g_signal_handlers_disconnect_by_func (chat,
1610 G_CALLBACK (chat_window_update_chat_tab),
1613 /* Keep list of chats up to date */
1614 priv->chats = g_list_remove (priv->chats, chat);
1615 empathy_chat_messages_read (chat);
1617 if (priv->chats == NULL) {
1618 g_object_unref (window);
1620 chat_window_update (window);
1625 chat_window_focus_in_event_cb (GtkWidget *widget,
1627 EmpathyChatWindow *window)
1629 EmpathyChatWindowPriv *priv;
1631 priv = GET_PRIV (window);
1633 empathy_chat_messages_read (priv->current_chat);
1635 chat_window_set_urgency_hint (window, FALSE);
1637 /* Update the title, since we now mark all unread messages as read. */
1638 chat_window_update_chat_tab (priv->current_chat);
1644 chat_window_drag_drop (GtkWidget *widget,
1645 GdkDragContext *context,
1649 EmpathyChatWindow *window)
1652 EmpathyChatWindowPriv *priv;
1654 priv = GET_PRIV (window);
1656 target = gtk_drag_dest_find_target (widget, context, priv->file_targets);
1657 if (target == GDK_NONE)
1658 target = gtk_drag_dest_find_target (widget, context, priv->contact_targets);
1660 if (target != GDK_NONE) {
1661 gtk_drag_get_data (widget, context, target, time_);
1669 chat_window_drag_motion (GtkWidget *widget,
1670 GdkDragContext *context,
1674 EmpathyChatWindow *window)
1677 EmpathyChatWindowPriv *priv;
1679 priv = GET_PRIV (window);
1681 target = gtk_drag_dest_find_target (widget, context, priv->file_targets);
1682 if (target != GDK_NONE) {
1683 /* This is a file drag. Ensure the contact is online and set the
1684 drag type to COPY. Note that it's possible that the tab will
1685 be switched by GTK+ after a timeout from drag_motion without
1686 getting another drag_motion to disable the drop. You have
1687 to hold your mouse really still.
1689 EmpathyContact *contact;
1691 priv = GET_PRIV (window);
1692 contact = empathy_chat_get_remote_contact (priv->current_chat);
1693 /* contact is NULL for multi-user chats. We don't do
1694 * file transfers to MUCs. We also don't send files
1695 * to offline contacts or contacts that don't support
1698 if ((contact == NULL) || !empathy_contact_is_online (contact)) {
1699 gdk_drag_status (context, 0, time_);
1702 if (!(empathy_contact_get_capabilities (contact)
1703 & EMPATHY_CAPABILITIES_FT)) {
1704 gdk_drag_status (context, 0, time_);
1707 gdk_drag_status (context, GDK_ACTION_COPY, time_);
1711 target = gtk_drag_dest_find_target (widget, context, priv->contact_targets);
1712 if (target != GDK_NONE) {
1713 /* This is a drag of a contact from a contact list. Set to COPY.
1714 FIXME: If this drag is to a MUC window, it invites the user.
1715 Otherwise, it opens a chat. Should we use a different drag
1716 type for invites? Should we allow ASK?
1718 gdk_drag_status (context, GDK_ACTION_COPY, time_);
1726 chat_window_drag_data_received (GtkWidget *widget,
1727 GdkDragContext *context,
1730 GtkSelectionData *selection,
1733 EmpathyChatWindow *window)
1735 if (info == DND_DRAG_TYPE_CONTACT_ID) {
1736 EmpathyChat *chat = NULL;
1737 EmpathyChatWindow *old_window;
1738 TpAccount *account = NULL;
1739 TpAccountManager *account_manager;
1742 const gchar *account_id;
1743 const gchar *contact_id;
1745 id = (const gchar*) gtk_selection_data_get_data (selection);
1747 /* FIXME: Perhaps should be sure that the account manager is
1748 * prepared before calling _ensure_account on it. */
1749 account_manager = tp_account_manager_dup ();
1751 DEBUG ("DND contact from roster with id:'%s'", id);
1753 strv = g_strsplit (id, ":", 2);
1754 if (g_strv_length (strv) == 2) {
1755 account_id = strv[0];
1756 contact_id = strv[1];
1758 tp_account_manager_ensure_account (account_manager, account_id);
1759 if (account != NULL)
1760 chat = empathy_chat_window_find_chat (account, contact_id);
1763 if (account == NULL) {
1765 gtk_drag_finish (context, FALSE, FALSE, time_);
1770 empathy_dispatcher_chat_with_contact_id (
1771 account, contact_id, gtk_get_current_event_time ());
1776 g_object_unref (account_manager);
1779 old_window = chat_window_find_chat (chat);
1781 if (old_window == window) {
1782 gtk_drag_finish (context, TRUE, FALSE, time_);
1786 empathy_chat_window_move_chat (old_window, window, chat);
1788 empathy_chat_window_add_chat (window, chat);
1791 /* Added to take care of any outstanding chat events */
1792 empathy_chat_window_present_chat (chat,
1793 TP_USER_ACTION_TIME_NOT_USER_ACTION);
1795 /* We should return TRUE to remove the data when doing
1796 * GDK_ACTION_MOVE, but we don't here otherwise it has
1797 * weird consequences, and we handle that internally
1798 * anyway with add_chat () and remove_chat ().
1800 gtk_drag_finish (context, TRUE, FALSE, time_);
1802 else if (info == DND_DRAG_TYPE_URI_LIST) {
1803 EmpathyChatWindowPriv *priv;
1804 EmpathyContact *contact;
1807 priv = GET_PRIV (window);
1808 contact = empathy_chat_get_remote_contact (priv->current_chat);
1810 /* contact is NULL when current_chat is a multi-user chat.
1811 * We don't do file transfers to MUCs, so just cancel the drag.
1813 if (contact == NULL) {
1814 gtk_drag_finish (context, TRUE, FALSE, time_);
1818 data = (const gchar *) gtk_selection_data_get_data (selection);
1819 empathy_send_file_from_uri_list (contact, data);
1821 gtk_drag_finish (context, TRUE, FALSE, time_);
1823 else if (info == DND_DRAG_TYPE_TAB) {
1825 EmpathyChatWindow *old_window = NULL;
1829 chat = (void *) gtk_selection_data_get_data (selection);
1830 old_window = chat_window_find_chat (*chat);
1833 EmpathyChatWindowPriv *priv;
1835 priv = GET_PRIV (window);
1836 priv->dnd_same_window = (old_window == window);
1837 DEBUG ("DND tab (within same window: %s)",
1838 priv->dnd_same_window ? "Yes" : "No");
1841 DEBUG ("DND from unknown source");
1842 gtk_drag_finish (context, FALSE, FALSE, time_);
1847 chat_window_chat_manager_chats_changed_cb (EmpathyChatManager *chat_manager,
1848 guint num_chats_in_manager,
1849 EmpathyChatWindow *window)
1851 EmpathyChatWindowPriv *priv = GET_PRIV (window);
1853 gtk_action_set_sensitive (priv->menu_tabs_undo_close_tab,
1854 num_chats_in_manager > 0);
1858 chat_window_finalize (GObject *object)
1860 EmpathyChatWindow *window;
1861 EmpathyChatWindowPriv *priv;
1863 window = EMPATHY_CHAT_WINDOW (object);
1864 priv = GET_PRIV (window);
1866 DEBUG ("Finalized: %p", object);
1868 g_object_unref (priv->ui_manager);
1869 g_object_unref (priv->chatroom_manager);
1870 g_object_unref (priv->notify_mgr);
1871 g_object_unref (priv->gsettings_chat);
1872 g_object_unref (priv->gsettings_notif);
1873 g_object_unref (priv->gsettings_ui);
1874 g_object_unref (priv->sound_mgr);
1876 if (priv->notification != NULL) {
1877 notify_notification_close (priv->notification, NULL);
1878 priv->notification = NULL;
1881 if (priv->contact_targets) {
1882 gtk_target_list_unref (priv->contact_targets);
1884 if (priv->file_targets) {
1885 gtk_target_list_unref (priv->file_targets);
1888 if (priv->chat_manager) {
1889 g_signal_handler_disconnect (priv->chat_manager,
1890 priv->chat_manager_chats_changed_id);
1891 g_object_unref (priv->chat_manager);
1892 priv->chat_manager = NULL;
1895 chat_windows = g_list_remove (chat_windows, window);
1896 gtk_widget_destroy (priv->dialog);
1898 G_OBJECT_CLASS (empathy_chat_window_parent_class)->finalize (object);
1902 empathy_chat_window_class_init (EmpathyChatWindowClass *klass)
1904 GObjectClass *object_class = G_OBJECT_CLASS (klass);
1906 object_class->finalize = chat_window_finalize;
1908 g_type_class_add_private (object_class, sizeof (EmpathyChatWindowPriv));
1910 /* Set up a style for the close button with no focus padding. */
1911 gtk_rc_parse_string (
1912 "style \"empathy-close-button-style\"\n"
1914 " GtkWidget::focus-padding = 0\n"
1918 "widget \"*.empathy-close-button\" style \"empathy-close-button-style\"");
1922 empathy_chat_window_init (EmpathyChatWindow *window)
1925 GtkAccelGroup *accel_group;
1930 GtkWidget *chat_vbox;
1932 EmpathySmileyManager *smiley_manager;
1933 EmpathyChatWindowPriv *priv = G_TYPE_INSTANCE_GET_PRIVATE (window,
1934 EMPATHY_TYPE_CHAT_WINDOW, EmpathyChatWindowPriv);
1936 window->priv = priv;
1937 filename = empathy_file_lookup ("empathy-chat-window.ui", "src");
1938 gui = empathy_builder_get_file (filename,
1939 "chat_window", &priv->dialog,
1940 "chat_vbox", &chat_vbox,
1941 "ui_manager", &priv->ui_manager,
1942 "menu_conv_insert_smiley", &priv->menu_conv_insert_smiley,
1943 "menu_conv_favorite", &priv->menu_conv_favorite,
1944 "menu_conv_always_urgent", &priv->menu_conv_always_urgent,
1945 "menu_conv_toggle_contacts", &priv->menu_conv_toggle_contacts,
1946 "menu_edit_cut", &priv->menu_edit_cut,
1947 "menu_edit_copy", &priv->menu_edit_copy,
1948 "menu_edit_paste", &priv->menu_edit_paste,
1949 "menu_edit_find", &priv->menu_edit_find,
1950 "menu_tabs_next", &priv->menu_tabs_next,
1951 "menu_tabs_prev", &priv->menu_tabs_prev,
1952 "menu_tabs_undo_close_tab", &priv->menu_tabs_undo_close_tab,
1953 "menu_tabs_left", &priv->menu_tabs_left,
1954 "menu_tabs_right", &priv->menu_tabs_right,
1955 "menu_tabs_detach", &priv->menu_tabs_detach,
1959 empathy_builder_connect (gui, window,
1960 "menu_conv", "activate", chat_window_conv_activate_cb,
1961 "menu_conv_clear", "activate", chat_window_clear_activate_cb,
1962 "menu_conv_favorite", "toggled", chat_window_favorite_toggled_cb,
1963 "menu_conv_always_urgent", "toggled", chat_window_always_urgent_toggled_cb,
1964 "menu_conv_toggle_contacts", "toggled", chat_window_contacts_toggled_cb,
1965 "menu_conv_invite_participant", "activate", chat_window_invite_participant_activate_cb,
1966 "menu_conv_close", "activate", chat_window_close_activate_cb,
1967 "menu_edit", "activate", chat_window_edit_activate_cb,
1968 "menu_edit_cut", "activate", chat_window_cut_activate_cb,
1969 "menu_edit_copy", "activate", chat_window_copy_activate_cb,
1970 "menu_edit_paste", "activate", chat_window_paste_activate_cb,
1971 "menu_edit_find", "activate", chat_window_find_activate_cb,
1972 "menu_tabs_next", "activate", chat_window_tabs_next_activate_cb,
1973 "menu_tabs_prev", "activate", chat_window_tabs_previous_activate_cb,
1974 "menu_tabs_undo_close_tab", "activate", chat_window_tabs_undo_close_tab_activate_cb,
1975 "menu_tabs_left", "activate", chat_window_tabs_left_activate_cb,
1976 "menu_tabs_right", "activate", chat_window_tabs_right_activate_cb,
1977 "menu_tabs_detach", "activate", chat_window_detach_activate_cb,
1978 "menu_help_contents", "activate", chat_window_help_contents_activate_cb,
1979 "menu_help_about", "activate", chat_window_help_about_activate_cb,
1982 g_object_ref (priv->ui_manager);
1983 g_object_unref (gui);
1985 priv->gsettings_chat = g_settings_new (EMPATHY_PREFS_CHAT_SCHEMA);
1986 priv->gsettings_notif = g_settings_new (EMPATHY_PREFS_NOTIFICATIONS_SCHEMA);
1987 priv->gsettings_ui = g_settings_new (EMPATHY_PREFS_UI_SCHEMA);
1988 priv->chatroom_manager = empathy_chatroom_manager_dup_singleton (NULL);
1990 priv->sound_mgr = empathy_sound_manager_dup_singleton ();
1992 priv->notebook = gtk_notebook_new ();
1994 g_signal_connect (priv->notebook, "create-window",
1995 G_CALLBACK (notebook_create_window_cb), window);
1997 gtk_notebook_set_group_name (GTK_NOTEBOOK (priv->notebook),
1998 "EmpathyChatWindow");
1999 gtk_notebook_set_scrollable (GTK_NOTEBOOK (priv->notebook), TRUE);
2000 gtk_notebook_popup_enable (GTK_NOTEBOOK (priv->notebook));
2001 gtk_box_pack_start (GTK_BOX (chat_vbox), priv->notebook, TRUE, TRUE, 0);
2002 gtk_widget_show (priv->notebook);
2005 accel_group = gtk_accel_group_new ();
2006 gtk_window_add_accel_group (GTK_WINDOW (priv->dialog), accel_group);
2008 for (i = 0; i < G_N_ELEMENTS (tab_accel_keys); i++) {
2009 closure = g_cclosure_new (G_CALLBACK (chat_window_accel_cb),
2012 gtk_accel_group_connect (accel_group,
2019 g_object_unref (accel_group);
2021 /* Set up drag target lists */
2022 priv->contact_targets = gtk_target_list_new (drag_types_dest_contact,
2023 G_N_ELEMENTS (drag_types_dest_contact));
2024 priv->file_targets = gtk_target_list_new (drag_types_dest_file,
2025 G_N_ELEMENTS (drag_types_dest_file));
2027 /* Set up smiley menu */
2028 smiley_manager = empathy_smiley_manager_dup_singleton ();
2029 submenu = empathy_smiley_menu_new (smiley_manager,
2030 chat_window_insert_smiley_activate_cb,
2032 menu = gtk_ui_manager_get_widget (priv->ui_manager,
2033 "/chats_menubar/menu_conv/menu_conv_insert_smiley");
2034 gtk_menu_item_set_submenu (GTK_MENU_ITEM (menu), submenu);
2035 g_object_unref (smiley_manager);
2037 /* Set up signals we can't do with ui file since we may need to
2038 * block/unblock them at some later stage.
2041 g_signal_connect (priv->dialog,
2043 G_CALLBACK (chat_window_delete_event_cb),
2045 g_signal_connect (priv->dialog,
2047 G_CALLBACK (chat_window_focus_in_event_cb),
2049 g_signal_connect_after (priv->notebook,
2051 G_CALLBACK (chat_window_page_switched_cb),
2053 g_signal_connect (priv->notebook,
2055 G_CALLBACK (chat_window_page_added_cb),
2057 g_signal_connect (priv->notebook,
2059 G_CALLBACK (chat_window_page_removed_cb),
2062 /* Set up drag and drop */
2063 gtk_drag_dest_set (GTK_WIDGET (priv->notebook),
2064 GTK_DEST_DEFAULT_HIGHLIGHT,
2066 G_N_ELEMENTS (drag_types_dest),
2067 GDK_ACTION_MOVE | GDK_ACTION_COPY);
2069 /* connect_after to allow GtkNotebook's built-in tab switching */
2070 g_signal_connect_after (priv->notebook,
2072 G_CALLBACK (chat_window_drag_motion),
2074 g_signal_connect (priv->notebook,
2075 "drag-data-received",
2076 G_CALLBACK (chat_window_drag_data_received),
2078 g_signal_connect (priv->notebook,
2080 G_CALLBACK (chat_window_drag_drop),
2083 chat_windows = g_list_prepend (chat_windows, window);
2085 /* Set up private details */
2087 priv->current_chat = NULL;
2088 priv->notification = NULL;
2090 priv->notify_mgr = empathy_notify_manager_dup_singleton ();
2092 priv->chat_manager = empathy_chat_manager_dup_singleton ();
2093 priv->chat_manager_chats_changed_id =
2094 g_signal_connect (priv->chat_manager, "closed-chats-changed",
2095 G_CALLBACK (chat_window_chat_manager_chats_changed_cb),
2098 chat_window_chat_manager_chats_changed_cb (priv->chat_manager,
2099 empathy_chat_manager_get_num_closed_chats (priv->chat_manager),
2104 empathy_chat_window_get_dialog (EmpathyChatWindow *window)
2106 EmpathyChatWindowPriv *priv;
2108 g_return_val_if_fail (window != NULL, NULL);
2110 priv = GET_PRIV (window);
2112 return priv->dialog;
2115 /* Returns the window to open a new tab in if there is a suitable window,
2116 * otherwise, returns NULL indicating that a new window should be added.
2118 static EmpathyChatWindow *
2119 empathy_chat_window_get_default (gboolean room)
2121 GSettings *gsettings = g_settings_new (EMPATHY_PREFS_UI_SCHEMA);
2123 gboolean separate_windows = TRUE;
2125 separate_windows = g_settings_get_boolean (gsettings,
2126 EMPATHY_PREFS_UI_SEPARATE_CHAT_WINDOWS);
2128 g_object_unref (gsettings);
2130 if (separate_windows) {
2131 /* Always create a new window */
2135 for (l = chat_windows; l; l = l->next) {
2136 EmpathyChatWindowPriv *priv;
2137 EmpathyChatWindow *chat_window;
2139 guint nb_rooms, nb_private;
2141 chat_window = l->data;
2142 priv = GET_PRIV (chat_window);
2144 dialog = empathy_chat_window_get_dialog (chat_window);
2146 empathy_chat_window_get_nb_chats (chat_window, &nb_rooms, &nb_private);
2148 /* Skip the window if there aren't any rooms in it */
2149 if (room && nb_rooms == 0)
2152 /* Skip the window if there aren't any 1-1 chats in it */
2153 if (!room && nb_private == 0)
2156 /* Found a window on this desktop, make it visible if necessary */
2157 if (!empathy_window_get_is_visible (GTK_WINDOW (dialog)))
2158 empathy_window_present (GTK_WINDOW (dialog));
2166 empathy_chat_window_add_chat (EmpathyChatWindow *window,
2169 EmpathyChatWindowPriv *priv;
2171 GtkWidget *popup_label;
2173 GValue value = { 0, };
2175 g_return_if_fail (window != NULL);
2176 g_return_if_fail (EMPATHY_IS_CHAT (chat));
2178 priv = GET_PRIV (window);
2180 /* Reference the chat object */
2181 g_object_ref (chat);
2183 /* If this window has just been created, position it */
2184 if (priv->chats == NULL) {
2185 const gchar *name = "chat-window";
2186 gboolean separate_windows;
2188 separate_windows = g_settings_get_boolean (priv->gsettings_ui,
2189 EMPATHY_PREFS_UI_SEPARATE_CHAT_WINDOWS);
2191 if (empathy_chat_is_room (chat))
2192 name = "room-window";
2194 if (separate_windows) {
2197 /* Save current position of the window */
2198 gtk_window_get_position (GTK_WINDOW (priv->dialog), &x, &y);
2200 /* First bind to the 'generic' name. So new window for which we didn't
2201 * save a geometry yet will have the geometry of the last saved
2202 * window (bgo #601191). */
2203 empathy_geometry_bind (GTK_WINDOW (priv->dialog), name);
2205 /* Restore previous position of the window so the newly created window
2206 * won't be in the same position as the latest saved window and so
2207 * completely hide it. */
2208 gtk_window_move (GTK_WINDOW (priv->dialog), x, y);
2210 /* Then bind it to the name of the contact/room so we'll save the
2211 * geometry specific to this window */
2212 name = empathy_chat_get_id (chat);
2215 empathy_geometry_bind (GTK_WINDOW (priv->dialog), name);
2218 child = GTK_WIDGET (chat);
2219 label = chat_window_create_label (window, chat, TRUE);
2220 popup_label = chat_window_create_label (window, chat, FALSE);
2221 gtk_widget_show (child);
2223 g_signal_connect (chat, "notify::name",
2224 G_CALLBACK (chat_window_chat_notify_cb),
2226 g_signal_connect (chat, "notify::subject",
2227 G_CALLBACK (chat_window_chat_notify_cb),
2229 g_signal_connect (chat, "notify::remote-contact",
2230 G_CALLBACK (chat_window_chat_notify_cb),
2232 chat_window_chat_notify_cb (chat);
2234 gtk_notebook_append_page_menu (GTK_NOTEBOOK (priv->notebook), child, label, popup_label);
2235 gtk_notebook_set_tab_reorderable (GTK_NOTEBOOK (priv->notebook), child, TRUE);
2236 gtk_notebook_set_tab_detachable (GTK_NOTEBOOK (priv->notebook), child, TRUE);
2237 g_value_init (&value, G_TYPE_BOOLEAN);
2238 g_value_set_boolean (&value, TRUE);
2239 gtk_container_child_set_property (GTK_CONTAINER (priv->notebook),
2240 child, "tab-expand" , &value);
2241 gtk_container_child_set_property (GTK_CONTAINER (priv->notebook),
2242 child, "tab-fill" , &value);
2243 g_value_unset (&value);
2245 DEBUG ("Chat added (%d references)", G_OBJECT (chat)->ref_count);
2249 empathy_chat_window_remove_chat (EmpathyChatWindow *window,
2252 EmpathyChatWindowPriv *priv;
2254 EmpathyContact *remote_contact;
2255 EmpathyChatManager *chat_manager;
2257 g_return_if_fail (window != NULL);
2258 g_return_if_fail (EMPATHY_IS_CHAT (chat));
2260 priv = GET_PRIV (window);
2262 g_signal_handlers_disconnect_by_func (chat,
2263 chat_window_chat_notify_cb,
2265 remote_contact = g_object_get_data (G_OBJECT (chat),
2266 "chat-window-remote-contact");
2267 if (remote_contact) {
2268 g_signal_handlers_disconnect_by_func (remote_contact,
2269 chat_window_update_chat_tab,
2273 chat_manager = empathy_chat_manager_dup_singleton ();
2274 empathy_chat_manager_closed_chat (chat_manager, chat);
2275 g_object_unref (chat_manager);
2277 position = gtk_notebook_page_num (GTK_NOTEBOOK (priv->notebook),
2279 gtk_notebook_remove_page (GTK_NOTEBOOK (priv->notebook), position);
2281 DEBUG ("Chat removed (%d references)", G_OBJECT (chat)->ref_count - 1);
2283 g_object_unref (chat);
2287 empathy_chat_window_move_chat (EmpathyChatWindow *old_window,
2288 EmpathyChatWindow *new_window,
2293 g_return_if_fail (EMPATHY_IS_CHAT_WINDOW (old_window));
2294 g_return_if_fail (EMPATHY_IS_CHAT_WINDOW (new_window));
2295 g_return_if_fail (EMPATHY_IS_CHAT (chat));
2297 widget = GTK_WIDGET (chat);
2299 DEBUG ("Chat moving with widget:%p (%d references)", widget,
2300 G_OBJECT (widget)->ref_count);
2302 /* We reference here to make sure we don't loose the widget
2303 * and the EmpathyChat object during the move.
2305 g_object_ref (chat);
2306 g_object_ref (widget);
2308 empathy_chat_window_remove_chat (old_window, chat);
2309 empathy_chat_window_add_chat (new_window, chat);
2311 g_object_unref (widget);
2312 g_object_unref (chat);
2316 empathy_chat_window_switch_to_chat (EmpathyChatWindow *window,
2319 EmpathyChatWindowPriv *priv;
2322 g_return_if_fail (window != NULL);
2323 g_return_if_fail (EMPATHY_IS_CHAT (chat));
2325 priv = GET_PRIV (window);
2327 page_num = gtk_notebook_page_num (GTK_NOTEBOOK (priv->notebook),
2329 gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->notebook),
2334 empathy_chat_window_find_chat (TpAccount *account,
2339 g_return_val_if_fail (!EMP_STR_EMPTY (id), NULL);
2341 for (l = chat_windows; l; l = l->next) {
2342 EmpathyChatWindowPriv *priv;
2343 EmpathyChatWindow *window;
2347 priv = GET_PRIV (window);
2349 for (ll = priv->chats; ll; ll = ll->next) {
2354 if (account == empathy_chat_get_account (chat) &&
2355 !tp_strdiff (id, empathy_chat_get_id (chat))) {
2365 empathy_chat_window_present_chat (EmpathyChat *chat,
2368 EmpathyChatWindow *window;
2369 EmpathyChatWindowPriv *priv;
2370 guint32 x_timestamp;
2372 g_return_if_fail (EMPATHY_IS_CHAT (chat));
2374 window = chat_window_find_chat (chat);
2376 /* If the chat has no window, create one */
2377 if (window == NULL) {
2378 window = empathy_chat_window_get_default (empathy_chat_is_room (chat));
2380 window = empathy_chat_window_new ();
2381 gtk_widget_show_all (GET_PRIV (window)->dialog);
2384 empathy_chat_window_add_chat (window, chat);
2387 /* Don't force the window to show itself when it wasn't
2388 * an action by the user
2390 if (!tp_user_action_time_should_present (timestamp, &x_timestamp))
2393 priv = GET_PRIV (window);
2395 if (x_timestamp != GDK_CURRENT_TIME) {
2396 /* Don't present or switch tab if the action was earlier than the
2397 * last actions X time, accounting for overflow and the first ever
2400 if (priv->x_user_action_time != 0
2401 && X_EARLIER_OR_EQL (x_timestamp, priv->x_user_action_time))
2404 priv->x_user_action_time = x_timestamp;
2407 empathy_chat_window_switch_to_chat (window, chat);
2408 empathy_window_present_with_time (GTK_WINDOW (priv->dialog),
2411 gtk_widget_grab_focus (chat->input_text_view);
2415 empathy_chat_window_get_nb_chats (EmpathyChatWindow *self,
2419 EmpathyChatWindowPriv *priv = GET_PRIV (self);
2421 guint _nb_rooms = 0, _nb_private = 0;
2423 for (l = priv->chats; l != NULL; l = g_list_next (l)) {
2424 if (empathy_chat_is_room (EMPATHY_CHAT (l->data)))
2430 if (nb_rooms != NULL)
2431 *nb_rooms = _nb_rooms;
2432 if (nb_private != NULL)
2433 *nb_private = _nb_private;