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;
317 GtkWidget *sending_spinner;
319 sending_spinner = gtk_spinner_new ();
321 gtk_box_pack_start (GTK_BOX (hbox), sending_spinner,
323 g_object_set_data (G_OBJECT (chat),
324 "chat-window-tab-sending-spinner",
327 close_button = gedit_close_button_new ();
328 g_object_set_data (G_OBJECT (chat), "chat-window-tab-close-button", close_button);
330 /* We don't want focus/keynav for the button to avoid clutter, and
331 * Ctrl-W works anyway.
333 gtk_widget_set_can_focus (close_button, FALSE);
334 gtk_widget_set_can_default (close_button, FALSE);
336 gtk_box_pack_end (GTK_BOX (hbox), close_button, FALSE, FALSE, 0);
338 g_signal_connect (close_button,
340 G_CALLBACK (chat_window_close_clicked_cb),
343 /* React to theme changes and also setup the size correctly. */
344 g_signal_connect (hbox,
346 G_CALLBACK (chat_tab_style_updated_cb),
350 gtk_widget_show_all (hbox);
356 _submenu_notify_visible_changed_cb (GObject *object,
360 g_signal_handlers_disconnect_by_func (object,
361 _submenu_notify_visible_changed_cb,
363 chat_window_update (EMPATHY_CHAT_WINDOW (userdata), TRUE);
367 chat_window_menu_context_update (EmpathyChatWindowPriv *priv,
372 gboolean wrap_around;
373 gboolean is_connected;
376 page_num = gtk_notebook_get_current_page (GTK_NOTEBOOK (priv->notebook));
377 first_page = (page_num == 0);
378 last_page = (page_num == (num_pages - 1));
379 g_object_get (gtk_settings_get_default (), "gtk-keynav-wrap-around",
381 is_connected = empathy_chat_get_tp_chat (priv->current_chat) != NULL;
383 gtk_action_set_sensitive (priv->menu_tabs_next, (!last_page ||
385 gtk_action_set_sensitive (priv->menu_tabs_prev, (!first_page ||
387 gtk_action_set_sensitive (priv->menu_tabs_detach, num_pages > 1);
388 gtk_action_set_sensitive (priv->menu_tabs_left, !first_page);
389 gtk_action_set_sensitive (priv->menu_tabs_right, !last_page);
390 gtk_action_set_sensitive (priv->menu_conv_insert_smiley, is_connected);
394 chat_window_conversation_menu_update (EmpathyChatWindowPriv *priv,
395 EmpathyChatWindow *self)
397 EmpathyTpChat *tp_chat;
398 TpConnection *connection;
400 gboolean sensitive = FALSE;
402 g_return_if_fail (priv->current_chat != NULL);
404 action = gtk_ui_manager_get_action (priv->ui_manager,
405 "/chats_menubar/menu_conv/menu_conv_invite_participant");
406 tp_chat = empathy_chat_get_tp_chat (priv->current_chat);
408 if (tp_chat != NULL) {
409 connection = empathy_tp_chat_get_connection (tp_chat);
411 sensitive = empathy_tp_chat_can_add_contact (tp_chat) &&
412 (tp_connection_get_status (connection, NULL) ==
413 TP_CONNECTION_STATUS_CONNECTED);
416 gtk_action_set_sensitive (action, sensitive);
420 chat_window_contact_menu_update (EmpathyChatWindowPriv *priv,
421 EmpathyChatWindow *window)
423 GtkWidget *menu, *submenu, *orig_submenu;
425 menu = gtk_ui_manager_get_widget (priv->ui_manager,
426 "/chats_menubar/menu_contact");
427 orig_submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (menu));
429 if (orig_submenu == NULL || !gtk_widget_get_visible (orig_submenu)) {
430 submenu = empathy_chat_get_contact_menu (priv->current_chat);
432 if (submenu != NULL) {
433 /* gtk_menu_attach_to_widget () doesn't behave nicely here */
434 g_object_set_data (G_OBJECT (submenu), "window", priv->dialog);
436 gtk_menu_item_set_submenu (GTK_MENU_ITEM (menu), submenu);
437 gtk_widget_show (menu);
438 gtk_widget_set_sensitive (menu, TRUE);
440 gtk_widget_set_sensitive (menu, FALSE);
443 tp_g_signal_connect_object (orig_submenu,
445 (GCallback)_submenu_notify_visible_changed_cb,
451 get_all_unread_messages (EmpathyChatWindowPriv *priv)
456 for (l = priv->chats; l != NULL; l = g_list_next (l))
457 nb += empathy_chat_get_nb_unread_messages (EMPATHY_CHAT (l->data));
463 get_window_title_name (EmpathyChatWindowPriv *priv)
465 gchar *active_name, *ret;
467 guint current_unread_msgs;
469 nb_chats = g_list_length (priv->chats);
470 g_assert (nb_chats > 0);
472 active_name = empathy_chat_dup_name (priv->current_chat);
474 current_unread_msgs = empathy_chat_get_nb_unread_messages (
479 if (current_unread_msgs == 0)
480 ret = g_strdup (active_name);
482 ret = g_strdup_printf (ngettext (
484 "%s (%d unread)", current_unread_msgs),
485 active_name, current_unread_msgs);
487 guint nb_others = nb_chats - 1;
488 guint all_unread_msgs;
490 all_unread_msgs = get_all_unread_messages (priv);
492 if (all_unread_msgs == 0) {
493 /* no unread message */
494 ret = g_strdup_printf (ngettext (
496 "%s (and %u others)", nb_others),
497 active_name, nb_others);
500 else if (all_unread_msgs == current_unread_msgs) {
501 /* unread messages are in the current tab */
502 ret = g_strdup_printf (ngettext (
504 "%s (%d unread)", current_unread_msgs),
505 active_name, current_unread_msgs);
508 else if (current_unread_msgs == 0) {
509 /* unread messages are in other tabs */
510 ret = g_strdup_printf (ngettext (
511 "%s (%d unread from others)",
512 "%s (%d unread from others)",
514 active_name, all_unread_msgs);
518 /* unread messages are in all the tabs */
519 ret = g_strdup_printf (ngettext (
520 "%s (%d unread from all)",
521 "%s (%d unread from all)",
523 active_name, all_unread_msgs);
527 g_free (active_name);
533 chat_window_title_update (EmpathyChatWindowPriv *priv)
537 name = get_window_title_name (priv);
538 gtk_window_set_title (GTK_WINDOW (priv->dialog), name);
543 chat_window_icon_update (EmpathyChatWindowPriv *priv, gboolean new_messages)
546 EmpathyContact *remote_contact;
547 gboolean avatar_in_icon;
550 n_chats = g_list_length (priv->chats);
552 /* Update window icon */
554 gtk_window_set_icon_name (GTK_WINDOW (priv->dialog),
555 EMPATHY_IMAGE_MESSAGE);
557 avatar_in_icon = g_settings_get_boolean (priv->gsettings_chat,
558 EMPATHY_PREFS_CHAT_AVATAR_IN_ICON);
560 if (n_chats == 1 && avatar_in_icon) {
561 remote_contact = empathy_chat_get_remote_contact (priv->current_chat);
562 icon = empathy_pixbuf_avatar_from_contact_scaled (remote_contact, 0, 0);
563 gtk_window_set_icon (GTK_WINDOW (priv->dialog), icon);
566 g_object_unref (icon);
569 gtk_window_set_icon_name (GTK_WINDOW (priv->dialog), NULL);
575 chat_window_close_button_update (EmpathyChatWindowPriv *priv,
579 GtkWidget *chat_close_button;
582 if (num_pages == 1) {
583 chat = gtk_notebook_get_nth_page (GTK_NOTEBOOK (priv->notebook), 0);
584 chat_close_button = g_object_get_data (G_OBJECT (chat),
585 "chat-window-tab-close-button");
586 gtk_widget_hide (chat_close_button);
588 for (i=0; i<num_pages; i++) {
589 chat = gtk_notebook_get_nth_page (GTK_NOTEBOOK (priv->notebook), i);
590 chat_close_button = g_object_get_data (G_OBJECT (chat),
591 "chat-window-tab-close-button");
592 gtk_widget_show (chat_close_button);
598 chat_window_update (EmpathyChatWindow *window,
599 gboolean update_contact_menu)
601 EmpathyChatWindowPriv *priv = GET_PRIV (window);
604 num_pages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (priv->notebook));
606 /* Update Tab menu */
607 chat_window_menu_context_update (priv,
610 chat_window_conversation_menu_update (priv, window);
612 /* If this update is due to a focus-in event, we know the menu will be
613 the same as when we last left it, so no work to do. Besides, if we
614 swap out the menu on a focus-in, we may confuse any external global
616 if (update_contact_menu) {
617 chat_window_contact_menu_update (priv,
621 chat_window_title_update (priv);
623 chat_window_icon_update (priv, get_all_unread_messages (priv) > 0);
625 chat_window_close_button_update (priv,
630 append_markup_printf (GString *string,
637 va_start (args, format);
639 tmp = g_markup_vprintf_escaped (format, args);
640 g_string_append (string, tmp);
647 chat_window_update_chat_tab_full (EmpathyChat *chat,
648 gboolean update_contact_menu)
650 EmpathyChatWindow *window;
651 EmpathyChatWindowPriv *priv;
652 EmpathyContact *remote_contact;
656 const gchar *subject;
657 const gchar *status = NULL;
661 const gchar *icon_name;
662 GtkWidget *tab_image;
663 GtkWidget *menu_image;
664 GtkWidget *sending_spinner;
667 window = chat_window_find_chat (chat);
671 priv = GET_PRIV (window);
673 /* Get information */
674 name = empathy_chat_dup_name (chat);
675 account = empathy_chat_get_account (chat);
676 subject = empathy_chat_get_subject (chat);
677 remote_contact = empathy_chat_get_remote_contact (chat);
679 DEBUG ("Updating chat tab, name=%s, account=%s, subject=%s, remote_contact=%p",
680 name, tp_proxy_get_object_path (account), subject, remote_contact);
682 /* Update tab image */
683 if (empathy_chat_get_tp_chat (chat) == NULL) {
684 /* No TpChat, we are disconnected */
687 else if (empathy_chat_get_nb_unread_messages (chat) > 0) {
688 icon_name = EMPATHY_IMAGE_MESSAGE;
690 else if (remote_contact && empathy_chat_is_composing (chat)) {
691 icon_name = EMPATHY_IMAGE_TYPING;
693 else if (empathy_chat_is_sms_channel (chat)) {
694 icon_name = EMPATHY_IMAGE_SMS;
696 else if (remote_contact) {
697 icon_name = empathy_icon_name_for_contact (remote_contact);
699 icon_name = EMPATHY_IMAGE_GROUP_MESSAGE;
702 tab_image = g_object_get_data (G_OBJECT (chat), "chat-window-tab-image");
703 menu_image = g_object_get_data (G_OBJECT (chat), "chat-window-menu-image");
704 if (icon_name != NULL) {
705 gtk_image_set_from_icon_name (GTK_IMAGE (tab_image), icon_name, GTK_ICON_SIZE_MENU);
706 gtk_widget_show (tab_image);
707 gtk_image_set_from_icon_name (GTK_IMAGE (menu_image), icon_name, GTK_ICON_SIZE_MENU);
708 gtk_widget_show (menu_image);
710 gtk_widget_hide (tab_image);
711 gtk_widget_hide (menu_image);
714 /* Update the sending spinner */
715 nb_sending = empathy_chat_get_n_messages_sending (chat);
716 sending_spinner = g_object_get_data (G_OBJECT (chat),
717 "chat-window-tab-sending-spinner");
719 g_object_set (sending_spinner,
720 "active", nb_sending > 0,
721 "visible", nb_sending > 0,
724 /* Update tab tooltip */
725 tooltip = g_string_new (NULL);
727 if (remote_contact) {
728 id = empathy_contact_get_id (remote_contact);
729 status = empathy_contact_get_presence_message (remote_contact);
734 if (empathy_chat_is_sms_channel (chat)) {
735 append_markup_printf (tooltip, "%s ", _("SMS:"));
738 append_markup_printf (tooltip,
739 "<b>%s</b><small> (%s)</small>",
741 tp_account_get_display_name (account));
743 if (nb_sending > 0) {
744 append_markup_printf (tooltip, "\n");
745 append_markup_printf (tooltip,
746 ngettext ("Sending %d message",
747 "Sending %d messages",
752 if (!EMP_STR_EMPTY (status)) {
753 append_markup_printf (tooltip, "\n<i>%s</i>", status);
757 append_markup_printf (tooltip, "\n<b>%s</b> %s",
758 _("Topic:"), subject);
761 if (remote_contact && empathy_chat_is_composing (chat)) {
762 append_markup_printf (tooltip, "\n%s", _("Typing a message."));
765 markup = g_string_free (tooltip, FALSE);
766 widget = g_object_get_data (G_OBJECT (chat), "chat-window-tab-tooltip-widget");
767 gtk_widget_set_tooltip_markup (widget, markup);
768 widget = g_object_get_data (G_OBJECT (chat), "chat-window-menu-tooltip-widget");
769 gtk_widget_set_tooltip_markup (widget, markup);
772 /* Update tab and menu label */
773 widget = g_object_get_data (G_OBJECT (chat), "chat-window-tab-label");
774 gtk_label_set_text (GTK_LABEL (widget), name);
775 widget = g_object_get_data (G_OBJECT (chat), "chat-window-menu-label");
776 gtk_label_set_text (GTK_LABEL (widget), name);
778 /* Update the window if it's the current chat */
779 if (priv->current_chat == chat) {
780 chat_window_update (window, update_contact_menu);
787 chat_window_update_chat_tab (EmpathyChat *chat)
789 chat_window_update_chat_tab_full (chat, TRUE);
793 chat_window_chat_notify_cb (EmpathyChat *chat)
795 EmpathyContact *old_remote_contact;
796 EmpathyContact *remote_contact = NULL;
798 old_remote_contact = g_object_get_data (G_OBJECT (chat), "chat-window-remote-contact");
799 remote_contact = empathy_chat_get_remote_contact (chat);
801 if (old_remote_contact != remote_contact) {
802 /* The remote-contact associated with the chat changed, we need
803 * to keep track of any change of that contact and update the
804 * window each time. */
805 if (remote_contact) {
806 g_signal_connect_swapped (remote_contact, "notify",
807 G_CALLBACK (chat_window_update_chat_tab),
810 if (old_remote_contact) {
811 g_signal_handlers_disconnect_by_func (old_remote_contact,
812 chat_window_update_chat_tab,
816 g_object_set_data_full (G_OBJECT (chat), "chat-window-remote-contact",
817 g_object_ref (remote_contact), (GDestroyNotify) g_object_unref);
820 chat_window_update_chat_tab (chat);
824 chat_window_insert_smiley_activate_cb (EmpathySmileyManager *manager,
825 EmpathySmiley *smiley,
828 EmpathyChatWindowPriv *priv = GET_PRIV (window);
830 GtkTextBuffer *buffer;
833 chat = priv->current_chat;
835 buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (chat->input_text_view));
836 gtk_text_buffer_get_end_iter (buffer, &iter);
837 gtk_text_buffer_insert (buffer, &iter, smiley->str, -1);
841 chat_window_conv_activate_cb (GtkAction *action,
842 EmpathyChatWindow *window)
844 EmpathyChatWindowPriv *priv = GET_PRIV (window);
847 EmpathyContact *remote_contact = NULL;
849 /* Favorite room menu */
850 is_room = empathy_chat_is_room (priv->current_chat);
854 gboolean found = FALSE;
855 EmpathyChatroom *chatroom;
857 room = empathy_chat_get_id (priv->current_chat);
858 account = empathy_chat_get_account (priv->current_chat);
859 chatroom = empathy_chatroom_manager_find (priv->chatroom_manager,
861 if (chatroom != NULL)
862 found = empathy_chatroom_is_favorite (chatroom);
864 DEBUG ("This room %s favorite", found ? "is" : "is not");
865 gtk_toggle_action_set_active (
866 GTK_TOGGLE_ACTION (priv->menu_conv_favorite), found);
868 if (chatroom != NULL)
869 found = empathy_chatroom_is_always_urgent (chatroom);
871 gtk_toggle_action_set_active (
872 GTK_TOGGLE_ACTION (priv->menu_conv_always_urgent),
875 gtk_action_set_visible (priv->menu_conv_favorite, is_room);
876 gtk_action_set_visible (priv->menu_conv_always_urgent, is_room);
878 /* Show contacts menu */
879 g_object_get (priv->current_chat,
880 "remote-contact", &remote_contact,
881 "show-contacts", &active,
883 if (remote_contact == NULL) {
884 gtk_toggle_action_set_active (
885 GTK_TOGGLE_ACTION (priv->menu_conv_toggle_contacts),
888 gtk_action_set_visible (priv->menu_conv_toggle_contacts,
889 (remote_contact == NULL));
890 if (remote_contact != NULL) {
891 g_object_unref (remote_contact);
896 chat_window_clear_activate_cb (GtkAction *action,
897 EmpathyChatWindow *window)
899 EmpathyChatWindowPriv *priv = GET_PRIV (window);
901 empathy_chat_clear (priv->current_chat);
905 chat_window_favorite_toggled_cb (GtkToggleAction *toggle_action,
906 EmpathyChatWindow *window)
908 EmpathyChatWindowPriv *priv = GET_PRIV (window);
913 EmpathyChatroom *chatroom;
915 active = gtk_toggle_action_get_active (toggle_action);
916 account = empathy_chat_get_account (priv->current_chat);
917 room = empathy_chat_get_id (priv->current_chat);
918 name = empathy_chat_dup_name (priv->current_chat);
920 chatroom = empathy_chatroom_manager_ensure_chatroom (
921 priv->chatroom_manager,
926 empathy_chatroom_set_favorite (chatroom, active);
927 g_object_unref (chatroom);
932 chat_window_always_urgent_toggled_cb (GtkToggleAction *toggle_action,
933 EmpathyChatWindow *window)
935 EmpathyChatWindowPriv *priv = GET_PRIV (window);
940 EmpathyChatroom *chatroom;
942 active = gtk_toggle_action_get_active (toggle_action);
943 account = empathy_chat_get_account (priv->current_chat);
944 room = empathy_chat_get_id (priv->current_chat);
945 name = empathy_chat_dup_name (priv->current_chat);
947 chatroom = empathy_chatroom_manager_ensure_chatroom (
948 priv->chatroom_manager,
953 empathy_chatroom_set_always_urgent (chatroom, active);
954 g_object_unref (chatroom);
959 chat_window_contacts_toggled_cb (GtkToggleAction *toggle_action,
960 EmpathyChatWindow *window)
962 EmpathyChatWindowPriv *priv = GET_PRIV (window);
965 active = gtk_toggle_action_get_active (toggle_action);
967 empathy_chat_set_show_contacts (priv->current_chat, active);
971 got_contact_cb (TpConnection *connection,
972 EmpathyContact *contact,
977 EmpathyTpChat *tp_chat = EMPATHY_TP_CHAT (user_data);
980 DEBUG ("Failed: %s", error->message);
983 empathy_contact_list_add (EMPATHY_CONTACT_LIST (tp_chat),
984 contact, _("Inviting you to this room"));
989 chat_window_invite_participant_activate_cb (GtkAction *action,
990 EmpathyChatWindow *window)
992 EmpathyChatWindowPriv *priv;
994 EmpathyTpChat *tp_chat;
999 priv = GET_PRIV (window);
1001 g_return_if_fail (priv->current_chat != NULL);
1003 tp_chat = empathy_chat_get_tp_chat (priv->current_chat);
1004 channel = empathy_tp_chat_get_channel (tp_chat);
1005 account = empathy_chat_get_account (priv->current_chat);
1007 dialog = empathy_invite_participant_dialog_new (
1008 GTK_WINDOW (priv->dialog), account);
1009 gtk_widget_show (dialog);
1011 response = gtk_dialog_run (GTK_DIALOG (dialog));
1013 if (response == GTK_RESPONSE_ACCEPT) {
1014 TpConnection *connection;
1017 id = empathy_contact_selector_dialog_get_selected (
1018 EMPATHY_CONTACT_SELECTOR_DIALOG (dialog), NULL, NULL);
1019 if (EMP_STR_EMPTY (id)) goto out;
1021 connection = tp_channel_borrow_connection (channel);
1022 empathy_tp_contact_factory_get_from_id (connection, id,
1023 got_contact_cb, tp_chat, NULL, NULL);
1027 gtk_widget_destroy (dialog);
1031 chat_window_close_activate_cb (GtkAction *action,
1032 EmpathyChatWindow *window)
1034 EmpathyChatWindowPriv *priv;
1036 priv = GET_PRIV (window);
1038 g_return_if_fail (priv->current_chat != NULL);
1040 empathy_chat_window_remove_chat (window, priv->current_chat);
1044 chat_window_edit_activate_cb (GtkAction *action,
1045 EmpathyChatWindow *window)
1047 EmpathyChatWindowPriv *priv;
1048 GtkClipboard *clipboard;
1049 GtkTextBuffer *buffer;
1050 gboolean text_available;
1052 priv = GET_PRIV (window);
1054 g_return_if_fail (priv->current_chat != NULL);
1056 if (!empathy_chat_get_tp_chat (priv->current_chat)) {
1057 gtk_action_set_sensitive (priv->menu_edit_copy, FALSE);
1058 gtk_action_set_sensitive (priv->menu_edit_cut, FALSE);
1059 gtk_action_set_sensitive (priv->menu_edit_paste, FALSE);
1063 buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (priv->current_chat->input_text_view));
1064 if (gtk_text_buffer_get_has_selection (buffer)) {
1065 gtk_action_set_sensitive (priv->menu_edit_copy, TRUE);
1066 gtk_action_set_sensitive (priv->menu_edit_cut, TRUE);
1070 selection = empathy_chat_view_get_has_selection (priv->current_chat->view);
1072 gtk_action_set_sensitive (priv->menu_edit_cut, FALSE);
1073 gtk_action_set_sensitive (priv->menu_edit_copy, selection);
1076 clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
1077 text_available = gtk_clipboard_wait_is_text_available (clipboard);
1078 gtk_action_set_sensitive (priv->menu_edit_paste, text_available);
1082 chat_window_cut_activate_cb (GtkAction *action,
1083 EmpathyChatWindow *window)
1085 EmpathyChatWindowPriv *priv;
1087 g_return_if_fail (EMPATHY_IS_CHAT_WINDOW (window));
1089 priv = GET_PRIV (window);
1091 empathy_chat_cut (priv->current_chat);
1095 chat_window_copy_activate_cb (GtkAction *action,
1096 EmpathyChatWindow *window)
1098 EmpathyChatWindowPriv *priv;
1100 g_return_if_fail (EMPATHY_IS_CHAT_WINDOW (window));
1102 priv = GET_PRIV (window);
1104 empathy_chat_copy (priv->current_chat);
1108 chat_window_paste_activate_cb (GtkAction *action,
1109 EmpathyChatWindow *window)
1111 EmpathyChatWindowPriv *priv;
1113 g_return_if_fail (EMPATHY_IS_CHAT_WINDOW (window));
1115 priv = GET_PRIV (window);
1117 empathy_chat_paste (priv->current_chat);
1121 chat_window_find_activate_cb (GtkAction *action,
1122 EmpathyChatWindow *window)
1124 EmpathyChatWindowPriv *priv;
1126 g_return_if_fail (EMPATHY_IS_CHAT_WINDOW (window));
1128 priv = GET_PRIV (window);
1130 empathy_chat_find (priv->current_chat);
1134 chat_window_tabs_next_activate_cb (GtkAction *action,
1135 EmpathyChatWindow *window)
1137 EmpathyChatWindowPriv *priv;
1139 gint index_, numPages;
1140 gboolean wrap_around;
1142 priv = GET_PRIV (window);
1144 g_object_get (gtk_settings_get_default (), "gtk-keynav-wrap-around",
1145 &wrap_around, NULL);
1147 chat = priv->current_chat;
1148 index_ = gtk_notebook_get_current_page (GTK_NOTEBOOK (priv->notebook));
1149 numPages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (priv->notebook));
1151 if (index_ == (numPages - 1) && wrap_around) {
1152 gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->notebook), 0);
1156 gtk_notebook_next_page (GTK_NOTEBOOK (priv->notebook));
1160 chat_window_tabs_previous_activate_cb (GtkAction *action,
1161 EmpathyChatWindow *window)
1163 EmpathyChatWindowPriv *priv;
1165 gint index_, numPages;
1166 gboolean wrap_around;
1168 priv = GET_PRIV (window);
1170 g_object_get (gtk_settings_get_default (), "gtk-keynav-wrap-around",
1171 &wrap_around, NULL);
1173 chat = priv->current_chat;
1174 index_ = gtk_notebook_get_current_page (GTK_NOTEBOOK (priv->notebook));
1175 numPages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (priv->notebook));
1177 if (index_ <= 0 && wrap_around) {
1178 gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->notebook), numPages - 1);
1182 gtk_notebook_prev_page (GTK_NOTEBOOK (priv->notebook));
1186 chat_window_tabs_undo_close_tab_activate_cb (GtkAction *action,
1187 EmpathyChatWindow *window)
1189 EmpathyChatWindowPriv *priv = GET_PRIV (window);
1190 empathy_chat_manager_undo_closed_chat (priv->chat_manager);
1194 chat_window_tabs_left_activate_cb (GtkAction *action,
1195 EmpathyChatWindow *window)
1197 EmpathyChatWindowPriv *priv;
1199 gint index_, num_pages;
1201 priv = GET_PRIV (window);
1203 chat = priv->current_chat;
1204 index_ = gtk_notebook_get_current_page (GTK_NOTEBOOK (priv->notebook));
1209 gtk_notebook_reorder_child (GTK_NOTEBOOK (priv->notebook),
1213 num_pages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (priv->notebook));
1214 chat_window_menu_context_update (priv, num_pages);
1218 chat_window_tabs_right_activate_cb (GtkAction *action,
1219 EmpathyChatWindow *window)
1221 EmpathyChatWindowPriv *priv;
1223 gint index_, num_pages;
1225 priv = GET_PRIV (window);
1227 chat = priv->current_chat;
1228 index_ = gtk_notebook_get_current_page (GTK_NOTEBOOK (priv->notebook));
1230 gtk_notebook_reorder_child (GTK_NOTEBOOK (priv->notebook),
1234 num_pages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (priv->notebook));
1235 chat_window_menu_context_update (priv, num_pages);
1238 static EmpathyChatWindow *
1239 empathy_chat_window_new (void)
1241 return EMPATHY_CHAT_WINDOW (g_object_new (EMPATHY_TYPE_CHAT_WINDOW, NULL));
1245 chat_window_detach_activate_cb (GtkAction *action,
1246 EmpathyChatWindow *window)
1248 EmpathyChatWindowPriv *priv;
1249 EmpathyChatWindow *new_window;
1252 priv = GET_PRIV (window);
1254 chat = priv->current_chat;
1255 new_window = empathy_chat_window_new ();
1257 empathy_chat_window_move_chat (window, new_window, chat);
1259 priv = GET_PRIV (new_window);
1260 gtk_widget_show (priv->dialog);
1264 chat_window_help_contents_activate_cb (GtkAction *action,
1265 EmpathyChatWindow *window)
1267 EmpathyChatWindowPriv *priv = GET_PRIV (window);
1269 empathy_url_show (priv->dialog, "ghelp:empathy");
1273 chat_window_help_about_activate_cb (GtkAction *action,
1274 EmpathyChatWindow *window)
1276 EmpathyChatWindowPriv *priv = GET_PRIV (window);
1278 empathy_about_dialog_new (GTK_WINDOW (priv->dialog));
1282 chat_window_delete_event_cb (GtkWidget *dialog,
1284 EmpathyChatWindow *window)
1286 EmpathyChatWindowPriv *priv = GET_PRIV (window);
1288 DEBUG ("Delete event received");
1290 g_object_ref (window);
1291 while (priv->chats) {
1292 empathy_chat_window_remove_chat (window, priv->chats->data);
1294 g_object_unref (window);
1300 chat_window_composing_cb (EmpathyChat *chat,
1301 gboolean is_composing,
1302 EmpathyChatWindow *window)
1304 chat_window_update_chat_tab (chat);
1308 chat_window_set_urgency_hint (EmpathyChatWindow *window,
1311 EmpathyChatWindowPriv *priv;
1313 priv = GET_PRIV (window);
1315 gtk_window_set_urgency_hint (GTK_WINDOW (priv->dialog), urgent);
1319 chat_window_notification_closed_cb (NotifyNotification *notify,
1320 EmpathyChatWindow *self)
1322 EmpathyChatWindowPriv *priv = GET_PRIV (self);
1324 g_object_unref (notify);
1325 if (priv->notification == notify) {
1326 priv->notification = NULL;
1331 chat_window_show_or_update_notification (EmpathyChatWindow *window,
1332 EmpathyMessage *message,
1335 EmpathyContact *sender;
1336 const gchar *header;
1340 EmpathyChatWindowPriv *priv = GET_PRIV (window);
1341 gboolean res, has_x_canonical_append;
1342 NotifyNotification *notification = priv->notification;
1344 if (!empathy_notify_manager_notification_is_enabled (priv->notify_mgr)) {
1347 res = g_settings_get_boolean (priv->gsettings_notif,
1348 EMPATHY_PREFS_NOTIFICATIONS_FOCUS);
1355 sender = empathy_message_get_sender (message);
1356 header = empathy_contact_get_alias (sender);
1357 body = empathy_message_get_body (message);
1358 escaped = g_markup_escape_text (body, -1);
1359 has_x_canonical_append = empathy_notify_manager_has_capability (
1360 priv->notify_mgr, EMPATHY_NOTIFY_MANAGER_CAP_X_CANONICAL_APPEND);
1362 if (notification != NULL && !has_x_canonical_append) {
1363 /* if the notification server supports x-canonical-append, it is
1364 better to not use notify_notification_update to avoid
1365 overwriting the current notification message */
1366 notify_notification_update (notification,
1367 header, escaped, NULL);
1369 /* if the notification server supports x-canonical-append,
1370 the hint will be added, so that the message from the
1371 just created notification will be automatically appended
1372 to an existing notification with the same title.
1373 In this way the previous message will not be lost: the new
1374 message will appear below it, in the same notification */
1375 notification = notify_notification_new (header, escaped, NULL);
1377 if (priv->notification == NULL) {
1378 priv->notification = notification;
1381 notify_notification_set_timeout (notification, NOTIFY_EXPIRES_DEFAULT);
1383 tp_g_signal_connect_object (notification, "closed",
1384 G_CALLBACK (chat_window_notification_closed_cb), window, 0);
1386 if (has_x_canonical_append) {
1387 notify_notification_set_hint_string (notification,
1388 EMPATHY_NOTIFY_MANAGER_CAP_X_CANONICAL_APPEND, "");
1392 pixbuf = empathy_notify_manager_get_pixbuf_for_notification (priv->notify_mgr,
1393 sender, EMPATHY_IMAGE_NEW_MESSAGE);
1395 if (pixbuf != NULL) {
1396 notify_notification_set_icon_from_pixbuf (notification, pixbuf);
1397 g_object_unref (pixbuf);
1400 notify_notification_show (notification, NULL);
1406 chat_window_set_highlight_room_labels (EmpathyChat *chat)
1408 gchar *markup, *name;
1411 if (!empathy_chat_is_room (chat))
1414 name = empathy_chat_dup_name (chat);
1415 markup = g_markup_printf_escaped (
1416 "<span color=\"red\" weight=\"bold\">%s</span>",
1419 widget = g_object_get_data (G_OBJECT (chat), "chat-window-tab-label");
1420 gtk_label_set_markup (GTK_LABEL (widget), markup);
1422 widget = g_object_get_data (G_OBJECT (chat), "chat-window-menu-label");
1423 gtk_label_set_markup (GTK_LABEL (widget), markup);
1430 empathy_chat_window_has_focus (EmpathyChatWindow *window)
1432 EmpathyChatWindowPriv *priv;
1435 g_return_val_if_fail (EMPATHY_IS_CHAT_WINDOW (window), FALSE);
1437 priv = GET_PRIV (window);
1439 g_object_get (priv->dialog, "has-toplevel-focus", &has_focus, NULL);
1445 chat_window_new_message_cb (EmpathyChat *chat,
1446 EmpathyMessage *message,
1448 EmpathyChatWindow *window)
1450 EmpathyChatWindowPriv *priv;
1452 gboolean needs_urgency;
1453 EmpathyContact *sender;
1455 priv = GET_PRIV (window);
1457 has_focus = empathy_chat_window_has_focus (window);
1459 /* - if we're the sender, we play the sound if it's specified in the
1460 * preferences and we're not away.
1461 * - if we receive a message, we play the sound if it's specified in the
1462 * preferences and the window does not have focus on the chat receiving
1466 sender = empathy_message_get_sender (message);
1468 if (empathy_contact_is_user (sender)) {
1469 empathy_sound_manager_play (priv->sound_mgr, GTK_WIDGET (priv->dialog),
1470 EMPATHY_SOUND_MESSAGE_OUTGOING);
1473 if (has_focus && priv->current_chat == chat) {
1474 /* window and tab are focused so consider the message to be read */
1476 /* FIXME: see Bug#610994 and coments about it in EmpathyChatPriv */
1477 empathy_chat_messages_read (chat);
1481 /* Update the chat tab if this is the first unread message */
1482 if (empathy_chat_get_nb_unread_messages (chat) == 1) {
1483 chat_window_update_chat_tab (chat);
1486 /* If empathy_chat_is_room () returns TRUE, that means it's a named MUC.
1487 * If empathy_chat_get_remote_contact () returns NULL, that means it's
1488 * an unamed MUC (msn-like).
1489 * In case of a MUC, we set urgency if either:
1490 * a) the chatroom's always_urgent property is TRUE
1491 * b) the message contains our alias
1493 if (empathy_chat_is_room (chat) ||
1494 empathy_chat_get_remote_contact (chat) == NULL) {
1497 EmpathyChatroom *chatroom;
1499 account = empathy_chat_get_account (chat);
1500 room = empathy_chat_get_id (chat);
1502 chatroom = empathy_chatroom_manager_find (priv->chatroom_manager,
1505 if (chatroom != NULL && empathy_chatroom_is_always_urgent (chatroom)) {
1506 needs_urgency = TRUE;
1508 needs_urgency = empathy_message_should_highlight (message);
1511 needs_urgency = TRUE;
1514 if (needs_urgency) {
1515 chat_window_set_highlight_room_labels (chat);
1518 chat_window_set_urgency_hint (window, TRUE);
1521 /* Pending messages have already been displayed and notified in the
1522 * approver, so we don't display a notification and play a sound for those */
1524 empathy_sound_manager_play (priv->sound_mgr, GTK_WIDGET (priv->dialog),
1525 EMPATHY_SOUND_MESSAGE_INCOMING);
1527 chat_window_show_or_update_notification (window, message, chat);
1531 /* update the number of unread messages and the window icon */
1532 chat_window_title_update (priv);
1533 chat_window_icon_update (priv, TRUE);
1536 static GtkNotebook *
1537 notebook_create_window_cb (GtkNotebook *source,
1543 EmpathyChatWindowPriv *priv;
1544 EmpathyChatWindow *window, *new_window;
1547 chat = EMPATHY_CHAT (page);
1548 window = chat_window_find_chat (chat);
1550 new_window = empathy_chat_window_new ();
1551 priv = GET_PRIV (new_window);
1553 DEBUG ("Detach hook called");
1555 empathy_chat_window_move_chat (window, new_window, chat);
1557 gtk_widget_show (priv->dialog);
1558 gtk_window_move (GTK_WINDOW (priv->dialog), x, y);
1564 chat_window_page_switched_cb (GtkNotebook *notebook,
1567 EmpathyChatWindow *window)
1569 EmpathyChatWindowPriv *priv = GET_PRIV (window);
1570 EmpathyChat *chat = EMPATHY_CHAT (child);
1572 DEBUG ("Page switched");
1574 if (priv->page_added) {
1575 priv->page_added = FALSE;
1576 empathy_chat_scroll_down (chat);
1578 else if (priv->current_chat == chat) {
1582 priv->current_chat = chat;
1583 empathy_chat_messages_read (chat);
1585 chat_window_update_chat_tab (chat);
1589 chat_window_page_added_cb (GtkNotebook *notebook,
1592 EmpathyChatWindow *window)
1594 EmpathyChatWindowPriv *priv;
1597 priv = GET_PRIV (window);
1599 /* If we just received DND to the same window, we don't want
1600 * to do anything here like removing the tab and then readding
1601 * it, so we return here and in "page-added".
1603 if (priv->dnd_same_window) {
1604 DEBUG ("Page added (back to the same window)");
1605 priv->dnd_same_window = FALSE;
1609 DEBUG ("Page added");
1611 /* Get chat object */
1612 chat = EMPATHY_CHAT (child);
1614 /* Connect chat signals for this window */
1615 g_signal_connect (chat, "composing",
1616 G_CALLBACK (chat_window_composing_cb),
1618 g_signal_connect (chat, "new-message",
1619 G_CALLBACK (chat_window_new_message_cb),
1621 g_signal_connect (chat, "notify::tp-chat",
1622 G_CALLBACK (chat_window_update_chat_tab),
1625 /* Set flag so we know to perform some special operations on
1626 * switch page due to the new page being added.
1628 priv->page_added = TRUE;
1630 /* Get list of chats up to date */
1631 priv->chats = g_list_append (priv->chats, chat);
1633 chat_window_update_chat_tab (chat);
1637 chat_window_page_removed_cb (GtkNotebook *notebook,
1640 EmpathyChatWindow *window)
1642 EmpathyChatWindowPriv *priv;
1645 priv = GET_PRIV (window);
1647 /* If we just received DND to the same window, we don't want
1648 * to do anything here like removing the tab and then readding
1649 * it, so we return here and in "page-added".
1651 if (priv->dnd_same_window) {
1652 DEBUG ("Page removed (and will be readded to same window)");
1656 DEBUG ("Page removed");
1658 /* Get chat object */
1659 chat = EMPATHY_CHAT (child);
1661 /* Disconnect all signal handlers for this chat and this window */
1662 g_signal_handlers_disconnect_by_func (chat,
1663 G_CALLBACK (chat_window_composing_cb),
1665 g_signal_handlers_disconnect_by_func (chat,
1666 G_CALLBACK (chat_window_new_message_cb),
1668 g_signal_handlers_disconnect_by_func (chat,
1669 G_CALLBACK (chat_window_update_chat_tab),
1672 /* Keep list of chats up to date */
1673 priv->chats = g_list_remove (priv->chats, chat);
1674 empathy_chat_messages_read (chat);
1676 if (priv->chats == NULL) {
1677 g_object_unref (window);
1679 chat_window_update (window, TRUE);
1684 chat_window_focus_in_event_cb (GtkWidget *widget,
1686 EmpathyChatWindow *window)
1688 EmpathyChatWindowPriv *priv;
1690 priv = GET_PRIV (window);
1692 empathy_chat_messages_read (priv->current_chat);
1694 chat_window_set_urgency_hint (window, FALSE);
1696 /* Update the title, since we now mark all unread messages as read. */
1697 chat_window_update_chat_tab_full (priv->current_chat, FALSE);
1703 chat_window_drag_drop (GtkWidget *widget,
1704 GdkDragContext *context,
1708 EmpathyChatWindow *window)
1711 EmpathyChatWindowPriv *priv;
1713 priv = GET_PRIV (window);
1715 target = gtk_drag_dest_find_target (widget, context, priv->file_targets);
1716 if (target == GDK_NONE)
1717 target = gtk_drag_dest_find_target (widget, context, priv->contact_targets);
1719 if (target != GDK_NONE) {
1720 gtk_drag_get_data (widget, context, target, time_);
1728 chat_window_drag_motion (GtkWidget *widget,
1729 GdkDragContext *context,
1733 EmpathyChatWindow *window)
1736 EmpathyChatWindowPriv *priv;
1738 priv = GET_PRIV (window);
1740 target = gtk_drag_dest_find_target (widget, context, priv->file_targets);
1741 if (target != GDK_NONE) {
1742 /* This is a file drag. Ensure the contact is online and set the
1743 drag type to COPY. Note that it's possible that the tab will
1744 be switched by GTK+ after a timeout from drag_motion without
1745 getting another drag_motion to disable the drop. You have
1746 to hold your mouse really still.
1748 EmpathyContact *contact;
1750 priv = GET_PRIV (window);
1751 contact = empathy_chat_get_remote_contact (priv->current_chat);
1752 /* contact is NULL for multi-user chats. We don't do
1753 * file transfers to MUCs. We also don't send files
1754 * to offline contacts or contacts that don't support
1757 if ((contact == NULL) || !empathy_contact_is_online (contact)) {
1758 gdk_drag_status (context, 0, time_);
1761 if (!(empathy_contact_get_capabilities (contact)
1762 & EMPATHY_CAPABILITIES_FT)) {
1763 gdk_drag_status (context, 0, time_);
1766 gdk_drag_status (context, GDK_ACTION_COPY, time_);
1770 target = gtk_drag_dest_find_target (widget, context, priv->contact_targets);
1771 if (target != GDK_NONE) {
1772 /* This is a drag of a contact from a contact list. Set to COPY.
1773 FIXME: If this drag is to a MUC window, it invites the user.
1774 Otherwise, it opens a chat. Should we use a different drag
1775 type for invites? Should we allow ASK?
1777 gdk_drag_status (context, GDK_ACTION_COPY, time_);
1785 chat_window_drag_data_received (GtkWidget *widget,
1786 GdkDragContext *context,
1789 GtkSelectionData *selection,
1792 EmpathyChatWindow *window)
1794 if (info == DND_DRAG_TYPE_CONTACT_ID) {
1795 EmpathyChat *chat = NULL;
1796 EmpathyChatWindow *old_window;
1797 TpAccount *account = NULL;
1798 TpAccountManager *account_manager;
1801 const gchar *account_id;
1802 const gchar *contact_id;
1804 id = (const gchar*) gtk_selection_data_get_data (selection);
1806 /* FIXME: Perhaps should be sure that the account manager is
1807 * prepared before calling _ensure_account on it. */
1808 account_manager = tp_account_manager_dup ();
1810 DEBUG ("DND contact from roster with id:'%s'", id);
1812 strv = g_strsplit (id, ":", 2);
1813 if (g_strv_length (strv) == 2) {
1814 account_id = strv[0];
1815 contact_id = strv[1];
1817 tp_account_manager_ensure_account (account_manager, account_id);
1818 if (account != NULL)
1819 chat = empathy_chat_window_find_chat (account, contact_id, FALSE);
1822 if (account == NULL) {
1824 gtk_drag_finish (context, FALSE, FALSE, time_);
1829 empathy_chat_with_contact_id (
1830 account, contact_id, gtk_get_current_event_time ());
1835 g_object_unref (account_manager);
1838 old_window = chat_window_find_chat (chat);
1840 if (old_window == window) {
1841 gtk_drag_finish (context, TRUE, FALSE, time_);
1845 empathy_chat_window_move_chat (old_window, window, chat);
1847 empathy_chat_window_add_chat (window, chat);
1850 /* Added to take care of any outstanding chat events */
1851 empathy_chat_window_present_chat (chat,
1852 TP_USER_ACTION_TIME_NOT_USER_ACTION);
1854 /* We should return TRUE to remove the data when doing
1855 * GDK_ACTION_MOVE, but we don't here otherwise it has
1856 * weird consequences, and we handle that internally
1857 * anyway with add_chat () and remove_chat ().
1859 gtk_drag_finish (context, TRUE, FALSE, time_);
1861 else if (info == DND_DRAG_TYPE_URI_LIST) {
1862 EmpathyChatWindowPriv *priv;
1863 EmpathyContact *contact;
1866 priv = GET_PRIV (window);
1867 contact = empathy_chat_get_remote_contact (priv->current_chat);
1869 /* contact is NULL when current_chat is a multi-user chat.
1870 * We don't do file transfers to MUCs, so just cancel the drag.
1872 if (contact == NULL) {
1873 gtk_drag_finish (context, TRUE, FALSE, time_);
1877 data = (const gchar *) gtk_selection_data_get_data (selection);
1878 empathy_send_file_from_uri_list (contact, data);
1880 gtk_drag_finish (context, TRUE, FALSE, time_);
1882 else if (info == DND_DRAG_TYPE_TAB) {
1884 EmpathyChatWindow *old_window = NULL;
1888 chat = (void *) gtk_selection_data_get_data (selection);
1889 old_window = chat_window_find_chat (*chat);
1892 EmpathyChatWindowPriv *priv;
1894 priv = GET_PRIV (window);
1895 priv->dnd_same_window = (old_window == window);
1896 DEBUG ("DND tab (within same window: %s)",
1897 priv->dnd_same_window ? "Yes" : "No");
1900 DEBUG ("DND from unknown source");
1901 gtk_drag_finish (context, FALSE, FALSE, time_);
1906 chat_window_chat_manager_chats_changed_cb (EmpathyChatManager *chat_manager,
1907 guint num_chats_in_manager,
1908 EmpathyChatWindow *window)
1910 EmpathyChatWindowPriv *priv = GET_PRIV (window);
1912 gtk_action_set_sensitive (priv->menu_tabs_undo_close_tab,
1913 num_chats_in_manager > 0);
1917 chat_window_finalize (GObject *object)
1919 EmpathyChatWindow *window;
1920 EmpathyChatWindowPriv *priv;
1922 window = EMPATHY_CHAT_WINDOW (object);
1923 priv = GET_PRIV (window);
1925 DEBUG ("Finalized: %p", object);
1927 g_object_unref (priv->ui_manager);
1928 g_object_unref (priv->chatroom_manager);
1929 g_object_unref (priv->notify_mgr);
1930 g_object_unref (priv->gsettings_chat);
1931 g_object_unref (priv->gsettings_notif);
1932 g_object_unref (priv->gsettings_ui);
1933 g_object_unref (priv->sound_mgr);
1935 if (priv->notification != NULL) {
1936 notify_notification_close (priv->notification, NULL);
1937 priv->notification = NULL;
1940 if (priv->contact_targets) {
1941 gtk_target_list_unref (priv->contact_targets);
1943 if (priv->file_targets) {
1944 gtk_target_list_unref (priv->file_targets);
1947 if (priv->chat_manager) {
1948 g_signal_handler_disconnect (priv->chat_manager,
1949 priv->chat_manager_chats_changed_id);
1950 g_object_unref (priv->chat_manager);
1951 priv->chat_manager = NULL;
1954 chat_windows = g_list_remove (chat_windows, window);
1955 gtk_widget_destroy (priv->dialog);
1957 G_OBJECT_CLASS (empathy_chat_window_parent_class)->finalize (object);
1961 empathy_chat_window_class_init (EmpathyChatWindowClass *klass)
1963 GObjectClass *object_class = G_OBJECT_CLASS (klass);
1965 object_class->finalize = chat_window_finalize;
1967 g_type_class_add_private (object_class, sizeof (EmpathyChatWindowPriv));
1971 empathy_chat_window_init (EmpathyChatWindow *window)
1974 GtkAccelGroup *accel_group;
1979 GtkWidget *chat_vbox;
1981 EmpathySmileyManager *smiley_manager;
1982 EmpathyChatWindowPriv *priv = G_TYPE_INSTANCE_GET_PRIVATE (window,
1983 EMPATHY_TYPE_CHAT_WINDOW, EmpathyChatWindowPriv);
1985 window->priv = priv;
1986 filename = empathy_file_lookup ("empathy-chat-window.ui", "src");
1987 gui = empathy_builder_get_file (filename,
1988 "chat_window", &priv->dialog,
1989 "chat_vbox", &chat_vbox,
1990 "ui_manager", &priv->ui_manager,
1991 "menu_conv_insert_smiley", &priv->menu_conv_insert_smiley,
1992 "menu_conv_favorite", &priv->menu_conv_favorite,
1993 "menu_conv_always_urgent", &priv->menu_conv_always_urgent,
1994 "menu_conv_toggle_contacts", &priv->menu_conv_toggle_contacts,
1995 "menu_edit_cut", &priv->menu_edit_cut,
1996 "menu_edit_copy", &priv->menu_edit_copy,
1997 "menu_edit_paste", &priv->menu_edit_paste,
1998 "menu_edit_find", &priv->menu_edit_find,
1999 "menu_tabs_next", &priv->menu_tabs_next,
2000 "menu_tabs_prev", &priv->menu_tabs_prev,
2001 "menu_tabs_undo_close_tab", &priv->menu_tabs_undo_close_tab,
2002 "menu_tabs_left", &priv->menu_tabs_left,
2003 "menu_tabs_right", &priv->menu_tabs_right,
2004 "menu_tabs_detach", &priv->menu_tabs_detach,
2008 empathy_builder_connect (gui, window,
2009 "menu_conv", "activate", chat_window_conv_activate_cb,
2010 "menu_conv_clear", "activate", chat_window_clear_activate_cb,
2011 "menu_conv_favorite", "toggled", chat_window_favorite_toggled_cb,
2012 "menu_conv_always_urgent", "toggled", chat_window_always_urgent_toggled_cb,
2013 "menu_conv_toggle_contacts", "toggled", chat_window_contacts_toggled_cb,
2014 "menu_conv_invite_participant", "activate", chat_window_invite_participant_activate_cb,
2015 "menu_conv_close", "activate", chat_window_close_activate_cb,
2016 "menu_edit", "activate", chat_window_edit_activate_cb,
2017 "menu_edit_cut", "activate", chat_window_cut_activate_cb,
2018 "menu_edit_copy", "activate", chat_window_copy_activate_cb,
2019 "menu_edit_paste", "activate", chat_window_paste_activate_cb,
2020 "menu_edit_find", "activate", chat_window_find_activate_cb,
2021 "menu_tabs_next", "activate", chat_window_tabs_next_activate_cb,
2022 "menu_tabs_prev", "activate", chat_window_tabs_previous_activate_cb,
2023 "menu_tabs_undo_close_tab", "activate", chat_window_tabs_undo_close_tab_activate_cb,
2024 "menu_tabs_left", "activate", chat_window_tabs_left_activate_cb,
2025 "menu_tabs_right", "activate", chat_window_tabs_right_activate_cb,
2026 "menu_tabs_detach", "activate", chat_window_detach_activate_cb,
2027 "menu_help_contents", "activate", chat_window_help_contents_activate_cb,
2028 "menu_help_about", "activate", chat_window_help_about_activate_cb,
2031 g_object_ref (priv->ui_manager);
2032 g_object_unref (gui);
2034 priv->gsettings_chat = g_settings_new (EMPATHY_PREFS_CHAT_SCHEMA);
2035 priv->gsettings_notif = g_settings_new (EMPATHY_PREFS_NOTIFICATIONS_SCHEMA);
2036 priv->gsettings_ui = g_settings_new (EMPATHY_PREFS_UI_SCHEMA);
2037 priv->chatroom_manager = empathy_chatroom_manager_dup_singleton (NULL);
2039 priv->sound_mgr = empathy_sound_manager_dup_singleton ();
2041 priv->notebook = gtk_notebook_new ();
2043 g_signal_connect (priv->notebook, "create-window",
2044 G_CALLBACK (notebook_create_window_cb), window);
2046 gtk_notebook_set_group_name (GTK_NOTEBOOK (priv->notebook),
2047 "EmpathyChatWindow");
2048 gtk_notebook_set_scrollable (GTK_NOTEBOOK (priv->notebook), TRUE);
2049 gtk_notebook_popup_enable (GTK_NOTEBOOK (priv->notebook));
2050 gtk_box_pack_start (GTK_BOX (chat_vbox), priv->notebook, TRUE, TRUE, 0);
2051 gtk_widget_show (priv->notebook);
2054 accel_group = gtk_accel_group_new ();
2055 gtk_window_add_accel_group (GTK_WINDOW (priv->dialog), accel_group);
2057 for (i = 0; i < G_N_ELEMENTS (tab_accel_keys); i++) {
2058 closure = g_cclosure_new (G_CALLBACK (chat_window_accel_cb),
2061 gtk_accel_group_connect (accel_group,
2068 g_object_unref (accel_group);
2070 /* Set up drag target lists */
2071 priv->contact_targets = gtk_target_list_new (drag_types_dest_contact,
2072 G_N_ELEMENTS (drag_types_dest_contact));
2073 priv->file_targets = gtk_target_list_new (drag_types_dest_file,
2074 G_N_ELEMENTS (drag_types_dest_file));
2076 /* Set up smiley menu */
2077 smiley_manager = empathy_smiley_manager_dup_singleton ();
2078 submenu = empathy_smiley_menu_new (smiley_manager,
2079 chat_window_insert_smiley_activate_cb,
2081 menu = gtk_ui_manager_get_widget (priv->ui_manager,
2082 "/chats_menubar/menu_conv/menu_conv_insert_smiley");
2083 gtk_menu_item_set_submenu (GTK_MENU_ITEM (menu), submenu);
2084 g_object_unref (smiley_manager);
2086 /* Set up signals we can't do with ui file since we may need to
2087 * block/unblock them at some later stage.
2090 g_signal_connect (priv->dialog,
2092 G_CALLBACK (chat_window_delete_event_cb),
2094 g_signal_connect (priv->dialog,
2096 G_CALLBACK (chat_window_focus_in_event_cb),
2098 g_signal_connect_after (priv->notebook,
2100 G_CALLBACK (chat_window_page_switched_cb),
2102 g_signal_connect (priv->notebook,
2104 G_CALLBACK (chat_window_page_added_cb),
2106 g_signal_connect (priv->notebook,
2108 G_CALLBACK (chat_window_page_removed_cb),
2111 /* Set up drag and drop */
2112 gtk_drag_dest_set (GTK_WIDGET (priv->notebook),
2113 GTK_DEST_DEFAULT_HIGHLIGHT,
2115 G_N_ELEMENTS (drag_types_dest),
2116 GDK_ACTION_MOVE | GDK_ACTION_COPY);
2118 /* connect_after to allow GtkNotebook's built-in tab switching */
2119 g_signal_connect_after (priv->notebook,
2121 G_CALLBACK (chat_window_drag_motion),
2123 g_signal_connect (priv->notebook,
2124 "drag-data-received",
2125 G_CALLBACK (chat_window_drag_data_received),
2127 g_signal_connect (priv->notebook,
2129 G_CALLBACK (chat_window_drag_drop),
2132 chat_windows = g_list_prepend (chat_windows, window);
2134 /* Set up private details */
2136 priv->current_chat = NULL;
2137 priv->notification = NULL;
2139 priv->notify_mgr = empathy_notify_manager_dup_singleton ();
2141 priv->chat_manager = empathy_chat_manager_dup_singleton ();
2142 priv->chat_manager_chats_changed_id =
2143 g_signal_connect (priv->chat_manager, "closed-chats-changed",
2144 G_CALLBACK (chat_window_chat_manager_chats_changed_cb),
2147 chat_window_chat_manager_chats_changed_cb (priv->chat_manager,
2148 empathy_chat_manager_get_num_closed_chats (priv->chat_manager),
2153 empathy_chat_window_get_dialog (EmpathyChatWindow *window)
2155 EmpathyChatWindowPriv *priv;
2157 g_return_val_if_fail (window != NULL, NULL);
2159 priv = GET_PRIV (window);
2161 return priv->dialog;
2164 /* Returns the window to open a new tab in if there is a suitable window,
2165 * otherwise, returns NULL indicating that a new window should be added.
2167 static EmpathyChatWindow *
2168 empathy_chat_window_get_default (gboolean room)
2170 GSettings *gsettings = g_settings_new (EMPATHY_PREFS_UI_SCHEMA);
2172 gboolean separate_windows = TRUE;
2174 separate_windows = g_settings_get_boolean (gsettings,
2175 EMPATHY_PREFS_UI_SEPARATE_CHAT_WINDOWS);
2177 g_object_unref (gsettings);
2179 if (separate_windows) {
2180 /* Always create a new window */
2184 for (l = chat_windows; l; l = l->next) {
2185 EmpathyChatWindowPriv *priv;
2186 EmpathyChatWindow *chat_window;
2188 guint nb_rooms, nb_private;
2190 chat_window = l->data;
2191 priv = GET_PRIV (chat_window);
2193 dialog = empathy_chat_window_get_dialog (chat_window);
2195 empathy_chat_window_get_nb_chats (chat_window, &nb_rooms, &nb_private);
2197 /* Skip the window if there aren't any rooms in it */
2198 if (room && nb_rooms == 0)
2201 /* Skip the window if there aren't any 1-1 chats in it */
2202 if (!room && nb_private == 0)
2205 /* Found a window on this desktop, make it visible if necessary */
2206 if (!empathy_window_get_is_visible (GTK_WINDOW (dialog)))
2207 empathy_window_present (GTK_WINDOW (dialog));
2215 empathy_chat_window_add_chat (EmpathyChatWindow *window,
2218 EmpathyChatWindowPriv *priv;
2220 GtkWidget *popup_label;
2222 GValue value = { 0, };
2224 g_return_if_fail (window != NULL);
2225 g_return_if_fail (EMPATHY_IS_CHAT (chat));
2227 priv = GET_PRIV (window);
2229 /* Reference the chat object */
2230 g_object_ref (chat);
2232 /* If this window has just been created, position it */
2233 if (priv->chats == NULL) {
2234 const gchar *name = "chat-window";
2235 gboolean separate_windows;
2237 separate_windows = g_settings_get_boolean (priv->gsettings_ui,
2238 EMPATHY_PREFS_UI_SEPARATE_CHAT_WINDOWS);
2240 if (empathy_chat_is_room (chat))
2241 name = "room-window";
2243 if (separate_windows) {
2246 /* Save current position of the window */
2247 gtk_window_get_position (GTK_WINDOW (priv->dialog), &x, &y);
2249 /* First bind to the 'generic' name. So new window for which we didn't
2250 * save a geometry yet will have the geometry of the last saved
2251 * window (bgo #601191). */
2252 empathy_geometry_bind (GTK_WINDOW (priv->dialog), name);
2254 /* Restore previous position of the window so the newly created window
2255 * won't be in the same position as the latest saved window and so
2256 * completely hide it. */
2257 gtk_window_move (GTK_WINDOW (priv->dialog), x, y);
2259 /* Then bind it to the name of the contact/room so we'll save the
2260 * geometry specific to this window */
2261 name = empathy_chat_get_id (chat);
2264 empathy_geometry_bind (GTK_WINDOW (priv->dialog), name);
2267 child = GTK_WIDGET (chat);
2268 label = chat_window_create_label (window, chat, TRUE);
2269 popup_label = chat_window_create_label (window, chat, FALSE);
2270 gtk_widget_show (child);
2272 g_signal_connect (chat, "notify::name",
2273 G_CALLBACK (chat_window_chat_notify_cb),
2275 g_signal_connect (chat, "notify::subject",
2276 G_CALLBACK (chat_window_chat_notify_cb),
2278 g_signal_connect (chat, "notify::remote-contact",
2279 G_CALLBACK (chat_window_chat_notify_cb),
2281 g_signal_connect (chat, "notify::sms-channel",
2282 G_CALLBACK (chat_window_chat_notify_cb),
2284 g_signal_connect (chat, "notify::n-messages-sending",
2285 G_CALLBACK (chat_window_chat_notify_cb),
2287 chat_window_chat_notify_cb (chat);
2289 gtk_notebook_append_page_menu (GTK_NOTEBOOK (priv->notebook), child, label, popup_label);
2290 gtk_notebook_set_tab_reorderable (GTK_NOTEBOOK (priv->notebook), child, TRUE);
2291 gtk_notebook_set_tab_detachable (GTK_NOTEBOOK (priv->notebook), child, TRUE);
2292 g_value_init (&value, G_TYPE_BOOLEAN);
2293 g_value_set_boolean (&value, TRUE);
2294 gtk_container_child_set_property (GTK_CONTAINER (priv->notebook),
2295 child, "tab-expand" , &value);
2296 gtk_container_child_set_property (GTK_CONTAINER (priv->notebook),
2297 child, "tab-fill" , &value);
2298 g_value_unset (&value);
2300 DEBUG ("Chat added (%d references)", G_OBJECT (chat)->ref_count);
2304 empathy_chat_window_remove_chat (EmpathyChatWindow *window,
2307 EmpathyChatWindowPriv *priv;
2309 EmpathyContact *remote_contact;
2310 EmpathyChatManager *chat_manager;
2312 g_return_if_fail (window != NULL);
2313 g_return_if_fail (EMPATHY_IS_CHAT (chat));
2315 priv = GET_PRIV (window);
2317 g_signal_handlers_disconnect_by_func (chat,
2318 chat_window_chat_notify_cb,
2320 remote_contact = g_object_get_data (G_OBJECT (chat),
2321 "chat-window-remote-contact");
2322 if (remote_contact) {
2323 g_signal_handlers_disconnect_by_func (remote_contact,
2324 chat_window_update_chat_tab,
2328 chat_manager = empathy_chat_manager_dup_singleton ();
2329 empathy_chat_manager_closed_chat (chat_manager, chat);
2330 g_object_unref (chat_manager);
2332 position = gtk_notebook_page_num (GTK_NOTEBOOK (priv->notebook),
2334 gtk_notebook_remove_page (GTK_NOTEBOOK (priv->notebook), position);
2336 DEBUG ("Chat removed (%d references)", G_OBJECT (chat)->ref_count - 1);
2338 g_object_unref (chat);
2342 empathy_chat_window_move_chat (EmpathyChatWindow *old_window,
2343 EmpathyChatWindow *new_window,
2348 g_return_if_fail (EMPATHY_IS_CHAT_WINDOW (old_window));
2349 g_return_if_fail (EMPATHY_IS_CHAT_WINDOW (new_window));
2350 g_return_if_fail (EMPATHY_IS_CHAT (chat));
2352 widget = GTK_WIDGET (chat);
2354 DEBUG ("Chat moving with widget:%p (%d references)", widget,
2355 G_OBJECT (widget)->ref_count);
2357 /* We reference here to make sure we don't loose the widget
2358 * and the EmpathyChat object during the move.
2360 g_object_ref (chat);
2361 g_object_ref (widget);
2363 empathy_chat_window_remove_chat (old_window, chat);
2364 empathy_chat_window_add_chat (new_window, chat);
2366 g_object_unref (widget);
2367 g_object_unref (chat);
2371 empathy_chat_window_switch_to_chat (EmpathyChatWindow *window,
2374 EmpathyChatWindowPriv *priv;
2377 g_return_if_fail (window != NULL);
2378 g_return_if_fail (EMPATHY_IS_CHAT (chat));
2380 priv = GET_PRIV (window);
2382 page_num = gtk_notebook_page_num (GTK_NOTEBOOK (priv->notebook),
2384 gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->notebook),
2389 empathy_chat_window_find_chat (TpAccount *account,
2391 gboolean sms_channel)
2395 g_return_val_if_fail (!EMP_STR_EMPTY (id), NULL);
2397 for (l = chat_windows; l; l = l->next) {
2398 EmpathyChatWindowPriv *priv;
2399 EmpathyChatWindow *window;
2403 priv = GET_PRIV (window);
2405 for (ll = priv->chats; ll; ll = ll->next) {
2410 if (account == empathy_chat_get_account (chat) &&
2411 !tp_strdiff (id, empathy_chat_get_id (chat)) &&
2412 sms_channel == empathy_chat_is_sms_channel (chat)) {
2422 empathy_chat_window_present_chat (EmpathyChat *chat,
2425 EmpathyChatWindow *window;
2426 EmpathyChatWindowPriv *priv;
2427 guint32 x_timestamp;
2429 g_return_if_fail (EMPATHY_IS_CHAT (chat));
2431 window = chat_window_find_chat (chat);
2433 /* If the chat has no window, create one */
2434 if (window == NULL) {
2435 window = empathy_chat_window_get_default (empathy_chat_is_room (chat));
2437 window = empathy_chat_window_new ();
2440 empathy_chat_window_add_chat (window, chat);
2443 /* Don't force the window to show itself when it wasn't
2444 * an action by the user
2446 if (!tp_user_action_time_should_present (timestamp, &x_timestamp))
2449 priv = GET_PRIV (window);
2451 if (x_timestamp != GDK_CURRENT_TIME) {
2452 /* Don't present or switch tab if the action was earlier than the
2453 * last actions X time, accounting for overflow and the first ever
2456 if (priv->x_user_action_time != 0
2457 && X_EARLIER_OR_EQL (x_timestamp, priv->x_user_action_time))
2460 priv->x_user_action_time = x_timestamp;
2463 empathy_chat_window_switch_to_chat (window, chat);
2464 empathy_window_present_with_time (GTK_WINDOW (priv->dialog),
2467 gtk_widget_grab_focus (chat->input_text_view);
2471 empathy_chat_window_get_nb_chats (EmpathyChatWindow *self,
2475 EmpathyChatWindowPriv *priv = GET_PRIV (self);
2477 guint _nb_rooms = 0, _nb_private = 0;
2479 for (l = priv->chats; l != NULL; l = g_list_next (l)) {
2480 if (empathy_chat_is_room (EMPATHY_CHAT (l->data)))
2486 if (nb_rooms != NULL)
2487 *nb_rooms = _nb_rooms;
2488 if (nb_private != NULL)
2489 *nb_private = _nb_private;