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)
263 GtkWidget *name_label;
264 GtkWidget *status_image;
265 GtkWidget *event_box;
266 GtkWidget *event_box_hbox;
267 PangoAttrList *attr_list;
268 PangoAttribute *attr;
270 /* The spacing between the button and the label. */
271 hbox = gtk_hbox_new (FALSE, 0);
273 event_box = gtk_event_box_new ();
274 gtk_event_box_set_visible_window (GTK_EVENT_BOX (event_box), FALSE);
276 name_label = gtk_label_new (NULL);
278 gtk_label_set_ellipsize (GTK_LABEL (name_label), PANGO_ELLIPSIZE_END);
280 attr_list = pango_attr_list_new ();
281 attr = pango_attr_scale_new (1/1.2);
282 attr->start_index = 0;
283 attr->end_index = -1;
284 pango_attr_list_insert (attr_list, attr);
285 gtk_label_set_attributes (GTK_LABEL (name_label), attr_list);
286 pango_attr_list_unref (attr_list);
288 gtk_misc_set_padding (GTK_MISC (name_label), 2, 0);
289 gtk_misc_set_alignment (GTK_MISC (name_label), 0.0, 0.5);
290 g_object_set_data (G_OBJECT (chat),
291 is_tab_label ? "chat-window-tab-label" : "chat-window-menu-label",
294 status_image = gtk_image_new ();
296 /* Spacing between the icon and label. */
297 event_box_hbox = gtk_hbox_new (FALSE, 0);
299 gtk_box_pack_start (GTK_BOX (event_box_hbox), status_image, FALSE, FALSE, 0);
300 gtk_box_pack_start (GTK_BOX (event_box_hbox), name_label, TRUE, TRUE, 0);
302 g_object_set_data (G_OBJECT (chat),
303 is_tab_label ? "chat-window-tab-image" : "chat-window-menu-image",
305 g_object_set_data (G_OBJECT (chat),
306 is_tab_label ? "chat-window-tab-tooltip-widget" : "chat-window-menu-tooltip-widget",
309 gtk_container_add (GTK_CONTAINER (event_box), event_box_hbox);
310 gtk_box_pack_start (GTK_BOX (hbox), event_box, TRUE, TRUE, 0);
313 GtkWidget *close_button;
314 GtkWidget *sending_spinner;
316 sending_spinner = gtk_spinner_new ();
318 gtk_box_pack_start (GTK_BOX (hbox), sending_spinner,
320 g_object_set_data (G_OBJECT (chat),
321 "chat-window-tab-sending-spinner",
324 close_button = gedit_close_button_new ();
325 g_object_set_data (G_OBJECT (chat), "chat-window-tab-close-button", close_button);
327 /* We don't want focus/keynav for the button to avoid clutter, and
328 * Ctrl-W works anyway.
330 gtk_widget_set_can_focus (close_button, FALSE);
331 gtk_widget_set_can_default (close_button, FALSE);
333 gtk_box_pack_end (GTK_BOX (hbox), close_button, FALSE, FALSE, 0);
335 g_signal_connect (close_button,
337 G_CALLBACK (chat_window_close_clicked_cb),
340 /* React to theme changes and also setup the size correctly. */
341 g_signal_connect (hbox,
343 G_CALLBACK (chat_tab_style_updated_cb),
347 gtk_widget_show_all (hbox);
353 _submenu_notify_visible_changed_cb (GObject *object,
357 g_signal_handlers_disconnect_by_func (object,
358 _submenu_notify_visible_changed_cb,
360 chat_window_update (EMPATHY_CHAT_WINDOW (userdata), TRUE);
364 chat_window_menu_context_update (EmpathyChatWindowPriv *priv,
369 gboolean wrap_around;
370 gboolean is_connected;
373 page_num = gtk_notebook_get_current_page (GTK_NOTEBOOK (priv->notebook));
374 first_page = (page_num == 0);
375 last_page = (page_num == (num_pages - 1));
376 g_object_get (gtk_settings_get_default (), "gtk-keynav-wrap-around",
378 is_connected = empathy_chat_get_tp_chat (priv->current_chat) != NULL;
380 gtk_action_set_sensitive (priv->menu_tabs_next, (!last_page ||
382 gtk_action_set_sensitive (priv->menu_tabs_prev, (!first_page ||
384 gtk_action_set_sensitive (priv->menu_tabs_detach, num_pages > 1);
385 gtk_action_set_sensitive (priv->menu_tabs_left, !first_page);
386 gtk_action_set_sensitive (priv->menu_tabs_right, !last_page);
387 gtk_action_set_sensitive (priv->menu_conv_insert_smiley, is_connected);
391 chat_window_conversation_menu_update (EmpathyChatWindowPriv *priv,
392 EmpathyChatWindow *self)
394 EmpathyTpChat *tp_chat;
395 TpConnection *connection;
397 gboolean sensitive = FALSE;
399 g_return_if_fail (priv->current_chat != NULL);
401 action = gtk_ui_manager_get_action (priv->ui_manager,
402 "/chats_menubar/menu_conv/menu_conv_invite_participant");
403 tp_chat = empathy_chat_get_tp_chat (priv->current_chat);
405 if (tp_chat != NULL) {
406 connection = empathy_tp_chat_get_connection (tp_chat);
408 sensitive = empathy_tp_chat_can_add_contact (tp_chat) &&
409 (tp_connection_get_status (connection, NULL) ==
410 TP_CONNECTION_STATUS_CONNECTED);
413 gtk_action_set_sensitive (action, sensitive);
417 chat_window_contact_menu_update (EmpathyChatWindowPriv *priv,
418 EmpathyChatWindow *window)
420 GtkWidget *menu, *submenu, *orig_submenu;
422 menu = gtk_ui_manager_get_widget (priv->ui_manager,
423 "/chats_menubar/menu_contact");
424 orig_submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (menu));
426 if (orig_submenu == NULL || !gtk_widget_get_visible (orig_submenu)) {
427 submenu = empathy_chat_get_contact_menu (priv->current_chat);
429 if (submenu != NULL) {
430 /* gtk_menu_attach_to_widget () doesn't behave nicely here */
431 g_object_set_data (G_OBJECT (submenu), "window", priv->dialog);
433 gtk_menu_item_set_submenu (GTK_MENU_ITEM (menu), submenu);
434 gtk_widget_show (menu);
435 gtk_widget_set_sensitive (menu, TRUE);
437 gtk_widget_set_sensitive (menu, FALSE);
440 tp_g_signal_connect_object (orig_submenu,
442 (GCallback)_submenu_notify_visible_changed_cb,
448 get_all_unread_messages (EmpathyChatWindowPriv *priv)
453 for (l = priv->chats; l != NULL; l = g_list_next (l))
454 nb += empathy_chat_get_nb_unread_messages (EMPATHY_CHAT (l->data));
460 get_window_title_name (EmpathyChatWindowPriv *priv)
462 gchar *active_name, *ret;
464 guint current_unread_msgs;
466 nb_chats = g_list_length (priv->chats);
467 g_assert (nb_chats > 0);
469 active_name = empathy_chat_dup_name (priv->current_chat);
471 current_unread_msgs = empathy_chat_get_nb_unread_messages (
476 if (current_unread_msgs == 0)
477 ret = g_strdup (active_name);
479 ret = g_strdup_printf (ngettext (
481 "%s (%d unread)", current_unread_msgs),
482 active_name, current_unread_msgs);
484 guint nb_others = nb_chats - 1;
485 guint all_unread_msgs;
487 all_unread_msgs = get_all_unread_messages (priv);
489 if (all_unread_msgs == 0) {
490 /* no unread message */
491 ret = g_strdup_printf (ngettext (
493 "%s (and %u others)", nb_others),
494 active_name, nb_others);
497 else if (all_unread_msgs == current_unread_msgs) {
498 /* unread messages are in the current tab */
499 ret = g_strdup_printf (ngettext (
501 "%s (%d unread)", current_unread_msgs),
502 active_name, current_unread_msgs);
505 else if (current_unread_msgs == 0) {
506 /* unread messages are in other tabs */
507 ret = g_strdup_printf (ngettext (
508 "%s (%d unread from others)",
509 "%s (%d unread from others)",
511 active_name, all_unread_msgs);
515 /* unread messages are in all the tabs */
516 ret = g_strdup_printf (ngettext (
517 "%s (%d unread from all)",
518 "%s (%d unread from all)",
520 active_name, all_unread_msgs);
524 g_free (active_name);
530 chat_window_title_update (EmpathyChatWindowPriv *priv)
534 name = get_window_title_name (priv);
535 gtk_window_set_title (GTK_WINDOW (priv->dialog), name);
540 chat_window_icon_update (EmpathyChatWindowPriv *priv, gboolean new_messages)
543 EmpathyContact *remote_contact;
544 gboolean avatar_in_icon;
547 n_chats = g_list_length (priv->chats);
549 /* Update window icon */
551 gtk_window_set_icon_name (GTK_WINDOW (priv->dialog),
552 EMPATHY_IMAGE_MESSAGE);
554 avatar_in_icon = g_settings_get_boolean (priv->gsettings_chat,
555 EMPATHY_PREFS_CHAT_AVATAR_IN_ICON);
557 if (n_chats == 1 && avatar_in_icon) {
558 remote_contact = empathy_chat_get_remote_contact (priv->current_chat);
559 icon = empathy_pixbuf_avatar_from_contact_scaled (remote_contact, 0, 0);
560 gtk_window_set_icon (GTK_WINDOW (priv->dialog), icon);
563 g_object_unref (icon);
566 gtk_window_set_icon_name (GTK_WINDOW (priv->dialog), NULL);
572 chat_window_close_button_update (EmpathyChatWindowPriv *priv,
576 GtkWidget *chat_close_button;
579 if (num_pages == 1) {
580 chat = gtk_notebook_get_nth_page (GTK_NOTEBOOK (priv->notebook), 0);
581 chat_close_button = g_object_get_data (G_OBJECT (chat),
582 "chat-window-tab-close-button");
583 gtk_widget_hide (chat_close_button);
585 for (i=0; i<num_pages; i++) {
586 chat = gtk_notebook_get_nth_page (GTK_NOTEBOOK (priv->notebook), i);
587 chat_close_button = g_object_get_data (G_OBJECT (chat),
588 "chat-window-tab-close-button");
589 gtk_widget_show (chat_close_button);
595 chat_window_update (EmpathyChatWindow *window,
596 gboolean update_contact_menu)
598 EmpathyChatWindowPriv *priv = GET_PRIV (window);
601 num_pages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (priv->notebook));
603 /* Update Tab menu */
604 chat_window_menu_context_update (priv,
607 chat_window_conversation_menu_update (priv, window);
609 /* If this update is due to a focus-in event, we know the menu will be
610 the same as when we last left it, so no work to do. Besides, if we
611 swap out the menu on a focus-in, we may confuse any external global
613 if (update_contact_menu) {
614 chat_window_contact_menu_update (priv,
618 chat_window_title_update (priv);
620 chat_window_icon_update (priv, get_all_unread_messages (priv) > 0);
622 chat_window_close_button_update (priv,
627 append_markup_printf (GString *string,
634 va_start (args, format);
636 tmp = g_markup_vprintf_escaped (format, args);
637 g_string_append (string, tmp);
644 chat_window_update_chat_tab_full (EmpathyChat *chat,
645 gboolean update_contact_menu)
647 EmpathyChatWindow *window;
648 EmpathyChatWindowPriv *priv;
649 EmpathyContact *remote_contact;
653 const gchar *subject;
654 const gchar *status = NULL;
658 const gchar *icon_name;
659 GtkWidget *tab_image;
660 GtkWidget *menu_image;
661 GtkWidget *sending_spinner;
664 window = chat_window_find_chat (chat);
668 priv = GET_PRIV (window);
670 /* Get information */
671 name = empathy_chat_dup_name (chat);
672 account = empathy_chat_get_account (chat);
673 subject = empathy_chat_get_subject (chat);
674 remote_contact = empathy_chat_get_remote_contact (chat);
676 DEBUG ("Updating chat tab, name=%s, account=%s, subject=%s, remote_contact=%p",
677 name, tp_proxy_get_object_path (account), subject, remote_contact);
679 /* Update tab image */
680 if (empathy_chat_get_tp_chat (chat) == NULL) {
681 /* No TpChat, we are disconnected */
684 else if (empathy_chat_get_nb_unread_messages (chat) > 0) {
685 icon_name = EMPATHY_IMAGE_MESSAGE;
687 else if (remote_contact && empathy_chat_is_composing (chat)) {
688 icon_name = EMPATHY_IMAGE_TYPING;
690 else if (empathy_chat_is_sms_channel (chat)) {
691 icon_name = EMPATHY_IMAGE_SMS;
693 else if (remote_contact) {
694 icon_name = empathy_icon_name_for_contact (remote_contact);
696 icon_name = EMPATHY_IMAGE_GROUP_MESSAGE;
699 tab_image = g_object_get_data (G_OBJECT (chat), "chat-window-tab-image");
700 menu_image = g_object_get_data (G_OBJECT (chat), "chat-window-menu-image");
701 if (icon_name != NULL) {
702 gtk_image_set_from_icon_name (GTK_IMAGE (tab_image), icon_name, GTK_ICON_SIZE_MENU);
703 gtk_widget_show (tab_image);
704 gtk_image_set_from_icon_name (GTK_IMAGE (menu_image), icon_name, GTK_ICON_SIZE_MENU);
705 gtk_widget_show (menu_image);
707 gtk_widget_hide (tab_image);
708 gtk_widget_hide (menu_image);
711 /* Update the sending spinner */
712 nb_sending = empathy_chat_get_n_messages_sending (chat);
713 sending_spinner = g_object_get_data (G_OBJECT (chat),
714 "chat-window-tab-sending-spinner");
716 g_object_set (sending_spinner,
717 "active", nb_sending > 0,
718 "visible", nb_sending > 0,
721 /* Update tab tooltip */
722 tooltip = g_string_new (NULL);
724 if (remote_contact) {
725 id = empathy_contact_get_id (remote_contact);
726 status = empathy_contact_get_presence_message (remote_contact);
731 if (empathy_chat_is_sms_channel (chat)) {
732 append_markup_printf (tooltip, "%s ", _("SMS:"));
735 append_markup_printf (tooltip,
736 "<b>%s</b><small> (%s)</small>",
738 tp_account_get_display_name (account));
740 if (nb_sending > 0) {
741 char *tmp = g_strdup_printf (
742 ngettext ("Sending %d message",
743 "Sending %d messages",
747 g_string_append (tooltip, "\n");
748 g_string_append (tooltip, tmp);
750 gtk_widget_set_tooltip_text (sending_spinner, tmp);
754 if (!EMP_STR_EMPTY (status)) {
755 append_markup_printf (tooltip, "\n<i>%s</i>", status);
759 append_markup_printf (tooltip, "\n<b>%s</b> %s",
760 _("Topic:"), subject);
763 if (remote_contact && empathy_chat_is_composing (chat)) {
764 append_markup_printf (tooltip, "\n%s", _("Typing a message."));
767 markup = g_string_free (tooltip, FALSE);
768 widget = g_object_get_data (G_OBJECT (chat), "chat-window-tab-tooltip-widget");
769 gtk_widget_set_tooltip_markup (widget, markup);
770 widget = g_object_get_data (G_OBJECT (chat), "chat-window-menu-tooltip-widget");
771 gtk_widget_set_tooltip_markup (widget, markup);
774 /* Update tab and menu label */
775 widget = g_object_get_data (G_OBJECT (chat), "chat-window-tab-label");
776 gtk_label_set_text (GTK_LABEL (widget), name);
777 widget = g_object_get_data (G_OBJECT (chat), "chat-window-menu-label");
778 gtk_label_set_text (GTK_LABEL (widget), name);
780 /* Update the window if it's the current chat */
781 if (priv->current_chat == chat) {
782 chat_window_update (window, update_contact_menu);
789 chat_window_update_chat_tab (EmpathyChat *chat)
791 chat_window_update_chat_tab_full (chat, TRUE);
795 chat_window_chat_notify_cb (EmpathyChat *chat)
797 EmpathyContact *old_remote_contact;
798 EmpathyContact *remote_contact = NULL;
800 old_remote_contact = g_object_get_data (G_OBJECT (chat), "chat-window-remote-contact");
801 remote_contact = empathy_chat_get_remote_contact (chat);
803 if (old_remote_contact != remote_contact) {
804 /* The remote-contact associated with the chat changed, we need
805 * to keep track of any change of that contact and update the
806 * window each time. */
807 if (remote_contact) {
808 g_signal_connect_swapped (remote_contact, "notify",
809 G_CALLBACK (chat_window_update_chat_tab),
812 if (old_remote_contact) {
813 g_signal_handlers_disconnect_by_func (old_remote_contact,
814 chat_window_update_chat_tab,
818 g_object_set_data_full (G_OBJECT (chat), "chat-window-remote-contact",
819 g_object_ref (remote_contact), (GDestroyNotify) g_object_unref);
822 chat_window_update_chat_tab (chat);
826 chat_window_insert_smiley_activate_cb (EmpathySmileyManager *manager,
827 EmpathySmiley *smiley,
830 EmpathyChatWindowPriv *priv = GET_PRIV (window);
832 GtkTextBuffer *buffer;
835 chat = priv->current_chat;
837 buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (chat->input_text_view));
838 gtk_text_buffer_get_end_iter (buffer, &iter);
839 gtk_text_buffer_insert (buffer, &iter, smiley->str, -1);
843 chat_window_conv_activate_cb (GtkAction *action,
844 EmpathyChatWindow *window)
846 EmpathyChatWindowPriv *priv = GET_PRIV (window);
849 EmpathyContact *remote_contact = NULL;
851 /* Favorite room menu */
852 is_room = empathy_chat_is_room (priv->current_chat);
856 gboolean found = FALSE;
857 EmpathyChatroom *chatroom;
859 room = empathy_chat_get_id (priv->current_chat);
860 account = empathy_chat_get_account (priv->current_chat);
861 chatroom = empathy_chatroom_manager_find (priv->chatroom_manager,
863 if (chatroom != NULL)
864 found = empathy_chatroom_is_favorite (chatroom);
866 DEBUG ("This room %s favorite", found ? "is" : "is not");
867 gtk_toggle_action_set_active (
868 GTK_TOGGLE_ACTION (priv->menu_conv_favorite), found);
870 if (chatroom != NULL)
871 found = empathy_chatroom_is_always_urgent (chatroom);
873 gtk_toggle_action_set_active (
874 GTK_TOGGLE_ACTION (priv->menu_conv_always_urgent),
877 gtk_action_set_visible (priv->menu_conv_favorite, is_room);
878 gtk_action_set_visible (priv->menu_conv_always_urgent, is_room);
880 /* Show contacts menu */
881 g_object_get (priv->current_chat,
882 "remote-contact", &remote_contact,
883 "show-contacts", &active,
885 if (remote_contact == NULL) {
886 gtk_toggle_action_set_active (
887 GTK_TOGGLE_ACTION (priv->menu_conv_toggle_contacts),
890 gtk_action_set_visible (priv->menu_conv_toggle_contacts,
891 (remote_contact == NULL));
892 if (remote_contact != NULL) {
893 g_object_unref (remote_contact);
898 chat_window_clear_activate_cb (GtkAction *action,
899 EmpathyChatWindow *window)
901 EmpathyChatWindowPriv *priv = GET_PRIV (window);
903 empathy_chat_clear (priv->current_chat);
907 chat_window_favorite_toggled_cb (GtkToggleAction *toggle_action,
908 EmpathyChatWindow *window)
910 EmpathyChatWindowPriv *priv = GET_PRIV (window);
915 EmpathyChatroom *chatroom;
917 active = gtk_toggle_action_get_active (toggle_action);
918 account = empathy_chat_get_account (priv->current_chat);
919 room = empathy_chat_get_id (priv->current_chat);
920 name = empathy_chat_dup_name (priv->current_chat);
922 chatroom = empathy_chatroom_manager_ensure_chatroom (
923 priv->chatroom_manager,
928 empathy_chatroom_set_favorite (chatroom, active);
929 g_object_unref (chatroom);
934 chat_window_always_urgent_toggled_cb (GtkToggleAction *toggle_action,
935 EmpathyChatWindow *window)
937 EmpathyChatWindowPriv *priv = GET_PRIV (window);
942 EmpathyChatroom *chatroom;
944 active = gtk_toggle_action_get_active (toggle_action);
945 account = empathy_chat_get_account (priv->current_chat);
946 room = empathy_chat_get_id (priv->current_chat);
947 name = empathy_chat_dup_name (priv->current_chat);
949 chatroom = empathy_chatroom_manager_ensure_chatroom (
950 priv->chatroom_manager,
955 empathy_chatroom_set_always_urgent (chatroom, active);
956 g_object_unref (chatroom);
961 chat_window_contacts_toggled_cb (GtkToggleAction *toggle_action,
962 EmpathyChatWindow *window)
964 EmpathyChatWindowPriv *priv = GET_PRIV (window);
967 active = gtk_toggle_action_get_active (toggle_action);
969 empathy_chat_set_show_contacts (priv->current_chat, active);
973 got_contact_cb (TpConnection *connection,
974 EmpathyContact *contact,
979 EmpathyTpChat *tp_chat = EMPATHY_TP_CHAT (user_data);
982 DEBUG ("Failed: %s", error->message);
985 empathy_contact_list_add (EMPATHY_CONTACT_LIST (tp_chat),
986 contact, _("Inviting you to this room"));
991 chat_window_invite_participant_activate_cb (GtkAction *action,
992 EmpathyChatWindow *window)
994 EmpathyChatWindowPriv *priv;
996 EmpathyTpChat *tp_chat;
1001 priv = GET_PRIV (window);
1003 g_return_if_fail (priv->current_chat != NULL);
1005 tp_chat = empathy_chat_get_tp_chat (priv->current_chat);
1006 channel = empathy_tp_chat_get_channel (tp_chat);
1007 account = empathy_chat_get_account (priv->current_chat);
1009 dialog = empathy_invite_participant_dialog_new (
1010 GTK_WINDOW (priv->dialog), account);
1011 gtk_widget_show (dialog);
1013 response = gtk_dialog_run (GTK_DIALOG (dialog));
1015 if (response == GTK_RESPONSE_ACCEPT) {
1016 TpConnection *connection;
1019 id = empathy_contact_selector_dialog_get_selected (
1020 EMPATHY_CONTACT_SELECTOR_DIALOG (dialog), NULL, NULL);
1021 if (EMP_STR_EMPTY (id)) goto out;
1023 connection = tp_channel_borrow_connection (channel);
1024 empathy_tp_contact_factory_get_from_id (connection, id,
1025 got_contact_cb, tp_chat, NULL, NULL);
1029 gtk_widget_destroy (dialog);
1033 chat_window_close_activate_cb (GtkAction *action,
1034 EmpathyChatWindow *window)
1036 EmpathyChatWindowPriv *priv;
1038 priv = GET_PRIV (window);
1040 g_return_if_fail (priv->current_chat != NULL);
1042 empathy_chat_window_remove_chat (window, priv->current_chat);
1046 chat_window_edit_activate_cb (GtkAction *action,
1047 EmpathyChatWindow *window)
1049 EmpathyChatWindowPriv *priv;
1050 GtkClipboard *clipboard;
1051 GtkTextBuffer *buffer;
1052 gboolean text_available;
1054 priv = GET_PRIV (window);
1056 g_return_if_fail (priv->current_chat != NULL);
1058 if (!empathy_chat_get_tp_chat (priv->current_chat)) {
1059 gtk_action_set_sensitive (priv->menu_edit_copy, FALSE);
1060 gtk_action_set_sensitive (priv->menu_edit_cut, FALSE);
1061 gtk_action_set_sensitive (priv->menu_edit_paste, FALSE);
1065 buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (priv->current_chat->input_text_view));
1066 if (gtk_text_buffer_get_has_selection (buffer)) {
1067 gtk_action_set_sensitive (priv->menu_edit_copy, TRUE);
1068 gtk_action_set_sensitive (priv->menu_edit_cut, TRUE);
1072 selection = empathy_chat_view_get_has_selection (priv->current_chat->view);
1074 gtk_action_set_sensitive (priv->menu_edit_cut, FALSE);
1075 gtk_action_set_sensitive (priv->menu_edit_copy, selection);
1078 clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
1079 text_available = gtk_clipboard_wait_is_text_available (clipboard);
1080 gtk_action_set_sensitive (priv->menu_edit_paste, text_available);
1084 chat_window_cut_activate_cb (GtkAction *action,
1085 EmpathyChatWindow *window)
1087 EmpathyChatWindowPriv *priv;
1089 g_return_if_fail (EMPATHY_IS_CHAT_WINDOW (window));
1091 priv = GET_PRIV (window);
1093 empathy_chat_cut (priv->current_chat);
1097 chat_window_copy_activate_cb (GtkAction *action,
1098 EmpathyChatWindow *window)
1100 EmpathyChatWindowPriv *priv;
1102 g_return_if_fail (EMPATHY_IS_CHAT_WINDOW (window));
1104 priv = GET_PRIV (window);
1106 empathy_chat_copy (priv->current_chat);
1110 chat_window_paste_activate_cb (GtkAction *action,
1111 EmpathyChatWindow *window)
1113 EmpathyChatWindowPriv *priv;
1115 g_return_if_fail (EMPATHY_IS_CHAT_WINDOW (window));
1117 priv = GET_PRIV (window);
1119 empathy_chat_paste (priv->current_chat);
1123 chat_window_find_activate_cb (GtkAction *action,
1124 EmpathyChatWindow *window)
1126 EmpathyChatWindowPriv *priv;
1128 g_return_if_fail (EMPATHY_IS_CHAT_WINDOW (window));
1130 priv = GET_PRIV (window);
1132 empathy_chat_find (priv->current_chat);
1136 chat_window_tabs_next_activate_cb (GtkAction *action,
1137 EmpathyChatWindow *window)
1139 EmpathyChatWindowPriv *priv;
1140 gint index_, numPages;
1141 gboolean wrap_around;
1143 priv = GET_PRIV (window);
1145 g_object_get (gtk_settings_get_default (), "gtk-keynav-wrap-around",
1146 &wrap_around, NULL);
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;
1164 gint index_, numPages;
1165 gboolean wrap_around;
1167 priv = GET_PRIV (window);
1169 g_object_get (gtk_settings_get_default (), "gtk-keynav-wrap-around",
1170 &wrap_around, NULL);
1172 index_ = gtk_notebook_get_current_page (GTK_NOTEBOOK (priv->notebook));
1173 numPages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (priv->notebook));
1175 if (index_ <= 0 && wrap_around) {
1176 gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->notebook), numPages - 1);
1180 gtk_notebook_prev_page (GTK_NOTEBOOK (priv->notebook));
1184 chat_window_tabs_undo_close_tab_activate_cb (GtkAction *action,
1185 EmpathyChatWindow *window)
1187 EmpathyChatWindowPriv *priv = GET_PRIV (window);
1188 empathy_chat_manager_undo_closed_chat (priv->chat_manager);
1192 chat_window_tabs_left_activate_cb (GtkAction *action,
1193 EmpathyChatWindow *window)
1195 EmpathyChatWindowPriv *priv;
1197 gint index_, num_pages;
1199 priv = GET_PRIV (window);
1201 chat = priv->current_chat;
1202 index_ = gtk_notebook_get_current_page (GTK_NOTEBOOK (priv->notebook));
1207 gtk_notebook_reorder_child (GTK_NOTEBOOK (priv->notebook),
1211 num_pages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (priv->notebook));
1212 chat_window_menu_context_update (priv, num_pages);
1216 chat_window_tabs_right_activate_cb (GtkAction *action,
1217 EmpathyChatWindow *window)
1219 EmpathyChatWindowPriv *priv;
1221 gint index_, num_pages;
1223 priv = GET_PRIV (window);
1225 chat = priv->current_chat;
1226 index_ = gtk_notebook_get_current_page (GTK_NOTEBOOK (priv->notebook));
1228 gtk_notebook_reorder_child (GTK_NOTEBOOK (priv->notebook),
1232 num_pages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (priv->notebook));
1233 chat_window_menu_context_update (priv, num_pages);
1236 static EmpathyChatWindow *
1237 empathy_chat_window_new (void)
1239 return EMPATHY_CHAT_WINDOW (g_object_new (EMPATHY_TYPE_CHAT_WINDOW, NULL));
1243 chat_window_detach_activate_cb (GtkAction *action,
1244 EmpathyChatWindow *window)
1246 EmpathyChatWindowPriv *priv;
1247 EmpathyChatWindow *new_window;
1250 priv = GET_PRIV (window);
1252 chat = priv->current_chat;
1253 new_window = empathy_chat_window_new ();
1255 empathy_chat_window_move_chat (window, new_window, chat);
1257 priv = GET_PRIV (new_window);
1258 gtk_widget_show (priv->dialog);
1262 chat_window_help_contents_activate_cb (GtkAction *action,
1263 EmpathyChatWindow *window)
1265 EmpathyChatWindowPriv *priv = GET_PRIV (window);
1267 empathy_url_show (priv->dialog, "ghelp:empathy");
1271 chat_window_help_about_activate_cb (GtkAction *action,
1272 EmpathyChatWindow *window)
1274 EmpathyChatWindowPriv *priv = GET_PRIV (window);
1276 empathy_about_dialog_new (GTK_WINDOW (priv->dialog));
1280 chat_window_delete_event_cb (GtkWidget *dialog,
1282 EmpathyChatWindow *window)
1284 EmpathyChatWindowPriv *priv = GET_PRIV (window);
1286 DEBUG ("Delete event received");
1288 g_object_ref (window);
1289 while (priv->chats) {
1290 empathy_chat_window_remove_chat (window, priv->chats->data);
1292 g_object_unref (window);
1298 chat_window_composing_cb (EmpathyChat *chat,
1299 gboolean is_composing,
1300 EmpathyChatWindow *window)
1302 chat_window_update_chat_tab (chat);
1306 chat_window_set_urgency_hint (EmpathyChatWindow *window,
1309 EmpathyChatWindowPriv *priv;
1311 priv = GET_PRIV (window);
1313 gtk_window_set_urgency_hint (GTK_WINDOW (priv->dialog), urgent);
1317 chat_window_notification_closed_cb (NotifyNotification *notify,
1318 EmpathyChatWindow *self)
1320 EmpathyChatWindowPriv *priv = GET_PRIV (self);
1322 g_object_unref (notify);
1323 if (priv->notification == notify) {
1324 priv->notification = NULL;
1329 chat_window_show_or_update_notification (EmpathyChatWindow *window,
1330 EmpathyMessage *message,
1333 EmpathyContact *sender;
1334 const gchar *header;
1338 EmpathyChatWindowPriv *priv = GET_PRIV (window);
1339 gboolean res, has_x_canonical_append;
1340 NotifyNotification *notification = priv->notification;
1342 if (!empathy_notify_manager_notification_is_enabled (priv->notify_mgr)) {
1345 res = g_settings_get_boolean (priv->gsettings_notif,
1346 EMPATHY_PREFS_NOTIFICATIONS_FOCUS);
1353 sender = empathy_message_get_sender (message);
1354 header = empathy_contact_get_alias (sender);
1355 body = empathy_message_get_body (message);
1356 escaped = g_markup_escape_text (body, -1);
1357 has_x_canonical_append = empathy_notify_manager_has_capability (
1358 priv->notify_mgr, EMPATHY_NOTIFY_MANAGER_CAP_X_CANONICAL_APPEND);
1360 if (notification != NULL && !has_x_canonical_append) {
1361 /* if the notification server supports x-canonical-append, it is
1362 better to not use notify_notification_update to avoid
1363 overwriting the current notification message */
1364 notify_notification_update (notification,
1365 header, escaped, NULL);
1367 /* if the notification server supports x-canonical-append,
1368 the hint will be added, so that the message from the
1369 just created notification will be automatically appended
1370 to an existing notification with the same title.
1371 In this way the previous message will not be lost: the new
1372 message will appear below it, in the same notification */
1373 notification = notify_notification_new (header, escaped, NULL);
1375 if (priv->notification == NULL) {
1376 priv->notification = notification;
1379 notify_notification_set_timeout (notification, NOTIFY_EXPIRES_DEFAULT);
1381 tp_g_signal_connect_object (notification, "closed",
1382 G_CALLBACK (chat_window_notification_closed_cb), window, 0);
1384 if (has_x_canonical_append) {
1385 /* We have to set a not empty string to keep libnotify happy */
1386 notify_notification_set_hint_string (notification,
1387 EMPATHY_NOTIFY_MANAGER_CAP_X_CANONICAL_APPEND, "1");
1391 pixbuf = empathy_notify_manager_get_pixbuf_for_notification (priv->notify_mgr,
1392 sender, EMPATHY_IMAGE_NEW_MESSAGE);
1394 if (pixbuf != NULL) {
1395 notify_notification_set_icon_from_pixbuf (notification, pixbuf);
1396 g_object_unref (pixbuf);
1399 notify_notification_show (notification, NULL);
1405 chat_window_set_highlight_room_labels (EmpathyChat *chat)
1407 gchar *markup, *name;
1410 if (!empathy_chat_is_room (chat))
1413 name = empathy_chat_dup_name (chat);
1414 markup = g_markup_printf_escaped (
1415 "<span color=\"red\" weight=\"bold\">%s</span>",
1418 widget = g_object_get_data (G_OBJECT (chat), "chat-window-tab-label");
1419 gtk_label_set_markup (GTK_LABEL (widget), markup);
1421 widget = g_object_get_data (G_OBJECT (chat), "chat-window-menu-label");
1422 gtk_label_set_markup (GTK_LABEL (widget), markup);
1429 empathy_chat_window_has_focus (EmpathyChatWindow *window)
1431 EmpathyChatWindowPriv *priv;
1434 g_return_val_if_fail (EMPATHY_IS_CHAT_WINDOW (window), FALSE);
1436 priv = GET_PRIV (window);
1438 g_object_get (priv->dialog, "has-toplevel-focus", &has_focus, NULL);
1444 chat_window_new_message_cb (EmpathyChat *chat,
1445 EmpathyMessage *message,
1447 EmpathyChatWindow *window)
1449 EmpathyChatWindowPriv *priv;
1451 gboolean needs_urgency;
1452 EmpathyContact *sender;
1454 priv = GET_PRIV (window);
1456 has_focus = empathy_chat_window_has_focus (window);
1458 /* - if we're the sender, we play the sound if it's specified in the
1459 * preferences and we're not away.
1460 * - if we receive a message, we play the sound if it's specified in the
1461 * preferences and the window does not have focus on the chat receiving
1465 sender = empathy_message_get_sender (message);
1467 if (empathy_contact_is_user (sender)) {
1468 empathy_sound_manager_play (priv->sound_mgr, GTK_WIDGET (priv->dialog),
1469 EMPATHY_SOUND_MESSAGE_OUTGOING);
1472 if (has_focus && priv->current_chat == chat) {
1473 /* window and tab are focused so consider the message to be read */
1475 /* FIXME: see Bug#610994 and coments about it in EmpathyChatPriv */
1476 empathy_chat_messages_read (chat);
1480 /* Update the chat tab if this is the first unread message */
1481 if (empathy_chat_get_nb_unread_messages (chat) == 1) {
1482 chat_window_update_chat_tab (chat);
1485 /* If empathy_chat_is_room () returns TRUE, that means it's a named MUC.
1486 * If empathy_chat_get_remote_contact () returns NULL, that means it's
1487 * an unamed MUC (msn-like).
1488 * In case of a MUC, we set urgency if either:
1489 * a) the chatroom's always_urgent property is TRUE
1490 * b) the message contains our alias
1492 if (empathy_chat_is_room (chat) ||
1493 empathy_chat_get_remote_contact (chat) == NULL) {
1496 EmpathyChatroom *chatroom;
1498 account = empathy_chat_get_account (chat);
1499 room = empathy_chat_get_id (chat);
1501 chatroom = empathy_chatroom_manager_find (priv->chatroom_manager,
1504 if (chatroom != NULL && empathy_chatroom_is_always_urgent (chatroom)) {
1505 needs_urgency = TRUE;
1507 needs_urgency = empathy_message_should_highlight (message);
1510 needs_urgency = TRUE;
1513 if (needs_urgency) {
1514 chat_window_set_highlight_room_labels (chat);
1517 chat_window_set_urgency_hint (window, TRUE);
1520 /* Pending messages have already been displayed and notified in the
1521 * approver, so we don't display a notification and play a sound for those */
1523 empathy_sound_manager_play (priv->sound_mgr, GTK_WIDGET (priv->dialog),
1524 EMPATHY_SOUND_MESSAGE_INCOMING);
1526 chat_window_show_or_update_notification (window, message, chat);
1530 /* update the number of unread messages and the window icon */
1531 chat_window_title_update (priv);
1532 chat_window_icon_update (priv, TRUE);
1535 static GtkNotebook *
1536 notebook_create_window_cb (GtkNotebook *source,
1542 EmpathyChatWindowPriv *priv;
1543 EmpathyChatWindow *window, *new_window;
1546 chat = EMPATHY_CHAT (page);
1547 window = chat_window_find_chat (chat);
1549 new_window = empathy_chat_window_new ();
1550 priv = GET_PRIV (new_window);
1552 DEBUG ("Detach hook called");
1554 empathy_chat_window_move_chat (window, new_window, chat);
1556 gtk_widget_show (priv->dialog);
1557 gtk_window_move (GTK_WINDOW (priv->dialog), x, y);
1563 chat_window_page_switched_cb (GtkNotebook *notebook,
1566 EmpathyChatWindow *window)
1568 EmpathyChatWindowPriv *priv = GET_PRIV (window);
1569 EmpathyChat *chat = EMPATHY_CHAT (child);
1571 DEBUG ("Page switched");
1573 if (priv->page_added) {
1574 priv->page_added = FALSE;
1575 empathy_chat_scroll_down (chat);
1577 else if (priv->current_chat == chat) {
1581 priv->current_chat = chat;
1582 empathy_chat_messages_read (chat);
1584 chat_window_update_chat_tab (chat);
1588 chat_window_page_added_cb (GtkNotebook *notebook,
1591 EmpathyChatWindow *window)
1593 EmpathyChatWindowPriv *priv;
1596 priv = GET_PRIV (window);
1598 /* If we just received DND to the same window, we don't want
1599 * to do anything here like removing the tab and then readding
1600 * it, so we return here and in "page-added".
1602 if (priv->dnd_same_window) {
1603 DEBUG ("Page added (back to the same window)");
1604 priv->dnd_same_window = FALSE;
1608 DEBUG ("Page added");
1610 /* Get chat object */
1611 chat = EMPATHY_CHAT (child);
1613 /* Connect chat signals for this window */
1614 g_signal_connect (chat, "composing",
1615 G_CALLBACK (chat_window_composing_cb),
1617 g_signal_connect (chat, "new-message",
1618 G_CALLBACK (chat_window_new_message_cb),
1620 g_signal_connect (chat, "notify::tp-chat",
1621 G_CALLBACK (chat_window_update_chat_tab),
1624 /* Set flag so we know to perform some special operations on
1625 * switch page due to the new page being added.
1627 priv->page_added = TRUE;
1629 /* Get list of chats up to date */
1630 priv->chats = g_list_append (priv->chats, chat);
1632 chat_window_update_chat_tab (chat);
1636 chat_window_page_removed_cb (GtkNotebook *notebook,
1639 EmpathyChatWindow *window)
1641 EmpathyChatWindowPriv *priv;
1644 priv = GET_PRIV (window);
1646 /* If we just received DND to the same window, we don't want
1647 * to do anything here like removing the tab and then readding
1648 * it, so we return here and in "page-added".
1650 if (priv->dnd_same_window) {
1651 DEBUG ("Page removed (and will be readded to same window)");
1655 DEBUG ("Page removed");
1657 /* Get chat object */
1658 chat = EMPATHY_CHAT (child);
1660 /* Disconnect all signal handlers for this chat and this window */
1661 g_signal_handlers_disconnect_by_func (chat,
1662 G_CALLBACK (chat_window_composing_cb),
1664 g_signal_handlers_disconnect_by_func (chat,
1665 G_CALLBACK (chat_window_new_message_cb),
1667 g_signal_handlers_disconnect_by_func (chat,
1668 G_CALLBACK (chat_window_update_chat_tab),
1671 /* Keep list of chats up to date */
1672 priv->chats = g_list_remove (priv->chats, chat);
1673 empathy_chat_messages_read (chat);
1675 if (priv->chats == NULL) {
1676 g_object_unref (window);
1678 chat_window_update (window, TRUE);
1683 chat_window_focus_in_event_cb (GtkWidget *widget,
1685 EmpathyChatWindow *window)
1687 EmpathyChatWindowPriv *priv;
1689 priv = GET_PRIV (window);
1691 empathy_chat_messages_read (priv->current_chat);
1693 chat_window_set_urgency_hint (window, FALSE);
1695 /* Update the title, since we now mark all unread messages as read. */
1696 chat_window_update_chat_tab_full (priv->current_chat, FALSE);
1702 chat_window_drag_drop (GtkWidget *widget,
1703 GdkDragContext *context,
1707 EmpathyChatWindow *window)
1710 EmpathyChatWindowPriv *priv;
1712 priv = GET_PRIV (window);
1714 target = gtk_drag_dest_find_target (widget, context, priv->file_targets);
1715 if (target == GDK_NONE)
1716 target = gtk_drag_dest_find_target (widget, context, priv->contact_targets);
1718 if (target != GDK_NONE) {
1719 gtk_drag_get_data (widget, context, target, time_);
1727 chat_window_drag_motion (GtkWidget *widget,
1728 GdkDragContext *context,
1732 EmpathyChatWindow *window)
1735 EmpathyChatWindowPriv *priv;
1737 priv = GET_PRIV (window);
1739 target = gtk_drag_dest_find_target (widget, context, priv->file_targets);
1740 if (target != GDK_NONE) {
1741 /* This is a file drag. Ensure the contact is online and set the
1742 drag type to COPY. Note that it's possible that the tab will
1743 be switched by GTK+ after a timeout from drag_motion without
1744 getting another drag_motion to disable the drop. You have
1745 to hold your mouse really still.
1747 EmpathyContact *contact;
1749 priv = GET_PRIV (window);
1750 contact = empathy_chat_get_remote_contact (priv->current_chat);
1751 /* contact is NULL for multi-user chats. We don't do
1752 * file transfers to MUCs. We also don't send files
1753 * to offline contacts or contacts that don't support
1756 if ((contact == NULL) || !empathy_contact_is_online (contact)) {
1757 gdk_drag_status (context, 0, time_);
1760 if (!(empathy_contact_get_capabilities (contact)
1761 & EMPATHY_CAPABILITIES_FT)) {
1762 gdk_drag_status (context, 0, time_);
1765 gdk_drag_status (context, GDK_ACTION_COPY, time_);
1769 target = gtk_drag_dest_find_target (widget, context, priv->contact_targets);
1770 if (target != GDK_NONE) {
1771 /* This is a drag of a contact from a contact list. Set to COPY.
1772 FIXME: If this drag is to a MUC window, it invites the user.
1773 Otherwise, it opens a chat. Should we use a different drag
1774 type for invites? Should we allow ASK?
1776 gdk_drag_status (context, GDK_ACTION_COPY, time_);
1784 chat_window_drag_data_received (GtkWidget *widget,
1785 GdkDragContext *context,
1788 GtkSelectionData *selection,
1791 EmpathyChatWindow *window)
1793 if (info == DND_DRAG_TYPE_CONTACT_ID) {
1794 EmpathyChat *chat = NULL;
1795 EmpathyChatWindow *old_window;
1796 TpAccount *account = NULL;
1797 TpAccountManager *account_manager;
1800 const gchar *account_id;
1801 const gchar *contact_id;
1803 id = (const gchar*) gtk_selection_data_get_data (selection);
1805 /* FIXME: Perhaps should be sure that the account manager is
1806 * prepared before calling _ensure_account on it. */
1807 account_manager = tp_account_manager_dup ();
1809 DEBUG ("DND contact from roster with id:'%s'", id);
1811 strv = g_strsplit (id, ":", 2);
1812 if (g_strv_length (strv) == 2) {
1813 account_id = strv[0];
1814 contact_id = strv[1];
1816 tp_account_manager_ensure_account (account_manager, account_id);
1817 if (account != NULL)
1818 chat = empathy_chat_window_find_chat (account, contact_id, FALSE);
1821 if (account == NULL) {
1823 gtk_drag_finish (context, FALSE, FALSE, time_);
1828 empathy_chat_with_contact_id (
1829 account, contact_id, gtk_get_current_event_time ());
1834 g_object_unref (account_manager);
1837 old_window = chat_window_find_chat (chat);
1839 if (old_window == window) {
1840 gtk_drag_finish (context, TRUE, FALSE, time_);
1844 empathy_chat_window_move_chat (old_window, window, chat);
1846 empathy_chat_window_add_chat (window, chat);
1849 /* Added to take care of any outstanding chat events */
1850 empathy_chat_window_present_chat (chat,
1851 TP_USER_ACTION_TIME_NOT_USER_ACTION);
1853 /* We should return TRUE to remove the data when doing
1854 * GDK_ACTION_MOVE, but we don't here otherwise it has
1855 * weird consequences, and we handle that internally
1856 * anyway with add_chat () and remove_chat ().
1858 gtk_drag_finish (context, TRUE, FALSE, time_);
1860 else if (info == DND_DRAG_TYPE_URI_LIST) {
1861 EmpathyChatWindowPriv *priv;
1862 EmpathyContact *contact;
1865 priv = GET_PRIV (window);
1866 contact = empathy_chat_get_remote_contact (priv->current_chat);
1868 /* contact is NULL when current_chat is a multi-user chat.
1869 * We don't do file transfers to MUCs, so just cancel the drag.
1871 if (contact == NULL) {
1872 gtk_drag_finish (context, TRUE, FALSE, time_);
1876 data = (const gchar *) gtk_selection_data_get_data (selection);
1877 empathy_send_file_from_uri_list (contact, data);
1879 gtk_drag_finish (context, TRUE, FALSE, time_);
1881 else if (info == DND_DRAG_TYPE_TAB) {
1883 EmpathyChatWindow *old_window = NULL;
1887 chat = (void *) gtk_selection_data_get_data (selection);
1888 old_window = chat_window_find_chat (*chat);
1891 EmpathyChatWindowPriv *priv;
1893 priv = GET_PRIV (window);
1894 priv->dnd_same_window = (old_window == window);
1895 DEBUG ("DND tab (within same window: %s)",
1896 priv->dnd_same_window ? "Yes" : "No");
1899 DEBUG ("DND from unknown source");
1900 gtk_drag_finish (context, FALSE, FALSE, time_);
1905 chat_window_chat_manager_chats_changed_cb (EmpathyChatManager *chat_manager,
1906 guint num_chats_in_manager,
1907 EmpathyChatWindow *window)
1909 EmpathyChatWindowPriv *priv = GET_PRIV (window);
1911 gtk_action_set_sensitive (priv->menu_tabs_undo_close_tab,
1912 num_chats_in_manager > 0);
1916 chat_window_finalize (GObject *object)
1918 EmpathyChatWindow *window;
1919 EmpathyChatWindowPriv *priv;
1921 window = EMPATHY_CHAT_WINDOW (object);
1922 priv = GET_PRIV (window);
1924 DEBUG ("Finalized: %p", object);
1926 g_object_unref (priv->ui_manager);
1927 g_object_unref (priv->chatroom_manager);
1928 g_object_unref (priv->notify_mgr);
1929 g_object_unref (priv->gsettings_chat);
1930 g_object_unref (priv->gsettings_notif);
1931 g_object_unref (priv->gsettings_ui);
1932 g_object_unref (priv->sound_mgr);
1934 if (priv->notification != NULL) {
1935 notify_notification_close (priv->notification, NULL);
1936 priv->notification = NULL;
1939 if (priv->contact_targets) {
1940 gtk_target_list_unref (priv->contact_targets);
1942 if (priv->file_targets) {
1943 gtk_target_list_unref (priv->file_targets);
1946 if (priv->chat_manager) {
1947 g_signal_handler_disconnect (priv->chat_manager,
1948 priv->chat_manager_chats_changed_id);
1949 g_object_unref (priv->chat_manager);
1950 priv->chat_manager = NULL;
1953 chat_windows = g_list_remove (chat_windows, window);
1954 gtk_widget_destroy (priv->dialog);
1956 G_OBJECT_CLASS (empathy_chat_window_parent_class)->finalize (object);
1960 empathy_chat_window_class_init (EmpathyChatWindowClass *klass)
1962 GObjectClass *object_class = G_OBJECT_CLASS (klass);
1964 object_class->finalize = chat_window_finalize;
1966 g_type_class_add_private (object_class, sizeof (EmpathyChatWindowPriv));
1970 empathy_chat_window_init (EmpathyChatWindow *window)
1973 GtkAccelGroup *accel_group;
1978 GtkWidget *chat_vbox;
1980 EmpathySmileyManager *smiley_manager;
1981 EmpathyChatWindowPriv *priv = G_TYPE_INSTANCE_GET_PRIVATE (window,
1982 EMPATHY_TYPE_CHAT_WINDOW, EmpathyChatWindowPriv);
1984 window->priv = priv;
1985 filename = empathy_file_lookup ("empathy-chat-window.ui", "src");
1986 gui = empathy_builder_get_file (filename,
1987 "chat_window", &priv->dialog,
1988 "chat_vbox", &chat_vbox,
1989 "ui_manager", &priv->ui_manager,
1990 "menu_conv_insert_smiley", &priv->menu_conv_insert_smiley,
1991 "menu_conv_favorite", &priv->menu_conv_favorite,
1992 "menu_conv_always_urgent", &priv->menu_conv_always_urgent,
1993 "menu_conv_toggle_contacts", &priv->menu_conv_toggle_contacts,
1994 "menu_edit_cut", &priv->menu_edit_cut,
1995 "menu_edit_copy", &priv->menu_edit_copy,
1996 "menu_edit_paste", &priv->menu_edit_paste,
1997 "menu_edit_find", &priv->menu_edit_find,
1998 "menu_tabs_next", &priv->menu_tabs_next,
1999 "menu_tabs_prev", &priv->menu_tabs_prev,
2000 "menu_tabs_undo_close_tab", &priv->menu_tabs_undo_close_tab,
2001 "menu_tabs_left", &priv->menu_tabs_left,
2002 "menu_tabs_right", &priv->menu_tabs_right,
2003 "menu_tabs_detach", &priv->menu_tabs_detach,
2007 empathy_builder_connect (gui, window,
2008 "menu_conv", "activate", chat_window_conv_activate_cb,
2009 "menu_conv_clear", "activate", chat_window_clear_activate_cb,
2010 "menu_conv_favorite", "toggled", chat_window_favorite_toggled_cb,
2011 "menu_conv_always_urgent", "toggled", chat_window_always_urgent_toggled_cb,
2012 "menu_conv_toggle_contacts", "toggled", chat_window_contacts_toggled_cb,
2013 "menu_conv_invite_participant", "activate", chat_window_invite_participant_activate_cb,
2014 "menu_conv_close", "activate", chat_window_close_activate_cb,
2015 "menu_edit", "activate", chat_window_edit_activate_cb,
2016 "menu_edit_cut", "activate", chat_window_cut_activate_cb,
2017 "menu_edit_copy", "activate", chat_window_copy_activate_cb,
2018 "menu_edit_paste", "activate", chat_window_paste_activate_cb,
2019 "menu_edit_find", "activate", chat_window_find_activate_cb,
2020 "menu_tabs_next", "activate", chat_window_tabs_next_activate_cb,
2021 "menu_tabs_prev", "activate", chat_window_tabs_previous_activate_cb,
2022 "menu_tabs_undo_close_tab", "activate", chat_window_tabs_undo_close_tab_activate_cb,
2023 "menu_tabs_left", "activate", chat_window_tabs_left_activate_cb,
2024 "menu_tabs_right", "activate", chat_window_tabs_right_activate_cb,
2025 "menu_tabs_detach", "activate", chat_window_detach_activate_cb,
2026 "menu_help_contents", "activate", chat_window_help_contents_activate_cb,
2027 "menu_help_about", "activate", chat_window_help_about_activate_cb,
2030 g_object_ref (priv->ui_manager);
2031 g_object_unref (gui);
2033 priv->gsettings_chat = g_settings_new (EMPATHY_PREFS_CHAT_SCHEMA);
2034 priv->gsettings_notif = g_settings_new (EMPATHY_PREFS_NOTIFICATIONS_SCHEMA);
2035 priv->gsettings_ui = g_settings_new (EMPATHY_PREFS_UI_SCHEMA);
2036 priv->chatroom_manager = empathy_chatroom_manager_dup_singleton (NULL);
2038 priv->sound_mgr = empathy_sound_manager_dup_singleton ();
2040 priv->notebook = gtk_notebook_new ();
2042 g_signal_connect (priv->notebook, "create-window",
2043 G_CALLBACK (notebook_create_window_cb), window);
2045 gtk_notebook_set_group_name (GTK_NOTEBOOK (priv->notebook),
2046 "EmpathyChatWindow");
2047 gtk_notebook_set_scrollable (GTK_NOTEBOOK (priv->notebook), TRUE);
2048 gtk_notebook_popup_enable (GTK_NOTEBOOK (priv->notebook));
2049 gtk_box_pack_start (GTK_BOX (chat_vbox), priv->notebook, TRUE, TRUE, 0);
2050 gtk_widget_show (priv->notebook);
2053 accel_group = gtk_accel_group_new ();
2054 gtk_window_add_accel_group (GTK_WINDOW (priv->dialog), accel_group);
2056 for (i = 0; i < G_N_ELEMENTS (tab_accel_keys); i++) {
2057 closure = g_cclosure_new (G_CALLBACK (chat_window_accel_cb),
2060 gtk_accel_group_connect (accel_group,
2067 g_object_unref (accel_group);
2069 /* Set up drag target lists */
2070 priv->contact_targets = gtk_target_list_new (drag_types_dest_contact,
2071 G_N_ELEMENTS (drag_types_dest_contact));
2072 priv->file_targets = gtk_target_list_new (drag_types_dest_file,
2073 G_N_ELEMENTS (drag_types_dest_file));
2075 /* Set up smiley menu */
2076 smiley_manager = empathy_smiley_manager_dup_singleton ();
2077 submenu = empathy_smiley_menu_new (smiley_manager,
2078 chat_window_insert_smiley_activate_cb,
2080 menu = gtk_ui_manager_get_widget (priv->ui_manager,
2081 "/chats_menubar/menu_conv/menu_conv_insert_smiley");
2082 gtk_menu_item_set_submenu (GTK_MENU_ITEM (menu), submenu);
2083 g_object_unref (smiley_manager);
2085 /* Set up signals we can't do with ui file since we may need to
2086 * block/unblock them at some later stage.
2089 g_signal_connect (priv->dialog,
2091 G_CALLBACK (chat_window_delete_event_cb),
2093 g_signal_connect (priv->dialog,
2095 G_CALLBACK (chat_window_focus_in_event_cb),
2097 g_signal_connect_after (priv->notebook,
2099 G_CALLBACK (chat_window_page_switched_cb),
2101 g_signal_connect (priv->notebook,
2103 G_CALLBACK (chat_window_page_added_cb),
2105 g_signal_connect (priv->notebook,
2107 G_CALLBACK (chat_window_page_removed_cb),
2110 /* Set up drag and drop */
2111 gtk_drag_dest_set (GTK_WIDGET (priv->notebook),
2112 GTK_DEST_DEFAULT_HIGHLIGHT,
2114 G_N_ELEMENTS (drag_types_dest),
2115 GDK_ACTION_MOVE | GDK_ACTION_COPY);
2117 /* connect_after to allow GtkNotebook's built-in tab switching */
2118 g_signal_connect_after (priv->notebook,
2120 G_CALLBACK (chat_window_drag_motion),
2122 g_signal_connect (priv->notebook,
2123 "drag-data-received",
2124 G_CALLBACK (chat_window_drag_data_received),
2126 g_signal_connect (priv->notebook,
2128 G_CALLBACK (chat_window_drag_drop),
2131 chat_windows = g_list_prepend (chat_windows, window);
2133 /* Set up private details */
2135 priv->current_chat = NULL;
2136 priv->notification = NULL;
2138 priv->notify_mgr = empathy_notify_manager_dup_singleton ();
2140 priv->chat_manager = empathy_chat_manager_dup_singleton ();
2141 priv->chat_manager_chats_changed_id =
2142 g_signal_connect (priv->chat_manager, "closed-chats-changed",
2143 G_CALLBACK (chat_window_chat_manager_chats_changed_cb),
2146 chat_window_chat_manager_chats_changed_cb (priv->chat_manager,
2147 empathy_chat_manager_get_num_closed_chats (priv->chat_manager),
2152 empathy_chat_window_get_dialog (EmpathyChatWindow *window)
2154 EmpathyChatWindowPriv *priv;
2156 g_return_val_if_fail (window != NULL, NULL);
2158 priv = GET_PRIV (window);
2160 return priv->dialog;
2163 /* Returns the window to open a new tab in if there is a suitable window,
2164 * otherwise, returns NULL indicating that a new window should be added.
2166 static EmpathyChatWindow *
2167 empathy_chat_window_get_default (gboolean room)
2169 GSettings *gsettings = g_settings_new (EMPATHY_PREFS_UI_SCHEMA);
2171 gboolean separate_windows = TRUE;
2173 separate_windows = g_settings_get_boolean (gsettings,
2174 EMPATHY_PREFS_UI_SEPARATE_CHAT_WINDOWS);
2176 g_object_unref (gsettings);
2178 if (separate_windows) {
2179 /* Always create a new window */
2183 for (l = chat_windows; l; l = l->next) {
2184 EmpathyChatWindow *chat_window;
2186 guint nb_rooms, nb_private;
2188 chat_window = l->data;
2190 dialog = empathy_chat_window_get_dialog (chat_window);
2192 empathy_chat_window_get_nb_chats (chat_window, &nb_rooms, &nb_private);
2194 /* Skip the window if there aren't any rooms in it */
2195 if (room && nb_rooms == 0)
2198 /* Skip the window if there aren't any 1-1 chats in it */
2199 if (!room && nb_private == 0)
2202 /* Found a window on this desktop, make it visible if necessary */
2203 if (!empathy_window_get_is_visible (GTK_WINDOW (dialog)))
2204 empathy_window_present (GTK_WINDOW (dialog));
2212 empathy_chat_window_add_chat (EmpathyChatWindow *window,
2215 EmpathyChatWindowPriv *priv;
2217 GtkWidget *popup_label;
2219 GValue value = { 0, };
2221 g_return_if_fail (window != NULL);
2222 g_return_if_fail (EMPATHY_IS_CHAT (chat));
2224 priv = GET_PRIV (window);
2226 /* Reference the chat object */
2227 g_object_ref (chat);
2229 /* If this window has just been created, position it */
2230 if (priv->chats == NULL) {
2231 const gchar *name = "chat-window";
2232 gboolean separate_windows;
2234 separate_windows = g_settings_get_boolean (priv->gsettings_ui,
2235 EMPATHY_PREFS_UI_SEPARATE_CHAT_WINDOWS);
2237 if (empathy_chat_is_room (chat))
2238 name = "room-window";
2240 if (separate_windows) {
2243 /* Save current position of the window */
2244 gtk_window_get_position (GTK_WINDOW (priv->dialog), &x, &y);
2246 /* First bind to the 'generic' name. So new window for which we didn't
2247 * save a geometry yet will have the geometry of the last saved
2248 * window (bgo #601191). */
2249 empathy_geometry_bind (GTK_WINDOW (priv->dialog), name);
2251 /* Restore previous position of the window so the newly created window
2252 * won't be in the same position as the latest saved window and so
2253 * completely hide it. */
2254 gtk_window_move (GTK_WINDOW (priv->dialog), x, y);
2256 /* Then bind it to the name of the contact/room so we'll save the
2257 * geometry specific to this window */
2258 name = empathy_chat_get_id (chat);
2261 empathy_geometry_bind (GTK_WINDOW (priv->dialog), name);
2264 child = GTK_WIDGET (chat);
2265 label = chat_window_create_label (window, chat, TRUE);
2266 popup_label = chat_window_create_label (window, chat, FALSE);
2267 gtk_widget_show (child);
2269 g_signal_connect (chat, "notify::name",
2270 G_CALLBACK (chat_window_chat_notify_cb),
2272 g_signal_connect (chat, "notify::subject",
2273 G_CALLBACK (chat_window_chat_notify_cb),
2275 g_signal_connect (chat, "notify::remote-contact",
2276 G_CALLBACK (chat_window_chat_notify_cb),
2278 g_signal_connect (chat, "notify::sms-channel",
2279 G_CALLBACK (chat_window_chat_notify_cb),
2281 g_signal_connect (chat, "notify::n-messages-sending",
2282 G_CALLBACK (chat_window_chat_notify_cb),
2284 g_signal_connect (chat, "notify::nb-unread-messages",
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;