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);
420 if (submenu != NULL) {
421 gtk_menu_item_set_submenu (GTK_MENU_ITEM (menu), submenu);
422 gtk_widget_show (menu);
423 gtk_widget_set_sensitive (menu, TRUE);
426 gtk_widget_set_sensitive (menu, FALSE);
429 tp_g_signal_connect_object (orig_submenu,
431 (GCallback)_submenu_notify_visible_changed_cb,
437 get_all_unread_messages (EmpathyChatWindowPriv *priv)
442 for (l = priv->chats; l != NULL; l = g_list_next (l))
443 nb += empathy_chat_get_nb_unread_messages (EMPATHY_CHAT (l->data));
449 get_window_title_name (EmpathyChatWindowPriv *priv)
451 const gchar *active_name;
453 guint current_unread_msgs;
455 nb_chats = g_list_length (priv->chats);
456 g_assert (nb_chats > 0);
458 active_name = empathy_chat_get_name (priv->current_chat);
460 current_unread_msgs = empathy_chat_get_nb_unread_messages (
465 if (current_unread_msgs == 0)
466 return g_strdup (active_name);
468 return g_strdup_printf (ngettext (
470 "%s (%d unread)", current_unread_msgs),
471 active_name, current_unread_msgs);
473 guint nb_others = nb_chats - 1;
474 guint all_unread_msgs;
476 all_unread_msgs = get_all_unread_messages (priv);
478 if (all_unread_msgs == 0) {
479 /* no unread message */
480 return g_strdup_printf (ngettext (
482 "%s (and %u others)", nb_others),
483 active_name, nb_others);
486 else if (all_unread_msgs == current_unread_msgs) {
487 /* unread messages are in the current tab */
488 return g_strdup_printf (ngettext (
490 "%s (%d unread)", current_unread_msgs),
491 active_name, current_unread_msgs);
494 else if (current_unread_msgs == 0) {
495 /* unread messages are in other tabs */
496 return g_strdup_printf (ngettext (
497 "%s (%d unread from others)",
498 "%s (%d unread from others)",
500 active_name, all_unread_msgs);
504 /* unread messages are in all the tabs */
505 return g_strdup_printf (ngettext (
506 "%s (%d unread from all)",
507 "%s (%d unread from all)",
509 active_name, all_unread_msgs);
515 chat_window_title_update (EmpathyChatWindowPriv *priv)
519 name = get_window_title_name (priv);
520 gtk_window_set_title (GTK_WINDOW (priv->dialog), name);
525 chat_window_icon_update (EmpathyChatWindowPriv *priv, gboolean new_messages)
528 EmpathyContact *remote_contact;
529 gboolean avatar_in_icon;
532 n_chats = g_list_length (priv->chats);
534 /* Update window icon */
536 gtk_window_set_icon_name (GTK_WINDOW (priv->dialog),
537 EMPATHY_IMAGE_MESSAGE);
539 avatar_in_icon = g_settings_get_boolean (priv->gsettings_chat,
540 EMPATHY_PREFS_CHAT_AVATAR_IN_ICON);
542 if (n_chats == 1 && avatar_in_icon) {
543 remote_contact = empathy_chat_get_remote_contact (priv->current_chat);
544 icon = empathy_pixbuf_avatar_from_contact_scaled (remote_contact, 0, 0);
545 gtk_window_set_icon (GTK_WINDOW (priv->dialog), icon);
548 g_object_unref (icon);
551 gtk_window_set_icon_name (GTK_WINDOW (priv->dialog), NULL);
557 chat_window_close_button_update (EmpathyChatWindowPriv *priv,
561 GtkWidget *chat_close_button;
564 if (num_pages == 1) {
565 chat = gtk_notebook_get_nth_page (GTK_NOTEBOOK (priv->notebook), 0);
566 chat_close_button = g_object_get_data (G_OBJECT (chat),
567 "chat-window-tab-close-button");
568 gtk_widget_hide (chat_close_button);
570 for (i=0; i<num_pages; i++) {
571 chat = gtk_notebook_get_nth_page (GTK_NOTEBOOK (priv->notebook), i);
572 chat_close_button = g_object_get_data (G_OBJECT (chat),
573 "chat-window-tab-close-button");
574 gtk_widget_show (chat_close_button);
580 chat_window_update (EmpathyChatWindow *window)
582 EmpathyChatWindowPriv *priv = GET_PRIV (window);
585 num_pages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (priv->notebook));
587 /* Update Tab menu */
588 chat_window_menu_context_update (priv,
591 chat_window_conversation_menu_update (priv, window);
593 chat_window_contact_menu_update (priv,
596 chat_window_title_update (priv);
598 chat_window_icon_update (priv, get_all_unread_messages (priv) > 0);
600 chat_window_close_button_update (priv,
605 append_markup_printf (GString *string,
612 va_start (args, format);
614 tmp = g_markup_vprintf_escaped (format, args);
615 g_string_append (string, tmp);
622 chat_window_update_chat_tab (EmpathyChat *chat)
624 EmpathyChatWindow *window;
625 EmpathyChatWindowPriv *priv;
626 EmpathyContact *remote_contact;
630 const gchar *subject;
631 const gchar *status = NULL;
635 const gchar *icon_name;
636 GtkWidget *tab_image;
637 GtkWidget *menu_image;
639 window = chat_window_find_chat (chat);
643 priv = GET_PRIV (window);
645 /* Get information */
646 name = empathy_chat_get_name (chat);
647 account = empathy_chat_get_account (chat);
648 subject = empathy_chat_get_subject (chat);
649 remote_contact = empathy_chat_get_remote_contact (chat);
651 DEBUG ("Updating chat tab, name=%s, account=%s, subject=%s, remote_contact=%p",
652 name, tp_proxy_get_object_path (account), subject, remote_contact);
654 /* Update tab image */
655 if (empathy_chat_get_tp_chat (chat) == NULL) {
656 /* No TpChat, we are disconnected */
659 else if (empathy_chat_get_nb_unread_messages (chat) > 0) {
660 icon_name = EMPATHY_IMAGE_MESSAGE;
662 else if (remote_contact && empathy_chat_is_composing (chat)) {
663 icon_name = EMPATHY_IMAGE_TYPING;
665 else if (remote_contact) {
666 icon_name = empathy_icon_name_for_contact (remote_contact);
668 icon_name = EMPATHY_IMAGE_GROUP_MESSAGE;
671 tab_image = g_object_get_data (G_OBJECT (chat), "chat-window-tab-image");
672 menu_image = g_object_get_data (G_OBJECT (chat), "chat-window-menu-image");
673 if (icon_name != NULL) {
674 gtk_image_set_from_icon_name (GTK_IMAGE (tab_image), icon_name, GTK_ICON_SIZE_MENU);
675 gtk_widget_show (tab_image);
676 gtk_image_set_from_icon_name (GTK_IMAGE (menu_image), icon_name, GTK_ICON_SIZE_MENU);
677 gtk_widget_show (menu_image);
679 gtk_widget_hide (tab_image);
680 gtk_widget_hide (menu_image);
683 /* Update tab tooltip */
684 tooltip = g_string_new (NULL);
686 if (remote_contact) {
687 id = empathy_contact_get_id (remote_contact);
688 status = empathy_contact_get_presence_message (remote_contact);
693 append_markup_printf (tooltip,
694 "<b>%s</b><small> (%s)</small>",
696 tp_account_get_display_name (account));
698 if (!EMP_STR_EMPTY (status)) {
699 append_markup_printf (tooltip, "\n<i>%s</i>", status);
703 append_markup_printf (tooltip, "\n<b>%s</b> %s",
704 _("Topic:"), subject);
707 if (remote_contact && empathy_chat_is_composing (chat)) {
708 append_markup_printf (tooltip, "\n%s", _("Typing a message."));
711 markup = g_string_free (tooltip, FALSE);
712 widget = g_object_get_data (G_OBJECT (chat), "chat-window-tab-tooltip-widget");
713 gtk_widget_set_tooltip_markup (widget, markup);
714 widget = g_object_get_data (G_OBJECT (chat), "chat-window-menu-tooltip-widget");
715 gtk_widget_set_tooltip_markup (widget, markup);
718 /* Update tab and menu label */
719 widget = g_object_get_data (G_OBJECT (chat), "chat-window-tab-label");
720 gtk_label_set_text (GTK_LABEL (widget), name);
721 widget = g_object_get_data (G_OBJECT (chat), "chat-window-menu-label");
722 gtk_label_set_text (GTK_LABEL (widget), name);
724 /* Update the window if it's the current chat */
725 if (priv->current_chat == chat) {
726 chat_window_update (window);
731 chat_window_chat_notify_cb (EmpathyChat *chat)
733 EmpathyContact *old_remote_contact;
734 EmpathyContact *remote_contact = NULL;
736 old_remote_contact = g_object_get_data (G_OBJECT (chat), "chat-window-remote-contact");
737 remote_contact = empathy_chat_get_remote_contact (chat);
739 if (old_remote_contact != remote_contact) {
740 /* The remote-contact associated with the chat changed, we need
741 * to keep track of any change of that contact and update the
742 * window each time. */
743 if (remote_contact) {
744 g_signal_connect_swapped (remote_contact, "notify",
745 G_CALLBACK (chat_window_update_chat_tab),
748 if (old_remote_contact) {
749 g_signal_handlers_disconnect_by_func (old_remote_contact,
750 chat_window_update_chat_tab,
754 g_object_set_data_full (G_OBJECT (chat), "chat-window-remote-contact",
755 g_object_ref (remote_contact), (GDestroyNotify) g_object_unref);
758 chat_window_update_chat_tab (chat);
762 chat_window_insert_smiley_activate_cb (EmpathySmileyManager *manager,
763 EmpathySmiley *smiley,
766 EmpathyChatWindowPriv *priv = GET_PRIV (window);
768 GtkTextBuffer *buffer;
771 chat = priv->current_chat;
773 buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (chat->input_text_view));
774 gtk_text_buffer_get_end_iter (buffer, &iter);
775 gtk_text_buffer_insert (buffer, &iter, smiley->str, -1);
779 chat_window_conv_activate_cb (GtkAction *action,
780 EmpathyChatWindow *window)
782 EmpathyChatWindowPriv *priv = GET_PRIV (window);
785 EmpathyContact *remote_contact = NULL;
787 /* Favorite room menu */
788 is_room = empathy_chat_is_room (priv->current_chat);
792 gboolean found = FALSE;
793 EmpathyChatroom *chatroom;
795 room = empathy_chat_get_id (priv->current_chat);
796 account = empathy_chat_get_account (priv->current_chat);
797 chatroom = empathy_chatroom_manager_find (priv->chatroom_manager,
799 if (chatroom != NULL)
800 found = empathy_chatroom_is_favorite (chatroom);
802 DEBUG ("This room %s favorite", found ? "is" : "is not");
803 gtk_toggle_action_set_active (
804 GTK_TOGGLE_ACTION (priv->menu_conv_favorite), found);
806 if (chatroom != NULL)
807 found = empathy_chatroom_is_always_urgent (chatroom);
809 gtk_toggle_action_set_active (
810 GTK_TOGGLE_ACTION (priv->menu_conv_always_urgent),
813 gtk_action_set_visible (priv->menu_conv_favorite, is_room);
814 gtk_action_set_visible (priv->menu_conv_always_urgent, is_room);
816 /* Show contacts menu */
817 g_object_get (priv->current_chat,
818 "remote-contact", &remote_contact,
819 "show-contacts", &active,
821 if (remote_contact == NULL) {
822 gtk_toggle_action_set_active (
823 GTK_TOGGLE_ACTION (priv->menu_conv_toggle_contacts),
826 gtk_action_set_visible (priv->menu_conv_toggle_contacts,
827 (remote_contact == NULL));
828 if (remote_contact != NULL) {
829 g_object_unref (remote_contact);
834 chat_window_clear_activate_cb (GtkAction *action,
835 EmpathyChatWindow *window)
837 EmpathyChatWindowPriv *priv = GET_PRIV (window);
839 empathy_chat_clear (priv->current_chat);
843 chat_window_favorite_toggled_cb (GtkToggleAction *toggle_action,
844 EmpathyChatWindow *window)
846 EmpathyChatWindowPriv *priv = GET_PRIV (window);
850 EmpathyChatroom *chatroom;
852 active = gtk_toggle_action_get_active (toggle_action);
853 account = empathy_chat_get_account (priv->current_chat);
854 room = empathy_chat_get_id (priv->current_chat);
856 chatroom = empathy_chatroom_manager_ensure_chatroom (
857 priv->chatroom_manager,
860 empathy_chat_get_name (priv->current_chat));
862 empathy_chatroom_set_favorite (chatroom, active);
863 g_object_unref (chatroom);
867 chat_window_always_urgent_toggled_cb (GtkToggleAction *toggle_action,
868 EmpathyChatWindow *window)
870 EmpathyChatWindowPriv *priv = GET_PRIV (window);
874 EmpathyChatroom *chatroom;
876 active = gtk_toggle_action_get_active (toggle_action);
877 account = empathy_chat_get_account (priv->current_chat);
878 room = empathy_chat_get_id (priv->current_chat);
880 chatroom = empathy_chatroom_manager_ensure_chatroom (
881 priv->chatroom_manager,
884 empathy_chat_get_name (priv->current_chat));
886 empathy_chatroom_set_always_urgent (chatroom, active);
887 g_object_unref (chatroom);
891 chat_window_contacts_toggled_cb (GtkToggleAction *toggle_action,
892 EmpathyChatWindow *window)
894 EmpathyChatWindowPriv *priv = GET_PRIV (window);
897 active = gtk_toggle_action_get_active (toggle_action);
899 empathy_chat_set_show_contacts (priv->current_chat, active);
903 got_contact_cb (TpConnection *connection,
904 EmpathyContact *contact,
909 EmpathyTpChat *tp_chat = EMPATHY_TP_CHAT (user_data);
912 DEBUG ("Failed: %s", error->message);
915 empathy_contact_list_add (EMPATHY_CONTACT_LIST (tp_chat),
916 contact, _("Inviting you to this room"));
921 chat_window_invite_participant_activate_cb (GtkAction *action,
922 EmpathyChatWindow *window)
924 EmpathyChatWindowPriv *priv;
926 EmpathyTpChat *tp_chat;
931 priv = GET_PRIV (window);
933 g_return_if_fail (priv->current_chat != NULL);
935 tp_chat = empathy_chat_get_tp_chat (priv->current_chat);
936 channel = empathy_tp_chat_get_channel (tp_chat);
937 account = empathy_chat_get_account (priv->current_chat);
939 dialog = empathy_invite_participant_dialog_new (
940 GTK_WINDOW (priv->dialog), account);
941 gtk_widget_show (dialog);
943 response = gtk_dialog_run (GTK_DIALOG (dialog));
945 if (response == GTK_RESPONSE_ACCEPT) {
946 TpConnection *connection;
949 id = empathy_contact_selector_dialog_get_selected (
950 EMPATHY_CONTACT_SELECTOR_DIALOG (dialog), NULL, NULL);
951 if (EMP_STR_EMPTY (id)) goto out;
953 connection = tp_channel_borrow_connection (channel);
954 empathy_tp_contact_factory_get_from_id (connection, id,
955 got_contact_cb, tp_chat, NULL, NULL);
959 gtk_widget_destroy (dialog);
963 chat_window_close_activate_cb (GtkAction *action,
964 EmpathyChatWindow *window)
966 EmpathyChatWindowPriv *priv;
968 priv = GET_PRIV (window);
970 g_return_if_fail (priv->current_chat != NULL);
972 empathy_chat_window_remove_chat (window, priv->current_chat);
976 chat_window_edit_activate_cb (GtkAction *action,
977 EmpathyChatWindow *window)
979 EmpathyChatWindowPriv *priv;
980 GtkClipboard *clipboard;
981 GtkTextBuffer *buffer;
982 gboolean text_available;
984 priv = GET_PRIV (window);
986 g_return_if_fail (priv->current_chat != NULL);
988 if (!empathy_chat_get_tp_chat (priv->current_chat)) {
989 gtk_action_set_sensitive (priv->menu_edit_copy, FALSE);
990 gtk_action_set_sensitive (priv->menu_edit_cut, FALSE);
991 gtk_action_set_sensitive (priv->menu_edit_paste, FALSE);
995 buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (priv->current_chat->input_text_view));
996 if (gtk_text_buffer_get_has_selection (buffer)) {
997 gtk_action_set_sensitive (priv->menu_edit_copy, TRUE);
998 gtk_action_set_sensitive (priv->menu_edit_cut, TRUE);
1002 selection = empathy_chat_view_get_has_selection (priv->current_chat->view);
1004 gtk_action_set_sensitive (priv->menu_edit_cut, FALSE);
1005 gtk_action_set_sensitive (priv->menu_edit_copy, selection);
1008 clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
1009 text_available = gtk_clipboard_wait_is_text_available (clipboard);
1010 gtk_action_set_sensitive (priv->menu_edit_paste, text_available);
1014 chat_window_cut_activate_cb (GtkAction *action,
1015 EmpathyChatWindow *window)
1017 EmpathyChatWindowPriv *priv;
1019 g_return_if_fail (EMPATHY_IS_CHAT_WINDOW (window));
1021 priv = GET_PRIV (window);
1023 empathy_chat_cut (priv->current_chat);
1027 chat_window_copy_activate_cb (GtkAction *action,
1028 EmpathyChatWindow *window)
1030 EmpathyChatWindowPriv *priv;
1032 g_return_if_fail (EMPATHY_IS_CHAT_WINDOW (window));
1034 priv = GET_PRIV (window);
1036 empathy_chat_copy (priv->current_chat);
1040 chat_window_paste_activate_cb (GtkAction *action,
1041 EmpathyChatWindow *window)
1043 EmpathyChatWindowPriv *priv;
1045 g_return_if_fail (EMPATHY_IS_CHAT_WINDOW (window));
1047 priv = GET_PRIV (window);
1049 empathy_chat_paste (priv->current_chat);
1053 chat_window_find_activate_cb (GtkAction *action,
1054 EmpathyChatWindow *window)
1056 EmpathyChatWindowPriv *priv;
1058 g_return_if_fail (EMPATHY_IS_CHAT_WINDOW (window));
1060 priv = GET_PRIV (window);
1062 empathy_chat_find (priv->current_chat);
1066 chat_window_tabs_next_activate_cb (GtkAction *action,
1067 EmpathyChatWindow *window)
1069 EmpathyChatWindowPriv *priv;
1071 gint index_, numPages;
1072 gboolean wrap_around;
1074 priv = GET_PRIV (window);
1076 g_object_get (gtk_settings_get_default (), "gtk-keynav-wrap-around",
1077 &wrap_around, NULL);
1079 chat = priv->current_chat;
1080 index_ = gtk_notebook_get_current_page (GTK_NOTEBOOK (priv->notebook));
1081 numPages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (priv->notebook));
1083 if (index_ == (numPages - 1) && wrap_around) {
1084 gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->notebook), 0);
1088 gtk_notebook_next_page (GTK_NOTEBOOK (priv->notebook));
1092 chat_window_tabs_previous_activate_cb (GtkAction *action,
1093 EmpathyChatWindow *window)
1095 EmpathyChatWindowPriv *priv;
1097 gint index_, numPages;
1098 gboolean wrap_around;
1100 priv = GET_PRIV (window);
1102 g_object_get (gtk_settings_get_default (), "gtk-keynav-wrap-around",
1103 &wrap_around, NULL);
1105 chat = priv->current_chat;
1106 index_ = gtk_notebook_get_current_page (GTK_NOTEBOOK (priv->notebook));
1107 numPages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (priv->notebook));
1109 if (index_ <= 0 && wrap_around) {
1110 gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->notebook), numPages - 1);
1114 gtk_notebook_prev_page (GTK_NOTEBOOK (priv->notebook));
1118 chat_window_tabs_undo_close_tab_activate_cb (GtkAction *action,
1119 EmpathyChatWindow *window)
1121 EmpathyChatWindowPriv *priv = GET_PRIV (window);
1122 empathy_chat_manager_undo_closed_chat (priv->chat_manager);
1126 chat_window_tabs_left_activate_cb (GtkAction *action,
1127 EmpathyChatWindow *window)
1129 EmpathyChatWindowPriv *priv;
1131 gint index_, num_pages;
1133 priv = GET_PRIV (window);
1135 chat = priv->current_chat;
1136 index_ = gtk_notebook_get_current_page (GTK_NOTEBOOK (priv->notebook));
1141 gtk_notebook_reorder_child (GTK_NOTEBOOK (priv->notebook),
1145 num_pages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (priv->notebook));
1146 chat_window_menu_context_update (priv, num_pages);
1150 chat_window_tabs_right_activate_cb (GtkAction *action,
1151 EmpathyChatWindow *window)
1153 EmpathyChatWindowPriv *priv;
1155 gint index_, num_pages;
1157 priv = GET_PRIV (window);
1159 chat = priv->current_chat;
1160 index_ = gtk_notebook_get_current_page (GTK_NOTEBOOK (priv->notebook));
1162 gtk_notebook_reorder_child (GTK_NOTEBOOK (priv->notebook),
1166 num_pages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (priv->notebook));
1167 chat_window_menu_context_update (priv, num_pages);
1170 static EmpathyChatWindow *
1171 empathy_chat_window_new (void)
1173 return EMPATHY_CHAT_WINDOW (g_object_new (EMPATHY_TYPE_CHAT_WINDOW, NULL));
1177 chat_window_detach_activate_cb (GtkAction *action,
1178 EmpathyChatWindow *window)
1180 EmpathyChatWindowPriv *priv;
1181 EmpathyChatWindow *new_window;
1184 priv = GET_PRIV (window);
1186 chat = priv->current_chat;
1187 new_window = empathy_chat_window_new ();
1189 empathy_chat_window_move_chat (window, new_window, chat);
1191 priv = GET_PRIV (new_window);
1192 gtk_widget_show (priv->dialog);
1196 chat_window_help_contents_activate_cb (GtkAction *action,
1197 EmpathyChatWindow *window)
1199 EmpathyChatWindowPriv *priv = GET_PRIV (window);
1201 empathy_url_show (priv->dialog, "ghelp:empathy");
1205 chat_window_help_about_activate_cb (GtkAction *action,
1206 EmpathyChatWindow *window)
1208 EmpathyChatWindowPriv *priv = GET_PRIV (window);
1210 empathy_about_dialog_new (GTK_WINDOW (priv->dialog));
1214 chat_window_delete_event_cb (GtkWidget *dialog,
1216 EmpathyChatWindow *window)
1218 EmpathyChatWindowPriv *priv = GET_PRIV (window);
1220 DEBUG ("Delete event received");
1222 g_object_ref (window);
1223 while (priv->chats) {
1224 empathy_chat_window_remove_chat (window, priv->chats->data);
1226 g_object_unref (window);
1232 chat_window_composing_cb (EmpathyChat *chat,
1233 gboolean is_composing,
1234 EmpathyChatWindow *window)
1236 chat_window_update_chat_tab (chat);
1240 chat_window_set_urgency_hint (EmpathyChatWindow *window,
1243 EmpathyChatWindowPriv *priv;
1245 priv = GET_PRIV (window);
1247 gtk_window_set_urgency_hint (GTK_WINDOW (priv->dialog), urgent);
1251 chat_window_notification_closed_cb (NotifyNotification *notify,
1252 EmpathyChatWindow *self)
1254 EmpathyChatWindowPriv *priv = GET_PRIV (self);
1256 g_object_unref (notify);
1257 if (priv->notification == notify) {
1258 priv->notification = NULL;
1263 chat_window_show_or_update_notification (EmpathyChatWindow *window,
1264 EmpathyMessage *message,
1267 EmpathyContact *sender;
1268 const gchar *header;
1272 EmpathyChatWindowPriv *priv = GET_PRIV (window);
1273 gboolean res, has_x_canonical_append;
1274 NotifyNotification *notification = priv->notification;
1276 if (!empathy_notify_manager_notification_is_enabled (priv->notify_mgr)) {
1279 res = g_settings_get_boolean (priv->gsettings_notif,
1280 EMPATHY_PREFS_NOTIFICATIONS_FOCUS);
1287 sender = empathy_message_get_sender (message);
1288 header = empathy_contact_get_alias (sender);
1289 body = empathy_message_get_body (message);
1290 escaped = g_markup_escape_text (body, -1);
1291 has_x_canonical_append = empathy_notify_manager_has_capability (
1292 priv->notify_mgr, EMPATHY_NOTIFY_MANAGER_CAP_X_CANONICAL_APPEND);
1294 if (notification != NULL && !has_x_canonical_append) {
1295 /* if the notification server supports x-canonical-append, it is
1296 better to not use notify_notification_update to avoid
1297 overwriting the current notification message */
1298 notify_notification_update (notification,
1299 header, escaped, NULL);
1301 /* if the notification server supports x-canonical-append,
1302 the hint will be added, so that the message from the
1303 just created notification will be automatically appended
1304 to an existing notification with the same title.
1305 In this way the previous message will not be lost: the new
1306 message will appear below it, in the same notification */
1307 notification = notify_notification_new (header, escaped, NULL);
1309 if (priv->notification == NULL) {
1310 priv->notification = notification;
1313 notify_notification_set_timeout (notification, NOTIFY_EXPIRES_DEFAULT);
1315 tp_g_signal_connect_object (notification, "closed",
1316 G_CALLBACK (chat_window_notification_closed_cb), window, 0);
1318 if (has_x_canonical_append) {
1319 notify_notification_set_hint_string (notification,
1320 EMPATHY_NOTIFY_MANAGER_CAP_X_CANONICAL_APPEND, "");
1324 pixbuf = empathy_notify_manager_get_pixbuf_for_notification (priv->notify_mgr,
1325 sender, EMPATHY_IMAGE_NEW_MESSAGE);
1327 if (pixbuf != NULL) {
1328 notify_notification_set_icon_from_pixbuf (notification, pixbuf);
1329 g_object_unref (pixbuf);
1332 notify_notification_show (notification, NULL);
1338 chat_window_set_highlight_room_labels (EmpathyChat *chat)
1343 if (!empathy_chat_is_room (chat))
1346 markup = g_markup_printf_escaped (
1347 "<span color=\"red\" weight=\"bold\">%s</span>",
1348 empathy_chat_get_name (chat));
1350 widget = g_object_get_data (G_OBJECT (chat), "chat-window-tab-label");
1351 gtk_label_set_markup (GTK_LABEL (widget), markup);
1353 widget = g_object_get_data (G_OBJECT (chat), "chat-window-menu-label");
1354 gtk_label_set_markup (GTK_LABEL (widget), markup);
1360 empathy_chat_window_has_focus (EmpathyChatWindow *window)
1362 EmpathyChatWindowPriv *priv;
1365 g_return_val_if_fail (EMPATHY_IS_CHAT_WINDOW (window), FALSE);
1367 priv = GET_PRIV (window);
1369 g_object_get (priv->dialog, "has-toplevel-focus", &has_focus, NULL);
1375 chat_window_new_message_cb (EmpathyChat *chat,
1376 EmpathyMessage *message,
1378 EmpathyChatWindow *window)
1380 EmpathyChatWindowPriv *priv;
1382 gboolean needs_urgency;
1383 EmpathyContact *sender;
1385 priv = GET_PRIV (window);
1387 has_focus = empathy_chat_window_has_focus (window);
1389 /* - if we're the sender, we play the sound if it's specified in the
1390 * preferences and we're not away.
1391 * - if we receive a message, we play the sound if it's specified in the
1392 * preferences and the window does not have focus on the chat receiving
1396 sender = empathy_message_get_sender (message);
1398 if (empathy_contact_is_user (sender)) {
1399 empathy_sound_manager_play (priv->sound_mgr, GTK_WIDGET (priv->dialog),
1400 EMPATHY_SOUND_MESSAGE_OUTGOING);
1403 if (has_focus && priv->current_chat == chat) {
1404 /* window and tab are focused so consider the message to be read */
1406 /* FIXME: see Bug#610994 and coments about it in EmpathyChatPriv */
1407 empathy_chat_messages_read (chat);
1411 /* Update the chat tab if this is the first unread message */
1412 if (empathy_chat_get_nb_unread_messages (chat) == 1) {
1413 chat_window_update_chat_tab (chat);
1416 /* If empathy_chat_is_room () returns TRUE, that means it's a named MUC.
1417 * If empathy_chat_get_remote_contact () returns NULL, that means it's
1418 * an unamed MUC (msn-like).
1419 * In case of a MUC, we set urgency if either:
1420 * a) the chatroom's always_urgent property is TRUE
1421 * b) the message contains our alias
1423 if (empathy_chat_is_room (chat) ||
1424 empathy_chat_get_remote_contact (chat) == NULL) {
1427 EmpathyChatroom *chatroom;
1429 account = empathy_chat_get_account (chat);
1430 room = empathy_chat_get_id (chat);
1432 chatroom = empathy_chatroom_manager_find (priv->chatroom_manager,
1435 if (empathy_chatroom_is_always_urgent (chatroom)) {
1436 needs_urgency = TRUE;
1438 needs_urgency = empathy_message_should_highlight (message);
1441 needs_urgency = TRUE;
1444 if (needs_urgency) {
1445 chat_window_set_highlight_room_labels (chat);
1448 chat_window_set_urgency_hint (window, TRUE);
1451 /* Pending messages have already been displayed and notified in the
1452 * approver, so we don't display a notification and play a sound for those */
1454 empathy_sound_manager_play (priv->sound_mgr, GTK_WIDGET (priv->dialog),
1455 EMPATHY_SOUND_MESSAGE_INCOMING);
1457 chat_window_show_or_update_notification (window, message, chat);
1461 /* update the number of unread messages and the window icon */
1462 chat_window_title_update (priv);
1463 chat_window_icon_update (priv, TRUE);
1466 static GtkNotebook *
1467 notebook_create_window_cb (GtkNotebook *source,
1473 EmpathyChatWindowPriv *priv;
1474 EmpathyChatWindow *window, *new_window;
1477 chat = EMPATHY_CHAT (page);
1478 window = chat_window_find_chat (chat);
1480 new_window = empathy_chat_window_new ();
1481 priv = GET_PRIV (new_window);
1483 DEBUG ("Detach hook called");
1485 empathy_chat_window_move_chat (window, new_window, chat);
1487 gtk_widget_show (priv->dialog);
1488 gtk_window_move (GTK_WINDOW (priv->dialog), x, y);
1494 chat_window_page_switched_cb (GtkNotebook *notebook,
1495 gpointer ignore, /* see note below */
1497 EmpathyChatWindow *window)
1499 EmpathyChatWindowPriv *priv;
1503 DEBUG ("Page switched");
1505 priv = GET_PRIV (window);
1507 /* N.B. in GTK+ 3 child is passed as the first argument to the signal,
1508 * but we can't use that while trying to retain GTK+ 2.x compatibility.
1510 child = gtk_notebook_get_nth_page (notebook, page_num);
1511 chat = EMPATHY_CHAT (child);
1513 if (priv->page_added) {
1514 priv->page_added = FALSE;
1515 empathy_chat_scroll_down (chat);
1517 else if (priv->current_chat == chat) {
1521 priv->current_chat = chat;
1522 empathy_chat_messages_read (chat);
1524 chat_window_update_chat_tab (chat);
1528 chat_window_page_added_cb (GtkNotebook *notebook,
1531 EmpathyChatWindow *window)
1533 EmpathyChatWindowPriv *priv;
1536 priv = GET_PRIV (window);
1538 /* If we just received DND to the same window, we don't want
1539 * to do anything here like removing the tab and then readding
1540 * it, so we return here and in "page-added".
1542 if (priv->dnd_same_window) {
1543 DEBUG ("Page added (back to the same window)");
1544 priv->dnd_same_window = FALSE;
1548 DEBUG ("Page added");
1550 /* Get chat object */
1551 chat = EMPATHY_CHAT (child);
1553 /* Connect chat signals for this window */
1554 g_signal_connect (chat, "composing",
1555 G_CALLBACK (chat_window_composing_cb),
1557 g_signal_connect (chat, "new-message",
1558 G_CALLBACK (chat_window_new_message_cb),
1560 g_signal_connect (chat, "notify::tp-chat",
1561 G_CALLBACK (chat_window_update_chat_tab),
1564 /* Set flag so we know to perform some special operations on
1565 * switch page due to the new page being added.
1567 priv->page_added = TRUE;
1569 /* Get list of chats up to date */
1570 priv->chats = g_list_append (priv->chats, chat);
1572 chat_window_update_chat_tab (chat);
1576 chat_window_page_removed_cb (GtkNotebook *notebook,
1579 EmpathyChatWindow *window)
1581 EmpathyChatWindowPriv *priv;
1584 priv = GET_PRIV (window);
1586 /* If we just received DND to the same window, we don't want
1587 * to do anything here like removing the tab and then readding
1588 * it, so we return here and in "page-added".
1590 if (priv->dnd_same_window) {
1591 DEBUG ("Page removed (and will be readded to same window)");
1595 DEBUG ("Page removed");
1597 /* Get chat object */
1598 chat = EMPATHY_CHAT (child);
1600 /* Disconnect all signal handlers for this chat and this window */
1601 g_signal_handlers_disconnect_by_func (chat,
1602 G_CALLBACK (chat_window_composing_cb),
1604 g_signal_handlers_disconnect_by_func (chat,
1605 G_CALLBACK (chat_window_new_message_cb),
1607 g_signal_handlers_disconnect_by_func (chat,
1608 G_CALLBACK (chat_window_update_chat_tab),
1611 /* Keep list of chats up to date */
1612 priv->chats = g_list_remove (priv->chats, chat);
1613 empathy_chat_messages_read (chat);
1615 if (priv->chats == NULL) {
1616 g_object_unref (window);
1618 chat_window_update (window);
1623 chat_window_focus_in_event_cb (GtkWidget *widget,
1625 EmpathyChatWindow *window)
1627 EmpathyChatWindowPriv *priv;
1629 priv = GET_PRIV (window);
1631 empathy_chat_messages_read (priv->current_chat);
1633 chat_window_set_urgency_hint (window, FALSE);
1635 /* Update the title, since we now mark all unread messages as read. */
1636 chat_window_update_chat_tab (priv->current_chat);
1642 chat_window_drag_drop (GtkWidget *widget,
1643 GdkDragContext *context,
1647 EmpathyChatWindow *window)
1650 EmpathyChatWindowPriv *priv;
1652 priv = GET_PRIV (window);
1654 target = gtk_drag_dest_find_target (widget, context, priv->file_targets);
1655 if (target == GDK_NONE)
1656 target = gtk_drag_dest_find_target (widget, context, priv->contact_targets);
1658 if (target != GDK_NONE) {
1659 gtk_drag_get_data (widget, context, target, time_);
1667 chat_window_drag_motion (GtkWidget *widget,
1668 GdkDragContext *context,
1672 EmpathyChatWindow *window)
1675 EmpathyChatWindowPriv *priv;
1677 priv = GET_PRIV (window);
1679 target = gtk_drag_dest_find_target (widget, context, priv->file_targets);
1680 if (target != GDK_NONE) {
1681 /* This is a file drag. Ensure the contact is online and set the
1682 drag type to COPY. Note that it's possible that the tab will
1683 be switched by GTK+ after a timeout from drag_motion without
1684 getting another drag_motion to disable the drop. You have
1685 to hold your mouse really still.
1687 EmpathyContact *contact;
1689 priv = GET_PRIV (window);
1690 contact = empathy_chat_get_remote_contact (priv->current_chat);
1691 /* contact is NULL for multi-user chats. We don't do
1692 * file transfers to MUCs. We also don't send files
1693 * to offline contacts or contacts that don't support
1696 if ((contact == NULL) || !empathy_contact_is_online (contact)) {
1697 gdk_drag_status (context, 0, time_);
1700 if (!(empathy_contact_get_capabilities (contact)
1701 & EMPATHY_CAPABILITIES_FT)) {
1702 gdk_drag_status (context, 0, time_);
1705 gdk_drag_status (context, GDK_ACTION_COPY, time_);
1709 target = gtk_drag_dest_find_target (widget, context, priv->contact_targets);
1710 if (target != GDK_NONE) {
1711 /* This is a drag of a contact from a contact list. Set to COPY.
1712 FIXME: If this drag is to a MUC window, it invites the user.
1713 Otherwise, it opens a chat. Should we use a different drag
1714 type for invites? Should we allow ASK?
1716 gdk_drag_status (context, GDK_ACTION_COPY, time_);
1724 chat_window_drag_data_received (GtkWidget *widget,
1725 GdkDragContext *context,
1728 GtkSelectionData *selection,
1731 EmpathyChatWindow *window)
1733 if (info == DND_DRAG_TYPE_CONTACT_ID) {
1734 EmpathyChat *chat = NULL;
1735 EmpathyChatWindow *old_window;
1736 TpAccount *account = NULL;
1737 TpAccountManager *account_manager;
1740 const gchar *account_id;
1741 const gchar *contact_id;
1743 id = (const gchar*) gtk_selection_data_get_data (selection);
1745 /* FIXME: Perhaps should be sure that the account manager is
1746 * prepared before calling _ensure_account on it. */
1747 account_manager = tp_account_manager_dup ();
1749 DEBUG ("DND contact from roster with id:'%s'", id);
1751 strv = g_strsplit (id, ":", 2);
1752 if (g_strv_length (strv) == 2) {
1753 account_id = strv[0];
1754 contact_id = strv[1];
1756 tp_account_manager_ensure_account (account_manager, account_id);
1757 if (account != NULL)
1758 chat = empathy_chat_window_find_chat (account, contact_id);
1761 if (account == NULL) {
1763 gtk_drag_finish (context, FALSE, FALSE, time_);
1768 empathy_dispatcher_chat_with_contact_id (
1769 account, contact_id, gtk_get_current_event_time ());
1774 g_object_unref (account_manager);
1777 old_window = chat_window_find_chat (chat);
1779 if (old_window == window) {
1780 gtk_drag_finish (context, TRUE, FALSE, time_);
1784 empathy_chat_window_move_chat (old_window, window, chat);
1786 empathy_chat_window_add_chat (window, chat);
1789 /* Added to take care of any outstanding chat events */
1790 empathy_chat_window_present_chat (chat,
1791 TP_USER_ACTION_TIME_NOT_USER_ACTION);
1793 /* We should return TRUE to remove the data when doing
1794 * GDK_ACTION_MOVE, but we don't here otherwise it has
1795 * weird consequences, and we handle that internally
1796 * anyway with add_chat () and remove_chat ().
1798 gtk_drag_finish (context, TRUE, FALSE, time_);
1800 else if (info == DND_DRAG_TYPE_URI_LIST) {
1801 EmpathyChatWindowPriv *priv;
1802 EmpathyContact *contact;
1805 priv = GET_PRIV (window);
1806 contact = empathy_chat_get_remote_contact (priv->current_chat);
1808 /* contact is NULL when current_chat is a multi-user chat.
1809 * We don't do file transfers to MUCs, so just cancel the drag.
1811 if (contact == NULL) {
1812 gtk_drag_finish (context, TRUE, FALSE, time_);
1816 data = (const gchar *) gtk_selection_data_get_data (selection);
1817 empathy_send_file_from_uri_list (contact, data);
1819 gtk_drag_finish (context, TRUE, FALSE, time_);
1821 else if (info == DND_DRAG_TYPE_TAB) {
1823 EmpathyChatWindow *old_window = NULL;
1827 chat = (void *) gtk_selection_data_get_data (selection);
1828 old_window = chat_window_find_chat (*chat);
1831 EmpathyChatWindowPriv *priv;
1833 priv = GET_PRIV (window);
1834 priv->dnd_same_window = (old_window == window);
1835 DEBUG ("DND tab (within same window: %s)",
1836 priv->dnd_same_window ? "Yes" : "No");
1839 DEBUG ("DND from unknown source");
1840 gtk_drag_finish (context, FALSE, FALSE, time_);
1845 chat_window_chat_manager_chats_changed_cb (EmpathyChatManager *chat_manager,
1846 guint num_chats_in_manager,
1847 EmpathyChatWindow *window)
1849 EmpathyChatWindowPriv *priv = GET_PRIV (window);
1851 gtk_action_set_sensitive (priv->menu_tabs_undo_close_tab,
1852 num_chats_in_manager > 0);
1856 chat_window_finalize (GObject *object)
1858 EmpathyChatWindow *window;
1859 EmpathyChatWindowPriv *priv;
1861 window = EMPATHY_CHAT_WINDOW (object);
1862 priv = GET_PRIV (window);
1864 DEBUG ("Finalized: %p", object);
1866 g_object_unref (priv->ui_manager);
1867 g_object_unref (priv->chatroom_manager);
1868 g_object_unref (priv->notify_mgr);
1869 g_object_unref (priv->gsettings_chat);
1870 g_object_unref (priv->gsettings_notif);
1871 g_object_unref (priv->gsettings_ui);
1872 g_object_unref (priv->sound_mgr);
1874 if (priv->notification != NULL) {
1875 notify_notification_close (priv->notification, NULL);
1876 priv->notification = NULL;
1879 if (priv->contact_targets) {
1880 gtk_target_list_unref (priv->contact_targets);
1882 if (priv->file_targets) {
1883 gtk_target_list_unref (priv->file_targets);
1886 if (priv->chat_manager) {
1887 g_signal_handler_disconnect (priv->chat_manager,
1888 priv->chat_manager_chats_changed_id);
1889 g_object_unref (priv->chat_manager);
1890 priv->chat_manager = NULL;
1893 chat_windows = g_list_remove (chat_windows, window);
1894 gtk_widget_destroy (priv->dialog);
1896 G_OBJECT_CLASS (empathy_chat_window_parent_class)->finalize (object);
1900 empathy_chat_window_class_init (EmpathyChatWindowClass *klass)
1902 GObjectClass *object_class = G_OBJECT_CLASS (klass);
1904 object_class->finalize = chat_window_finalize;
1906 g_type_class_add_private (object_class, sizeof (EmpathyChatWindowPriv));
1910 empathy_chat_window_init (EmpathyChatWindow *window)
1913 GtkAccelGroup *accel_group;
1918 GtkWidget *chat_vbox;
1920 EmpathySmileyManager *smiley_manager;
1921 EmpathyChatWindowPriv *priv = G_TYPE_INSTANCE_GET_PRIVATE (window,
1922 EMPATHY_TYPE_CHAT_WINDOW, EmpathyChatWindowPriv);
1924 window->priv = priv;
1925 filename = empathy_file_lookup ("empathy-chat-window.ui", "src");
1926 gui = empathy_builder_get_file (filename,
1927 "chat_window", &priv->dialog,
1928 "chat_vbox", &chat_vbox,
1929 "ui_manager", &priv->ui_manager,
1930 "menu_conv_insert_smiley", &priv->menu_conv_insert_smiley,
1931 "menu_conv_favorite", &priv->menu_conv_favorite,
1932 "menu_conv_always_urgent", &priv->menu_conv_always_urgent,
1933 "menu_conv_toggle_contacts", &priv->menu_conv_toggle_contacts,
1934 "menu_edit_cut", &priv->menu_edit_cut,
1935 "menu_edit_copy", &priv->menu_edit_copy,
1936 "menu_edit_paste", &priv->menu_edit_paste,
1937 "menu_edit_find", &priv->menu_edit_find,
1938 "menu_tabs_next", &priv->menu_tabs_next,
1939 "menu_tabs_prev", &priv->menu_tabs_prev,
1940 "menu_tabs_undo_close_tab", &priv->menu_tabs_undo_close_tab,
1941 "menu_tabs_left", &priv->menu_tabs_left,
1942 "menu_tabs_right", &priv->menu_tabs_right,
1943 "menu_tabs_detach", &priv->menu_tabs_detach,
1947 empathy_builder_connect (gui, window,
1948 "menu_conv", "activate", chat_window_conv_activate_cb,
1949 "menu_conv_clear", "activate", chat_window_clear_activate_cb,
1950 "menu_conv_favorite", "toggled", chat_window_favorite_toggled_cb,
1951 "menu_conv_always_urgent", "toggled", chat_window_always_urgent_toggled_cb,
1952 "menu_conv_toggle_contacts", "toggled", chat_window_contacts_toggled_cb,
1953 "menu_conv_invite_participant", "activate", chat_window_invite_participant_activate_cb,
1954 "menu_conv_close", "activate", chat_window_close_activate_cb,
1955 "menu_edit", "activate", chat_window_edit_activate_cb,
1956 "menu_edit_cut", "activate", chat_window_cut_activate_cb,
1957 "menu_edit_copy", "activate", chat_window_copy_activate_cb,
1958 "menu_edit_paste", "activate", chat_window_paste_activate_cb,
1959 "menu_edit_find", "activate", chat_window_find_activate_cb,
1960 "menu_tabs_next", "activate", chat_window_tabs_next_activate_cb,
1961 "menu_tabs_prev", "activate", chat_window_tabs_previous_activate_cb,
1962 "menu_tabs_undo_close_tab", "activate", chat_window_tabs_undo_close_tab_activate_cb,
1963 "menu_tabs_left", "activate", chat_window_tabs_left_activate_cb,
1964 "menu_tabs_right", "activate", chat_window_tabs_right_activate_cb,
1965 "menu_tabs_detach", "activate", chat_window_detach_activate_cb,
1966 "menu_help_contents", "activate", chat_window_help_contents_activate_cb,
1967 "menu_help_about", "activate", chat_window_help_about_activate_cb,
1970 g_object_ref (priv->ui_manager);
1971 g_object_unref (gui);
1973 priv->gsettings_chat = g_settings_new (EMPATHY_PREFS_CHAT_SCHEMA);
1974 priv->gsettings_notif = g_settings_new (EMPATHY_PREFS_NOTIFICATIONS_SCHEMA);
1975 priv->gsettings_ui = g_settings_new (EMPATHY_PREFS_UI_SCHEMA);
1976 priv->chatroom_manager = empathy_chatroom_manager_dup_singleton (NULL);
1978 priv->sound_mgr = empathy_sound_manager_dup_singleton ();
1980 priv->notebook = gtk_notebook_new ();
1982 g_signal_connect (priv->notebook, "create-window",
1983 G_CALLBACK (notebook_create_window_cb), window);
1985 gtk_notebook_set_group_name (GTK_NOTEBOOK (priv->notebook),
1986 "EmpathyChatWindow");
1987 gtk_notebook_set_scrollable (GTK_NOTEBOOK (priv->notebook), TRUE);
1988 gtk_notebook_popup_enable (GTK_NOTEBOOK (priv->notebook));
1989 gtk_box_pack_start (GTK_BOX (chat_vbox), priv->notebook, TRUE, TRUE, 0);
1990 gtk_widget_show (priv->notebook);
1993 accel_group = gtk_accel_group_new ();
1994 gtk_window_add_accel_group (GTK_WINDOW (priv->dialog), accel_group);
1996 for (i = 0; i < G_N_ELEMENTS (tab_accel_keys); i++) {
1997 closure = g_cclosure_new (G_CALLBACK (chat_window_accel_cb),
2000 gtk_accel_group_connect (accel_group,
2007 g_object_unref (accel_group);
2009 /* Set up drag target lists */
2010 priv->contact_targets = gtk_target_list_new (drag_types_dest_contact,
2011 G_N_ELEMENTS (drag_types_dest_contact));
2012 priv->file_targets = gtk_target_list_new (drag_types_dest_file,
2013 G_N_ELEMENTS (drag_types_dest_file));
2015 /* Set up smiley menu */
2016 smiley_manager = empathy_smiley_manager_dup_singleton ();
2017 submenu = empathy_smiley_menu_new (smiley_manager,
2018 chat_window_insert_smiley_activate_cb,
2020 menu = gtk_ui_manager_get_widget (priv->ui_manager,
2021 "/chats_menubar/menu_conv/menu_conv_insert_smiley");
2022 gtk_menu_item_set_submenu (GTK_MENU_ITEM (menu), submenu);
2023 g_object_unref (smiley_manager);
2025 /* Set up signals we can't do with ui file since we may need to
2026 * block/unblock them at some later stage.
2029 g_signal_connect (priv->dialog,
2031 G_CALLBACK (chat_window_delete_event_cb),
2033 g_signal_connect (priv->dialog,
2035 G_CALLBACK (chat_window_focus_in_event_cb),
2037 g_signal_connect_after (priv->notebook,
2039 G_CALLBACK (chat_window_page_switched_cb),
2041 g_signal_connect (priv->notebook,
2043 G_CALLBACK (chat_window_page_added_cb),
2045 g_signal_connect (priv->notebook,
2047 G_CALLBACK (chat_window_page_removed_cb),
2050 /* Set up drag and drop */
2051 gtk_drag_dest_set (GTK_WIDGET (priv->notebook),
2052 GTK_DEST_DEFAULT_HIGHLIGHT,
2054 G_N_ELEMENTS (drag_types_dest),
2055 GDK_ACTION_MOVE | GDK_ACTION_COPY);
2057 /* connect_after to allow GtkNotebook's built-in tab switching */
2058 g_signal_connect_after (priv->notebook,
2060 G_CALLBACK (chat_window_drag_motion),
2062 g_signal_connect (priv->notebook,
2063 "drag-data-received",
2064 G_CALLBACK (chat_window_drag_data_received),
2066 g_signal_connect (priv->notebook,
2068 G_CALLBACK (chat_window_drag_drop),
2071 chat_windows = g_list_prepend (chat_windows, window);
2073 /* Set up private details */
2075 priv->current_chat = NULL;
2076 priv->notification = NULL;
2078 priv->notify_mgr = empathy_notify_manager_dup_singleton ();
2080 priv->chat_manager = empathy_chat_manager_dup_singleton ();
2081 priv->chat_manager_chats_changed_id =
2082 g_signal_connect (priv->chat_manager, "closed-chats-changed",
2083 G_CALLBACK (chat_window_chat_manager_chats_changed_cb),
2086 chat_window_chat_manager_chats_changed_cb (priv->chat_manager,
2087 empathy_chat_manager_get_num_closed_chats (priv->chat_manager),
2092 empathy_chat_window_get_dialog (EmpathyChatWindow *window)
2094 EmpathyChatWindowPriv *priv;
2096 g_return_val_if_fail (window != NULL, NULL);
2098 priv = GET_PRIV (window);
2100 return priv->dialog;
2103 /* Returns the window to open a new tab in if there is a suitable window,
2104 * otherwise, returns NULL indicating that a new window should be added.
2106 static EmpathyChatWindow *
2107 empathy_chat_window_get_default (gboolean room)
2109 GSettings *gsettings = g_settings_new (EMPATHY_PREFS_UI_SCHEMA);
2111 gboolean separate_windows = TRUE;
2113 separate_windows = g_settings_get_boolean (gsettings,
2114 EMPATHY_PREFS_UI_SEPARATE_CHAT_WINDOWS);
2116 g_object_unref (gsettings);
2118 if (separate_windows) {
2119 /* Always create a new window */
2123 for (l = chat_windows; l; l = l->next) {
2124 EmpathyChatWindowPriv *priv;
2125 EmpathyChatWindow *chat_window;
2127 guint nb_rooms, nb_private;
2129 chat_window = l->data;
2130 priv = GET_PRIV (chat_window);
2132 dialog = empathy_chat_window_get_dialog (chat_window);
2134 empathy_chat_window_get_nb_chats (chat_window, &nb_rooms, &nb_private);
2136 /* Skip the window if there aren't any rooms in it */
2137 if (room && nb_rooms == 0)
2140 /* Skip the window if there aren't any 1-1 chats in it */
2141 if (!room && nb_private == 0)
2144 /* Found a window on this desktop, make it visible if necessary */
2145 if (!empathy_window_get_is_visible (GTK_WINDOW (dialog)))
2146 empathy_window_present (GTK_WINDOW (dialog));
2154 empathy_chat_window_add_chat (EmpathyChatWindow *window,
2157 EmpathyChatWindowPriv *priv;
2159 GtkWidget *popup_label;
2161 GValue value = { 0, };
2163 g_return_if_fail (window != NULL);
2164 g_return_if_fail (EMPATHY_IS_CHAT (chat));
2166 priv = GET_PRIV (window);
2168 /* Reference the chat object */
2169 g_object_ref (chat);
2171 /* If this window has just been created, position it */
2172 if (priv->chats == NULL) {
2173 const gchar *name = "chat-window";
2174 gboolean separate_windows;
2176 separate_windows = g_settings_get_boolean (priv->gsettings_ui,
2177 EMPATHY_PREFS_UI_SEPARATE_CHAT_WINDOWS);
2179 if (empathy_chat_is_room (chat))
2180 name = "room-window";
2182 if (separate_windows) {
2185 /* Save current position of the window */
2186 gtk_window_get_position (GTK_WINDOW (priv->dialog), &x, &y);
2188 /* First bind to the 'generic' name. So new window for which we didn't
2189 * save a geometry yet will have the geometry of the last saved
2190 * window (bgo #601191). */
2191 empathy_geometry_bind (GTK_WINDOW (priv->dialog), name);
2193 /* Restore previous position of the window so the newly created window
2194 * won't be in the same position as the latest saved window and so
2195 * completely hide it. */
2196 gtk_window_move (GTK_WINDOW (priv->dialog), x, y);
2198 /* Then bind it to the name of the contact/room so we'll save the
2199 * geometry specific to this window */
2200 name = empathy_chat_get_id (chat);
2203 empathy_geometry_bind (GTK_WINDOW (priv->dialog), name);
2206 child = GTK_WIDGET (chat);
2207 label = chat_window_create_label (window, chat, TRUE);
2208 popup_label = chat_window_create_label (window, chat, FALSE);
2209 gtk_widget_show (child);
2211 g_signal_connect (chat, "notify::name",
2212 G_CALLBACK (chat_window_chat_notify_cb),
2214 g_signal_connect (chat, "notify::subject",
2215 G_CALLBACK (chat_window_chat_notify_cb),
2217 g_signal_connect (chat, "notify::remote-contact",
2218 G_CALLBACK (chat_window_chat_notify_cb),
2220 chat_window_chat_notify_cb (chat);
2222 gtk_notebook_append_page_menu (GTK_NOTEBOOK (priv->notebook), child, label, popup_label);
2223 gtk_notebook_set_tab_reorderable (GTK_NOTEBOOK (priv->notebook), child, TRUE);
2224 gtk_notebook_set_tab_detachable (GTK_NOTEBOOK (priv->notebook), child, TRUE);
2225 g_value_init (&value, G_TYPE_BOOLEAN);
2226 g_value_set_boolean (&value, TRUE);
2227 gtk_container_child_set_property (GTK_CONTAINER (priv->notebook),
2228 child, "tab-expand" , &value);
2229 gtk_container_child_set_property (GTK_CONTAINER (priv->notebook),
2230 child, "tab-fill" , &value);
2231 g_value_unset (&value);
2233 DEBUG ("Chat added (%d references)", G_OBJECT (chat)->ref_count);
2237 empathy_chat_window_remove_chat (EmpathyChatWindow *window,
2240 EmpathyChatWindowPriv *priv;
2242 EmpathyContact *remote_contact;
2243 EmpathyChatManager *chat_manager;
2245 g_return_if_fail (window != NULL);
2246 g_return_if_fail (EMPATHY_IS_CHAT (chat));
2248 priv = GET_PRIV (window);
2250 g_signal_handlers_disconnect_by_func (chat,
2251 chat_window_chat_notify_cb,
2253 remote_contact = g_object_get_data (G_OBJECT (chat),
2254 "chat-window-remote-contact");
2255 if (remote_contact) {
2256 g_signal_handlers_disconnect_by_func (remote_contact,
2257 chat_window_update_chat_tab,
2261 chat_manager = empathy_chat_manager_dup_singleton ();
2262 empathy_chat_manager_closed_chat (chat_manager, chat);
2263 g_object_unref (chat_manager);
2265 position = gtk_notebook_page_num (GTK_NOTEBOOK (priv->notebook),
2267 gtk_notebook_remove_page (GTK_NOTEBOOK (priv->notebook), position);
2269 DEBUG ("Chat removed (%d references)", G_OBJECT (chat)->ref_count - 1);
2271 g_object_unref (chat);
2275 empathy_chat_window_move_chat (EmpathyChatWindow *old_window,
2276 EmpathyChatWindow *new_window,
2281 g_return_if_fail (EMPATHY_IS_CHAT_WINDOW (old_window));
2282 g_return_if_fail (EMPATHY_IS_CHAT_WINDOW (new_window));
2283 g_return_if_fail (EMPATHY_IS_CHAT (chat));
2285 widget = GTK_WIDGET (chat);
2287 DEBUG ("Chat moving with widget:%p (%d references)", widget,
2288 G_OBJECT (widget)->ref_count);
2290 /* We reference here to make sure we don't loose the widget
2291 * and the EmpathyChat object during the move.
2293 g_object_ref (chat);
2294 g_object_ref (widget);
2296 empathy_chat_window_remove_chat (old_window, chat);
2297 empathy_chat_window_add_chat (new_window, chat);
2299 g_object_unref (widget);
2300 g_object_unref (chat);
2304 empathy_chat_window_switch_to_chat (EmpathyChatWindow *window,
2307 EmpathyChatWindowPriv *priv;
2310 g_return_if_fail (window != NULL);
2311 g_return_if_fail (EMPATHY_IS_CHAT (chat));
2313 priv = GET_PRIV (window);
2315 page_num = gtk_notebook_page_num (GTK_NOTEBOOK (priv->notebook),
2317 gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->notebook),
2322 empathy_chat_window_find_chat (TpAccount *account,
2327 g_return_val_if_fail (!EMP_STR_EMPTY (id), NULL);
2329 for (l = chat_windows; l; l = l->next) {
2330 EmpathyChatWindowPriv *priv;
2331 EmpathyChatWindow *window;
2335 priv = GET_PRIV (window);
2337 for (ll = priv->chats; ll; ll = ll->next) {
2342 if (account == empathy_chat_get_account (chat) &&
2343 !tp_strdiff (id, empathy_chat_get_id (chat))) {
2353 empathy_chat_window_present_chat (EmpathyChat *chat,
2356 EmpathyChatWindow *window;
2357 EmpathyChatWindowPriv *priv;
2358 guint32 x_timestamp;
2360 g_return_if_fail (EMPATHY_IS_CHAT (chat));
2362 window = chat_window_find_chat (chat);
2364 /* If the chat has no window, create one */
2365 if (window == NULL) {
2366 window = empathy_chat_window_get_default (empathy_chat_is_room (chat));
2368 window = empathy_chat_window_new ();
2369 gtk_widget_show_all (GET_PRIV (window)->dialog);
2372 empathy_chat_window_add_chat (window, chat);
2375 /* Don't force the window to show itself when it wasn't
2376 * an action by the user
2378 if (!tp_user_action_time_should_present (timestamp, &x_timestamp))
2381 priv = GET_PRIV (window);
2383 if (x_timestamp != GDK_CURRENT_TIME) {
2384 /* Don't present or switch tab if the action was earlier than the
2385 * last actions X time, accounting for overflow and the first ever
2388 if (priv->x_user_action_time != 0
2389 && X_EARLIER_OR_EQL (x_timestamp, priv->x_user_action_time))
2392 priv->x_user_action_time = x_timestamp;
2395 empathy_chat_window_switch_to_chat (window, chat);
2396 empathy_window_present_with_time (GTK_WINDOW (priv->dialog),
2399 gtk_widget_grab_focus (chat->input_text_view);
2403 empathy_chat_window_get_nb_chats (EmpathyChatWindow *self,
2407 EmpathyChatWindowPriv *priv = GET_PRIV (self);
2409 guint _nb_rooms = 0, _nb_private = 0;
2411 for (l = priv->chats; l != NULL; l = g_list_next (l)) {
2412 if (empathy_chat_is_room (EMPATHY_CHAT (l->data)))
2418 if (nb_rooms != NULL)
2419 *nb_rooms = _nb_rooms;
2420 if (nb_private != NULL)
2421 *nb_private = _nb_private;