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"
62 #include "gedit-close-button.h"
64 #define DEBUG_FLAG EMPATHY_DEBUG_CHAT
65 #include <libempathy/empathy-debug.h>
67 /* Macro to compare guint32 X timestamps, while accounting for wrapping around
69 #define X_EARLIER_OR_EQL(t1, t2) \
70 ((t1 <= t2 && ((t2 - t1) < G_MAXUINT32/2)) \
71 || (t1 >= t2 && (t1 - t2) > (G_MAXUINT32/2)) \
74 #define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyChatWindow)
76 EmpathyChat *current_chat;
79 gboolean dnd_same_window;
80 EmpathyChatroomManager *chatroom_manager;
81 EmpathyNotifyManager *notify_mgr;
84 NotifyNotification *notification;
86 GtkTargetList *contact_targets;
87 GtkTargetList *file_targets;
89 EmpathyChatManager *chat_manager;
90 gulong chat_manager_chats_changed_id;
93 GtkUIManager *ui_manager;
94 GtkAction *menu_conv_insert_smiley;
95 GtkAction *menu_conv_favorite;
96 GtkAction *menu_conv_always_urgent;
97 GtkAction *menu_conv_toggle_contacts;
99 GtkAction *menu_edit_cut;
100 GtkAction *menu_edit_copy;
101 GtkAction *menu_edit_paste;
102 GtkAction *menu_edit_find;
104 GtkAction *menu_tabs_next;
105 GtkAction *menu_tabs_prev;
106 GtkAction *menu_tabs_undo_close_tab;
107 GtkAction *menu_tabs_left;
108 GtkAction *menu_tabs_right;
109 GtkAction *menu_tabs_detach;
111 /* Last user action time we acted upon to show a tab */
112 guint32 x_user_action_time;
114 GSettings *gsettings_chat;
115 GSettings *gsettings_notif;
116 GSettings *gsettings_ui;
118 EmpathySoundManager *sound_mgr;
119 } EmpathyChatWindowPriv;
121 static GList *chat_windows = NULL;
123 static const guint tab_accel_keys[] = {
124 GDK_KEY_1, GDK_KEY_2, GDK_KEY_3, GDK_KEY_4, GDK_KEY_5,
125 GDK_KEY_6, GDK_KEY_7, GDK_KEY_8, GDK_KEY_9, GDK_KEY_0
129 DND_DRAG_TYPE_CONTACT_ID,
130 DND_DRAG_TYPE_URI_LIST,
134 static const GtkTargetEntry drag_types_dest[] = {
135 { "text/contact-id", 0, DND_DRAG_TYPE_CONTACT_ID },
136 { "GTK_NOTEBOOK_TAB", GTK_TARGET_SAME_APP, DND_DRAG_TYPE_TAB },
137 { "text/uri-list", 0, DND_DRAG_TYPE_URI_LIST },
138 { "text/path-list", 0, DND_DRAG_TYPE_URI_LIST },
141 static const GtkTargetEntry drag_types_dest_contact[] = {
142 { "text/contact-id", 0, DND_DRAG_TYPE_CONTACT_ID },
145 static const GtkTargetEntry drag_types_dest_file[] = {
146 /* must be first to be prioritized, in order to receive the
147 * note's file path from Tomboy instead of an URI */
148 { "text/path-list", 0, DND_DRAG_TYPE_URI_LIST },
149 { "text/uri-list", 0, DND_DRAG_TYPE_URI_LIST },
152 static void chat_window_update (EmpathyChatWindow *window);
154 static void empathy_chat_window_add_chat (EmpathyChatWindow *window,
157 static void empathy_chat_window_remove_chat (EmpathyChatWindow *window,
160 static void empathy_chat_window_move_chat (EmpathyChatWindow *old_window,
161 EmpathyChatWindow *new_window,
164 static void empathy_chat_window_get_nb_chats (EmpathyChatWindow *self,
168 G_DEFINE_TYPE (EmpathyChatWindow, empathy_chat_window, G_TYPE_OBJECT);
171 chat_window_accel_cb (GtkAccelGroup *accelgroup,
175 EmpathyChatWindow *window)
177 EmpathyChatWindowPriv *priv;
181 priv = GET_PRIV (window);
183 for (i = 0; i < G_N_ELEMENTS (tab_accel_keys); i++) {
184 if (tab_accel_keys[i] == key) {
191 gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->notebook), num);
195 static EmpathyChatWindow *
196 chat_window_find_chat (EmpathyChat *chat)
198 EmpathyChatWindowPriv *priv;
201 for (l = chat_windows; l; l = l->next) {
202 priv = GET_PRIV (l->data);
203 ll = g_list_find (priv->chats, chat);
213 chat_window_close_clicked_cb (GtkAction *action,
216 EmpathyChatWindow *window;
218 window = chat_window_find_chat (chat);
219 empathy_chat_window_remove_chat (window, chat);
223 chat_tab_style_updated_cb (GtkWidget *hbox,
227 int char_width, h, w;
228 PangoContext *context;
229 const PangoFontDescription *font_desc;
230 PangoFontMetrics *metrics;
232 button = g_object_get_data (G_OBJECT (user_data),
233 "chat-window-tab-close-button");
234 context = gtk_widget_get_pango_context (hbox);
236 font_desc = gtk_style_context_get_font (gtk_widget_get_style_context (hbox),
237 GTK_STATE_FLAG_NORMAL);
239 metrics = pango_context_get_metrics (context, font_desc,
240 pango_context_get_language (context));
241 char_width = pango_font_metrics_get_approximate_char_width (metrics);
242 pango_font_metrics_unref (metrics);
244 gtk_icon_size_lookup_for_settings (gtk_widget_get_settings (button),
245 GTK_ICON_SIZE_MENU, &w, &h);
247 /* Request at least about 12 chars width plus at least space for the status
248 * image and the close button */
249 gtk_widget_set_size_request (hbox,
250 12 * PANGO_PIXELS (char_width) + 2 * w, -1);
252 gtk_widget_set_size_request (button, w, h);
256 chat_window_create_label (EmpathyChatWindow *window,
258 gboolean is_tab_label)
260 EmpathyChatWindowPriv *priv;
262 GtkWidget *name_label;
263 GtkWidget *status_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 GtkWidget *close_button;
316 close_button = gedit_close_button_new ();
317 g_object_set_data (G_OBJECT (chat), "chat-window-tab-close-button", close_button);
319 /* We don't want focus/keynav for the button to avoid clutter, and
320 * Ctrl-W works anyway.
322 gtk_widget_set_can_focus (close_button, FALSE);
323 gtk_widget_set_can_default (close_button, FALSE);
325 gtk_box_pack_end (GTK_BOX (hbox), close_button, FALSE, FALSE, 0);
327 g_signal_connect (close_button,
329 G_CALLBACK (chat_window_close_clicked_cb),
332 /* React to theme changes and also setup the size correctly. */
333 g_signal_connect (hbox,
335 G_CALLBACK (chat_tab_style_updated_cb),
339 gtk_widget_show_all (hbox);
345 _submenu_notify_visible_changed_cb (GObject *object,
349 g_signal_handlers_disconnect_by_func (object,
350 _submenu_notify_visible_changed_cb,
352 chat_window_update (EMPATHY_CHAT_WINDOW (userdata));
356 chat_window_menu_context_update (EmpathyChatWindowPriv *priv,
361 gboolean wrap_around;
362 gboolean is_connected;
365 page_num = gtk_notebook_get_current_page (GTK_NOTEBOOK (priv->notebook));
366 first_page = (page_num == 0);
367 last_page = (page_num == (num_pages - 1));
368 g_object_get (gtk_settings_get_default (), "gtk-keynav-wrap-around",
370 is_connected = empathy_chat_get_tp_chat (priv->current_chat) != NULL;
372 gtk_action_set_sensitive (priv->menu_tabs_next, (!last_page ||
374 gtk_action_set_sensitive (priv->menu_tabs_prev, (!first_page ||
376 gtk_action_set_sensitive (priv->menu_tabs_detach, num_pages > 1);
377 gtk_action_set_sensitive (priv->menu_tabs_left, !first_page);
378 gtk_action_set_sensitive (priv->menu_tabs_right, !last_page);
379 gtk_action_set_sensitive (priv->menu_conv_insert_smiley, is_connected);
383 chat_window_conversation_menu_update (EmpathyChatWindowPriv *priv,
384 EmpathyChatWindow *self)
386 EmpathyTpChat *tp_chat;
387 TpConnection *connection;
389 gboolean sensitive = FALSE;
391 g_return_if_fail (priv->current_chat != NULL);
393 action = gtk_ui_manager_get_action (priv->ui_manager,
394 "/chats_menubar/menu_conv/menu_conv_invite_participant");
395 tp_chat = empathy_chat_get_tp_chat (priv->current_chat);
397 if (tp_chat != NULL) {
398 connection = empathy_tp_chat_get_connection (tp_chat);
400 sensitive = empathy_tp_chat_can_add_contact (tp_chat) &&
401 (tp_connection_get_status (connection, NULL) ==
402 TP_CONNECTION_STATUS_CONNECTED);
405 gtk_action_set_sensitive (action, sensitive);
409 chat_window_contact_menu_update (EmpathyChatWindowPriv *priv,
410 EmpathyChatWindow *window)
412 GtkWidget *menu, *submenu, *orig_submenu;
414 menu = gtk_ui_manager_get_widget (priv->ui_manager,
415 "/chats_menubar/menu_contact");
416 orig_submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (menu));
418 if (orig_submenu == NULL || !gtk_widget_get_visible (orig_submenu)) {
419 submenu = empathy_chat_get_contact_menu (priv->current_chat);
421 if (submenu != NULL) {
422 /* gtk_menu_attach_to_widget() doesn't behave nicely here */
423 g_object_set_data (G_OBJECT (submenu), "window", priv->dialog);
425 gtk_menu_item_set_submenu (GTK_MENU_ITEM (menu), submenu);
426 gtk_widget_show (menu);
427 gtk_widget_set_sensitive (menu, TRUE);
429 gtk_widget_set_sensitive (menu, FALSE);
432 tp_g_signal_connect_object (orig_submenu,
434 (GCallback)_submenu_notify_visible_changed_cb,
440 get_all_unread_messages (EmpathyChatWindowPriv *priv)
445 for (l = priv->chats; l != NULL; l = g_list_next (l))
446 nb += empathy_chat_get_nb_unread_messages (EMPATHY_CHAT (l->data));
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, gboolean new_messages)
531 EmpathyContact *remote_contact;
532 gboolean avatar_in_icon;
535 n_chats = g_list_length (priv->chats);
537 /* Update window icon */
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, get_all_unread_messages (priv) > 0);
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 (empathy_chat_get_nb_unread_messages (chat) > 0) {
663 icon_name = EMPATHY_IMAGE_MESSAGE;
665 else if (remote_contact && empathy_chat_is_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 (remote_contact && empathy_chat_is_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 chat_window_update_chat_tab (chat);
1243 chat_window_set_urgency_hint (EmpathyChatWindow *window,
1246 EmpathyChatWindowPriv *priv;
1248 priv = GET_PRIV (window);
1250 gtk_window_set_urgency_hint (GTK_WINDOW (priv->dialog), urgent);
1254 chat_window_notification_closed_cb (NotifyNotification *notify,
1255 EmpathyChatWindow *self)
1257 EmpathyChatWindowPriv *priv = GET_PRIV (self);
1259 g_object_unref (notify);
1260 if (priv->notification == notify) {
1261 priv->notification = NULL;
1266 chat_window_show_or_update_notification (EmpathyChatWindow *window,
1267 EmpathyMessage *message,
1270 EmpathyContact *sender;
1271 const gchar *header;
1275 EmpathyChatWindowPriv *priv = GET_PRIV (window);
1276 gboolean res, has_x_canonical_append;
1277 NotifyNotification *notification = priv->notification;
1279 if (!empathy_notify_manager_notification_is_enabled (priv->notify_mgr)) {
1282 res = g_settings_get_boolean (priv->gsettings_notif,
1283 EMPATHY_PREFS_NOTIFICATIONS_FOCUS);
1290 sender = empathy_message_get_sender (message);
1291 header = empathy_contact_get_alias (sender);
1292 body = empathy_message_get_body (message);
1293 escaped = g_markup_escape_text (body, -1);
1294 has_x_canonical_append = empathy_notify_manager_has_capability (
1295 priv->notify_mgr, EMPATHY_NOTIFY_MANAGER_CAP_X_CANONICAL_APPEND);
1297 if (notification != NULL && !has_x_canonical_append) {
1298 /* if the notification server supports x-canonical-append, it is
1299 better to not use notify_notification_update to avoid
1300 overwriting the current notification message */
1301 notify_notification_update (notification,
1302 header, escaped, NULL);
1304 /* if the notification server supports x-canonical-append,
1305 the hint will be added, so that the message from the
1306 just created notification will be automatically appended
1307 to an existing notification with the same title.
1308 In this way the previous message will not be lost: the new
1309 message will appear below it, in the same notification */
1310 notification = notify_notification_new (header, escaped, NULL);
1312 if (priv->notification == NULL) {
1313 priv->notification = notification;
1316 notify_notification_set_timeout (notification, NOTIFY_EXPIRES_DEFAULT);
1318 tp_g_signal_connect_object (notification, "closed",
1319 G_CALLBACK (chat_window_notification_closed_cb), window, 0);
1321 if (has_x_canonical_append) {
1322 notify_notification_set_hint_string (notification,
1323 EMPATHY_NOTIFY_MANAGER_CAP_X_CANONICAL_APPEND, "");
1327 pixbuf = empathy_notify_manager_get_pixbuf_for_notification (priv->notify_mgr,
1328 sender, EMPATHY_IMAGE_NEW_MESSAGE);
1330 if (pixbuf != NULL) {
1331 notify_notification_set_icon_from_pixbuf (notification, pixbuf);
1332 g_object_unref (pixbuf);
1335 notify_notification_show (notification, NULL);
1341 chat_window_set_highlight_room_labels (EmpathyChat *chat)
1346 if (!empathy_chat_is_room (chat))
1349 markup = g_markup_printf_escaped (
1350 "<span color=\"red\" weight=\"bold\">%s</span>",
1351 empathy_chat_get_name (chat));
1353 widget = g_object_get_data (G_OBJECT (chat), "chat-window-tab-label");
1354 gtk_label_set_markup (GTK_LABEL (widget), markup);
1356 widget = g_object_get_data (G_OBJECT (chat), "chat-window-menu-label");
1357 gtk_label_set_markup (GTK_LABEL (widget), markup);
1363 empathy_chat_window_has_focus (EmpathyChatWindow *window)
1365 EmpathyChatWindowPriv *priv;
1368 g_return_val_if_fail (EMPATHY_IS_CHAT_WINDOW (window), FALSE);
1370 priv = GET_PRIV (window);
1372 g_object_get (priv->dialog, "has-toplevel-focus", &has_focus, NULL);
1378 chat_window_new_message_cb (EmpathyChat *chat,
1379 EmpathyMessage *message,
1381 EmpathyChatWindow *window)
1383 EmpathyChatWindowPriv *priv;
1385 gboolean needs_urgency;
1386 EmpathyContact *sender;
1388 priv = GET_PRIV (window);
1390 has_focus = empathy_chat_window_has_focus (window);
1392 /* - if we're the sender, we play the sound if it's specified in the
1393 * preferences and we're not away.
1394 * - if we receive a message, we play the sound if it's specified in the
1395 * preferences and the window does not have focus on the chat receiving
1399 sender = empathy_message_get_sender (message);
1401 if (empathy_contact_is_user (sender)) {
1402 empathy_sound_manager_play (priv->sound_mgr, GTK_WIDGET (priv->dialog),
1403 EMPATHY_SOUND_MESSAGE_OUTGOING);
1406 if (has_focus && priv->current_chat == chat) {
1407 /* window and tab are focused so consider the message to be read */
1409 /* FIXME: see Bug#610994 and coments about it in EmpathyChatPriv */
1410 empathy_chat_messages_read (chat);
1414 /* Update the chat tab if this is the first unread message */
1415 if (empathy_chat_get_nb_unread_messages (chat) == 1) {
1416 chat_window_update_chat_tab (chat);
1419 /* If empathy_chat_is_room () returns TRUE, that means it's a named MUC.
1420 * If empathy_chat_get_remote_contact () returns NULL, that means it's
1421 * an unamed MUC (msn-like).
1422 * In case of a MUC, we set urgency if either:
1423 * a) the chatroom's always_urgent property is TRUE
1424 * b) the message contains our alias
1426 if (empathy_chat_is_room (chat) ||
1427 empathy_chat_get_remote_contact (chat) == NULL) {
1430 EmpathyChatroom *chatroom;
1432 account = empathy_chat_get_account (chat);
1433 room = empathy_chat_get_id (chat);
1435 chatroom = empathy_chatroom_manager_find (priv->chatroom_manager,
1438 if (empathy_chatroom_is_always_urgent (chatroom)) {
1439 needs_urgency = TRUE;
1441 needs_urgency = empathy_message_should_highlight (message);
1444 needs_urgency = TRUE;
1447 if (needs_urgency) {
1448 chat_window_set_highlight_room_labels (chat);
1451 chat_window_set_urgency_hint (window, TRUE);
1454 /* Pending messages have already been displayed and notified in the
1455 * approver, so we don't display a notification and play a sound for those */
1457 empathy_sound_manager_play (priv->sound_mgr, GTK_WIDGET (priv->dialog),
1458 EMPATHY_SOUND_MESSAGE_INCOMING);
1460 chat_window_show_or_update_notification (window, message, chat);
1464 /* update the number of unread messages and the window icon */
1465 chat_window_title_update (priv);
1466 chat_window_icon_update (priv, TRUE);
1469 static GtkNotebook *
1470 notebook_create_window_cb (GtkNotebook *source,
1476 EmpathyChatWindowPriv *priv;
1477 EmpathyChatWindow *window, *new_window;
1480 chat = EMPATHY_CHAT (page);
1481 window = chat_window_find_chat (chat);
1483 new_window = empathy_chat_window_new ();
1484 priv = GET_PRIV (new_window);
1486 DEBUG ("Detach hook called");
1488 empathy_chat_window_move_chat (window, new_window, chat);
1490 gtk_widget_show (priv->dialog);
1491 gtk_window_move (GTK_WINDOW (priv->dialog), x, y);
1497 chat_window_page_switched_cb (GtkNotebook *notebook,
1498 gpointer ignore, /* see note below */
1500 EmpathyChatWindow *window)
1502 EmpathyChatWindowPriv *priv;
1506 DEBUG ("Page switched");
1508 priv = GET_PRIV (window);
1510 /* N.B. in GTK+ 3 child is passed as the first argument to the signal,
1511 * but we can't use that while trying to retain GTK+ 2.x compatibility.
1513 child = gtk_notebook_get_nth_page (notebook, page_num);
1514 chat = EMPATHY_CHAT (child);
1516 if (priv->page_added) {
1517 priv->page_added = FALSE;
1518 empathy_chat_scroll_down (chat);
1520 else if (priv->current_chat == chat) {
1524 priv->current_chat = chat;
1525 empathy_chat_messages_read (chat);
1527 chat_window_update_chat_tab (chat);
1531 chat_window_page_added_cb (GtkNotebook *notebook,
1534 EmpathyChatWindow *window)
1536 EmpathyChatWindowPriv *priv;
1539 priv = GET_PRIV (window);
1541 /* If we just received DND to the same window, we don't want
1542 * to do anything here like removing the tab and then readding
1543 * it, so we return here and in "page-added".
1545 if (priv->dnd_same_window) {
1546 DEBUG ("Page added (back to the same window)");
1547 priv->dnd_same_window = FALSE;
1551 DEBUG ("Page added");
1553 /* Get chat object */
1554 chat = EMPATHY_CHAT (child);
1556 /* Connect chat signals for this window */
1557 g_signal_connect (chat, "composing",
1558 G_CALLBACK (chat_window_composing_cb),
1560 g_signal_connect (chat, "new-message",
1561 G_CALLBACK (chat_window_new_message_cb),
1563 g_signal_connect (chat, "notify::tp-chat",
1564 G_CALLBACK (chat_window_update_chat_tab),
1567 /* Set flag so we know to perform some special operations on
1568 * switch page due to the new page being added.
1570 priv->page_added = TRUE;
1572 /* Get list of chats up to date */
1573 priv->chats = g_list_append (priv->chats, chat);
1575 chat_window_update_chat_tab (chat);
1579 chat_window_page_removed_cb (GtkNotebook *notebook,
1582 EmpathyChatWindow *window)
1584 EmpathyChatWindowPriv *priv;
1587 priv = GET_PRIV (window);
1589 /* If we just received DND to the same window, we don't want
1590 * to do anything here like removing the tab and then readding
1591 * it, so we return here and in "page-added".
1593 if (priv->dnd_same_window) {
1594 DEBUG ("Page removed (and will be readded to same window)");
1598 DEBUG ("Page removed");
1600 /* Get chat object */
1601 chat = EMPATHY_CHAT (child);
1603 /* Disconnect all signal handlers for this chat and this window */
1604 g_signal_handlers_disconnect_by_func (chat,
1605 G_CALLBACK (chat_window_composing_cb),
1607 g_signal_handlers_disconnect_by_func (chat,
1608 G_CALLBACK (chat_window_new_message_cb),
1610 g_signal_handlers_disconnect_by_func (chat,
1611 G_CALLBACK (chat_window_update_chat_tab),
1614 /* Keep list of chats up to date */
1615 priv->chats = g_list_remove (priv->chats, chat);
1616 empathy_chat_messages_read (chat);
1618 if (priv->chats == NULL) {
1619 g_object_unref (window);
1621 chat_window_update (window);
1626 chat_window_focus_in_event_cb (GtkWidget *widget,
1628 EmpathyChatWindow *window)
1630 EmpathyChatWindowPriv *priv;
1632 priv = GET_PRIV (window);
1634 empathy_chat_messages_read (priv->current_chat);
1636 chat_window_set_urgency_hint (window, FALSE);
1638 /* Update the title, since we now mark all unread messages as read. */
1639 chat_window_update_chat_tab (priv->current_chat);
1645 chat_window_drag_drop (GtkWidget *widget,
1646 GdkDragContext *context,
1650 EmpathyChatWindow *window)
1653 EmpathyChatWindowPriv *priv;
1655 priv = GET_PRIV (window);
1657 target = gtk_drag_dest_find_target (widget, context, priv->file_targets);
1658 if (target == GDK_NONE)
1659 target = gtk_drag_dest_find_target (widget, context, priv->contact_targets);
1661 if (target != GDK_NONE) {
1662 gtk_drag_get_data (widget, context, target, time_);
1670 chat_window_drag_motion (GtkWidget *widget,
1671 GdkDragContext *context,
1675 EmpathyChatWindow *window)
1678 EmpathyChatWindowPriv *priv;
1680 priv = GET_PRIV (window);
1682 target = gtk_drag_dest_find_target (widget, context, priv->file_targets);
1683 if (target != GDK_NONE) {
1684 /* This is a file drag. Ensure the contact is online and set the
1685 drag type to COPY. Note that it's possible that the tab will
1686 be switched by GTK+ after a timeout from drag_motion without
1687 getting another drag_motion to disable the drop. You have
1688 to hold your mouse really still.
1690 EmpathyContact *contact;
1692 priv = GET_PRIV (window);
1693 contact = empathy_chat_get_remote_contact (priv->current_chat);
1694 /* contact is NULL for multi-user chats. We don't do
1695 * file transfers to MUCs. We also don't send files
1696 * to offline contacts or contacts that don't support
1699 if ((contact == NULL) || !empathy_contact_is_online (contact)) {
1700 gdk_drag_status (context, 0, time_);
1703 if (!(empathy_contact_get_capabilities (contact)
1704 & EMPATHY_CAPABILITIES_FT)) {
1705 gdk_drag_status (context, 0, time_);
1708 gdk_drag_status (context, GDK_ACTION_COPY, time_);
1712 target = gtk_drag_dest_find_target (widget, context, priv->contact_targets);
1713 if (target != GDK_NONE) {
1714 /* This is a drag of a contact from a contact list. Set to COPY.
1715 FIXME: If this drag is to a MUC window, it invites the user.
1716 Otherwise, it opens a chat. Should we use a different drag
1717 type for invites? Should we allow ASK?
1719 gdk_drag_status (context, GDK_ACTION_COPY, time_);
1727 chat_window_drag_data_received (GtkWidget *widget,
1728 GdkDragContext *context,
1731 GtkSelectionData *selection,
1734 EmpathyChatWindow *window)
1736 if (info == DND_DRAG_TYPE_CONTACT_ID) {
1737 EmpathyChat *chat = NULL;
1738 EmpathyChatWindow *old_window;
1739 TpAccount *account = NULL;
1740 TpAccountManager *account_manager;
1743 const gchar *account_id;
1744 const gchar *contact_id;
1746 id = (const gchar*) gtk_selection_data_get_data (selection);
1748 /* FIXME: Perhaps should be sure that the account manager is
1749 * prepared before calling _ensure_account on it. */
1750 account_manager = tp_account_manager_dup ();
1752 DEBUG ("DND contact from roster with id:'%s'", id);
1754 strv = g_strsplit (id, ":", 2);
1755 if (g_strv_length (strv) == 2) {
1756 account_id = strv[0];
1757 contact_id = strv[1];
1759 tp_account_manager_ensure_account (account_manager, account_id);
1760 if (account != NULL)
1761 chat = empathy_chat_window_find_chat (account, contact_id);
1764 if (account == NULL) {
1766 gtk_drag_finish (context, FALSE, FALSE, time_);
1771 empathy_dispatcher_chat_with_contact_id (
1772 account, contact_id, gtk_get_current_event_time ());
1777 g_object_unref (account_manager);
1780 old_window = chat_window_find_chat (chat);
1782 if (old_window == window) {
1783 gtk_drag_finish (context, TRUE, FALSE, time_);
1787 empathy_chat_window_move_chat (old_window, window, chat);
1789 empathy_chat_window_add_chat (window, chat);
1792 /* Added to take care of any outstanding chat events */
1793 empathy_chat_window_present_chat (chat,
1794 TP_USER_ACTION_TIME_NOT_USER_ACTION);
1796 /* We should return TRUE to remove the data when doing
1797 * GDK_ACTION_MOVE, but we don't here otherwise it has
1798 * weird consequences, and we handle that internally
1799 * anyway with add_chat () and remove_chat ().
1801 gtk_drag_finish (context, TRUE, FALSE, time_);
1803 else if (info == DND_DRAG_TYPE_URI_LIST) {
1804 EmpathyChatWindowPriv *priv;
1805 EmpathyContact *contact;
1808 priv = GET_PRIV (window);
1809 contact = empathy_chat_get_remote_contact (priv->current_chat);
1811 /* contact is NULL when current_chat is a multi-user chat.
1812 * We don't do file transfers to MUCs, so just cancel the drag.
1814 if (contact == NULL) {
1815 gtk_drag_finish (context, TRUE, FALSE, time_);
1819 data = (const gchar *) gtk_selection_data_get_data (selection);
1820 empathy_send_file_from_uri_list (contact, data);
1822 gtk_drag_finish (context, TRUE, FALSE, time_);
1824 else if (info == DND_DRAG_TYPE_TAB) {
1826 EmpathyChatWindow *old_window = NULL;
1830 chat = (void *) gtk_selection_data_get_data (selection);
1831 old_window = chat_window_find_chat (*chat);
1834 EmpathyChatWindowPriv *priv;
1836 priv = GET_PRIV (window);
1837 priv->dnd_same_window = (old_window == window);
1838 DEBUG ("DND tab (within same window: %s)",
1839 priv->dnd_same_window ? "Yes" : "No");
1842 DEBUG ("DND from unknown source");
1843 gtk_drag_finish (context, FALSE, FALSE, time_);
1848 chat_window_chat_manager_chats_changed_cb (EmpathyChatManager *chat_manager,
1849 guint num_chats_in_manager,
1850 EmpathyChatWindow *window)
1852 EmpathyChatWindowPriv *priv = GET_PRIV (window);
1854 gtk_action_set_sensitive (priv->menu_tabs_undo_close_tab,
1855 num_chats_in_manager > 0);
1859 chat_window_finalize (GObject *object)
1861 EmpathyChatWindow *window;
1862 EmpathyChatWindowPriv *priv;
1864 window = EMPATHY_CHAT_WINDOW (object);
1865 priv = GET_PRIV (window);
1867 DEBUG ("Finalized: %p", object);
1869 g_object_unref (priv->ui_manager);
1870 g_object_unref (priv->chatroom_manager);
1871 g_object_unref (priv->notify_mgr);
1872 g_object_unref (priv->gsettings_chat);
1873 g_object_unref (priv->gsettings_notif);
1874 g_object_unref (priv->gsettings_ui);
1875 g_object_unref (priv->sound_mgr);
1877 if (priv->notification != NULL) {
1878 notify_notification_close (priv->notification, NULL);
1879 priv->notification = NULL;
1882 if (priv->contact_targets) {
1883 gtk_target_list_unref (priv->contact_targets);
1885 if (priv->file_targets) {
1886 gtk_target_list_unref (priv->file_targets);
1889 if (priv->chat_manager) {
1890 g_signal_handler_disconnect (priv->chat_manager,
1891 priv->chat_manager_chats_changed_id);
1892 g_object_unref (priv->chat_manager);
1893 priv->chat_manager = NULL;
1896 chat_windows = g_list_remove (chat_windows, window);
1897 gtk_widget_destroy (priv->dialog);
1899 G_OBJECT_CLASS (empathy_chat_window_parent_class)->finalize (object);
1903 empathy_chat_window_class_init (EmpathyChatWindowClass *klass)
1905 GObjectClass *object_class = G_OBJECT_CLASS (klass);
1907 object_class->finalize = chat_window_finalize;
1909 g_type_class_add_private (object_class, sizeof (EmpathyChatWindowPriv));
1913 empathy_chat_window_init (EmpathyChatWindow *window)
1916 GtkAccelGroup *accel_group;
1921 GtkWidget *chat_vbox;
1923 EmpathySmileyManager *smiley_manager;
1924 EmpathyChatWindowPriv *priv = G_TYPE_INSTANCE_GET_PRIVATE (window,
1925 EMPATHY_TYPE_CHAT_WINDOW, EmpathyChatWindowPriv);
1927 window->priv = priv;
1928 filename = empathy_file_lookup ("empathy-chat-window.ui", "src");
1929 gui = empathy_builder_get_file (filename,
1930 "chat_window", &priv->dialog,
1931 "chat_vbox", &chat_vbox,
1932 "ui_manager", &priv->ui_manager,
1933 "menu_conv_insert_smiley", &priv->menu_conv_insert_smiley,
1934 "menu_conv_favorite", &priv->menu_conv_favorite,
1935 "menu_conv_always_urgent", &priv->menu_conv_always_urgent,
1936 "menu_conv_toggle_contacts", &priv->menu_conv_toggle_contacts,
1937 "menu_edit_cut", &priv->menu_edit_cut,
1938 "menu_edit_copy", &priv->menu_edit_copy,
1939 "menu_edit_paste", &priv->menu_edit_paste,
1940 "menu_edit_find", &priv->menu_edit_find,
1941 "menu_tabs_next", &priv->menu_tabs_next,
1942 "menu_tabs_prev", &priv->menu_tabs_prev,
1943 "menu_tabs_undo_close_tab", &priv->menu_tabs_undo_close_tab,
1944 "menu_tabs_left", &priv->menu_tabs_left,
1945 "menu_tabs_right", &priv->menu_tabs_right,
1946 "menu_tabs_detach", &priv->menu_tabs_detach,
1950 empathy_builder_connect (gui, window,
1951 "menu_conv", "activate", chat_window_conv_activate_cb,
1952 "menu_conv_clear", "activate", chat_window_clear_activate_cb,
1953 "menu_conv_favorite", "toggled", chat_window_favorite_toggled_cb,
1954 "menu_conv_always_urgent", "toggled", chat_window_always_urgent_toggled_cb,
1955 "menu_conv_toggle_contacts", "toggled", chat_window_contacts_toggled_cb,
1956 "menu_conv_invite_participant", "activate", chat_window_invite_participant_activate_cb,
1957 "menu_conv_close", "activate", chat_window_close_activate_cb,
1958 "menu_edit", "activate", chat_window_edit_activate_cb,
1959 "menu_edit_cut", "activate", chat_window_cut_activate_cb,
1960 "menu_edit_copy", "activate", chat_window_copy_activate_cb,
1961 "menu_edit_paste", "activate", chat_window_paste_activate_cb,
1962 "menu_edit_find", "activate", chat_window_find_activate_cb,
1963 "menu_tabs_next", "activate", chat_window_tabs_next_activate_cb,
1964 "menu_tabs_prev", "activate", chat_window_tabs_previous_activate_cb,
1965 "menu_tabs_undo_close_tab", "activate", chat_window_tabs_undo_close_tab_activate_cb,
1966 "menu_tabs_left", "activate", chat_window_tabs_left_activate_cb,
1967 "menu_tabs_right", "activate", chat_window_tabs_right_activate_cb,
1968 "menu_tabs_detach", "activate", chat_window_detach_activate_cb,
1969 "menu_help_contents", "activate", chat_window_help_contents_activate_cb,
1970 "menu_help_about", "activate", chat_window_help_about_activate_cb,
1973 g_object_ref (priv->ui_manager);
1974 g_object_unref (gui);
1976 priv->gsettings_chat = g_settings_new (EMPATHY_PREFS_CHAT_SCHEMA);
1977 priv->gsettings_notif = g_settings_new (EMPATHY_PREFS_NOTIFICATIONS_SCHEMA);
1978 priv->gsettings_ui = g_settings_new (EMPATHY_PREFS_UI_SCHEMA);
1979 priv->chatroom_manager = empathy_chatroom_manager_dup_singleton (NULL);
1981 priv->sound_mgr = empathy_sound_manager_dup_singleton ();
1983 priv->notebook = gtk_notebook_new ();
1985 g_signal_connect (priv->notebook, "create-window",
1986 G_CALLBACK (notebook_create_window_cb), window);
1988 gtk_notebook_set_group_name (GTK_NOTEBOOK (priv->notebook),
1989 "EmpathyChatWindow");
1990 gtk_notebook_set_scrollable (GTK_NOTEBOOK (priv->notebook), TRUE);
1991 gtk_notebook_popup_enable (GTK_NOTEBOOK (priv->notebook));
1992 gtk_box_pack_start (GTK_BOX (chat_vbox), priv->notebook, TRUE, TRUE, 0);
1993 gtk_widget_show (priv->notebook);
1996 accel_group = gtk_accel_group_new ();
1997 gtk_window_add_accel_group (GTK_WINDOW (priv->dialog), accel_group);
1999 for (i = 0; i < G_N_ELEMENTS (tab_accel_keys); i++) {
2000 closure = g_cclosure_new (G_CALLBACK (chat_window_accel_cb),
2003 gtk_accel_group_connect (accel_group,
2010 g_object_unref (accel_group);
2012 /* Set up drag target lists */
2013 priv->contact_targets = gtk_target_list_new (drag_types_dest_contact,
2014 G_N_ELEMENTS (drag_types_dest_contact));
2015 priv->file_targets = gtk_target_list_new (drag_types_dest_file,
2016 G_N_ELEMENTS (drag_types_dest_file));
2018 /* Set up smiley menu */
2019 smiley_manager = empathy_smiley_manager_dup_singleton ();
2020 submenu = empathy_smiley_menu_new (smiley_manager,
2021 chat_window_insert_smiley_activate_cb,
2023 menu = gtk_ui_manager_get_widget (priv->ui_manager,
2024 "/chats_menubar/menu_conv/menu_conv_insert_smiley");
2025 gtk_menu_item_set_submenu (GTK_MENU_ITEM (menu), submenu);
2026 g_object_unref (smiley_manager);
2028 /* Set up signals we can't do with ui file since we may need to
2029 * block/unblock them at some later stage.
2032 g_signal_connect (priv->dialog,
2034 G_CALLBACK (chat_window_delete_event_cb),
2036 g_signal_connect (priv->dialog,
2038 G_CALLBACK (chat_window_focus_in_event_cb),
2040 g_signal_connect_after (priv->notebook,
2042 G_CALLBACK (chat_window_page_switched_cb),
2044 g_signal_connect (priv->notebook,
2046 G_CALLBACK (chat_window_page_added_cb),
2048 g_signal_connect (priv->notebook,
2050 G_CALLBACK (chat_window_page_removed_cb),
2053 /* Set up drag and drop */
2054 gtk_drag_dest_set (GTK_WIDGET (priv->notebook),
2055 GTK_DEST_DEFAULT_HIGHLIGHT,
2057 G_N_ELEMENTS (drag_types_dest),
2058 GDK_ACTION_MOVE | GDK_ACTION_COPY);
2060 /* connect_after to allow GtkNotebook's built-in tab switching */
2061 g_signal_connect_after (priv->notebook,
2063 G_CALLBACK (chat_window_drag_motion),
2065 g_signal_connect (priv->notebook,
2066 "drag-data-received",
2067 G_CALLBACK (chat_window_drag_data_received),
2069 g_signal_connect (priv->notebook,
2071 G_CALLBACK (chat_window_drag_drop),
2074 chat_windows = g_list_prepend (chat_windows, window);
2076 /* Set up private details */
2078 priv->current_chat = NULL;
2079 priv->notification = NULL;
2081 priv->notify_mgr = empathy_notify_manager_dup_singleton ();
2083 priv->chat_manager = empathy_chat_manager_dup_singleton ();
2084 priv->chat_manager_chats_changed_id =
2085 g_signal_connect (priv->chat_manager, "closed-chats-changed",
2086 G_CALLBACK (chat_window_chat_manager_chats_changed_cb),
2089 chat_window_chat_manager_chats_changed_cb (priv->chat_manager,
2090 empathy_chat_manager_get_num_closed_chats (priv->chat_manager),
2095 empathy_chat_window_get_dialog (EmpathyChatWindow *window)
2097 EmpathyChatWindowPriv *priv;
2099 g_return_val_if_fail (window != NULL, NULL);
2101 priv = GET_PRIV (window);
2103 return priv->dialog;
2106 /* Returns the window to open a new tab in if there is a suitable window,
2107 * otherwise, returns NULL indicating that a new window should be added.
2109 static EmpathyChatWindow *
2110 empathy_chat_window_get_default (gboolean room)
2112 GSettings *gsettings = g_settings_new (EMPATHY_PREFS_UI_SCHEMA);
2114 gboolean separate_windows = TRUE;
2116 separate_windows = g_settings_get_boolean (gsettings,
2117 EMPATHY_PREFS_UI_SEPARATE_CHAT_WINDOWS);
2119 g_object_unref (gsettings);
2121 if (separate_windows) {
2122 /* Always create a new window */
2126 for (l = chat_windows; l; l = l->next) {
2127 EmpathyChatWindowPriv *priv;
2128 EmpathyChatWindow *chat_window;
2130 guint nb_rooms, nb_private;
2132 chat_window = l->data;
2133 priv = GET_PRIV (chat_window);
2135 dialog = empathy_chat_window_get_dialog (chat_window);
2137 empathy_chat_window_get_nb_chats (chat_window, &nb_rooms, &nb_private);
2139 /* Skip the window if there aren't any rooms in it */
2140 if (room && nb_rooms == 0)
2143 /* Skip the window if there aren't any 1-1 chats in it */
2144 if (!room && nb_private == 0)
2147 /* Found a window on this desktop, make it visible if necessary */
2148 if (!empathy_window_get_is_visible (GTK_WINDOW (dialog)))
2149 empathy_window_present (GTK_WINDOW (dialog));
2157 empathy_chat_window_add_chat (EmpathyChatWindow *window,
2160 EmpathyChatWindowPriv *priv;
2162 GtkWidget *popup_label;
2164 GValue value = { 0, };
2166 g_return_if_fail (window != NULL);
2167 g_return_if_fail (EMPATHY_IS_CHAT (chat));
2169 priv = GET_PRIV (window);
2171 /* Reference the chat object */
2172 g_object_ref (chat);
2174 /* If this window has just been created, position it */
2175 if (priv->chats == NULL) {
2176 const gchar *name = "chat-window";
2177 gboolean separate_windows;
2179 separate_windows = g_settings_get_boolean (priv->gsettings_ui,
2180 EMPATHY_PREFS_UI_SEPARATE_CHAT_WINDOWS);
2182 if (empathy_chat_is_room (chat))
2183 name = "room-window";
2185 if (separate_windows) {
2188 /* Save current position of the window */
2189 gtk_window_get_position (GTK_WINDOW (priv->dialog), &x, &y);
2191 /* First bind to the 'generic' name. So new window for which we didn't
2192 * save a geometry yet will have the geometry of the last saved
2193 * window (bgo #601191). */
2194 empathy_geometry_bind (GTK_WINDOW (priv->dialog), name);
2196 /* Restore previous position of the window so the newly created window
2197 * won't be in the same position as the latest saved window and so
2198 * completely hide it. */
2199 gtk_window_move (GTK_WINDOW (priv->dialog), x, y);
2201 /* Then bind it to the name of the contact/room so we'll save the
2202 * geometry specific to this window */
2203 name = empathy_chat_get_id (chat);
2206 empathy_geometry_bind (GTK_WINDOW (priv->dialog), name);
2209 child = GTK_WIDGET (chat);
2210 label = chat_window_create_label (window, chat, TRUE);
2211 popup_label = chat_window_create_label (window, chat, FALSE);
2212 gtk_widget_show (child);
2214 g_signal_connect (chat, "notify::name",
2215 G_CALLBACK (chat_window_chat_notify_cb),
2217 g_signal_connect (chat, "notify::subject",
2218 G_CALLBACK (chat_window_chat_notify_cb),
2220 g_signal_connect (chat, "notify::remote-contact",
2221 G_CALLBACK (chat_window_chat_notify_cb),
2223 chat_window_chat_notify_cb (chat);
2225 gtk_notebook_append_page_menu (GTK_NOTEBOOK (priv->notebook), child, label, popup_label);
2226 gtk_notebook_set_tab_reorderable (GTK_NOTEBOOK (priv->notebook), child, TRUE);
2227 gtk_notebook_set_tab_detachable (GTK_NOTEBOOK (priv->notebook), child, TRUE);
2228 g_value_init (&value, G_TYPE_BOOLEAN);
2229 g_value_set_boolean (&value, TRUE);
2230 gtk_container_child_set_property (GTK_CONTAINER (priv->notebook),
2231 child, "tab-expand" , &value);
2232 gtk_container_child_set_property (GTK_CONTAINER (priv->notebook),
2233 child, "tab-fill" , &value);
2234 g_value_unset (&value);
2236 DEBUG ("Chat added (%d references)", G_OBJECT (chat)->ref_count);
2240 empathy_chat_window_remove_chat (EmpathyChatWindow *window,
2243 EmpathyChatWindowPriv *priv;
2245 EmpathyContact *remote_contact;
2246 EmpathyChatManager *chat_manager;
2248 g_return_if_fail (window != NULL);
2249 g_return_if_fail (EMPATHY_IS_CHAT (chat));
2251 priv = GET_PRIV (window);
2253 g_signal_handlers_disconnect_by_func (chat,
2254 chat_window_chat_notify_cb,
2256 remote_contact = g_object_get_data (G_OBJECT (chat),
2257 "chat-window-remote-contact");
2258 if (remote_contact) {
2259 g_signal_handlers_disconnect_by_func (remote_contact,
2260 chat_window_update_chat_tab,
2264 chat_manager = empathy_chat_manager_dup_singleton ();
2265 empathy_chat_manager_closed_chat (chat_manager, chat);
2266 g_object_unref (chat_manager);
2268 position = gtk_notebook_page_num (GTK_NOTEBOOK (priv->notebook),
2270 gtk_notebook_remove_page (GTK_NOTEBOOK (priv->notebook), position);
2272 DEBUG ("Chat removed (%d references)", G_OBJECT (chat)->ref_count - 1);
2274 g_object_unref (chat);
2278 empathy_chat_window_move_chat (EmpathyChatWindow *old_window,
2279 EmpathyChatWindow *new_window,
2284 g_return_if_fail (EMPATHY_IS_CHAT_WINDOW (old_window));
2285 g_return_if_fail (EMPATHY_IS_CHAT_WINDOW (new_window));
2286 g_return_if_fail (EMPATHY_IS_CHAT (chat));
2288 widget = GTK_WIDGET (chat);
2290 DEBUG ("Chat moving with widget:%p (%d references)", widget,
2291 G_OBJECT (widget)->ref_count);
2293 /* We reference here to make sure we don't loose the widget
2294 * and the EmpathyChat object during the move.
2296 g_object_ref (chat);
2297 g_object_ref (widget);
2299 empathy_chat_window_remove_chat (old_window, chat);
2300 empathy_chat_window_add_chat (new_window, chat);
2302 g_object_unref (widget);
2303 g_object_unref (chat);
2307 empathy_chat_window_switch_to_chat (EmpathyChatWindow *window,
2310 EmpathyChatWindowPriv *priv;
2313 g_return_if_fail (window != NULL);
2314 g_return_if_fail (EMPATHY_IS_CHAT (chat));
2316 priv = GET_PRIV (window);
2318 page_num = gtk_notebook_page_num (GTK_NOTEBOOK (priv->notebook),
2320 gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->notebook),
2325 empathy_chat_window_find_chat (TpAccount *account,
2330 g_return_val_if_fail (!EMP_STR_EMPTY (id), NULL);
2332 for (l = chat_windows; l; l = l->next) {
2333 EmpathyChatWindowPriv *priv;
2334 EmpathyChatWindow *window;
2338 priv = GET_PRIV (window);
2340 for (ll = priv->chats; ll; ll = ll->next) {
2345 if (account == empathy_chat_get_account (chat) &&
2346 !tp_strdiff (id, empathy_chat_get_id (chat))) {
2356 empathy_chat_window_present_chat (EmpathyChat *chat,
2359 EmpathyChatWindow *window;
2360 EmpathyChatWindowPriv *priv;
2361 guint32 x_timestamp;
2363 g_return_if_fail (EMPATHY_IS_CHAT (chat));
2365 window = chat_window_find_chat (chat);
2367 /* If the chat has no window, create one */
2368 if (window == NULL) {
2369 window = empathy_chat_window_get_default (empathy_chat_is_room (chat));
2371 window = empathy_chat_window_new ();
2372 gtk_widget_show_all (GET_PRIV (window)->dialog);
2375 empathy_chat_window_add_chat (window, chat);
2378 /* Don't force the window to show itself when it wasn't
2379 * an action by the user
2381 if (!tp_user_action_time_should_present (timestamp, &x_timestamp))
2384 priv = GET_PRIV (window);
2386 if (x_timestamp != GDK_CURRENT_TIME) {
2387 /* Don't present or switch tab if the action was earlier than the
2388 * last actions X time, accounting for overflow and the first ever
2391 if (priv->x_user_action_time != 0
2392 && X_EARLIER_OR_EQL (x_timestamp, priv->x_user_action_time))
2395 priv->x_user_action_time = x_timestamp;
2398 empathy_chat_window_switch_to_chat (window, chat);
2399 empathy_window_present_with_time (GTK_WINDOW (priv->dialog),
2402 gtk_widget_grab_focus (chat->input_text_view);
2406 empathy_chat_window_get_nb_chats (EmpathyChatWindow *self,
2410 EmpathyChatWindowPriv *priv = GET_PRIV (self);
2412 guint _nb_rooms = 0, _nb_private = 0;
2414 for (l = priv->chats; l != NULL; l = g_list_next (l)) {
2415 if (empathy_chat_is_room (EMPATHY_CHAT (l->data)))
2421 if (nb_rooms != NULL)
2422 *nb_rooms = _nb_rooms;
2423 if (nb_private != NULL)
2424 *nb_private = _nb_private;