1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * Copyright (C) 2003-2007 Imendio AB
4 * Copyright (C) 2007-2010 Collabora Ltd.
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License as
8 * published by the Free Software Foundation; either version 2 of the
9 * License, or (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
16 * You should have received a copy of the GNU General Public
17 * License along with this program; if not, write to the
18 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
19 * Boston, MA 02110-1301 USA
21 * Authors: Mikael Hallendal <micke@imendio.com>
22 * Richard Hult <richard@imendio.com>
23 * Martyn Russell <martyn@imendio.com>
24 * Geert-Jan Van den Bogaerde <geertjan@gnome.org>
25 * Xavier Claessens <xclaesse@gmail.com>
26 * RĂ´mulo Fernandes Machado <romulo@castorgroup.net>
34 #include <gdk/gdkkeysyms.h>
36 #include <glib/gi18n.h>
37 #include <libnotify/notification.h>
39 #include <telepathy-glib/telepathy-glib.h>
41 #include <libempathy/empathy-contact.h>
42 #include <libempathy/empathy-message.h>
43 #include <libempathy/empathy-chatroom-manager.h>
44 #include <libempathy/empathy-gsettings.h>
45 #include <libempathy/empathy-utils.h>
46 #include <libempathy/empathy-tp-contact-factory.h>
47 #include <libempathy/empathy-contact-list.h>
48 #include <libempathy/empathy-request-util.h>
50 #include <libempathy-gtk/empathy-images.h>
51 #include <libempathy-gtk/empathy-contact-dialogs.h>
52 #include <libempathy-gtk/empathy-log-window.h>
53 #include <libempathy-gtk/empathy-geometry.h>
54 #include <libempathy-gtk/empathy-smiley-manager.h>
55 #include <libempathy-gtk/empathy-sound-manager.h>
56 #include <libempathy-gtk/empathy-ui-utils.h>
57 #include <libempathy-gtk/empathy-notify-manager.h>
59 #include "empathy-chat-manager.h"
60 #include "empathy-chat-window.h"
61 #include "empathy-about-dialog.h"
62 #include "empathy-invite-participant-dialog.h"
63 #include "gedit-close-button.h"
65 #define DEBUG_FLAG EMPATHY_DEBUG_CHAT
66 #include <libempathy/empathy-debug.h>
68 /* Macro to compare guint32 X timestamps, while accounting for wrapping around
70 #define X_EARLIER_OR_EQL(t1, t2) \
71 ((t1 <= t2 && ((t2 - t1) < G_MAXUINT32/2)) \
72 || (t1 >= t2 && (t1 - t2) > (G_MAXUINT32/2)) \
75 #define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyChatWindow)
77 EmpathyChat *current_chat;
80 gboolean dnd_same_window;
81 EmpathyChatroomManager *chatroom_manager;
82 EmpathyNotifyManager *notify_mgr;
85 NotifyNotification *notification;
87 GtkTargetList *contact_targets;
88 GtkTargetList *file_targets;
90 EmpathyChatManager *chat_manager;
91 gulong chat_manager_chats_changed_id;
94 GtkUIManager *ui_manager;
95 GtkAction *menu_conv_insert_smiley;
96 GtkAction *menu_conv_favorite;
97 GtkAction *menu_conv_always_urgent;
98 GtkAction *menu_conv_toggle_contacts;
100 GtkAction *menu_edit_cut;
101 GtkAction *menu_edit_copy;
102 GtkAction *menu_edit_paste;
103 GtkAction *menu_edit_find;
105 GtkAction *menu_tabs_next;
106 GtkAction *menu_tabs_prev;
107 GtkAction *menu_tabs_undo_close_tab;
108 GtkAction *menu_tabs_left;
109 GtkAction *menu_tabs_right;
110 GtkAction *menu_tabs_detach;
112 /* Last user action time we acted upon to show a tab */
113 guint32 x_user_action_time;
115 GSettings *gsettings_chat;
116 GSettings *gsettings_notif;
117 GSettings *gsettings_ui;
119 EmpathySoundManager *sound_mgr;
120 } EmpathyChatWindowPriv;
122 static GList *chat_windows = NULL;
124 static const guint tab_accel_keys[] = {
125 GDK_KEY_1, GDK_KEY_2, GDK_KEY_3, GDK_KEY_4, GDK_KEY_5,
126 GDK_KEY_6, GDK_KEY_7, GDK_KEY_8, GDK_KEY_9, GDK_KEY_0
130 DND_DRAG_TYPE_CONTACT_ID,
131 DND_DRAG_TYPE_URI_LIST,
135 static const GtkTargetEntry drag_types_dest[] = {
136 { "text/contact-id", 0, DND_DRAG_TYPE_CONTACT_ID },
137 { "GTK_NOTEBOOK_TAB", GTK_TARGET_SAME_APP, DND_DRAG_TYPE_TAB },
138 { "text/uri-list", 0, DND_DRAG_TYPE_URI_LIST },
139 { "text/path-list", 0, DND_DRAG_TYPE_URI_LIST },
142 static const GtkTargetEntry drag_types_dest_contact[] = {
143 { "text/contact-id", 0, DND_DRAG_TYPE_CONTACT_ID },
146 static const GtkTargetEntry drag_types_dest_file[] = {
147 /* must be first to be prioritized, in order to receive the
148 * note's file path from Tomboy instead of an URI */
149 { "text/path-list", 0, DND_DRAG_TYPE_URI_LIST },
150 { "text/uri-list", 0, DND_DRAG_TYPE_URI_LIST },
153 static void chat_window_update (EmpathyChatWindow *window,
154 gboolean update_contact_menu);
156 static void empathy_chat_window_add_chat (EmpathyChatWindow *window,
159 static void empathy_chat_window_remove_chat (EmpathyChatWindow *window,
162 static void empathy_chat_window_move_chat (EmpathyChatWindow *old_window,
163 EmpathyChatWindow *new_window,
166 static void empathy_chat_window_get_nb_chats (EmpathyChatWindow *self,
170 G_DEFINE_TYPE (EmpathyChatWindow, empathy_chat_window, G_TYPE_OBJECT);
173 chat_window_accel_cb (GtkAccelGroup *accelgroup,
177 EmpathyChatWindow *window)
179 EmpathyChatWindowPriv *priv;
183 priv = GET_PRIV (window);
185 for (i = 0; i < G_N_ELEMENTS (tab_accel_keys); i++) {
186 if (tab_accel_keys[i] == key) {
193 gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->notebook), num);
197 static EmpathyChatWindow *
198 chat_window_find_chat (EmpathyChat *chat)
200 EmpathyChatWindowPriv *priv;
203 for (l = chat_windows; l; l = l->next) {
204 priv = GET_PRIV (l->data);
205 ll = g_list_find (priv->chats, chat);
215 chat_window_close_clicked_cb (GtkAction *action,
218 EmpathyChatWindow *window;
220 window = chat_window_find_chat (chat);
221 empathy_chat_window_remove_chat (window, chat);
225 chat_tab_style_updated_cb (GtkWidget *hbox,
229 int char_width, h, w;
230 PangoContext *context;
231 const PangoFontDescription *font_desc;
232 PangoFontMetrics *metrics;
234 button = g_object_get_data (G_OBJECT (user_data),
235 "chat-window-tab-close-button");
236 context = gtk_widget_get_pango_context (hbox);
238 font_desc = gtk_style_context_get_font (gtk_widget_get_style_context (hbox),
239 GTK_STATE_FLAG_NORMAL);
241 metrics = pango_context_get_metrics (context, font_desc,
242 pango_context_get_language (context));
243 char_width = pango_font_metrics_get_approximate_char_width (metrics);
244 pango_font_metrics_unref (metrics);
246 gtk_icon_size_lookup_for_settings (gtk_widget_get_settings (button),
247 GTK_ICON_SIZE_MENU, &w, &h);
249 /* Request at least about 12 chars width plus at least space for the status
250 * image and the close button */
251 gtk_widget_set_size_request (hbox,
252 12 * PANGO_PIXELS (char_width) + 2 * w, -1);
254 gtk_widget_set_size_request (button, w, h);
258 chat_window_create_label (EmpathyChatWindow *window,
260 gboolean is_tab_label)
262 EmpathyChatWindowPriv *priv;
264 GtkWidget *name_label;
265 GtkWidget *status_image;
266 GtkWidget *event_box;
267 GtkWidget *event_box_hbox;
268 PangoAttrList *attr_list;
269 PangoAttribute *attr;
271 priv = GET_PRIV (window);
273 /* The spacing between the button and the label. */
274 hbox = gtk_hbox_new (FALSE, 0);
276 event_box = gtk_event_box_new ();
277 gtk_event_box_set_visible_window (GTK_EVENT_BOX (event_box), FALSE);
279 name_label = gtk_label_new (NULL);
281 gtk_label_set_ellipsize (GTK_LABEL (name_label), PANGO_ELLIPSIZE_END);
283 attr_list = pango_attr_list_new ();
284 attr = pango_attr_scale_new (1/1.2);
285 attr->start_index = 0;
286 attr->end_index = -1;
287 pango_attr_list_insert (attr_list, attr);
288 gtk_label_set_attributes (GTK_LABEL (name_label), attr_list);
289 pango_attr_list_unref (attr_list);
291 gtk_misc_set_padding (GTK_MISC (name_label), 2, 0);
292 gtk_misc_set_alignment (GTK_MISC (name_label), 0.0, 0.5);
293 g_object_set_data (G_OBJECT (chat),
294 is_tab_label ? "chat-window-tab-label" : "chat-window-menu-label",
297 status_image = gtk_image_new ();
299 /* Spacing between the icon and label. */
300 event_box_hbox = gtk_hbox_new (FALSE, 0);
302 gtk_box_pack_start (GTK_BOX (event_box_hbox), status_image, FALSE, FALSE, 0);
303 gtk_box_pack_start (GTK_BOX (event_box_hbox), name_label, TRUE, TRUE, 0);
305 g_object_set_data (G_OBJECT (chat),
306 is_tab_label ? "chat-window-tab-image" : "chat-window-menu-image",
308 g_object_set_data (G_OBJECT (chat),
309 is_tab_label ? "chat-window-tab-tooltip-widget" : "chat-window-menu-tooltip-widget",
312 gtk_container_add (GTK_CONTAINER (event_box), event_box_hbox);
313 gtk_box_pack_start (GTK_BOX (hbox), event_box, TRUE, TRUE, 0);
316 GtkWidget *close_button;
318 close_button = gedit_close_button_new ();
319 g_object_set_data (G_OBJECT (chat), "chat-window-tab-close-button", close_button);
321 /* We don't want focus/keynav for the button to avoid clutter, and
322 * Ctrl-W works anyway.
324 gtk_widget_set_can_focus (close_button, FALSE);
325 gtk_widget_set_can_default (close_button, FALSE);
327 gtk_box_pack_end (GTK_BOX (hbox), close_button, FALSE, FALSE, 0);
329 g_signal_connect (close_button,
331 G_CALLBACK (chat_window_close_clicked_cb),
334 /* React to theme changes and also setup the size correctly. */
335 g_signal_connect (hbox,
337 G_CALLBACK (chat_tab_style_updated_cb),
341 gtk_widget_show_all (hbox);
347 _submenu_notify_visible_changed_cb (GObject *object,
351 g_signal_handlers_disconnect_by_func (object,
352 _submenu_notify_visible_changed_cb,
354 chat_window_update (EMPATHY_CHAT_WINDOW (userdata), TRUE);
358 chat_window_menu_context_update (EmpathyChatWindowPriv *priv,
363 gboolean wrap_around;
364 gboolean is_connected;
367 page_num = gtk_notebook_get_current_page (GTK_NOTEBOOK (priv->notebook));
368 first_page = (page_num == 0);
369 last_page = (page_num == (num_pages - 1));
370 g_object_get (gtk_settings_get_default (), "gtk-keynav-wrap-around",
372 is_connected = empathy_chat_get_tp_chat (priv->current_chat) != NULL;
374 gtk_action_set_sensitive (priv->menu_tabs_next, (!last_page ||
376 gtk_action_set_sensitive (priv->menu_tabs_prev, (!first_page ||
378 gtk_action_set_sensitive (priv->menu_tabs_detach, num_pages > 1);
379 gtk_action_set_sensitive (priv->menu_tabs_left, !first_page);
380 gtk_action_set_sensitive (priv->menu_tabs_right, !last_page);
381 gtk_action_set_sensitive (priv->menu_conv_insert_smiley, is_connected);
385 chat_window_conversation_menu_update (EmpathyChatWindowPriv *priv,
386 EmpathyChatWindow *self)
388 EmpathyTpChat *tp_chat;
389 TpConnection *connection;
391 gboolean sensitive = FALSE;
393 g_return_if_fail (priv->current_chat != NULL);
395 action = gtk_ui_manager_get_action (priv->ui_manager,
396 "/chats_menubar/menu_conv/menu_conv_invite_participant");
397 tp_chat = empathy_chat_get_tp_chat (priv->current_chat);
399 if (tp_chat != NULL) {
400 connection = empathy_tp_chat_get_connection (tp_chat);
402 sensitive = empathy_tp_chat_can_add_contact (tp_chat) &&
403 (tp_connection_get_status (connection, NULL) ==
404 TP_CONNECTION_STATUS_CONNECTED);
407 gtk_action_set_sensitive (action, sensitive);
411 chat_window_contact_menu_update (EmpathyChatWindowPriv *priv,
412 EmpathyChatWindow *window)
414 GtkWidget *menu, *submenu, *orig_submenu;
416 menu = gtk_ui_manager_get_widget (priv->ui_manager,
417 "/chats_menubar/menu_contact");
418 orig_submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (menu));
420 if (orig_submenu == NULL || !gtk_widget_get_visible (orig_submenu)) {
421 submenu = empathy_chat_get_contact_menu (priv->current_chat);
423 if (submenu != NULL) {
424 /* gtk_menu_attach_to_widget () doesn't behave nicely here */
425 g_object_set_data (G_OBJECT (submenu), "window", priv->dialog);
427 gtk_menu_item_set_submenu (GTK_MENU_ITEM (menu), submenu);
428 gtk_widget_show (menu);
429 gtk_widget_set_sensitive (menu, TRUE);
431 gtk_widget_set_sensitive (menu, FALSE);
434 tp_g_signal_connect_object (orig_submenu,
436 (GCallback)_submenu_notify_visible_changed_cb,
442 get_all_unread_messages (EmpathyChatWindowPriv *priv)
447 for (l = priv->chats; l != NULL; l = g_list_next (l))
448 nb += empathy_chat_get_nb_unread_messages (EMPATHY_CHAT (l->data));
454 get_window_title_name (EmpathyChatWindowPriv *priv)
456 const gchar *active_name;
458 guint current_unread_msgs;
460 nb_chats = g_list_length (priv->chats);
461 g_assert (nb_chats > 0);
463 active_name = empathy_chat_get_name (priv->current_chat);
465 current_unread_msgs = empathy_chat_get_nb_unread_messages (
470 if (current_unread_msgs == 0)
471 return g_strdup (active_name);
473 return g_strdup_printf (ngettext (
475 "%s (%d unread)", current_unread_msgs),
476 active_name, current_unread_msgs);
478 guint nb_others = nb_chats - 1;
479 guint all_unread_msgs;
481 all_unread_msgs = get_all_unread_messages (priv);
483 if (all_unread_msgs == 0) {
484 /* no unread message */
485 return g_strdup_printf (ngettext (
487 "%s (and %u others)", nb_others),
488 active_name, nb_others);
491 else if (all_unread_msgs == current_unread_msgs) {
492 /* unread messages are in the current tab */
493 return g_strdup_printf (ngettext (
495 "%s (%d unread)", current_unread_msgs),
496 active_name, current_unread_msgs);
499 else if (current_unread_msgs == 0) {
500 /* unread messages are in other tabs */
501 return g_strdup_printf (ngettext (
502 "%s (%d unread from others)",
503 "%s (%d unread from others)",
505 active_name, all_unread_msgs);
509 /* unread messages are in all the tabs */
510 return g_strdup_printf (ngettext (
511 "%s (%d unread from all)",
512 "%s (%d unread from all)",
514 active_name, all_unread_msgs);
520 chat_window_title_update (EmpathyChatWindowPriv *priv)
524 name = get_window_title_name (priv);
525 gtk_window_set_title (GTK_WINDOW (priv->dialog), name);
530 chat_window_icon_update (EmpathyChatWindowPriv *priv, gboolean new_messages)
533 EmpathyContact *remote_contact;
534 gboolean avatar_in_icon;
537 n_chats = g_list_length (priv->chats);
539 /* Update window icon */
541 gtk_window_set_icon_name (GTK_WINDOW (priv->dialog),
542 EMPATHY_IMAGE_MESSAGE);
544 avatar_in_icon = g_settings_get_boolean (priv->gsettings_chat,
545 EMPATHY_PREFS_CHAT_AVATAR_IN_ICON);
547 if (n_chats == 1 && avatar_in_icon) {
548 remote_contact = empathy_chat_get_remote_contact (priv->current_chat);
549 icon = empathy_pixbuf_avatar_from_contact_scaled (remote_contact, 0, 0);
550 gtk_window_set_icon (GTK_WINDOW (priv->dialog), icon);
553 g_object_unref (icon);
556 gtk_window_set_icon_name (GTK_WINDOW (priv->dialog), NULL);
562 chat_window_close_button_update (EmpathyChatWindowPriv *priv,
566 GtkWidget *chat_close_button;
569 if (num_pages == 1) {
570 chat = gtk_notebook_get_nth_page (GTK_NOTEBOOK (priv->notebook), 0);
571 chat_close_button = g_object_get_data (G_OBJECT (chat),
572 "chat-window-tab-close-button");
573 gtk_widget_hide (chat_close_button);
575 for (i=0; i<num_pages; i++) {
576 chat = gtk_notebook_get_nth_page (GTK_NOTEBOOK (priv->notebook), i);
577 chat_close_button = g_object_get_data (G_OBJECT (chat),
578 "chat-window-tab-close-button");
579 gtk_widget_show (chat_close_button);
585 chat_window_update (EmpathyChatWindow *window,
586 gboolean update_contact_menu)
588 EmpathyChatWindowPriv *priv = GET_PRIV (window);
591 num_pages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (priv->notebook));
593 /* Update Tab menu */
594 chat_window_menu_context_update (priv,
597 chat_window_conversation_menu_update (priv, window);
599 /* If this update is due to a focus-in event, we know the menu will be
600 the same as when we last left it, so no work to do. Besides, if we
601 swap out the menu on a focus-in, we may confuse any external global
603 if (update_contact_menu) {
604 chat_window_contact_menu_update (priv,
608 chat_window_title_update (priv);
610 chat_window_icon_update (priv, get_all_unread_messages (priv) > 0);
612 chat_window_close_button_update (priv,
617 append_markup_printf (GString *string,
624 va_start (args, format);
626 tmp = g_markup_vprintf_escaped (format, args);
627 g_string_append (string, tmp);
634 chat_window_update_chat_tab_full (EmpathyChat *chat,
635 gboolean update_contact_menu)
637 EmpathyChatWindow *window;
638 EmpathyChatWindowPriv *priv;
639 EmpathyContact *remote_contact;
643 const gchar *subject;
644 const gchar *status = NULL;
648 const gchar *icon_name;
649 GtkWidget *tab_image;
650 GtkWidget *menu_image;
652 window = chat_window_find_chat (chat);
656 priv = GET_PRIV (window);
658 /* Get information */
659 name = empathy_chat_get_name (chat);
660 account = empathy_chat_get_account (chat);
661 subject = empathy_chat_get_subject (chat);
662 remote_contact = empathy_chat_get_remote_contact (chat);
664 DEBUG ("Updating chat tab, name=%s, account=%s, subject=%s, remote_contact=%p",
665 name, tp_proxy_get_object_path (account), subject, remote_contact);
667 /* Update tab image */
668 if (empathy_chat_get_tp_chat (chat) == NULL) {
669 /* No TpChat, we are disconnected */
672 else if (empathy_chat_get_nb_unread_messages (chat) > 0) {
673 icon_name = EMPATHY_IMAGE_MESSAGE;
675 else if (remote_contact && empathy_chat_is_composing (chat)) {
676 icon_name = EMPATHY_IMAGE_TYPING;
678 else if (remote_contact) {
679 icon_name = empathy_icon_name_for_contact (remote_contact);
681 icon_name = EMPATHY_IMAGE_GROUP_MESSAGE;
684 tab_image = g_object_get_data (G_OBJECT (chat), "chat-window-tab-image");
685 menu_image = g_object_get_data (G_OBJECT (chat), "chat-window-menu-image");
686 if (icon_name != NULL) {
687 gtk_image_set_from_icon_name (GTK_IMAGE (tab_image), icon_name, GTK_ICON_SIZE_MENU);
688 gtk_widget_show (tab_image);
689 gtk_image_set_from_icon_name (GTK_IMAGE (menu_image), icon_name, GTK_ICON_SIZE_MENU);
690 gtk_widget_show (menu_image);
692 gtk_widget_hide (tab_image);
693 gtk_widget_hide (menu_image);
696 /* Update tab tooltip */
697 tooltip = g_string_new (NULL);
699 if (remote_contact) {
700 id = empathy_contact_get_id (remote_contact);
701 status = empathy_contact_get_presence_message (remote_contact);
706 append_markup_printf (tooltip,
707 "<b>%s</b><small> (%s)</small>",
709 tp_account_get_display_name (account));
711 if (!EMP_STR_EMPTY (status)) {
712 append_markup_printf (tooltip, "\n<i>%s</i>", status);
716 append_markup_printf (tooltip, "\n<b>%s</b> %s",
717 _("Topic:"), subject);
720 if (remote_contact && empathy_chat_is_composing (chat)) {
721 append_markup_printf (tooltip, "\n%s", _("Typing a message."));
724 markup = g_string_free (tooltip, FALSE);
725 widget = g_object_get_data (G_OBJECT (chat), "chat-window-tab-tooltip-widget");
726 gtk_widget_set_tooltip_markup (widget, markup);
727 widget = g_object_get_data (G_OBJECT (chat), "chat-window-menu-tooltip-widget");
728 gtk_widget_set_tooltip_markup (widget, markup);
731 /* Update tab and menu label */
732 widget = g_object_get_data (G_OBJECT (chat), "chat-window-tab-label");
733 gtk_label_set_text (GTK_LABEL (widget), name);
734 widget = g_object_get_data (G_OBJECT (chat), "chat-window-menu-label");
735 gtk_label_set_text (GTK_LABEL (widget), name);
737 /* Update the window if it's the current chat */
738 if (priv->current_chat == chat) {
739 chat_window_update (window, update_contact_menu);
744 chat_window_update_chat_tab (EmpathyChat *chat)
746 chat_window_update_chat_tab_full (chat, TRUE);
750 chat_window_chat_notify_cb (EmpathyChat *chat)
752 EmpathyContact *old_remote_contact;
753 EmpathyContact *remote_contact = NULL;
755 old_remote_contact = g_object_get_data (G_OBJECT (chat), "chat-window-remote-contact");
756 remote_contact = empathy_chat_get_remote_contact (chat);
758 if (old_remote_contact != remote_contact) {
759 /* The remote-contact associated with the chat changed, we need
760 * to keep track of any change of that contact and update the
761 * window each time. */
762 if (remote_contact) {
763 g_signal_connect_swapped (remote_contact, "notify",
764 G_CALLBACK (chat_window_update_chat_tab),
767 if (old_remote_contact) {
768 g_signal_handlers_disconnect_by_func (old_remote_contact,
769 chat_window_update_chat_tab,
773 g_object_set_data_full (G_OBJECT (chat), "chat-window-remote-contact",
774 g_object_ref (remote_contact), (GDestroyNotify) g_object_unref);
777 chat_window_update_chat_tab (chat);
781 chat_window_insert_smiley_activate_cb (EmpathySmileyManager *manager,
782 EmpathySmiley *smiley,
785 EmpathyChatWindowPriv *priv = GET_PRIV (window);
787 GtkTextBuffer *buffer;
790 chat = priv->current_chat;
792 buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (chat->input_text_view));
793 gtk_text_buffer_get_end_iter (buffer, &iter);
794 gtk_text_buffer_insert (buffer, &iter, smiley->str, -1);
798 chat_window_conv_activate_cb (GtkAction *action,
799 EmpathyChatWindow *window)
801 EmpathyChatWindowPriv *priv = GET_PRIV (window);
804 EmpathyContact *remote_contact = NULL;
806 /* Favorite room menu */
807 is_room = empathy_chat_is_room (priv->current_chat);
811 gboolean found = FALSE;
812 EmpathyChatroom *chatroom;
814 room = empathy_chat_get_id (priv->current_chat);
815 account = empathy_chat_get_account (priv->current_chat);
816 chatroom = empathy_chatroom_manager_find (priv->chatroom_manager,
818 if (chatroom != NULL)
819 found = empathy_chatroom_is_favorite (chatroom);
821 DEBUG ("This room %s favorite", found ? "is" : "is not");
822 gtk_toggle_action_set_active (
823 GTK_TOGGLE_ACTION (priv->menu_conv_favorite), found);
825 if (chatroom != NULL)
826 found = empathy_chatroom_is_always_urgent (chatroom);
828 gtk_toggle_action_set_active (
829 GTK_TOGGLE_ACTION (priv->menu_conv_always_urgent),
832 gtk_action_set_visible (priv->menu_conv_favorite, is_room);
833 gtk_action_set_visible (priv->menu_conv_always_urgent, is_room);
835 /* Show contacts menu */
836 g_object_get (priv->current_chat,
837 "remote-contact", &remote_contact,
838 "show-contacts", &active,
840 if (remote_contact == NULL) {
841 gtk_toggle_action_set_active (
842 GTK_TOGGLE_ACTION (priv->menu_conv_toggle_contacts),
845 gtk_action_set_visible (priv->menu_conv_toggle_contacts,
846 (remote_contact == NULL));
847 if (remote_contact != NULL) {
848 g_object_unref (remote_contact);
853 chat_window_clear_activate_cb (GtkAction *action,
854 EmpathyChatWindow *window)
856 EmpathyChatWindowPriv *priv = GET_PRIV (window);
858 empathy_chat_clear (priv->current_chat);
862 chat_window_favorite_toggled_cb (GtkToggleAction *toggle_action,
863 EmpathyChatWindow *window)
865 EmpathyChatWindowPriv *priv = GET_PRIV (window);
869 EmpathyChatroom *chatroom;
871 active = gtk_toggle_action_get_active (toggle_action);
872 account = empathy_chat_get_account (priv->current_chat);
873 room = empathy_chat_get_id (priv->current_chat);
875 chatroom = empathy_chatroom_manager_ensure_chatroom (
876 priv->chatroom_manager,
879 empathy_chat_get_name (priv->current_chat));
881 empathy_chatroom_set_favorite (chatroom, active);
882 g_object_unref (chatroom);
886 chat_window_always_urgent_toggled_cb (GtkToggleAction *toggle_action,
887 EmpathyChatWindow *window)
889 EmpathyChatWindowPriv *priv = GET_PRIV (window);
893 EmpathyChatroom *chatroom;
895 active = gtk_toggle_action_get_active (toggle_action);
896 account = empathy_chat_get_account (priv->current_chat);
897 room = empathy_chat_get_id (priv->current_chat);
899 chatroom = empathy_chatroom_manager_ensure_chatroom (
900 priv->chatroom_manager,
903 empathy_chat_get_name (priv->current_chat));
905 empathy_chatroom_set_always_urgent (chatroom, active);
906 g_object_unref (chatroom);
910 chat_window_contacts_toggled_cb (GtkToggleAction *toggle_action,
911 EmpathyChatWindow *window)
913 EmpathyChatWindowPriv *priv = GET_PRIV (window);
916 active = gtk_toggle_action_get_active (toggle_action);
918 empathy_chat_set_show_contacts (priv->current_chat, active);
922 got_contact_cb (TpConnection *connection,
923 EmpathyContact *contact,
928 EmpathyTpChat *tp_chat = EMPATHY_TP_CHAT (user_data);
931 DEBUG ("Failed: %s", error->message);
934 empathy_contact_list_add (EMPATHY_CONTACT_LIST (tp_chat),
935 contact, _("Inviting you to this room"));
940 chat_window_invite_participant_activate_cb (GtkAction *action,
941 EmpathyChatWindow *window)
943 EmpathyChatWindowPriv *priv;
945 EmpathyTpChat *tp_chat;
950 priv = GET_PRIV (window);
952 g_return_if_fail (priv->current_chat != NULL);
954 tp_chat = empathy_chat_get_tp_chat (priv->current_chat);
955 channel = empathy_tp_chat_get_channel (tp_chat);
956 account = empathy_chat_get_account (priv->current_chat);
958 dialog = empathy_invite_participant_dialog_new (
959 GTK_WINDOW (priv->dialog), account);
960 gtk_widget_show (dialog);
962 response = gtk_dialog_run (GTK_DIALOG (dialog));
964 if (response == GTK_RESPONSE_ACCEPT) {
965 TpConnection *connection;
968 id = empathy_contact_selector_dialog_get_selected (
969 EMPATHY_CONTACT_SELECTOR_DIALOG (dialog), NULL, NULL);
970 if (EMP_STR_EMPTY (id)) goto out;
972 connection = tp_channel_borrow_connection (channel);
973 empathy_tp_contact_factory_get_from_id (connection, id,
974 got_contact_cb, tp_chat, NULL, NULL);
978 gtk_widget_destroy (dialog);
982 chat_window_close_activate_cb (GtkAction *action,
983 EmpathyChatWindow *window)
985 EmpathyChatWindowPriv *priv;
987 priv = GET_PRIV (window);
989 g_return_if_fail (priv->current_chat != NULL);
991 empathy_chat_window_remove_chat (window, priv->current_chat);
995 chat_window_edit_activate_cb (GtkAction *action,
996 EmpathyChatWindow *window)
998 EmpathyChatWindowPriv *priv;
999 GtkClipboard *clipboard;
1000 GtkTextBuffer *buffer;
1001 gboolean text_available;
1003 priv = GET_PRIV (window);
1005 g_return_if_fail (priv->current_chat != NULL);
1007 if (!empathy_chat_get_tp_chat (priv->current_chat)) {
1008 gtk_action_set_sensitive (priv->menu_edit_copy, FALSE);
1009 gtk_action_set_sensitive (priv->menu_edit_cut, FALSE);
1010 gtk_action_set_sensitive (priv->menu_edit_paste, FALSE);
1014 buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (priv->current_chat->input_text_view));
1015 if (gtk_text_buffer_get_has_selection (buffer)) {
1016 gtk_action_set_sensitive (priv->menu_edit_copy, TRUE);
1017 gtk_action_set_sensitive (priv->menu_edit_cut, TRUE);
1021 selection = empathy_chat_view_get_has_selection (priv->current_chat->view);
1023 gtk_action_set_sensitive (priv->menu_edit_cut, FALSE);
1024 gtk_action_set_sensitive (priv->menu_edit_copy, selection);
1027 clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
1028 text_available = gtk_clipboard_wait_is_text_available (clipboard);
1029 gtk_action_set_sensitive (priv->menu_edit_paste, text_available);
1033 chat_window_cut_activate_cb (GtkAction *action,
1034 EmpathyChatWindow *window)
1036 EmpathyChatWindowPriv *priv;
1038 g_return_if_fail (EMPATHY_IS_CHAT_WINDOW (window));
1040 priv = GET_PRIV (window);
1042 empathy_chat_cut (priv->current_chat);
1046 chat_window_copy_activate_cb (GtkAction *action,
1047 EmpathyChatWindow *window)
1049 EmpathyChatWindowPriv *priv;
1051 g_return_if_fail (EMPATHY_IS_CHAT_WINDOW (window));
1053 priv = GET_PRIV (window);
1055 empathy_chat_copy (priv->current_chat);
1059 chat_window_paste_activate_cb (GtkAction *action,
1060 EmpathyChatWindow *window)
1062 EmpathyChatWindowPriv *priv;
1064 g_return_if_fail (EMPATHY_IS_CHAT_WINDOW (window));
1066 priv = GET_PRIV (window);
1068 empathy_chat_paste (priv->current_chat);
1072 chat_window_find_activate_cb (GtkAction *action,
1073 EmpathyChatWindow *window)
1075 EmpathyChatWindowPriv *priv;
1077 g_return_if_fail (EMPATHY_IS_CHAT_WINDOW (window));
1079 priv = GET_PRIV (window);
1081 empathy_chat_find (priv->current_chat);
1085 chat_window_tabs_next_activate_cb (GtkAction *action,
1086 EmpathyChatWindow *window)
1088 EmpathyChatWindowPriv *priv;
1090 gint index_, numPages;
1091 gboolean wrap_around;
1093 priv = GET_PRIV (window);
1095 g_object_get (gtk_settings_get_default (), "gtk-keynav-wrap-around",
1096 &wrap_around, NULL);
1098 chat = priv->current_chat;
1099 index_ = gtk_notebook_get_current_page (GTK_NOTEBOOK (priv->notebook));
1100 numPages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (priv->notebook));
1102 if (index_ == (numPages - 1) && wrap_around) {
1103 gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->notebook), 0);
1107 gtk_notebook_next_page (GTK_NOTEBOOK (priv->notebook));
1111 chat_window_tabs_previous_activate_cb (GtkAction *action,
1112 EmpathyChatWindow *window)
1114 EmpathyChatWindowPriv *priv;
1116 gint index_, numPages;
1117 gboolean wrap_around;
1119 priv = GET_PRIV (window);
1121 g_object_get (gtk_settings_get_default (), "gtk-keynav-wrap-around",
1122 &wrap_around, NULL);
1124 chat = priv->current_chat;
1125 index_ = gtk_notebook_get_current_page (GTK_NOTEBOOK (priv->notebook));
1126 numPages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (priv->notebook));
1128 if (index_ <= 0 && wrap_around) {
1129 gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->notebook), numPages - 1);
1133 gtk_notebook_prev_page (GTK_NOTEBOOK (priv->notebook));
1137 chat_window_tabs_undo_close_tab_activate_cb (GtkAction *action,
1138 EmpathyChatWindow *window)
1140 EmpathyChatWindowPriv *priv = GET_PRIV (window);
1141 empathy_chat_manager_undo_closed_chat (priv->chat_manager);
1145 chat_window_tabs_left_activate_cb (GtkAction *action,
1146 EmpathyChatWindow *window)
1148 EmpathyChatWindowPriv *priv;
1150 gint index_, num_pages;
1152 priv = GET_PRIV (window);
1154 chat = priv->current_chat;
1155 index_ = gtk_notebook_get_current_page (GTK_NOTEBOOK (priv->notebook));
1160 gtk_notebook_reorder_child (GTK_NOTEBOOK (priv->notebook),
1164 num_pages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (priv->notebook));
1165 chat_window_menu_context_update (priv, num_pages);
1169 chat_window_tabs_right_activate_cb (GtkAction *action,
1170 EmpathyChatWindow *window)
1172 EmpathyChatWindowPriv *priv;
1174 gint index_, num_pages;
1176 priv = GET_PRIV (window);
1178 chat = priv->current_chat;
1179 index_ = gtk_notebook_get_current_page (GTK_NOTEBOOK (priv->notebook));
1181 gtk_notebook_reorder_child (GTK_NOTEBOOK (priv->notebook),
1185 num_pages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (priv->notebook));
1186 chat_window_menu_context_update (priv, num_pages);
1189 static EmpathyChatWindow *
1190 empathy_chat_window_new (void)
1192 return EMPATHY_CHAT_WINDOW (g_object_new (EMPATHY_TYPE_CHAT_WINDOW, NULL));
1196 chat_window_detach_activate_cb (GtkAction *action,
1197 EmpathyChatWindow *window)
1199 EmpathyChatWindowPriv *priv;
1200 EmpathyChatWindow *new_window;
1203 priv = GET_PRIV (window);
1205 chat = priv->current_chat;
1206 new_window = empathy_chat_window_new ();
1208 empathy_chat_window_move_chat (window, new_window, chat);
1210 priv = GET_PRIV (new_window);
1211 gtk_widget_show (priv->dialog);
1215 chat_window_help_contents_activate_cb (GtkAction *action,
1216 EmpathyChatWindow *window)
1218 EmpathyChatWindowPriv *priv = GET_PRIV (window);
1220 empathy_url_show (priv->dialog, "ghelp:empathy");
1224 chat_window_help_about_activate_cb (GtkAction *action,
1225 EmpathyChatWindow *window)
1227 EmpathyChatWindowPriv *priv = GET_PRIV (window);
1229 empathy_about_dialog_new (GTK_WINDOW (priv->dialog));
1233 chat_window_delete_event_cb (GtkWidget *dialog,
1235 EmpathyChatWindow *window)
1237 EmpathyChatWindowPriv *priv = GET_PRIV (window);
1239 DEBUG ("Delete event received");
1241 g_object_ref (window);
1242 while (priv->chats) {
1243 empathy_chat_window_remove_chat (window, priv->chats->data);
1245 g_object_unref (window);
1251 chat_window_composing_cb (EmpathyChat *chat,
1252 gboolean is_composing,
1253 EmpathyChatWindow *window)
1255 chat_window_update_chat_tab (chat);
1259 chat_window_set_urgency_hint (EmpathyChatWindow *window,
1262 EmpathyChatWindowPriv *priv;
1264 priv = GET_PRIV (window);
1266 gtk_window_set_urgency_hint (GTK_WINDOW (priv->dialog), urgent);
1270 chat_window_notification_closed_cb (NotifyNotification *notify,
1271 EmpathyChatWindow *self)
1273 EmpathyChatWindowPriv *priv = GET_PRIV (self);
1275 g_object_unref (notify);
1276 if (priv->notification == notify) {
1277 priv->notification = NULL;
1282 chat_window_show_or_update_notification (EmpathyChatWindow *window,
1283 EmpathyMessage *message,
1286 EmpathyContact *sender;
1287 const gchar *header;
1291 EmpathyChatWindowPriv *priv = GET_PRIV (window);
1292 gboolean res, has_x_canonical_append;
1293 NotifyNotification *notification = priv->notification;
1295 if (!empathy_notify_manager_notification_is_enabled (priv->notify_mgr)) {
1298 res = g_settings_get_boolean (priv->gsettings_notif,
1299 EMPATHY_PREFS_NOTIFICATIONS_FOCUS);
1306 sender = empathy_message_get_sender (message);
1307 header = empathy_contact_get_alias (sender);
1308 body = empathy_message_get_body (message);
1309 escaped = g_markup_escape_text (body, -1);
1310 has_x_canonical_append = empathy_notify_manager_has_capability (
1311 priv->notify_mgr, EMPATHY_NOTIFY_MANAGER_CAP_X_CANONICAL_APPEND);
1313 if (notification != NULL && !has_x_canonical_append) {
1314 /* if the notification server supports x-canonical-append, it is
1315 better to not use notify_notification_update to avoid
1316 overwriting the current notification message */
1317 notify_notification_update (notification,
1318 header, escaped, NULL);
1320 /* if the notification server supports x-canonical-append,
1321 the hint will be added, so that the message from the
1322 just created notification will be automatically appended
1323 to an existing notification with the same title.
1324 In this way the previous message will not be lost: the new
1325 message will appear below it, in the same notification */
1326 notification = notify_notification_new (header, escaped, NULL);
1328 if (priv->notification == NULL) {
1329 priv->notification = notification;
1332 notify_notification_set_timeout (notification, NOTIFY_EXPIRES_DEFAULT);
1334 tp_g_signal_connect_object (notification, "closed",
1335 G_CALLBACK (chat_window_notification_closed_cb), window, 0);
1337 if (has_x_canonical_append) {
1338 notify_notification_set_hint_string (notification,
1339 EMPATHY_NOTIFY_MANAGER_CAP_X_CANONICAL_APPEND, "");
1343 pixbuf = empathy_notify_manager_get_pixbuf_for_notification (priv->notify_mgr,
1344 sender, EMPATHY_IMAGE_NEW_MESSAGE);
1346 if (pixbuf != NULL) {
1347 notify_notification_set_icon_from_pixbuf (notification, pixbuf);
1348 g_object_unref (pixbuf);
1351 notify_notification_show (notification, NULL);
1357 chat_window_set_highlight_room_labels (EmpathyChat *chat)
1362 if (!empathy_chat_is_room (chat))
1365 markup = g_markup_printf_escaped (
1366 "<span color=\"red\" weight=\"bold\">%s</span>",
1367 empathy_chat_get_name (chat));
1369 widget = g_object_get_data (G_OBJECT (chat), "chat-window-tab-label");
1370 gtk_label_set_markup (GTK_LABEL (widget), markup);
1372 widget = g_object_get_data (G_OBJECT (chat), "chat-window-menu-label");
1373 gtk_label_set_markup (GTK_LABEL (widget), markup);
1379 empathy_chat_window_has_focus (EmpathyChatWindow *window)
1381 EmpathyChatWindowPriv *priv;
1384 g_return_val_if_fail (EMPATHY_IS_CHAT_WINDOW (window), FALSE);
1386 priv = GET_PRIV (window);
1388 g_object_get (priv->dialog, "has-toplevel-focus", &has_focus, NULL);
1394 chat_window_new_message_cb (EmpathyChat *chat,
1395 EmpathyMessage *message,
1397 EmpathyChatWindow *window)
1399 EmpathyChatWindowPriv *priv;
1401 gboolean needs_urgency;
1402 EmpathyContact *sender;
1404 priv = GET_PRIV (window);
1406 has_focus = empathy_chat_window_has_focus (window);
1408 /* - if we're the sender, we play the sound if it's specified in the
1409 * preferences and we're not away.
1410 * - if we receive a message, we play the sound if it's specified in the
1411 * preferences and the window does not have focus on the chat receiving
1415 sender = empathy_message_get_sender (message);
1417 if (empathy_contact_is_user (sender)) {
1418 empathy_sound_manager_play (priv->sound_mgr, GTK_WIDGET (priv->dialog),
1419 EMPATHY_SOUND_MESSAGE_OUTGOING);
1422 if (has_focus && priv->current_chat == chat) {
1423 /* window and tab are focused so consider the message to be read */
1425 /* FIXME: see Bug#610994 and coments about it in EmpathyChatPriv */
1426 empathy_chat_messages_read (chat);
1430 /* Update the chat tab if this is the first unread message */
1431 if (empathy_chat_get_nb_unread_messages (chat) == 1) {
1432 chat_window_update_chat_tab (chat);
1435 /* If empathy_chat_is_room () returns TRUE, that means it's a named MUC.
1436 * If empathy_chat_get_remote_contact () returns NULL, that means it's
1437 * an unamed MUC (msn-like).
1438 * In case of a MUC, we set urgency if either:
1439 * a) the chatroom's always_urgent property is TRUE
1440 * b) the message contains our alias
1442 if (empathy_chat_is_room (chat) ||
1443 empathy_chat_get_remote_contact (chat) == NULL) {
1446 EmpathyChatroom *chatroom;
1448 account = empathy_chat_get_account (chat);
1449 room = empathy_chat_get_id (chat);
1451 chatroom = empathy_chatroom_manager_find (priv->chatroom_manager,
1454 if (chatroom != NULL && empathy_chatroom_is_always_urgent (chatroom)) {
1455 needs_urgency = TRUE;
1457 needs_urgency = empathy_message_should_highlight (message);
1460 needs_urgency = TRUE;
1463 if (needs_urgency) {
1464 chat_window_set_highlight_room_labels (chat);
1467 chat_window_set_urgency_hint (window, TRUE);
1470 /* Pending messages have already been displayed and notified in the
1471 * approver, so we don't display a notification and play a sound for those */
1473 empathy_sound_manager_play (priv->sound_mgr, GTK_WIDGET (priv->dialog),
1474 EMPATHY_SOUND_MESSAGE_INCOMING);
1476 chat_window_show_or_update_notification (window, message, chat);
1480 /* update the number of unread messages and the window icon */
1481 chat_window_title_update (priv);
1482 chat_window_icon_update (priv, TRUE);
1486 chat_window_command_part (EmpathyChat *chat,
1489 EmpathyChat *chat_to_be_parted;
1491 if (strv[1] == NULL) {
1492 empathy_tp_chat_leave (empathy_chat_get_tp_chat (chat), "");
1495 chat_to_be_parted = empathy_chat_window_find_chat (
1496 empathy_chat_get_account (chat), strv[1]);
1498 if (chat_to_be_parted != NULL) {
1499 empathy_tp_chat_leave (empathy_chat_get_tp_chat (chat_to_be_parted),
1502 empathy_tp_chat_leave (empathy_chat_get_tp_chat (chat), strv[1]);
1506 static GtkNotebook *
1507 notebook_create_window_cb (GtkNotebook *source,
1513 EmpathyChatWindowPriv *priv;
1514 EmpathyChatWindow *window, *new_window;
1517 chat = EMPATHY_CHAT (page);
1518 window = chat_window_find_chat (chat);
1520 new_window = empathy_chat_window_new ();
1521 priv = GET_PRIV (new_window);
1523 DEBUG ("Detach hook called");
1525 empathy_chat_window_move_chat (window, new_window, chat);
1527 gtk_widget_show (priv->dialog);
1528 gtk_window_move (GTK_WINDOW (priv->dialog), x, y);
1534 chat_window_page_switched_cb (GtkNotebook *notebook,
1537 EmpathyChatWindow *window)
1539 EmpathyChatWindowPriv *priv = GET_PRIV (window);
1540 EmpathyChat *chat = EMPATHY_CHAT (child);
1542 DEBUG ("Page switched");
1544 if (priv->page_added) {
1545 priv->page_added = FALSE;
1546 empathy_chat_scroll_down (chat);
1548 else if (priv->current_chat == chat) {
1552 priv->current_chat = chat;
1553 empathy_chat_messages_read (chat);
1555 chat_window_update_chat_tab (chat);
1559 chat_window_page_added_cb (GtkNotebook *notebook,
1562 EmpathyChatWindow *window)
1564 EmpathyChatWindowPriv *priv;
1567 priv = GET_PRIV (window);
1569 /* If we just received DND to the same window, we don't want
1570 * to do anything here like removing the tab and then readding
1571 * it, so we return here and in "page-added".
1573 if (priv->dnd_same_window) {
1574 DEBUG ("Page added (back to the same window)");
1575 priv->dnd_same_window = FALSE;
1579 DEBUG ("Page added");
1581 /* Get chat object */
1582 chat = EMPATHY_CHAT (child);
1584 /* Connect chat signals for this window */
1585 g_signal_connect (chat, "composing",
1586 G_CALLBACK (chat_window_composing_cb),
1588 g_signal_connect (chat, "new-message",
1589 G_CALLBACK (chat_window_new_message_cb),
1591 g_signal_connect (chat, "part-command-entered",
1592 G_CALLBACK (chat_window_command_part),
1594 g_signal_connect (chat, "notify::tp-chat",
1595 G_CALLBACK (chat_window_update_chat_tab),
1598 /* Set flag so we know to perform some special operations on
1599 * switch page due to the new page being added.
1601 priv->page_added = TRUE;
1603 /* Get list of chats up to date */
1604 priv->chats = g_list_append (priv->chats, chat);
1606 chat_window_update_chat_tab (chat);
1610 chat_window_page_removed_cb (GtkNotebook *notebook,
1613 EmpathyChatWindow *window)
1615 EmpathyChatWindowPriv *priv;
1618 priv = GET_PRIV (window);
1620 /* If we just received DND to the same window, we don't want
1621 * to do anything here like removing the tab and then readding
1622 * it, so we return here and in "page-added".
1624 if (priv->dnd_same_window) {
1625 DEBUG ("Page removed (and will be readded to same window)");
1629 DEBUG ("Page removed");
1631 /* Get chat object */
1632 chat = EMPATHY_CHAT (child);
1634 /* Disconnect all signal handlers for this chat and this window */
1635 g_signal_handlers_disconnect_by_func (chat,
1636 G_CALLBACK (chat_window_composing_cb),
1638 g_signal_handlers_disconnect_by_func (chat,
1639 G_CALLBACK (chat_window_new_message_cb),
1641 g_signal_handlers_disconnect_by_func (chat,
1642 G_CALLBACK (chat_window_update_chat_tab),
1645 /* Keep list of chats up to date */
1646 priv->chats = g_list_remove (priv->chats, chat);
1647 empathy_chat_messages_read (chat);
1649 if (priv->chats == NULL) {
1650 g_object_unref (window);
1652 chat_window_update (window, TRUE);
1657 chat_window_focus_in_event_cb (GtkWidget *widget,
1659 EmpathyChatWindow *window)
1661 EmpathyChatWindowPriv *priv;
1663 priv = GET_PRIV (window);
1665 empathy_chat_messages_read (priv->current_chat);
1667 chat_window_set_urgency_hint (window, FALSE);
1669 /* Update the title, since we now mark all unread messages as read. */
1670 chat_window_update_chat_tab_full (priv->current_chat, FALSE);
1676 chat_window_drag_drop (GtkWidget *widget,
1677 GdkDragContext *context,
1681 EmpathyChatWindow *window)
1684 EmpathyChatWindowPriv *priv;
1686 priv = GET_PRIV (window);
1688 target = gtk_drag_dest_find_target (widget, context, priv->file_targets);
1689 if (target == GDK_NONE)
1690 target = gtk_drag_dest_find_target (widget, context, priv->contact_targets);
1692 if (target != GDK_NONE) {
1693 gtk_drag_get_data (widget, context, target, time_);
1701 chat_window_drag_motion (GtkWidget *widget,
1702 GdkDragContext *context,
1706 EmpathyChatWindow *window)
1709 EmpathyChatWindowPriv *priv;
1711 priv = GET_PRIV (window);
1713 target = gtk_drag_dest_find_target (widget, context, priv->file_targets);
1714 if (target != GDK_NONE) {
1715 /* This is a file drag. Ensure the contact is online and set the
1716 drag type to COPY. Note that it's possible that the tab will
1717 be switched by GTK+ after a timeout from drag_motion without
1718 getting another drag_motion to disable the drop. You have
1719 to hold your mouse really still.
1721 EmpathyContact *contact;
1723 priv = GET_PRIV (window);
1724 contact = empathy_chat_get_remote_contact (priv->current_chat);
1725 /* contact is NULL for multi-user chats. We don't do
1726 * file transfers to MUCs. We also don't send files
1727 * to offline contacts or contacts that don't support
1730 if ((contact == NULL) || !empathy_contact_is_online (contact)) {
1731 gdk_drag_status (context, 0, time_);
1734 if (!(empathy_contact_get_capabilities (contact)
1735 & EMPATHY_CAPABILITIES_FT)) {
1736 gdk_drag_status (context, 0, time_);
1739 gdk_drag_status (context, GDK_ACTION_COPY, time_);
1743 target = gtk_drag_dest_find_target (widget, context, priv->contact_targets);
1744 if (target != GDK_NONE) {
1745 /* This is a drag of a contact from a contact list. Set to COPY.
1746 FIXME: If this drag is to a MUC window, it invites the user.
1747 Otherwise, it opens a chat. Should we use a different drag
1748 type for invites? Should we allow ASK?
1750 gdk_drag_status (context, GDK_ACTION_COPY, time_);
1758 chat_window_drag_data_received (GtkWidget *widget,
1759 GdkDragContext *context,
1762 GtkSelectionData *selection,
1765 EmpathyChatWindow *window)
1767 if (info == DND_DRAG_TYPE_CONTACT_ID) {
1768 EmpathyChat *chat = NULL;
1769 EmpathyChatWindow *old_window;
1770 TpAccount *account = NULL;
1771 TpAccountManager *account_manager;
1774 const gchar *account_id;
1775 const gchar *contact_id;
1777 id = (const gchar*) gtk_selection_data_get_data (selection);
1779 /* FIXME: Perhaps should be sure that the account manager is
1780 * prepared before calling _ensure_account on it. */
1781 account_manager = tp_account_manager_dup ();
1783 DEBUG ("DND contact from roster with id:'%s'", id);
1785 strv = g_strsplit (id, ":", 2);
1786 if (g_strv_length (strv) == 2) {
1787 account_id = strv[0];
1788 contact_id = strv[1];
1790 tp_account_manager_ensure_account (account_manager, account_id);
1791 if (account != NULL)
1792 chat = empathy_chat_window_find_chat (account, contact_id);
1795 if (account == NULL) {
1797 gtk_drag_finish (context, FALSE, FALSE, time_);
1802 empathy_chat_with_contact_id (
1803 account, contact_id, gtk_get_current_event_time ());
1808 g_object_unref (account_manager);
1811 old_window = chat_window_find_chat (chat);
1813 if (old_window == window) {
1814 gtk_drag_finish (context, TRUE, FALSE, time_);
1818 empathy_chat_window_move_chat (old_window, window, chat);
1820 empathy_chat_window_add_chat (window, chat);
1823 /* Added to take care of any outstanding chat events */
1824 empathy_chat_window_present_chat (chat,
1825 TP_USER_ACTION_TIME_NOT_USER_ACTION);
1827 /* We should return TRUE to remove the data when doing
1828 * GDK_ACTION_MOVE, but we don't here otherwise it has
1829 * weird consequences, and we handle that internally
1830 * anyway with add_chat () and remove_chat ().
1832 gtk_drag_finish (context, TRUE, FALSE, time_);
1834 else if (info == DND_DRAG_TYPE_URI_LIST) {
1835 EmpathyChatWindowPriv *priv;
1836 EmpathyContact *contact;
1839 priv = GET_PRIV (window);
1840 contact = empathy_chat_get_remote_contact (priv->current_chat);
1842 /* contact is NULL when current_chat is a multi-user chat.
1843 * We don't do file transfers to MUCs, so just cancel the drag.
1845 if (contact == NULL) {
1846 gtk_drag_finish (context, TRUE, FALSE, time_);
1850 data = (const gchar *) gtk_selection_data_get_data (selection);
1851 empathy_send_file_from_uri_list (contact, data);
1853 gtk_drag_finish (context, TRUE, FALSE, time_);
1855 else if (info == DND_DRAG_TYPE_TAB) {
1857 EmpathyChatWindow *old_window = NULL;
1861 chat = (void *) gtk_selection_data_get_data (selection);
1862 old_window = chat_window_find_chat (*chat);
1865 EmpathyChatWindowPriv *priv;
1867 priv = GET_PRIV (window);
1868 priv->dnd_same_window = (old_window == window);
1869 DEBUG ("DND tab (within same window: %s)",
1870 priv->dnd_same_window ? "Yes" : "No");
1873 DEBUG ("DND from unknown source");
1874 gtk_drag_finish (context, FALSE, FALSE, time_);
1879 chat_window_chat_manager_chats_changed_cb (EmpathyChatManager *chat_manager,
1880 guint num_chats_in_manager,
1881 EmpathyChatWindow *window)
1883 EmpathyChatWindowPriv *priv = GET_PRIV (window);
1885 gtk_action_set_sensitive (priv->menu_tabs_undo_close_tab,
1886 num_chats_in_manager > 0);
1890 chat_window_finalize (GObject *object)
1892 EmpathyChatWindow *window;
1893 EmpathyChatWindowPriv *priv;
1895 window = EMPATHY_CHAT_WINDOW (object);
1896 priv = GET_PRIV (window);
1898 DEBUG ("Finalized: %p", object);
1900 g_object_unref (priv->ui_manager);
1901 g_object_unref (priv->chatroom_manager);
1902 g_object_unref (priv->notify_mgr);
1903 g_object_unref (priv->gsettings_chat);
1904 g_object_unref (priv->gsettings_notif);
1905 g_object_unref (priv->gsettings_ui);
1906 g_object_unref (priv->sound_mgr);
1908 if (priv->notification != NULL) {
1909 notify_notification_close (priv->notification, NULL);
1910 priv->notification = NULL;
1913 if (priv->contact_targets) {
1914 gtk_target_list_unref (priv->contact_targets);
1916 if (priv->file_targets) {
1917 gtk_target_list_unref (priv->file_targets);
1920 if (priv->chat_manager) {
1921 g_signal_handler_disconnect (priv->chat_manager,
1922 priv->chat_manager_chats_changed_id);
1923 g_object_unref (priv->chat_manager);
1924 priv->chat_manager = NULL;
1927 chat_windows = g_list_remove (chat_windows, window);
1928 gtk_widget_destroy (priv->dialog);
1930 G_OBJECT_CLASS (empathy_chat_window_parent_class)->finalize (object);
1934 empathy_chat_window_class_init (EmpathyChatWindowClass *klass)
1936 GObjectClass *object_class = G_OBJECT_CLASS (klass);
1938 object_class->finalize = chat_window_finalize;
1940 g_type_class_add_private (object_class, sizeof (EmpathyChatWindowPriv));
1944 empathy_chat_window_init (EmpathyChatWindow *window)
1947 GtkAccelGroup *accel_group;
1952 GtkWidget *chat_vbox;
1954 EmpathySmileyManager *smiley_manager;
1955 EmpathyChatWindowPriv *priv = G_TYPE_INSTANCE_GET_PRIVATE (window,
1956 EMPATHY_TYPE_CHAT_WINDOW, EmpathyChatWindowPriv);
1958 window->priv = priv;
1959 filename = empathy_file_lookup ("empathy-chat-window.ui", "src");
1960 gui = empathy_builder_get_file (filename,
1961 "chat_window", &priv->dialog,
1962 "chat_vbox", &chat_vbox,
1963 "ui_manager", &priv->ui_manager,
1964 "menu_conv_insert_smiley", &priv->menu_conv_insert_smiley,
1965 "menu_conv_favorite", &priv->menu_conv_favorite,
1966 "menu_conv_always_urgent", &priv->menu_conv_always_urgent,
1967 "menu_conv_toggle_contacts", &priv->menu_conv_toggle_contacts,
1968 "menu_edit_cut", &priv->menu_edit_cut,
1969 "menu_edit_copy", &priv->menu_edit_copy,
1970 "menu_edit_paste", &priv->menu_edit_paste,
1971 "menu_edit_find", &priv->menu_edit_find,
1972 "menu_tabs_next", &priv->menu_tabs_next,
1973 "menu_tabs_prev", &priv->menu_tabs_prev,
1974 "menu_tabs_undo_close_tab", &priv->menu_tabs_undo_close_tab,
1975 "menu_tabs_left", &priv->menu_tabs_left,
1976 "menu_tabs_right", &priv->menu_tabs_right,
1977 "menu_tabs_detach", &priv->menu_tabs_detach,
1981 empathy_builder_connect (gui, window,
1982 "menu_conv", "activate", chat_window_conv_activate_cb,
1983 "menu_conv_clear", "activate", chat_window_clear_activate_cb,
1984 "menu_conv_favorite", "toggled", chat_window_favorite_toggled_cb,
1985 "menu_conv_always_urgent", "toggled", chat_window_always_urgent_toggled_cb,
1986 "menu_conv_toggle_contacts", "toggled", chat_window_contacts_toggled_cb,
1987 "menu_conv_invite_participant", "activate", chat_window_invite_participant_activate_cb,
1988 "menu_conv_close", "activate", chat_window_close_activate_cb,
1989 "menu_edit", "activate", chat_window_edit_activate_cb,
1990 "menu_edit_cut", "activate", chat_window_cut_activate_cb,
1991 "menu_edit_copy", "activate", chat_window_copy_activate_cb,
1992 "menu_edit_paste", "activate", chat_window_paste_activate_cb,
1993 "menu_edit_find", "activate", chat_window_find_activate_cb,
1994 "menu_tabs_next", "activate", chat_window_tabs_next_activate_cb,
1995 "menu_tabs_prev", "activate", chat_window_tabs_previous_activate_cb,
1996 "menu_tabs_undo_close_tab", "activate", chat_window_tabs_undo_close_tab_activate_cb,
1997 "menu_tabs_left", "activate", chat_window_tabs_left_activate_cb,
1998 "menu_tabs_right", "activate", chat_window_tabs_right_activate_cb,
1999 "menu_tabs_detach", "activate", chat_window_detach_activate_cb,
2000 "menu_help_contents", "activate", chat_window_help_contents_activate_cb,
2001 "menu_help_about", "activate", chat_window_help_about_activate_cb,
2004 g_object_ref (priv->ui_manager);
2005 g_object_unref (gui);
2007 priv->gsettings_chat = g_settings_new (EMPATHY_PREFS_CHAT_SCHEMA);
2008 priv->gsettings_notif = g_settings_new (EMPATHY_PREFS_NOTIFICATIONS_SCHEMA);
2009 priv->gsettings_ui = g_settings_new (EMPATHY_PREFS_UI_SCHEMA);
2010 priv->chatroom_manager = empathy_chatroom_manager_dup_singleton (NULL);
2012 priv->sound_mgr = empathy_sound_manager_dup_singleton ();
2014 priv->notebook = gtk_notebook_new ();
2016 g_signal_connect (priv->notebook, "create-window",
2017 G_CALLBACK (notebook_create_window_cb), window);
2019 gtk_notebook_set_group_name (GTK_NOTEBOOK (priv->notebook),
2020 "EmpathyChatWindow");
2021 gtk_notebook_set_scrollable (GTK_NOTEBOOK (priv->notebook), TRUE);
2022 gtk_notebook_popup_enable (GTK_NOTEBOOK (priv->notebook));
2023 gtk_box_pack_start (GTK_BOX (chat_vbox), priv->notebook, TRUE, TRUE, 0);
2024 gtk_widget_show (priv->notebook);
2027 accel_group = gtk_accel_group_new ();
2028 gtk_window_add_accel_group (GTK_WINDOW (priv->dialog), accel_group);
2030 for (i = 0; i < G_N_ELEMENTS (tab_accel_keys); i++) {
2031 closure = g_cclosure_new (G_CALLBACK (chat_window_accel_cb),
2034 gtk_accel_group_connect (accel_group,
2041 g_object_unref (accel_group);
2043 /* Set up drag target lists */
2044 priv->contact_targets = gtk_target_list_new (drag_types_dest_contact,
2045 G_N_ELEMENTS (drag_types_dest_contact));
2046 priv->file_targets = gtk_target_list_new (drag_types_dest_file,
2047 G_N_ELEMENTS (drag_types_dest_file));
2049 /* Set up smiley menu */
2050 smiley_manager = empathy_smiley_manager_dup_singleton ();
2051 submenu = empathy_smiley_menu_new (smiley_manager,
2052 chat_window_insert_smiley_activate_cb,
2054 menu = gtk_ui_manager_get_widget (priv->ui_manager,
2055 "/chats_menubar/menu_conv/menu_conv_insert_smiley");
2056 gtk_menu_item_set_submenu (GTK_MENU_ITEM (menu), submenu);
2057 g_object_unref (smiley_manager);
2059 /* Set up signals we can't do with ui file since we may need to
2060 * block/unblock them at some later stage.
2063 g_signal_connect (priv->dialog,
2065 G_CALLBACK (chat_window_delete_event_cb),
2067 g_signal_connect (priv->dialog,
2069 G_CALLBACK (chat_window_focus_in_event_cb),
2071 g_signal_connect_after (priv->notebook,
2073 G_CALLBACK (chat_window_page_switched_cb),
2075 g_signal_connect (priv->notebook,
2077 G_CALLBACK (chat_window_page_added_cb),
2079 g_signal_connect (priv->notebook,
2081 G_CALLBACK (chat_window_page_removed_cb),
2084 /* Set up drag and drop */
2085 gtk_drag_dest_set (GTK_WIDGET (priv->notebook),
2086 GTK_DEST_DEFAULT_HIGHLIGHT,
2088 G_N_ELEMENTS (drag_types_dest),
2089 GDK_ACTION_MOVE | GDK_ACTION_COPY);
2091 /* connect_after to allow GtkNotebook's built-in tab switching */
2092 g_signal_connect_after (priv->notebook,
2094 G_CALLBACK (chat_window_drag_motion),
2096 g_signal_connect (priv->notebook,
2097 "drag-data-received",
2098 G_CALLBACK (chat_window_drag_data_received),
2100 g_signal_connect (priv->notebook,
2102 G_CALLBACK (chat_window_drag_drop),
2105 chat_windows = g_list_prepend (chat_windows, window);
2107 /* Set up private details */
2109 priv->current_chat = NULL;
2110 priv->notification = NULL;
2112 priv->notify_mgr = empathy_notify_manager_dup_singleton ();
2114 priv->chat_manager = empathy_chat_manager_dup_singleton ();
2115 priv->chat_manager_chats_changed_id =
2116 g_signal_connect (priv->chat_manager, "closed-chats-changed",
2117 G_CALLBACK (chat_window_chat_manager_chats_changed_cb),
2120 chat_window_chat_manager_chats_changed_cb (priv->chat_manager,
2121 empathy_chat_manager_get_num_closed_chats (priv->chat_manager),
2126 empathy_chat_window_get_dialog (EmpathyChatWindow *window)
2128 EmpathyChatWindowPriv *priv;
2130 g_return_val_if_fail (window != NULL, NULL);
2132 priv = GET_PRIV (window);
2134 return priv->dialog;
2137 /* Returns the window to open a new tab in if there is a suitable window,
2138 * otherwise, returns NULL indicating that a new window should be added.
2140 static EmpathyChatWindow *
2141 empathy_chat_window_get_default (gboolean room)
2143 GSettings *gsettings = g_settings_new (EMPATHY_PREFS_UI_SCHEMA);
2145 gboolean separate_windows = TRUE;
2147 separate_windows = g_settings_get_boolean (gsettings,
2148 EMPATHY_PREFS_UI_SEPARATE_CHAT_WINDOWS);
2150 g_object_unref (gsettings);
2152 if (separate_windows) {
2153 /* Always create a new window */
2157 for (l = chat_windows; l; l = l->next) {
2158 EmpathyChatWindowPriv *priv;
2159 EmpathyChatWindow *chat_window;
2161 guint nb_rooms, nb_private;
2163 chat_window = l->data;
2164 priv = GET_PRIV (chat_window);
2166 dialog = empathy_chat_window_get_dialog (chat_window);
2168 empathy_chat_window_get_nb_chats (chat_window, &nb_rooms, &nb_private);
2170 /* Skip the window if there aren't any rooms in it */
2171 if (room && nb_rooms == 0)
2174 /* Skip the window if there aren't any 1-1 chats in it */
2175 if (!room && nb_private == 0)
2178 /* Found a window on this desktop, make it visible if necessary */
2179 if (!empathy_window_get_is_visible (GTK_WINDOW (dialog)))
2180 empathy_window_present (GTK_WINDOW (dialog));
2188 empathy_chat_window_add_chat (EmpathyChatWindow *window,
2191 EmpathyChatWindowPriv *priv;
2193 GtkWidget *popup_label;
2195 GValue value = { 0, };
2197 g_return_if_fail (window != NULL);
2198 g_return_if_fail (EMPATHY_IS_CHAT (chat));
2200 priv = GET_PRIV (window);
2202 /* Reference the chat object */
2203 g_object_ref (chat);
2205 /* If this window has just been created, position it */
2206 if (priv->chats == NULL) {
2207 const gchar *name = "chat-window";
2208 gboolean separate_windows;
2210 separate_windows = g_settings_get_boolean (priv->gsettings_ui,
2211 EMPATHY_PREFS_UI_SEPARATE_CHAT_WINDOWS);
2213 if (empathy_chat_is_room (chat))
2214 name = "room-window";
2216 if (separate_windows) {
2219 /* Save current position of the window */
2220 gtk_window_get_position (GTK_WINDOW (priv->dialog), &x, &y);
2222 /* First bind to the 'generic' name. So new window for which we didn't
2223 * save a geometry yet will have the geometry of the last saved
2224 * window (bgo #601191). */
2225 empathy_geometry_bind (GTK_WINDOW (priv->dialog), name);
2227 /* Restore previous position of the window so the newly created window
2228 * won't be in the same position as the latest saved window and so
2229 * completely hide it. */
2230 gtk_window_move (GTK_WINDOW (priv->dialog), x, y);
2232 /* Then bind it to the name of the contact/room so we'll save the
2233 * geometry specific to this window */
2234 name = empathy_chat_get_id (chat);
2237 empathy_geometry_bind (GTK_WINDOW (priv->dialog), name);
2240 child = GTK_WIDGET (chat);
2241 label = chat_window_create_label (window, chat, TRUE);
2242 popup_label = chat_window_create_label (window, chat, FALSE);
2243 gtk_widget_show (child);
2245 g_signal_connect (chat, "notify::name",
2246 G_CALLBACK (chat_window_chat_notify_cb),
2248 g_signal_connect (chat, "notify::subject",
2249 G_CALLBACK (chat_window_chat_notify_cb),
2251 g_signal_connect (chat, "notify::remote-contact",
2252 G_CALLBACK (chat_window_chat_notify_cb),
2254 chat_window_chat_notify_cb (chat);
2256 gtk_notebook_append_page_menu (GTK_NOTEBOOK (priv->notebook), child, label, popup_label);
2257 gtk_notebook_set_tab_reorderable (GTK_NOTEBOOK (priv->notebook), child, TRUE);
2258 gtk_notebook_set_tab_detachable (GTK_NOTEBOOK (priv->notebook), child, TRUE);
2259 g_value_init (&value, G_TYPE_BOOLEAN);
2260 g_value_set_boolean (&value, TRUE);
2261 gtk_container_child_set_property (GTK_CONTAINER (priv->notebook),
2262 child, "tab-expand" , &value);
2263 gtk_container_child_set_property (GTK_CONTAINER (priv->notebook),
2264 child, "tab-fill" , &value);
2265 g_value_unset (&value);
2267 DEBUG ("Chat added (%d references)", G_OBJECT (chat)->ref_count);
2271 empathy_chat_window_remove_chat (EmpathyChatWindow *window,
2274 EmpathyChatWindowPriv *priv;
2276 EmpathyContact *remote_contact;
2277 EmpathyChatManager *chat_manager;
2279 g_return_if_fail (window != NULL);
2280 g_return_if_fail (EMPATHY_IS_CHAT (chat));
2282 priv = GET_PRIV (window);
2284 g_signal_handlers_disconnect_by_func (chat,
2285 chat_window_chat_notify_cb,
2287 remote_contact = g_object_get_data (G_OBJECT (chat),
2288 "chat-window-remote-contact");
2289 if (remote_contact) {
2290 g_signal_handlers_disconnect_by_func (remote_contact,
2291 chat_window_update_chat_tab,
2295 chat_manager = empathy_chat_manager_dup_singleton ();
2296 empathy_chat_manager_closed_chat (chat_manager, chat);
2297 g_object_unref (chat_manager);
2299 position = gtk_notebook_page_num (GTK_NOTEBOOK (priv->notebook),
2301 gtk_notebook_remove_page (GTK_NOTEBOOK (priv->notebook), position);
2303 DEBUG ("Chat removed (%d references)", G_OBJECT (chat)->ref_count - 1);
2305 g_object_unref (chat);
2309 empathy_chat_window_move_chat (EmpathyChatWindow *old_window,
2310 EmpathyChatWindow *new_window,
2315 g_return_if_fail (EMPATHY_IS_CHAT_WINDOW (old_window));
2316 g_return_if_fail (EMPATHY_IS_CHAT_WINDOW (new_window));
2317 g_return_if_fail (EMPATHY_IS_CHAT (chat));
2319 widget = GTK_WIDGET (chat);
2321 DEBUG ("Chat moving with widget:%p (%d references)", widget,
2322 G_OBJECT (widget)->ref_count);
2324 /* We reference here to make sure we don't loose the widget
2325 * and the EmpathyChat object during the move.
2327 g_object_ref (chat);
2328 g_object_ref (widget);
2330 empathy_chat_window_remove_chat (old_window, chat);
2331 empathy_chat_window_add_chat (new_window, chat);
2333 g_object_unref (widget);
2334 g_object_unref (chat);
2338 empathy_chat_window_switch_to_chat (EmpathyChatWindow *window,
2341 EmpathyChatWindowPriv *priv;
2344 g_return_if_fail (window != NULL);
2345 g_return_if_fail (EMPATHY_IS_CHAT (chat));
2347 priv = GET_PRIV (window);
2349 page_num = gtk_notebook_page_num (GTK_NOTEBOOK (priv->notebook),
2351 gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->notebook),
2356 empathy_chat_window_find_chat (TpAccount *account,
2361 g_return_val_if_fail (!EMP_STR_EMPTY (id), NULL);
2363 for (l = chat_windows; l; l = l->next) {
2364 EmpathyChatWindowPriv *priv;
2365 EmpathyChatWindow *window;
2369 priv = GET_PRIV (window);
2371 for (ll = priv->chats; ll; ll = ll->next) {
2376 if (account == empathy_chat_get_account (chat) &&
2377 !tp_strdiff (id, empathy_chat_get_id (chat))) {
2387 empathy_chat_window_present_chat (EmpathyChat *chat,
2390 EmpathyChatWindow *window;
2391 EmpathyChatWindowPriv *priv;
2392 guint32 x_timestamp;
2394 g_return_if_fail (EMPATHY_IS_CHAT (chat));
2396 window = chat_window_find_chat (chat);
2398 /* If the chat has no window, create one */
2399 if (window == NULL) {
2400 window = empathy_chat_window_get_default (empathy_chat_is_room (chat));
2402 window = empathy_chat_window_new ();
2405 empathy_chat_window_add_chat (window, chat);
2408 /* Don't force the window to show itself when it wasn't
2409 * an action by the user
2411 if (!tp_user_action_time_should_present (timestamp, &x_timestamp))
2414 priv = GET_PRIV (window);
2416 if (x_timestamp != GDK_CURRENT_TIME) {
2417 /* Don't present or switch tab if the action was earlier than the
2418 * last actions X time, accounting for overflow and the first ever
2421 if (priv->x_user_action_time != 0
2422 && X_EARLIER_OR_EQL (x_timestamp, priv->x_user_action_time))
2425 priv->x_user_action_time = x_timestamp;
2428 empathy_chat_window_switch_to_chat (window, chat);
2429 empathy_window_present_with_time (GTK_WINDOW (priv->dialog),
2432 gtk_widget_grab_focus (chat->input_text_view);
2436 empathy_chat_window_get_nb_chats (EmpathyChatWindow *self,
2440 EmpathyChatWindowPriv *priv = GET_PRIV (self);
2442 guint _nb_rooms = 0, _nb_private = 0;
2444 for (l = priv->chats; l != NULL; l = g_list_next (l)) {
2445 if (empathy_chat_is_room (EMPATHY_CHAT (l->data)))
2451 if (nb_rooms != NULL)
2452 *nb_rooms = _nb_rooms;
2453 if (nb_private != NULL)
2454 *nb_private = _nb_private;