1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * Copyright (C) 2003-2007 Imendio AB
4 * Copyright (C) 2007-2010 Collabora Ltd.
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License as
8 * published by the Free Software Foundation; either version 2 of the
9 * License, or (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
16 * You should have received a copy of the GNU General Public
17 * License along with this program; if not, write to the
18 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
19 * Boston, MA 02110-1301 USA
21 * Authors: Mikael Hallendal <micke@imendio.com>
22 * Richard Hult <richard@imendio.com>
23 * Martyn Russell <martyn@imendio.com>
24 * Geert-Jan Van den Bogaerde <geertjan@gnome.org>
25 * Xavier Claessens <xclaesse@gmail.com>
26 * RĂ´mulo Fernandes Machado <romulo@castorgroup.net>
34 #include <gdk/gdkkeysyms.h>
36 #include <glib/gi18n.h>
37 #include <libnotify/notification.h>
39 #include <telepathy-glib/telepathy-glib.h>
41 #include <libempathy/empathy-contact.h>
42 #include <libempathy/empathy-message.h>
43 #include <libempathy/empathy-chatroom-manager.h>
44 #include <libempathy/empathy-gsettings.h>
45 #include <libempathy/empathy-utils.h>
46 #include <libempathy/empathy-tp-contact-factory.h>
47 #include <libempathy/empathy-contact-list.h>
49 #include <libempathy-gtk/empathy-images.h>
50 #include <libempathy-gtk/empathy-contact-dialogs.h>
51 #include <libempathy-gtk/empathy-log-window.h>
52 #include <libempathy-gtk/empathy-geometry.h>
53 #include <libempathy-gtk/empathy-smiley-manager.h>
54 #include <libempathy-gtk/empathy-sound-manager.h>
55 #include <libempathy-gtk/empathy-ui-utils.h>
56 #include <libempathy-gtk/empathy-notify-manager.h>
58 #include "empathy-chat-manager.h"
59 #include "empathy-chat-window.h"
60 #include "empathy-about-dialog.h"
61 #include "empathy-invite-participant-dialog.h"
63 #define DEBUG_FLAG EMPATHY_DEBUG_CHAT
64 #include <libempathy/empathy-debug.h>
66 /* Macro to compare guint32 X timestamps, while accounting for wrapping around
68 #define X_EARLIER_OR_EQL(t1, t2) \
69 ((t1 <= t2 && ((t2 - t1) < G_MAXUINT32/2)) \
70 || (t1 >= t2 && (t1 - t2) > (G_MAXUINT32/2)) \
73 #define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyChatWindow)
75 EmpathyChat *current_chat;
78 GList *chats_composing;
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);
155 static void empathy_chat_window_add_chat (EmpathyChatWindow *window,
158 static void empathy_chat_window_remove_chat (EmpathyChatWindow *window,
161 static void empathy_chat_window_move_chat (EmpathyChatWindow *old_window,
162 EmpathyChatWindow *new_window,
165 static void empathy_chat_window_get_nb_chats (EmpathyChatWindow *self,
169 G_DEFINE_TYPE (EmpathyChatWindow, empathy_chat_window, G_TYPE_OBJECT);
172 chat_window_accel_cb (GtkAccelGroup *accelgroup,
176 EmpathyChatWindow *window)
178 EmpathyChatWindowPriv *priv;
182 priv = GET_PRIV (window);
184 for (i = 0; i < G_N_ELEMENTS (tab_accel_keys); i++) {
185 if (tab_accel_keys[i] == key) {
192 gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->notebook), num);
196 static EmpathyChatWindow *
197 chat_window_find_chat (EmpathyChat *chat)
199 EmpathyChatWindowPriv *priv;
202 for (l = chat_windows; l; l = l->next) {
203 priv = GET_PRIV (l->data);
204 ll = g_list_find (priv->chats, chat);
214 chat_window_close_clicked_cb (GtkAction *action,
217 EmpathyChatWindow *window;
219 window = chat_window_find_chat (chat);
220 empathy_chat_window_remove_chat (window, chat);
224 chat_tab_style_set_cb (GtkWidget *hbox,
225 GtkStyle *previous_style,
229 int char_width, h, w;
230 PangoContext *context;
231 PangoFontMetrics *metrics;
233 button = g_object_get_data (G_OBJECT (user_data),
234 "chat-window-tab-close-button");
235 context = gtk_widget_get_pango_context (hbox);
237 metrics = pango_context_get_metrics (context, gtk_widget_get_style (hbox)->font_desc,
238 pango_context_get_language (context));
239 char_width = pango_font_metrics_get_approximate_char_width (metrics);
240 pango_font_metrics_unref (metrics);
242 gtk_icon_size_lookup_for_settings (gtk_widget_get_settings (button),
243 GTK_ICON_SIZE_MENU, &w, &h);
245 /* Request at least about 12 chars width plus at least space for the status
246 * image and the close button */
247 gtk_widget_set_size_request (hbox,
248 12 * PANGO_PIXELS (char_width) + 2 * w, -1);
250 gtk_widget_set_size_request (button, w, h);
254 chat_window_create_label (EmpathyChatWindow *window,
256 gboolean is_tab_label)
258 EmpathyChatWindowPriv *priv;
260 GtkWidget *name_label;
261 GtkWidget *status_image;
262 GtkWidget *close_button;
263 GtkWidget *close_image;
264 GtkWidget *event_box;
265 GtkWidget *event_box_hbox;
266 PangoAttrList *attr_list;
267 PangoAttribute *attr;
269 priv = GET_PRIV (window);
271 /* The spacing between the button and the label. */
272 hbox = gtk_hbox_new (FALSE, 0);
274 event_box = gtk_event_box_new ();
275 gtk_event_box_set_visible_window (GTK_EVENT_BOX (event_box), FALSE);
277 name_label = gtk_label_new (NULL);
279 gtk_label_set_ellipsize (GTK_LABEL (name_label), PANGO_ELLIPSIZE_END);
281 attr_list = pango_attr_list_new ();
282 attr = pango_attr_scale_new (1/1.2);
283 attr->start_index = 0;
284 attr->end_index = -1;
285 pango_attr_list_insert (attr_list, attr);
286 gtk_label_set_attributes (GTK_LABEL (name_label), attr_list);
287 pango_attr_list_unref (attr_list);
289 gtk_misc_set_padding (GTK_MISC (name_label), 2, 0);
290 gtk_misc_set_alignment (GTK_MISC (name_label), 0.0, 0.5);
291 g_object_set_data (G_OBJECT (chat),
292 is_tab_label ? "chat-window-tab-label" : "chat-window-menu-label",
295 status_image = gtk_image_new ();
297 /* Spacing between the icon and label. */
298 event_box_hbox = gtk_hbox_new (FALSE, 0);
300 gtk_box_pack_start (GTK_BOX (event_box_hbox), status_image, FALSE, FALSE, 0);
301 gtk_box_pack_start (GTK_BOX (event_box_hbox), name_label, TRUE, TRUE, 0);
303 g_object_set_data (G_OBJECT (chat),
304 is_tab_label ? "chat-window-tab-image" : "chat-window-menu-image",
306 g_object_set_data (G_OBJECT (chat),
307 is_tab_label ? "chat-window-tab-tooltip-widget" : "chat-window-menu-tooltip-widget",
310 gtk_container_add (GTK_CONTAINER (event_box), event_box_hbox);
311 gtk_box_pack_start (GTK_BOX (hbox), event_box, TRUE, TRUE, 0);
314 close_button = gtk_button_new ();
315 gtk_button_set_relief (GTK_BUTTON (close_button), GTK_RELIEF_NONE);
316 g_object_set_data (G_OBJECT (chat), "chat-window-tab-close-button", close_button);
318 /* We don't want focus/keynav for the button to avoid clutter, and
319 * Ctrl-W works anyway.
321 gtk_widget_set_can_focus (close_button, FALSE);
322 gtk_widget_set_can_default (close_button, FALSE);
324 /* Set the name to make the special rc style match. */
325 gtk_widget_set_name (close_button, "empathy-close-button");
327 close_image = gtk_image_new_from_stock (GTK_STOCK_CLOSE, GTK_ICON_SIZE_MENU);
329 gtk_container_add (GTK_CONTAINER (close_button), close_image);
331 gtk_box_pack_end (GTK_BOX (hbox), close_button, FALSE, FALSE, 0);
333 g_signal_connect (close_button,
335 G_CALLBACK (chat_window_close_clicked_cb),
338 /* React to theme changes and also setup the size correctly. */
339 g_signal_connect (hbox,
341 G_CALLBACK (chat_tab_style_set_cb),
345 gtk_widget_show_all (hbox);
351 _submenu_notify_visible_changed_cb (GObject *object,
355 g_signal_handlers_disconnect_by_func (object,
356 _submenu_notify_visible_changed_cb,
358 chat_window_update (EMPATHY_CHAT_WINDOW (userdata));
362 chat_window_menu_context_update (EmpathyChatWindowPriv *priv,
367 gboolean wrap_around;
368 gboolean is_connected;
371 page_num = gtk_notebook_get_current_page (GTK_NOTEBOOK (priv->notebook));
372 first_page = (page_num == 0);
373 last_page = (page_num == (num_pages - 1));
374 g_object_get (gtk_settings_get_default (), "gtk-keynav-wrap-around",
376 is_connected = empathy_chat_get_tp_chat (priv->current_chat) != NULL;
378 gtk_action_set_sensitive (priv->menu_tabs_next, (!last_page ||
380 gtk_action_set_sensitive (priv->menu_tabs_prev, (!first_page ||
382 gtk_action_set_sensitive (priv->menu_tabs_detach, num_pages > 1);
383 gtk_action_set_sensitive (priv->menu_tabs_left, !first_page);
384 gtk_action_set_sensitive (priv->menu_tabs_right, !last_page);
385 gtk_action_set_sensitive (priv->menu_conv_insert_smiley, is_connected);
389 chat_window_conversation_menu_update (EmpathyChatWindowPriv *priv,
390 EmpathyChatWindow *self)
392 EmpathyTpChat *tp_chat;
393 TpConnection *connection;
395 gboolean sensitive = FALSE;
397 g_return_if_fail (priv->current_chat != NULL);
399 action = gtk_ui_manager_get_action (priv->ui_manager,
400 "/chats_menubar/menu_conv/menu_conv_invite_participant");
401 tp_chat = empathy_chat_get_tp_chat (priv->current_chat);
403 if (tp_chat != NULL) {
404 connection = empathy_tp_chat_get_connection (tp_chat);
406 sensitive = empathy_tp_chat_can_add_contact (tp_chat) &&
407 (tp_connection_get_status (connection, NULL) ==
408 TP_CONNECTION_STATUS_CONNECTED);
411 gtk_action_set_sensitive (action, sensitive);
415 chat_window_contact_menu_update (EmpathyChatWindowPriv *priv,
416 EmpathyChatWindow *window)
418 GtkWidget *menu, *submenu, *orig_submenu;
420 menu = gtk_ui_manager_get_widget (priv->ui_manager,
421 "/chats_menubar/menu_contact");
422 orig_submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (menu));
424 if (orig_submenu == NULL || !gtk_widget_get_visible (orig_submenu)) {
425 submenu = empathy_chat_get_contact_menu (priv->current_chat);
426 gtk_menu_item_set_submenu (GTK_MENU_ITEM (menu), submenu);
427 gtk_widget_show (menu);
429 tp_g_signal_connect_object (orig_submenu,
431 (GCallback)_submenu_notify_visible_changed_cb,
437 get_all_unread_messages (EmpathyChatWindowPriv *priv)
442 for (l = priv->chats_new_msg; l != NULL; l = g_list_next (l)) {
443 EmpathyChat *chat = l->data;
445 nb += empathy_chat_get_nb_unread_messages (chat);
452 get_window_title_name (EmpathyChatWindowPriv *priv)
454 const gchar *active_name;
456 guint current_unread_msgs;
458 nb_chats = g_list_length (priv->chats);
459 g_assert (nb_chats > 0);
461 active_name = empathy_chat_get_name (priv->current_chat);
463 current_unread_msgs = empathy_chat_get_nb_unread_messages (
468 if (current_unread_msgs == 0)
469 return g_strdup (active_name);
471 return g_strdup_printf (ngettext (
473 "%s (%d unread)", current_unread_msgs),
474 active_name, current_unread_msgs);
476 guint nb_others = nb_chats - 1;
477 guint all_unread_msgs;
479 all_unread_msgs = get_all_unread_messages (priv);
481 if (all_unread_msgs == 0) {
482 /* no unread message */
483 return g_strdup_printf (ngettext (
485 "%s (and %u others)", nb_others),
486 active_name, nb_others);
489 else if (all_unread_msgs == current_unread_msgs) {
490 /* unread messages are in the current tab */
491 return g_strdup_printf (ngettext (
493 "%s (%d unread)", current_unread_msgs),
494 active_name, current_unread_msgs);
497 else if (current_unread_msgs == 0) {
498 /* unread messages are in other tabs */
499 return g_strdup_printf (ngettext (
500 "%s (%d unread from others)",
501 "%s (%d unread from others)",
503 active_name, all_unread_msgs);
507 /* unread messages are in all the tabs */
508 return g_strdup_printf (ngettext (
509 "%s (%d unread from all)",
510 "%s (%d unread from all)",
512 active_name, all_unread_msgs);
518 chat_window_title_update (EmpathyChatWindowPriv *priv)
522 name = get_window_title_name (priv);
523 gtk_window_set_title (GTK_WINDOW (priv->dialog), name);
528 chat_window_icon_update (EmpathyChatWindowPriv *priv)
531 EmpathyContact *remote_contact;
532 gboolean avatar_in_icon;
535 n_chats = g_list_length (priv->chats);
537 /* Update window icon */
538 if (priv->chats_new_msg) {
539 gtk_window_set_icon_name (GTK_WINDOW (priv->dialog),
540 EMPATHY_IMAGE_MESSAGE);
542 avatar_in_icon = g_settings_get_boolean (priv->gsettings_chat,
543 EMPATHY_PREFS_CHAT_AVATAR_IN_ICON);
545 if (n_chats == 1 && avatar_in_icon) {
546 remote_contact = empathy_chat_get_remote_contact (priv->current_chat);
547 icon = empathy_pixbuf_avatar_from_contact_scaled (remote_contact, 0, 0);
548 gtk_window_set_icon (GTK_WINDOW (priv->dialog), icon);
551 g_object_unref (icon);
554 gtk_window_set_icon_name (GTK_WINDOW (priv->dialog), NULL);
560 chat_window_close_button_update (EmpathyChatWindowPriv *priv,
564 GtkWidget *chat_close_button;
567 if (num_pages == 1) {
568 chat = gtk_notebook_get_nth_page (GTK_NOTEBOOK (priv->notebook), 0);
569 chat_close_button = g_object_get_data (G_OBJECT (chat),
570 "chat-window-tab-close-button");
571 gtk_widget_hide (chat_close_button);
573 for (i=0; i<num_pages; i++) {
574 chat = gtk_notebook_get_nth_page (GTK_NOTEBOOK (priv->notebook), i);
575 chat_close_button = g_object_get_data (G_OBJECT (chat),
576 "chat-window-tab-close-button");
577 gtk_widget_show (chat_close_button);
583 chat_window_update (EmpathyChatWindow *window)
585 EmpathyChatWindowPriv *priv = GET_PRIV (window);
588 num_pages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (priv->notebook));
590 /* Update Tab menu */
591 chat_window_menu_context_update (priv,
594 chat_window_conversation_menu_update (priv, window);
596 chat_window_contact_menu_update (priv,
599 chat_window_title_update (priv);
601 chat_window_icon_update (priv);
603 chat_window_close_button_update (priv,
608 append_markup_printf (GString *string,
615 va_start (args, format);
617 tmp = g_markup_vprintf_escaped (format, args);
618 g_string_append (string, tmp);
625 chat_window_update_chat_tab (EmpathyChat *chat)
627 EmpathyChatWindow *window;
628 EmpathyChatWindowPriv *priv;
629 EmpathyContact *remote_contact;
633 const gchar *subject;
634 const gchar *status = NULL;
638 const gchar *icon_name;
639 GtkWidget *tab_image;
640 GtkWidget *menu_image;
642 window = chat_window_find_chat (chat);
646 priv = GET_PRIV (window);
648 /* Get information */
649 name = empathy_chat_get_name (chat);
650 account = empathy_chat_get_account (chat);
651 subject = empathy_chat_get_subject (chat);
652 remote_contact = empathy_chat_get_remote_contact (chat);
654 DEBUG ("Updating chat tab, name=%s, account=%s, subject=%s, remote_contact=%p",
655 name, tp_proxy_get_object_path (account), subject, remote_contact);
657 /* Update tab image */
658 if (empathy_chat_get_tp_chat (chat) == NULL) {
659 /* No TpChat, we are disconnected */
662 else if (g_list_find (priv->chats_new_msg, chat)) {
663 icon_name = EMPATHY_IMAGE_MESSAGE;
665 else if (g_list_find (priv->chats_composing, chat)) {
666 icon_name = EMPATHY_IMAGE_TYPING;
668 else if (remote_contact) {
669 icon_name = empathy_icon_name_for_contact (remote_contact);
671 icon_name = EMPATHY_IMAGE_GROUP_MESSAGE;
674 tab_image = g_object_get_data (G_OBJECT (chat), "chat-window-tab-image");
675 menu_image = g_object_get_data (G_OBJECT (chat), "chat-window-menu-image");
676 if (icon_name != NULL) {
677 gtk_image_set_from_icon_name (GTK_IMAGE (tab_image), icon_name, GTK_ICON_SIZE_MENU);
678 gtk_widget_show (tab_image);
679 gtk_image_set_from_icon_name (GTK_IMAGE (menu_image), icon_name, GTK_ICON_SIZE_MENU);
680 gtk_widget_show (menu_image);
682 gtk_widget_hide (tab_image);
683 gtk_widget_hide (menu_image);
686 /* Update tab tooltip */
687 tooltip = g_string_new (NULL);
689 if (remote_contact) {
690 id = empathy_contact_get_id (remote_contact);
691 status = empathy_contact_get_presence_message (remote_contact);
696 append_markup_printf (tooltip,
697 "<b>%s</b><small> (%s)</small>",
699 tp_account_get_display_name (account));
701 if (!EMP_STR_EMPTY (status)) {
702 append_markup_printf (tooltip, "\n<i>%s</i>", status);
706 append_markup_printf (tooltip, "\n<b>%s</b> %s",
707 _("Topic:"), subject);
710 if (g_list_find (priv->chats_composing, chat)) {
711 append_markup_printf (tooltip, "\n%s", _("Typing a message."));
714 markup = g_string_free (tooltip, FALSE);
715 widget = g_object_get_data (G_OBJECT (chat), "chat-window-tab-tooltip-widget");
716 gtk_widget_set_tooltip_markup (widget, markup);
717 widget = g_object_get_data (G_OBJECT (chat), "chat-window-menu-tooltip-widget");
718 gtk_widget_set_tooltip_markup (widget, markup);
721 /* Update tab and menu label */
722 widget = g_object_get_data (G_OBJECT (chat), "chat-window-tab-label");
723 gtk_label_set_text (GTK_LABEL (widget), name);
724 widget = g_object_get_data (G_OBJECT (chat), "chat-window-menu-label");
725 gtk_label_set_text (GTK_LABEL (widget), name);
727 /* Update the window if it's the current chat */
728 if (priv->current_chat == chat) {
729 chat_window_update (window);
734 chat_window_chat_notify_cb (EmpathyChat *chat)
736 EmpathyContact *old_remote_contact;
737 EmpathyContact *remote_contact = NULL;
739 old_remote_contact = g_object_get_data (G_OBJECT (chat), "chat-window-remote-contact");
740 remote_contact = empathy_chat_get_remote_contact (chat);
742 if (old_remote_contact != remote_contact) {
743 /* The remote-contact associated with the chat changed, we need
744 * to keep track of any change of that contact and update the
745 * window each time. */
746 if (remote_contact) {
747 g_signal_connect_swapped (remote_contact, "notify",
748 G_CALLBACK (chat_window_update_chat_tab),
751 if (old_remote_contact) {
752 g_signal_handlers_disconnect_by_func (old_remote_contact,
753 chat_window_update_chat_tab,
757 g_object_set_data_full (G_OBJECT (chat), "chat-window-remote-contact",
758 g_object_ref (remote_contact), (GDestroyNotify) g_object_unref);
761 chat_window_update_chat_tab (chat);
765 chat_window_insert_smiley_activate_cb (EmpathySmileyManager *manager,
766 EmpathySmiley *smiley,
769 EmpathyChatWindowPriv *priv = GET_PRIV (window);
771 GtkTextBuffer *buffer;
774 chat = priv->current_chat;
776 buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (chat->input_text_view));
777 gtk_text_buffer_get_end_iter (buffer, &iter);
778 gtk_text_buffer_insert (buffer, &iter, smiley->str, -1);
782 chat_window_conv_activate_cb (GtkAction *action,
783 EmpathyChatWindow *window)
785 EmpathyChatWindowPriv *priv = GET_PRIV (window);
788 EmpathyContact *remote_contact = NULL;
790 /* Favorite room menu */
791 is_room = empathy_chat_is_room (priv->current_chat);
795 gboolean found = FALSE;
796 EmpathyChatroom *chatroom;
798 room = empathy_chat_get_id (priv->current_chat);
799 account = empathy_chat_get_account (priv->current_chat);
800 chatroom = empathy_chatroom_manager_find (priv->chatroom_manager,
802 if (chatroom != NULL)
803 found = empathy_chatroom_is_favorite (chatroom);
805 DEBUG ("This room %s favorite", found ? "is" : "is not");
806 gtk_toggle_action_set_active (
807 GTK_TOGGLE_ACTION (priv->menu_conv_favorite), found);
809 if (chatroom != NULL)
810 found = empathy_chatroom_is_always_urgent (chatroom);
812 gtk_toggle_action_set_active (
813 GTK_TOGGLE_ACTION (priv->menu_conv_always_urgent),
816 gtk_action_set_visible (priv->menu_conv_favorite, is_room);
817 gtk_action_set_visible (priv->menu_conv_always_urgent, is_room);
819 /* Show contacts menu */
820 g_object_get (priv->current_chat,
821 "remote-contact", &remote_contact,
822 "show-contacts", &active,
824 if (remote_contact == NULL) {
825 gtk_toggle_action_set_active (
826 GTK_TOGGLE_ACTION (priv->menu_conv_toggle_contacts),
829 gtk_action_set_visible (priv->menu_conv_toggle_contacts,
830 (remote_contact == NULL));
831 if (remote_contact != NULL) {
832 g_object_unref (remote_contact);
837 chat_window_clear_activate_cb (GtkAction *action,
838 EmpathyChatWindow *window)
840 EmpathyChatWindowPriv *priv = GET_PRIV (window);
842 empathy_chat_clear (priv->current_chat);
846 chat_window_favorite_toggled_cb (GtkToggleAction *toggle_action,
847 EmpathyChatWindow *window)
849 EmpathyChatWindowPriv *priv = GET_PRIV (window);
853 EmpathyChatroom *chatroom;
855 active = gtk_toggle_action_get_active (toggle_action);
856 account = empathy_chat_get_account (priv->current_chat);
857 room = empathy_chat_get_id (priv->current_chat);
859 chatroom = empathy_chatroom_manager_ensure_chatroom (
860 priv->chatroom_manager,
863 empathy_chat_get_name (priv->current_chat));
865 empathy_chatroom_set_favorite (chatroom, active);
866 g_object_unref (chatroom);
870 chat_window_always_urgent_toggled_cb (GtkToggleAction *toggle_action,
871 EmpathyChatWindow *window)
873 EmpathyChatWindowPriv *priv = GET_PRIV (window);
877 EmpathyChatroom *chatroom;
879 active = gtk_toggle_action_get_active (toggle_action);
880 account = empathy_chat_get_account (priv->current_chat);
881 room = empathy_chat_get_id (priv->current_chat);
883 chatroom = empathy_chatroom_manager_ensure_chatroom (
884 priv->chatroom_manager,
887 empathy_chat_get_name (priv->current_chat));
889 empathy_chatroom_set_always_urgent (chatroom, active);
890 g_object_unref (chatroom);
894 chat_window_contacts_toggled_cb (GtkToggleAction *toggle_action,
895 EmpathyChatWindow *window)
897 EmpathyChatWindowPriv *priv = GET_PRIV (window);
900 active = gtk_toggle_action_get_active (toggle_action);
902 empathy_chat_set_show_contacts (priv->current_chat, active);
906 got_contact_cb (TpConnection *connection,
907 EmpathyContact *contact,
912 EmpathyTpChat *tp_chat = EMPATHY_TP_CHAT (user_data);
915 DEBUG ("Failed: %s", error->message);
918 empathy_contact_list_add (EMPATHY_CONTACT_LIST (tp_chat),
919 contact, _("Inviting you to this room"));
924 chat_window_invite_participant_activate_cb (GtkAction *action,
925 EmpathyChatWindow *window)
927 EmpathyChatWindowPriv *priv;
929 EmpathyTpChat *tp_chat;
934 priv = GET_PRIV (window);
936 g_return_if_fail (priv->current_chat != NULL);
938 tp_chat = empathy_chat_get_tp_chat (priv->current_chat);
939 channel = empathy_tp_chat_get_channel (tp_chat);
940 account = empathy_chat_get_account (priv->current_chat);
942 dialog = empathy_invite_participant_dialog_new (
943 GTK_WINDOW (priv->dialog), account);
944 gtk_widget_show (dialog);
946 response = gtk_dialog_run (GTK_DIALOG (dialog));
948 if (response == GTK_RESPONSE_ACCEPT) {
949 TpConnection *connection;
952 id = empathy_contact_selector_dialog_get_selected (
953 EMPATHY_CONTACT_SELECTOR_DIALOG (dialog), NULL, NULL);
954 if (EMP_STR_EMPTY (id)) goto out;
956 connection = tp_channel_borrow_connection (channel);
957 empathy_tp_contact_factory_get_from_id (connection, id,
958 got_contact_cb, tp_chat, NULL, NULL);
962 gtk_widget_destroy (dialog);
966 chat_window_close_activate_cb (GtkAction *action,
967 EmpathyChatWindow *window)
969 EmpathyChatWindowPriv *priv;
971 priv = GET_PRIV (window);
973 g_return_if_fail (priv->current_chat != NULL);
975 empathy_chat_window_remove_chat (window, priv->current_chat);
979 chat_window_edit_activate_cb (GtkAction *action,
980 EmpathyChatWindow *window)
982 EmpathyChatWindowPriv *priv;
983 GtkClipboard *clipboard;
984 GtkTextBuffer *buffer;
985 gboolean text_available;
987 priv = GET_PRIV (window);
989 g_return_if_fail (priv->current_chat != NULL);
991 if (!empathy_chat_get_tp_chat (priv->current_chat)) {
992 gtk_action_set_sensitive (priv->menu_edit_copy, FALSE);
993 gtk_action_set_sensitive (priv->menu_edit_cut, FALSE);
994 gtk_action_set_sensitive (priv->menu_edit_paste, FALSE);
998 buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (priv->current_chat->input_text_view));
999 if (gtk_text_buffer_get_has_selection (buffer)) {
1000 gtk_action_set_sensitive (priv->menu_edit_copy, TRUE);
1001 gtk_action_set_sensitive (priv->menu_edit_cut, TRUE);
1005 selection = empathy_chat_view_get_has_selection (priv->current_chat->view);
1007 gtk_action_set_sensitive (priv->menu_edit_cut, FALSE);
1008 gtk_action_set_sensitive (priv->menu_edit_copy, selection);
1011 clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
1012 text_available = gtk_clipboard_wait_is_text_available (clipboard);
1013 gtk_action_set_sensitive (priv->menu_edit_paste, text_available);
1017 chat_window_cut_activate_cb (GtkAction *action,
1018 EmpathyChatWindow *window)
1020 EmpathyChatWindowPriv *priv;
1022 g_return_if_fail (EMPATHY_IS_CHAT_WINDOW (window));
1024 priv = GET_PRIV (window);
1026 empathy_chat_cut (priv->current_chat);
1030 chat_window_copy_activate_cb (GtkAction *action,
1031 EmpathyChatWindow *window)
1033 EmpathyChatWindowPriv *priv;
1035 g_return_if_fail (EMPATHY_IS_CHAT_WINDOW (window));
1037 priv = GET_PRIV (window);
1039 empathy_chat_copy (priv->current_chat);
1043 chat_window_paste_activate_cb (GtkAction *action,
1044 EmpathyChatWindow *window)
1046 EmpathyChatWindowPriv *priv;
1048 g_return_if_fail (EMPATHY_IS_CHAT_WINDOW (window));
1050 priv = GET_PRIV (window);
1052 empathy_chat_paste (priv->current_chat);
1056 chat_window_find_activate_cb (GtkAction *action,
1057 EmpathyChatWindow *window)
1059 EmpathyChatWindowPriv *priv;
1061 g_return_if_fail (EMPATHY_IS_CHAT_WINDOW (window));
1063 priv = GET_PRIV (window);
1065 empathy_chat_find (priv->current_chat);
1069 chat_window_tabs_next_activate_cb (GtkAction *action,
1070 EmpathyChatWindow *window)
1072 EmpathyChatWindowPriv *priv;
1074 gint index_, numPages;
1075 gboolean wrap_around;
1077 priv = GET_PRIV (window);
1079 g_object_get (gtk_settings_get_default (), "gtk-keynav-wrap-around",
1080 &wrap_around, NULL);
1082 chat = priv->current_chat;
1083 index_ = gtk_notebook_get_current_page (GTK_NOTEBOOK (priv->notebook));
1084 numPages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (priv->notebook));
1086 if (index_ == (numPages - 1) && wrap_around) {
1087 gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->notebook), 0);
1091 gtk_notebook_next_page (GTK_NOTEBOOK (priv->notebook));
1095 chat_window_tabs_previous_activate_cb (GtkAction *action,
1096 EmpathyChatWindow *window)
1098 EmpathyChatWindowPriv *priv;
1100 gint index_, numPages;
1101 gboolean wrap_around;
1103 priv = GET_PRIV (window);
1105 g_object_get (gtk_settings_get_default (), "gtk-keynav-wrap-around",
1106 &wrap_around, NULL);
1108 chat = priv->current_chat;
1109 index_ = gtk_notebook_get_current_page (GTK_NOTEBOOK (priv->notebook));
1110 numPages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (priv->notebook));
1112 if (index_ <= 0 && wrap_around) {
1113 gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->notebook), numPages - 1);
1117 gtk_notebook_prev_page (GTK_NOTEBOOK (priv->notebook));
1121 chat_window_tabs_undo_close_tab_activate_cb (GtkAction *action,
1122 EmpathyChatWindow *window)
1124 EmpathyChatWindowPriv *priv = GET_PRIV (window);
1125 empathy_chat_manager_undo_closed_chat (priv->chat_manager);
1129 chat_window_tabs_left_activate_cb (GtkAction *action,
1130 EmpathyChatWindow *window)
1132 EmpathyChatWindowPriv *priv;
1134 gint index_, num_pages;
1136 priv = GET_PRIV (window);
1138 chat = priv->current_chat;
1139 index_ = gtk_notebook_get_current_page (GTK_NOTEBOOK (priv->notebook));
1144 gtk_notebook_reorder_child (GTK_NOTEBOOK (priv->notebook),
1148 num_pages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (priv->notebook));
1149 chat_window_menu_context_update (priv, num_pages);
1153 chat_window_tabs_right_activate_cb (GtkAction *action,
1154 EmpathyChatWindow *window)
1156 EmpathyChatWindowPriv *priv;
1158 gint index_, num_pages;
1160 priv = GET_PRIV (window);
1162 chat = priv->current_chat;
1163 index_ = gtk_notebook_get_current_page (GTK_NOTEBOOK (priv->notebook));
1165 gtk_notebook_reorder_child (GTK_NOTEBOOK (priv->notebook),
1169 num_pages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (priv->notebook));
1170 chat_window_menu_context_update (priv, num_pages);
1173 static EmpathyChatWindow *
1174 empathy_chat_window_new (void)
1176 return EMPATHY_CHAT_WINDOW (g_object_new (EMPATHY_TYPE_CHAT_WINDOW, NULL));
1180 chat_window_detach_activate_cb (GtkAction *action,
1181 EmpathyChatWindow *window)
1183 EmpathyChatWindowPriv *priv;
1184 EmpathyChatWindow *new_window;
1187 priv = GET_PRIV (window);
1189 chat = priv->current_chat;
1190 new_window = empathy_chat_window_new ();
1192 empathy_chat_window_move_chat (window, new_window, chat);
1194 priv = GET_PRIV (new_window);
1195 gtk_widget_show (priv->dialog);
1199 chat_window_help_contents_activate_cb (GtkAction *action,
1200 EmpathyChatWindow *window)
1202 EmpathyChatWindowPriv *priv = GET_PRIV (window);
1204 empathy_url_show (priv->dialog, "ghelp:empathy");
1208 chat_window_help_about_activate_cb (GtkAction *action,
1209 EmpathyChatWindow *window)
1211 EmpathyChatWindowPriv *priv = GET_PRIV (window);
1213 empathy_about_dialog_new (GTK_WINDOW (priv->dialog));
1217 chat_window_delete_event_cb (GtkWidget *dialog,
1219 EmpathyChatWindow *window)
1221 EmpathyChatWindowPriv *priv = GET_PRIV (window);
1223 DEBUG ("Delete event received");
1225 g_object_ref (window);
1226 while (priv->chats) {
1227 empathy_chat_window_remove_chat (window, priv->chats->data);
1229 g_object_unref (window);
1235 chat_window_composing_cb (EmpathyChat *chat,
1236 gboolean is_composing,
1237 EmpathyChatWindow *window)
1239 EmpathyChatWindowPriv *priv;
1241 priv = GET_PRIV (window);
1243 if (is_composing && !g_list_find (priv->chats_composing, chat)) {
1244 priv->chats_composing = g_list_prepend (priv->chats_composing, chat);
1246 priv->chats_composing = g_list_remove (priv->chats_composing, chat);
1249 chat_window_update_chat_tab (chat);
1253 chat_window_set_urgency_hint (EmpathyChatWindow *window,
1256 EmpathyChatWindowPriv *priv;
1258 priv = GET_PRIV (window);
1260 gtk_window_set_urgency_hint (GTK_WINDOW (priv->dialog), urgent);
1264 chat_window_notification_closed_cb (NotifyNotification *notify,
1265 EmpathyChatWindow *self)
1267 EmpathyChatWindowPriv *priv = GET_PRIV (self);
1269 g_object_unref (notify);
1270 if (priv->notification == notify) {
1271 priv->notification = NULL;
1276 chat_window_show_or_update_notification (EmpathyChatWindow *window,
1277 EmpathyMessage *message,
1280 EmpathyContact *sender;
1281 const gchar *header;
1285 EmpathyChatWindowPriv *priv = GET_PRIV (window);
1286 gboolean res, has_x_canonical_append;
1287 NotifyNotification *notification = priv->notification;
1289 if (!empathy_notify_manager_notification_is_enabled (priv->notify_mgr)) {
1292 res = g_settings_get_boolean (priv->gsettings_notif,
1293 EMPATHY_PREFS_NOTIFICATIONS_FOCUS);
1300 sender = empathy_message_get_sender (message);
1301 header = empathy_contact_get_alias (sender);
1302 body = empathy_message_get_body (message);
1303 escaped = g_markup_escape_text (body, -1);
1304 has_x_canonical_append = empathy_notify_manager_has_capability (
1305 priv->notify_mgr, EMPATHY_NOTIFY_MANAGER_CAP_X_CANONICAL_APPEND);
1307 if (notification != NULL && !has_x_canonical_append) {
1308 /* if the notification server supports x-canonical-append, it is
1309 better to not use notify_notification_update to avoid
1310 overwriting the current notification message */
1311 notify_notification_update (notification,
1312 header, escaped, NULL);
1314 /* if the notification server supports x-canonical-append,
1315 the hint will be added, so that the message from the
1316 just created notification will be automatically appended
1317 to an existing notification with the same title.
1318 In this way the previous message will not be lost: the new
1319 message will appear below it, in the same notification */
1320 notification = notify_notification_new (header, escaped, NULL);
1322 if (priv->notification == NULL) {
1323 priv->notification = notification;
1326 notify_notification_set_timeout (notification, NOTIFY_EXPIRES_DEFAULT);
1328 tp_g_signal_connect_object (notification, "closed",
1329 G_CALLBACK (chat_window_notification_closed_cb), window, 0);
1331 if (has_x_canonical_append) {
1332 notify_notification_set_hint_string (notification,
1333 EMPATHY_NOTIFY_MANAGER_CAP_X_CANONICAL_APPEND, "");
1337 pixbuf = empathy_notify_manager_get_pixbuf_for_notification (priv->notify_mgr,
1338 sender, EMPATHY_IMAGE_NEW_MESSAGE);
1340 if (pixbuf != NULL) {
1341 notify_notification_set_icon_from_pixbuf (notification, pixbuf);
1342 g_object_unref (pixbuf);
1345 notify_notification_show (notification, NULL);
1351 chat_window_set_highlight_room_tab_label (EmpathyChat *chat)
1356 if (!empathy_chat_is_room (chat))
1359 markup = g_markup_printf_escaped (
1360 "<span color=\"red\" weight=\"bold\">%s</span>",
1361 empathy_chat_get_name (chat));
1363 widget = g_object_get_data (G_OBJECT (chat), "chat-window-tab-label");
1364 gtk_label_set_markup (GTK_LABEL (widget), markup);
1369 empathy_chat_window_has_focus (EmpathyChatWindow *window)
1371 EmpathyChatWindowPriv *priv;
1374 g_return_val_if_fail (EMPATHY_IS_CHAT_WINDOW (window), FALSE);
1376 priv = GET_PRIV (window);
1378 g_object_get (priv->dialog, "has-toplevel-focus", &has_focus, NULL);
1384 chat_window_new_message_cb (EmpathyChat *chat,
1385 EmpathyMessage *message,
1387 EmpathyChatWindow *window)
1389 EmpathyChatWindowPriv *priv;
1391 gboolean needs_urgency;
1392 EmpathyContact *sender;
1394 priv = GET_PRIV (window);
1396 has_focus = empathy_chat_window_has_focus (window);
1398 /* - if we're the sender, we play the sound if it's specified in the
1399 * preferences and we're not away.
1400 * - if we receive a message, we play the sound if it's specified in the
1401 * preferences and the window does not have focus on the chat receiving
1405 sender = empathy_message_get_sender (message);
1407 if (empathy_contact_is_user (sender)) {
1408 empathy_sound_manager_play (priv->sound_mgr, GTK_WIDGET (priv->dialog),
1409 EMPATHY_SOUND_MESSAGE_OUTGOING);
1412 if (has_focus && priv->current_chat == chat) {
1413 /* window and tab are focused so consider the message to be read */
1415 /* FIXME: see Bug#610994 and coments about it in EmpathyChatPriv */
1416 empathy_chat_messages_read (chat);
1420 if (!g_list_find (priv->chats_new_msg, chat)) {
1421 priv->chats_new_msg = g_list_prepend (priv->chats_new_msg, chat);
1422 chat_window_update_chat_tab (chat);
1425 /* If empathy_chat_is_room () returns TRUE, that means it's a named MUC.
1426 * If empathy_chat_get_remote_contact () returns NULL, that means it's
1427 * an unamed MUC (msn-like).
1428 * In case of a MUC, we set urgency if either:
1429 * a) the chatroom's always_urgent property is TRUE
1430 * b) the message contains our alias
1432 if (empathy_chat_is_room (chat) ||
1433 empathy_chat_get_remote_contact (chat) == NULL) {
1436 EmpathyChatroom *chatroom;
1438 account = empathy_chat_get_account (chat);
1439 room = empathy_chat_get_id (chat);
1441 chatroom = empathy_chatroom_manager_find (priv->chatroom_manager,
1444 if (empathy_chatroom_is_always_urgent (chatroom)) {
1445 needs_urgency = TRUE;
1447 needs_urgency = empathy_message_should_highlight (message);
1450 needs_urgency = TRUE;
1453 if (needs_urgency) {
1455 chat_window_set_urgency_hint (window, TRUE);
1456 chat_window_set_highlight_room_tab_label (chat);
1459 empathy_sound_manager_play (priv->sound_mgr, GTK_WIDGET (priv->dialog),
1460 EMPATHY_SOUND_MESSAGE_INCOMING);
1462 /* Pending messages have already been displayed in the approver, so we don't
1463 * display a notification for those. */
1465 chat_window_show_or_update_notification (window, message, chat);
1468 /* update the number of unread messages and the window icon */
1469 chat_window_title_update (priv);
1470 chat_window_icon_update (priv);
1473 static GtkNotebook *
1474 notebook_create_window_cb (GtkNotebook *source,
1480 EmpathyChatWindowPriv *priv;
1481 EmpathyChatWindow *window, *new_window;
1484 chat = EMPATHY_CHAT (page);
1485 window = chat_window_find_chat (chat);
1487 new_window = empathy_chat_window_new ();
1488 priv = GET_PRIV (new_window);
1490 DEBUG ("Detach hook called");
1492 empathy_chat_window_move_chat (window, new_window, chat);
1494 gtk_widget_show (priv->dialog);
1495 gtk_window_move (GTK_WINDOW (priv->dialog), x, y);
1501 chat_window_page_switched_cb (GtkNotebook *notebook,
1502 gpointer ignore, /* see note below */
1504 EmpathyChatWindow *window)
1506 EmpathyChatWindowPriv *priv;
1510 DEBUG ("Page switched");
1512 priv = GET_PRIV (window);
1514 /* N.B. in GTK+ 3 child is passed as the first argument to the signal,
1515 * but we can't use that while trying to retain GTK+ 2.x compatibility.
1517 child = gtk_notebook_get_nth_page (notebook, page_num);
1518 chat = EMPATHY_CHAT (child);
1520 if (priv->page_added) {
1521 priv->page_added = FALSE;
1522 empathy_chat_scroll_down (chat);
1524 else if (priv->current_chat == chat) {
1528 priv->current_chat = chat;
1529 priv->chats_new_msg = g_list_remove (priv->chats_new_msg, chat);
1530 empathy_chat_messages_read (chat);
1532 chat_window_update_chat_tab (chat);
1536 chat_window_page_added_cb (GtkNotebook *notebook,
1539 EmpathyChatWindow *window)
1541 EmpathyChatWindowPriv *priv;
1544 priv = GET_PRIV (window);
1546 /* If we just received DND to the same window, we don't want
1547 * to do anything here like removing the tab and then readding
1548 * it, so we return here and in "page-added".
1550 if (priv->dnd_same_window) {
1551 DEBUG ("Page added (back to the same window)");
1552 priv->dnd_same_window = FALSE;
1556 DEBUG ("Page added");
1558 /* Get chat object */
1559 chat = EMPATHY_CHAT (child);
1561 /* Connect chat signals for this window */
1562 g_signal_connect (chat, "composing",
1563 G_CALLBACK (chat_window_composing_cb),
1565 g_signal_connect (chat, "new-message",
1566 G_CALLBACK (chat_window_new_message_cb),
1568 g_signal_connect (chat, "notify::tp-chat",
1569 G_CALLBACK (chat_window_update_chat_tab),
1572 /* Set flag so we know to perform some special operations on
1573 * switch page due to the new page being added.
1575 priv->page_added = TRUE;
1577 /* Get list of chats up to date */
1578 priv->chats = g_list_append (priv->chats, chat);
1580 chat_window_update_chat_tab (chat);
1584 chat_window_page_removed_cb (GtkNotebook *notebook,
1587 EmpathyChatWindow *window)
1589 EmpathyChatWindowPriv *priv;
1592 priv = GET_PRIV (window);
1594 /* If we just received DND to the same window, we don't want
1595 * to do anything here like removing the tab and then readding
1596 * it, so we return here and in "page-added".
1598 if (priv->dnd_same_window) {
1599 DEBUG ("Page removed (and will be readded to same window)");
1603 DEBUG ("Page removed");
1605 /* Get chat object */
1606 chat = EMPATHY_CHAT (child);
1608 /* Disconnect all signal handlers for this chat and this window */
1609 g_signal_handlers_disconnect_by_func (chat,
1610 G_CALLBACK (chat_window_composing_cb),
1612 g_signal_handlers_disconnect_by_func (chat,
1613 G_CALLBACK (chat_window_new_message_cb),
1615 g_signal_handlers_disconnect_by_func (chat,
1616 G_CALLBACK (chat_window_update_chat_tab),
1619 /* Keep list of chats up to date */
1620 priv->chats = g_list_remove (priv->chats, chat);
1621 priv->chats_new_msg = g_list_remove (priv->chats_new_msg, chat);
1622 empathy_chat_messages_read (chat);
1623 priv->chats_composing = g_list_remove (priv->chats_composing, chat);
1625 if (priv->chats == NULL) {
1626 g_object_unref (window);
1628 chat_window_update (window);
1633 chat_window_focus_in_event_cb (GtkWidget *widget,
1635 EmpathyChatWindow *window)
1637 EmpathyChatWindowPriv *priv;
1639 priv = GET_PRIV (window);
1641 priv->chats_new_msg = g_list_remove (priv->chats_new_msg, priv->current_chat);
1642 empathy_chat_messages_read (priv->current_chat);
1644 chat_window_set_urgency_hint (window, FALSE);
1646 /* Update the title, since we now mark all unread messages as read. */
1647 chat_window_update_chat_tab (priv->current_chat);
1653 chat_window_drag_drop (GtkWidget *widget,
1654 GdkDragContext *context,
1658 EmpathyChatWindow *window)
1661 EmpathyChatWindowPriv *priv;
1663 priv = GET_PRIV (window);
1665 target = gtk_drag_dest_find_target (widget, context, priv->file_targets);
1666 if (target == GDK_NONE)
1667 target = gtk_drag_dest_find_target (widget, context, priv->contact_targets);
1669 if (target != GDK_NONE) {
1670 gtk_drag_get_data (widget, context, target, time_);
1678 chat_window_drag_motion (GtkWidget *widget,
1679 GdkDragContext *context,
1683 EmpathyChatWindow *window)
1686 EmpathyChatWindowPriv *priv;
1688 priv = GET_PRIV (window);
1690 target = gtk_drag_dest_find_target (widget, context, priv->file_targets);
1691 if (target != GDK_NONE) {
1692 /* This is a file drag. Ensure the contact is online and set the
1693 drag type to COPY. Note that it's possible that the tab will
1694 be switched by GTK+ after a timeout from drag_motion without
1695 getting another drag_motion to disable the drop. You have
1696 to hold your mouse really still.
1698 EmpathyContact *contact;
1700 priv = GET_PRIV (window);
1701 contact = empathy_chat_get_remote_contact (priv->current_chat);
1702 /* contact is NULL for multi-user chats. We don't do
1703 * file transfers to MUCs. We also don't send files
1704 * to offline contacts or contacts that don't support
1707 if ((contact == NULL) || !empathy_contact_is_online (contact)) {
1708 gdk_drag_status (context, 0, time_);
1711 if (!(empathy_contact_get_capabilities (contact)
1712 & EMPATHY_CAPABILITIES_FT)) {
1713 gdk_drag_status (context, 0, time_);
1716 gdk_drag_status (context, GDK_ACTION_COPY, time_);
1720 target = gtk_drag_dest_find_target (widget, context, priv->contact_targets);
1721 if (target != GDK_NONE) {
1722 /* This is a drag of a contact from a contact list. Set to COPY.
1723 FIXME: If this drag is to a MUC window, it invites the user.
1724 Otherwise, it opens a chat. Should we use a different drag
1725 type for invites? Should we allow ASK?
1727 gdk_drag_status (context, GDK_ACTION_COPY, time_);
1735 chat_window_drag_data_received (GtkWidget *widget,
1736 GdkDragContext *context,
1739 GtkSelectionData *selection,
1742 EmpathyChatWindow *window)
1744 if (info == DND_DRAG_TYPE_CONTACT_ID) {
1745 EmpathyChat *chat = NULL;
1746 EmpathyChatWindow *old_window;
1747 TpAccount *account = NULL;
1748 TpAccountManager *account_manager;
1751 const gchar *account_id;
1752 const gchar *contact_id;
1754 id = (const gchar*) gtk_selection_data_get_data (selection);
1756 /* FIXME: Perhaps should be sure that the account manager is
1757 * prepared before calling _ensure_account on it. */
1758 account_manager = tp_account_manager_dup ();
1760 DEBUG ("DND contact from roster with id:'%s'", id);
1762 strv = g_strsplit (id, ":", 2);
1763 if (g_strv_length (strv) == 2) {
1764 account_id = strv[0];
1765 contact_id = strv[1];
1767 tp_account_manager_ensure_account (account_manager, account_id);
1768 if (account != NULL)
1769 chat = empathy_chat_window_find_chat (account, contact_id);
1772 if (account == NULL) {
1774 gtk_drag_finish (context, FALSE, FALSE, time_);
1779 empathy_dispatcher_chat_with_contact_id (
1780 account, contact_id, gtk_get_current_event_time ());
1785 g_object_unref (account_manager);
1788 old_window = chat_window_find_chat (chat);
1790 if (old_window == window) {
1791 gtk_drag_finish (context, TRUE, FALSE, time_);
1795 empathy_chat_window_move_chat (old_window, window, chat);
1797 empathy_chat_window_add_chat (window, chat);
1800 /* Added to take care of any outstanding chat events */
1801 empathy_chat_window_present_chat (chat,
1802 TP_USER_ACTION_TIME_NOT_USER_ACTION);
1804 /* We should return TRUE to remove the data when doing
1805 * GDK_ACTION_MOVE, but we don't here otherwise it has
1806 * weird consequences, and we handle that internally
1807 * anyway with add_chat () and remove_chat ().
1809 gtk_drag_finish (context, TRUE, FALSE, time_);
1811 else if (info == DND_DRAG_TYPE_URI_LIST) {
1812 EmpathyChatWindowPriv *priv;
1813 EmpathyContact *contact;
1816 priv = GET_PRIV (window);
1817 contact = empathy_chat_get_remote_contact (priv->current_chat);
1819 /* contact is NULL when current_chat is a multi-user chat.
1820 * We don't do file transfers to MUCs, so just cancel the drag.
1822 if (contact == NULL) {
1823 gtk_drag_finish (context, TRUE, FALSE, time_);
1827 data = (const gchar *) gtk_selection_data_get_data (selection);
1828 empathy_send_file_from_uri_list (contact, data);
1830 gtk_drag_finish (context, TRUE, FALSE, time_);
1832 else if (info == DND_DRAG_TYPE_TAB) {
1834 EmpathyChatWindow *old_window = NULL;
1838 chat = (void *) gtk_selection_data_get_data (selection);
1839 old_window = chat_window_find_chat (*chat);
1842 EmpathyChatWindowPriv *priv;
1844 priv = GET_PRIV (window);
1845 priv->dnd_same_window = (old_window == window);
1846 DEBUG ("DND tab (within same window: %s)",
1847 priv->dnd_same_window ? "Yes" : "No");
1850 DEBUG ("DND from unknown source");
1851 gtk_drag_finish (context, FALSE, FALSE, time_);
1856 chat_window_chat_manager_chats_changed_cb (EmpathyChatManager *chat_manager,
1857 guint num_chats_in_manager,
1858 EmpathyChatWindow *window)
1860 EmpathyChatWindowPriv *priv = GET_PRIV (window);
1862 gtk_action_set_sensitive (priv->menu_tabs_undo_close_tab,
1863 num_chats_in_manager > 0);
1867 chat_window_finalize (GObject *object)
1869 EmpathyChatWindow *window;
1870 EmpathyChatWindowPriv *priv;
1872 window = EMPATHY_CHAT_WINDOW (object);
1873 priv = GET_PRIV (window);
1875 DEBUG ("Finalized: %p", object);
1877 g_object_unref (priv->ui_manager);
1878 g_object_unref (priv->chatroom_manager);
1879 g_object_unref (priv->notify_mgr);
1880 g_object_unref (priv->gsettings_chat);
1881 g_object_unref (priv->gsettings_notif);
1882 g_object_unref (priv->gsettings_ui);
1883 g_object_unref (priv->sound_mgr);
1885 if (priv->notification != NULL) {
1886 notify_notification_close (priv->notification, NULL);
1887 priv->notification = NULL;
1890 if (priv->contact_targets) {
1891 gtk_target_list_unref (priv->contact_targets);
1893 if (priv->file_targets) {
1894 gtk_target_list_unref (priv->file_targets);
1897 if (priv->chat_manager) {
1898 g_signal_handler_disconnect (priv->chat_manager,
1899 priv->chat_manager_chats_changed_id);
1900 g_object_unref (priv->chat_manager);
1901 priv->chat_manager = NULL;
1904 chat_windows = g_list_remove (chat_windows, window);
1905 gtk_widget_destroy (priv->dialog);
1907 G_OBJECT_CLASS (empathy_chat_window_parent_class)->finalize (object);
1911 empathy_chat_window_class_init (EmpathyChatWindowClass *klass)
1913 GObjectClass *object_class = G_OBJECT_CLASS (klass);
1915 object_class->finalize = chat_window_finalize;
1917 g_type_class_add_private (object_class, sizeof (EmpathyChatWindowPriv));
1919 /* Set up a style for the close button with no focus padding. */
1920 gtk_rc_parse_string (
1921 "style \"empathy-close-button-style\"\n"
1923 " GtkWidget::focus-padding = 0\n"
1927 "widget \"*.empathy-close-button\" style \"empathy-close-button-style\"");
1931 empathy_chat_window_init (EmpathyChatWindow *window)
1934 GtkAccelGroup *accel_group;
1939 GtkWidget *chat_vbox;
1941 EmpathySmileyManager *smiley_manager;
1942 EmpathyChatWindowPriv *priv = G_TYPE_INSTANCE_GET_PRIVATE (window,
1943 EMPATHY_TYPE_CHAT_WINDOW, EmpathyChatWindowPriv);
1945 window->priv = priv;
1946 filename = empathy_file_lookup ("empathy-chat-window.ui", "src");
1947 gui = empathy_builder_get_file (filename,
1948 "chat_window", &priv->dialog,
1949 "chat_vbox", &chat_vbox,
1950 "ui_manager", &priv->ui_manager,
1951 "menu_conv_insert_smiley", &priv->menu_conv_insert_smiley,
1952 "menu_conv_favorite", &priv->menu_conv_favorite,
1953 "menu_conv_always_urgent", &priv->menu_conv_always_urgent,
1954 "menu_conv_toggle_contacts", &priv->menu_conv_toggle_contacts,
1955 "menu_edit_cut", &priv->menu_edit_cut,
1956 "menu_edit_copy", &priv->menu_edit_copy,
1957 "menu_edit_paste", &priv->menu_edit_paste,
1958 "menu_edit_find", &priv->menu_edit_find,
1959 "menu_tabs_next", &priv->menu_tabs_next,
1960 "menu_tabs_prev", &priv->menu_tabs_prev,
1961 "menu_tabs_undo_close_tab", &priv->menu_tabs_undo_close_tab,
1962 "menu_tabs_left", &priv->menu_tabs_left,
1963 "menu_tabs_right", &priv->menu_tabs_right,
1964 "menu_tabs_detach", &priv->menu_tabs_detach,
1968 empathy_builder_connect (gui, window,
1969 "menu_conv", "activate", chat_window_conv_activate_cb,
1970 "menu_conv_clear", "activate", chat_window_clear_activate_cb,
1971 "menu_conv_favorite", "toggled", chat_window_favorite_toggled_cb,
1972 "menu_conv_always_urgent", "toggled", chat_window_always_urgent_toggled_cb,
1973 "menu_conv_toggle_contacts", "toggled", chat_window_contacts_toggled_cb,
1974 "menu_conv_invite_participant", "activate", chat_window_invite_participant_activate_cb,
1975 "menu_conv_close", "activate", chat_window_close_activate_cb,
1976 "menu_edit", "activate", chat_window_edit_activate_cb,
1977 "menu_edit_cut", "activate", chat_window_cut_activate_cb,
1978 "menu_edit_copy", "activate", chat_window_copy_activate_cb,
1979 "menu_edit_paste", "activate", chat_window_paste_activate_cb,
1980 "menu_edit_find", "activate", chat_window_find_activate_cb,
1981 "menu_tabs_next", "activate", chat_window_tabs_next_activate_cb,
1982 "menu_tabs_prev", "activate", chat_window_tabs_previous_activate_cb,
1983 "menu_tabs_undo_close_tab", "activate", chat_window_tabs_undo_close_tab_activate_cb,
1984 "menu_tabs_left", "activate", chat_window_tabs_left_activate_cb,
1985 "menu_tabs_right", "activate", chat_window_tabs_right_activate_cb,
1986 "menu_tabs_detach", "activate", chat_window_detach_activate_cb,
1987 "menu_help_contents", "activate", chat_window_help_contents_activate_cb,
1988 "menu_help_about", "activate", chat_window_help_about_activate_cb,
1991 g_object_ref (priv->ui_manager);
1992 g_object_unref (gui);
1994 priv->gsettings_chat = g_settings_new (EMPATHY_PREFS_CHAT_SCHEMA);
1995 priv->gsettings_notif = g_settings_new (EMPATHY_PREFS_NOTIFICATIONS_SCHEMA);
1996 priv->gsettings_ui = g_settings_new (EMPATHY_PREFS_UI_SCHEMA);
1997 priv->chatroom_manager = empathy_chatroom_manager_dup_singleton (NULL);
1999 priv->sound_mgr = empathy_sound_manager_dup_singleton ();
2001 priv->notebook = gtk_notebook_new ();
2003 g_signal_connect (priv->notebook, "create-window",
2004 G_CALLBACK (notebook_create_window_cb), window);
2006 gtk_notebook_set_group_name (GTK_NOTEBOOK (priv->notebook),
2007 "EmpathyChatWindow");
2008 gtk_notebook_set_scrollable (GTK_NOTEBOOK (priv->notebook), TRUE);
2009 gtk_notebook_popup_enable (GTK_NOTEBOOK (priv->notebook));
2010 gtk_box_pack_start (GTK_BOX (chat_vbox), priv->notebook, TRUE, TRUE, 0);
2011 gtk_widget_show (priv->notebook);
2014 accel_group = gtk_accel_group_new ();
2015 gtk_window_add_accel_group (GTK_WINDOW (priv->dialog), accel_group);
2017 for (i = 0; i < G_N_ELEMENTS (tab_accel_keys); i++) {
2018 closure = g_cclosure_new (G_CALLBACK (chat_window_accel_cb),
2021 gtk_accel_group_connect (accel_group,
2028 g_object_unref (accel_group);
2030 /* Set up drag target lists */
2031 priv->contact_targets = gtk_target_list_new (drag_types_dest_contact,
2032 G_N_ELEMENTS (drag_types_dest_contact));
2033 priv->file_targets = gtk_target_list_new (drag_types_dest_file,
2034 G_N_ELEMENTS (drag_types_dest_file));
2036 /* Set up smiley menu */
2037 smiley_manager = empathy_smiley_manager_dup_singleton ();
2038 submenu = empathy_smiley_menu_new (smiley_manager,
2039 chat_window_insert_smiley_activate_cb,
2041 menu = gtk_ui_manager_get_widget (priv->ui_manager,
2042 "/chats_menubar/menu_conv/menu_conv_insert_smiley");
2043 gtk_menu_item_set_submenu (GTK_MENU_ITEM (menu), submenu);
2044 g_object_unref (smiley_manager);
2046 /* Set up signals we can't do with ui file since we may need to
2047 * block/unblock them at some later stage.
2050 g_signal_connect (priv->dialog,
2052 G_CALLBACK (chat_window_delete_event_cb),
2054 g_signal_connect (priv->dialog,
2056 G_CALLBACK (chat_window_focus_in_event_cb),
2058 g_signal_connect_after (priv->notebook,
2060 G_CALLBACK (chat_window_page_switched_cb),
2062 g_signal_connect (priv->notebook,
2064 G_CALLBACK (chat_window_page_added_cb),
2066 g_signal_connect (priv->notebook,
2068 G_CALLBACK (chat_window_page_removed_cb),
2071 /* Set up drag and drop */
2072 gtk_drag_dest_set (GTK_WIDGET (priv->notebook),
2073 GTK_DEST_DEFAULT_HIGHLIGHT,
2075 G_N_ELEMENTS (drag_types_dest),
2076 GDK_ACTION_MOVE | GDK_ACTION_COPY);
2078 /* connect_after to allow GtkNotebook's built-in tab switching */
2079 g_signal_connect_after (priv->notebook,
2081 G_CALLBACK (chat_window_drag_motion),
2083 g_signal_connect (priv->notebook,
2084 "drag-data-received",
2085 G_CALLBACK (chat_window_drag_data_received),
2087 g_signal_connect (priv->notebook,
2089 G_CALLBACK (chat_window_drag_drop),
2092 chat_windows = g_list_prepend (chat_windows, window);
2094 /* Set up private details */
2096 priv->chats_new_msg = NULL;
2097 priv->chats_composing = NULL;
2098 priv->current_chat = NULL;
2099 priv->notification = NULL;
2101 priv->notify_mgr = empathy_notify_manager_dup_singleton ();
2103 priv->chat_manager = empathy_chat_manager_dup_singleton ();
2104 priv->chat_manager_chats_changed_id =
2105 g_signal_connect (priv->chat_manager, "closed-chats-changed",
2106 G_CALLBACK (chat_window_chat_manager_chats_changed_cb),
2109 chat_window_chat_manager_chats_changed_cb (priv->chat_manager,
2110 empathy_chat_manager_get_num_closed_chats (priv->chat_manager),
2115 empathy_chat_window_get_dialog (EmpathyChatWindow *window)
2117 EmpathyChatWindowPriv *priv;
2119 g_return_val_if_fail (window != NULL, NULL);
2121 priv = GET_PRIV (window);
2123 return priv->dialog;
2126 /* Returns the window to open a new tab in if there is a suitable window,
2127 * otherwise, returns NULL indicating that a new window should be added.
2129 static EmpathyChatWindow *
2130 empathy_chat_window_get_default (gboolean room)
2132 GSettings *gsettings = g_settings_new (EMPATHY_PREFS_UI_SCHEMA);
2134 gboolean separate_windows = TRUE;
2136 separate_windows = g_settings_get_boolean (gsettings,
2137 EMPATHY_PREFS_UI_SEPARATE_CHAT_WINDOWS);
2139 g_object_unref (gsettings);
2141 if (separate_windows) {
2142 /* Always create a new window */
2146 for (l = chat_windows; l; l = l->next) {
2147 EmpathyChatWindowPriv *priv;
2148 EmpathyChatWindow *chat_window;
2150 guint nb_rooms, nb_private;
2152 chat_window = l->data;
2153 priv = GET_PRIV (chat_window);
2155 dialog = empathy_chat_window_get_dialog (chat_window);
2157 empathy_chat_window_get_nb_chats (chat_window, &nb_rooms, &nb_private);
2159 /* Skip the window if there aren't any rooms in it */
2160 if (room && nb_rooms == 0)
2163 /* Skip the window if there aren't any 1-1 chats in it */
2164 if (!room && nb_private == 0)
2167 /* Found a window on this desktop, make it visible if necessary */
2168 if (!empathy_window_get_is_visible (GTK_WINDOW (dialog)))
2169 empathy_window_present (GTK_WINDOW (dialog));
2177 empathy_chat_window_add_chat (EmpathyChatWindow *window,
2180 EmpathyChatWindowPriv *priv;
2182 GtkWidget *popup_label;
2184 GValue value = { 0, };
2186 g_return_if_fail (window != NULL);
2187 g_return_if_fail (EMPATHY_IS_CHAT (chat));
2189 priv = GET_PRIV (window);
2191 /* Reference the chat object */
2192 g_object_ref (chat);
2194 /* If this window has just been created, position it */
2195 if (priv->chats == NULL) {
2196 const gchar *name = "chat-window";
2197 gboolean separate_windows;
2199 separate_windows = g_settings_get_boolean (priv->gsettings_ui,
2200 EMPATHY_PREFS_UI_SEPARATE_CHAT_WINDOWS);
2202 if (empathy_chat_is_room (chat))
2203 name = "room-window";
2205 if (separate_windows) {
2208 /* Save current position of the window */
2209 gtk_window_get_position (GTK_WINDOW (priv->dialog), &x, &y);
2211 /* First bind to the 'generic' name. So new window for which we didn't
2212 * save a geometry yet will have the geometry of the last saved
2213 * window (bgo #601191). */
2214 empathy_geometry_bind (GTK_WINDOW (priv->dialog), name);
2216 /* Restore previous position of the window so the newly created window
2217 * won't be in the same position as the latest saved window and so
2218 * completely hide it. */
2219 gtk_window_move (GTK_WINDOW (priv->dialog), x, y);
2221 /* Then bind it to the name of the contact/room so we'll save the
2222 * geometry specific to this window */
2223 name = empathy_chat_get_id (chat);
2226 empathy_geometry_bind (GTK_WINDOW (priv->dialog), name);
2229 child = GTK_WIDGET (chat);
2230 label = chat_window_create_label (window, chat, TRUE);
2231 popup_label = chat_window_create_label (window, chat, FALSE);
2232 gtk_widget_show (child);
2234 g_signal_connect (chat, "notify::name",
2235 G_CALLBACK (chat_window_chat_notify_cb),
2237 g_signal_connect (chat, "notify::subject",
2238 G_CALLBACK (chat_window_chat_notify_cb),
2240 g_signal_connect (chat, "notify::remote-contact",
2241 G_CALLBACK (chat_window_chat_notify_cb),
2243 chat_window_chat_notify_cb (chat);
2245 gtk_notebook_append_page_menu (GTK_NOTEBOOK (priv->notebook), child, label, popup_label);
2246 gtk_notebook_set_tab_reorderable (GTK_NOTEBOOK (priv->notebook), child, TRUE);
2247 gtk_notebook_set_tab_detachable (GTK_NOTEBOOK (priv->notebook), child, TRUE);
2248 g_value_init (&value, G_TYPE_BOOLEAN);
2249 g_value_set_boolean (&value, TRUE);
2250 gtk_container_child_set_property (GTK_CONTAINER (priv->notebook),
2251 child, "tab-expand" , &value);
2252 gtk_container_child_set_property (GTK_CONTAINER (priv->notebook),
2253 child, "tab-fill" , &value);
2254 g_value_unset (&value);
2256 DEBUG ("Chat added (%d references)", G_OBJECT (chat)->ref_count);
2260 empathy_chat_window_remove_chat (EmpathyChatWindow *window,
2263 EmpathyChatWindowPriv *priv;
2265 EmpathyContact *remote_contact;
2266 EmpathyChatManager *chat_manager;
2268 g_return_if_fail (window != NULL);
2269 g_return_if_fail (EMPATHY_IS_CHAT (chat));
2271 priv = GET_PRIV (window);
2273 g_signal_handlers_disconnect_by_func (chat,
2274 chat_window_chat_notify_cb,
2276 remote_contact = g_object_get_data (G_OBJECT (chat),
2277 "chat-window-remote-contact");
2278 if (remote_contact) {
2279 g_signal_handlers_disconnect_by_func (remote_contact,
2280 chat_window_update_chat_tab,
2284 chat_manager = empathy_chat_manager_dup_singleton ();
2285 empathy_chat_manager_closed_chat (chat_manager, chat);
2286 g_object_unref (chat_manager);
2288 position = gtk_notebook_page_num (GTK_NOTEBOOK (priv->notebook),
2290 gtk_notebook_remove_page (GTK_NOTEBOOK (priv->notebook), position);
2292 DEBUG ("Chat removed (%d references)", G_OBJECT (chat)->ref_count - 1);
2294 g_object_unref (chat);
2298 empathy_chat_window_move_chat (EmpathyChatWindow *old_window,
2299 EmpathyChatWindow *new_window,
2304 g_return_if_fail (EMPATHY_IS_CHAT_WINDOW (old_window));
2305 g_return_if_fail (EMPATHY_IS_CHAT_WINDOW (new_window));
2306 g_return_if_fail (EMPATHY_IS_CHAT (chat));
2308 widget = GTK_WIDGET (chat);
2310 DEBUG ("Chat moving with widget:%p (%d references)", widget,
2311 G_OBJECT (widget)->ref_count);
2313 /* We reference here to make sure we don't loose the widget
2314 * and the EmpathyChat object during the move.
2316 g_object_ref (chat);
2317 g_object_ref (widget);
2319 empathy_chat_window_remove_chat (old_window, chat);
2320 empathy_chat_window_add_chat (new_window, chat);
2322 g_object_unref (widget);
2323 g_object_unref (chat);
2327 empathy_chat_window_switch_to_chat (EmpathyChatWindow *window,
2330 EmpathyChatWindowPriv *priv;
2333 g_return_if_fail (window != NULL);
2334 g_return_if_fail (EMPATHY_IS_CHAT (chat));
2336 priv = GET_PRIV (window);
2338 page_num = gtk_notebook_page_num (GTK_NOTEBOOK (priv->notebook),
2340 gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->notebook),
2345 empathy_chat_window_find_chat (TpAccount *account,
2350 g_return_val_if_fail (!EMP_STR_EMPTY (id), NULL);
2352 for (l = chat_windows; l; l = l->next) {
2353 EmpathyChatWindowPriv *priv;
2354 EmpathyChatWindow *window;
2358 priv = GET_PRIV (window);
2360 for (ll = priv->chats; ll; ll = ll->next) {
2365 if (account == empathy_chat_get_account (chat) &&
2366 !tp_strdiff (id, empathy_chat_get_id (chat))) {
2376 empathy_chat_window_present_chat (EmpathyChat *chat,
2379 EmpathyChatWindow *window;
2380 EmpathyChatWindowPriv *priv;
2381 guint32 x_timestamp;
2383 g_return_if_fail (EMPATHY_IS_CHAT (chat));
2385 window = chat_window_find_chat (chat);
2387 /* If the chat has no window, create one */
2388 if (window == NULL) {
2389 window = empathy_chat_window_get_default (empathy_chat_is_room (chat));
2391 window = empathy_chat_window_new ();
2392 gtk_widget_show_all (GET_PRIV (window)->dialog);
2395 empathy_chat_window_add_chat (window, chat);
2398 /* Don't force the window to show itself when it wasn't
2399 * an action by the user
2401 if (!tp_user_action_time_should_present (timestamp, &x_timestamp))
2404 priv = GET_PRIV (window);
2406 if (x_timestamp != GDK_CURRENT_TIME) {
2407 /* Don't present or switch tab if the action was earlier than the
2408 * last actions X time, accounting for overflow and the first ever
2411 if (priv->x_user_action_time != 0
2412 && X_EARLIER_OR_EQL (x_timestamp, priv->x_user_action_time))
2415 priv->x_user_action_time = x_timestamp;
2418 empathy_chat_window_switch_to_chat (window, chat);
2419 empathy_window_present_with_time (GTK_WINDOW (priv->dialog),
2422 gtk_widget_grab_focus (chat->input_text_view);
2426 empathy_chat_window_get_nb_chats (EmpathyChatWindow *self,
2430 EmpathyChatWindowPriv *priv = GET_PRIV (self);
2432 guint _nb_rooms = 0, _nb_private = 0;
2434 for (l = priv->chats; l != NULL; l = g_list_next (l)) {
2435 if (empathy_chat_is_room (EMPATHY_CHAT (l->data)))
2441 if (nb_rooms != NULL)
2442 *nb_rooms = _nb_rooms;
2443 if (nb_private != NULL)
2444 *nb_private = _nb_private;