1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * Copyright (C) 2003-2007 Imendio AB
4 * Copyright (C) 2007-2010 Collabora Ltd.
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License as
8 * published by the Free Software Foundation; either version 2 of the
9 * License, or (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
16 * You should have received a copy of the GNU General Public
17 * License along with this program; if not, write to the
18 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
19 * Boston, MA 02110-1301 USA
21 * Authors: Mikael Hallendal <micke@imendio.com>
22 * Richard Hult <richard@imendio.com>
23 * Martyn Russell <martyn@imendio.com>
24 * Geert-Jan Van den Bogaerde <geertjan@gnome.org>
25 * Xavier Claessens <xclaesse@gmail.com>
26 * RĂ´mulo Fernandes Machado <romulo@castorgroup.net>
34 #include <gdk/gdkkeysyms.h>
36 #include <glib/gi18n.h>
37 #include <libnotify/notification.h>
39 #include <telepathy-glib/telepathy-glib.h>
41 #include <libempathy/empathy-contact.h>
42 #include <libempathy/empathy-message.h>
43 #include <libempathy/empathy-chatroom-manager.h>
44 #include <libempathy/empathy-gsettings.h>
45 #include <libempathy/empathy-utils.h>
46 #include <libempathy/empathy-tp-contact-factory.h>
47 #include <libempathy/empathy-contact-list.h>
48 #include <libempathy/empathy-request-util.h>
50 #include <libempathy-gtk/empathy-images.h>
51 #include <libempathy-gtk/empathy-contact-dialogs.h>
52 #include <libempathy-gtk/empathy-log-window.h>
53 #include <libempathy-gtk/empathy-geometry.h>
54 #include <libempathy-gtk/empathy-smiley-manager.h>
55 #include <libempathy-gtk/empathy-sound-manager.h>
56 #include <libempathy-gtk/empathy-ui-utils.h>
57 #include <libempathy-gtk/empathy-notify-manager.h>
59 #include "empathy-chat-manager.h"
60 #include "empathy-chat-window.h"
61 #include "empathy-about-dialog.h"
62 #include "empathy-invite-participant-dialog.h"
63 #include "gedit-close-button.h"
65 #define DEBUG_FLAG EMPATHY_DEBUG_CHAT
66 #include <libempathy/empathy-debug.h>
68 /* Macro to compare guint32 X timestamps, while accounting for wrapping around
70 #define X_EARLIER_OR_EQL(t1, t2) \
71 ((t1 <= t2 && ((t2 - t1) < G_MAXUINT32/2)) \
72 || (t1 >= t2 && (t1 - t2) > (G_MAXUINT32/2)) \
75 #define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyChatWindow)
77 EmpathyChat *current_chat;
80 gboolean dnd_same_window;
81 EmpathyChatroomManager *chatroom_manager;
82 EmpathyNotifyManager *notify_mgr;
85 NotifyNotification *notification;
87 GtkTargetList *contact_targets;
88 GtkTargetList *file_targets;
90 EmpathyChatManager *chat_manager;
91 gulong chat_manager_chats_changed_id;
94 GtkUIManager *ui_manager;
95 GtkAction *menu_conv_insert_smiley;
96 GtkAction *menu_conv_favorite;
97 GtkAction *menu_conv_always_urgent;
98 GtkAction *menu_conv_toggle_contacts;
100 GtkAction *menu_edit_cut;
101 GtkAction *menu_edit_copy;
102 GtkAction *menu_edit_paste;
103 GtkAction *menu_edit_find;
105 GtkAction *menu_tabs_next;
106 GtkAction *menu_tabs_prev;
107 GtkAction *menu_tabs_undo_close_tab;
108 GtkAction *menu_tabs_left;
109 GtkAction *menu_tabs_right;
110 GtkAction *menu_tabs_detach;
112 /* Last user action time we acted upon to show a tab */
113 guint32 x_user_action_time;
115 GSettings *gsettings_chat;
116 GSettings *gsettings_notif;
117 GSettings *gsettings_ui;
119 EmpathySoundManager *sound_mgr;
120 } EmpathyChatWindowPriv;
122 static GList *chat_windows = NULL;
124 static const guint tab_accel_keys[] = {
125 GDK_KEY_1, GDK_KEY_2, GDK_KEY_3, GDK_KEY_4, GDK_KEY_5,
126 GDK_KEY_6, GDK_KEY_7, GDK_KEY_8, GDK_KEY_9, GDK_KEY_0
130 DND_DRAG_TYPE_CONTACT_ID,
131 DND_DRAG_TYPE_URI_LIST,
135 static const GtkTargetEntry drag_types_dest[] = {
136 { "text/contact-id", 0, DND_DRAG_TYPE_CONTACT_ID },
137 { "GTK_NOTEBOOK_TAB", GTK_TARGET_SAME_APP, DND_DRAG_TYPE_TAB },
138 { "text/uri-list", 0, DND_DRAG_TYPE_URI_LIST },
139 { "text/path-list", 0, DND_DRAG_TYPE_URI_LIST },
142 static const GtkTargetEntry drag_types_dest_contact[] = {
143 { "text/contact-id", 0, DND_DRAG_TYPE_CONTACT_ID },
146 static const GtkTargetEntry drag_types_dest_file[] = {
147 /* must be first to be prioritized, in order to receive the
148 * note's file path from Tomboy instead of an URI */
149 { "text/path-list", 0, DND_DRAG_TYPE_URI_LIST },
150 { "text/uri-list", 0, DND_DRAG_TYPE_URI_LIST },
153 static void chat_window_update (EmpathyChatWindow *window,
154 gboolean update_contact_menu);
156 static void empathy_chat_window_add_chat (EmpathyChatWindow *window,
159 static void empathy_chat_window_remove_chat (EmpathyChatWindow *window,
162 static void empathy_chat_window_move_chat (EmpathyChatWindow *old_window,
163 EmpathyChatWindow *new_window,
166 static void empathy_chat_window_get_nb_chats (EmpathyChatWindow *self,
170 G_DEFINE_TYPE (EmpathyChatWindow, empathy_chat_window, G_TYPE_OBJECT);
173 chat_window_accel_cb (GtkAccelGroup *accelgroup,
177 EmpathyChatWindow *window)
179 EmpathyChatWindowPriv *priv;
183 priv = GET_PRIV (window);
185 for (i = 0; i < G_N_ELEMENTS (tab_accel_keys); i++) {
186 if (tab_accel_keys[i] == key) {
193 gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->notebook), num);
197 static EmpathyChatWindow *
198 chat_window_find_chat (EmpathyChat *chat)
200 EmpathyChatWindowPriv *priv;
203 for (l = chat_windows; l; l = l->next) {
204 priv = GET_PRIV (l->data);
205 ll = g_list_find (priv->chats, chat);
215 chat_window_close_clicked_cb (GtkAction *action,
218 EmpathyChatWindow *window;
220 window = chat_window_find_chat (chat);
221 empathy_chat_window_remove_chat (window, chat);
225 chat_tab_style_updated_cb (GtkWidget *hbox,
229 int char_width, h, w;
230 PangoContext *context;
231 const PangoFontDescription *font_desc;
232 PangoFontMetrics *metrics;
234 button = g_object_get_data (G_OBJECT (user_data),
235 "chat-window-tab-close-button");
236 context = gtk_widget_get_pango_context (hbox);
238 font_desc = gtk_style_context_get_font (gtk_widget_get_style_context (hbox),
239 GTK_STATE_FLAG_NORMAL);
241 metrics = pango_context_get_metrics (context, font_desc,
242 pango_context_get_language (context));
243 char_width = pango_font_metrics_get_approximate_char_width (metrics);
244 pango_font_metrics_unref (metrics);
246 gtk_icon_size_lookup_for_settings (gtk_widget_get_settings (button),
247 GTK_ICON_SIZE_MENU, &w, &h);
249 /* Request at least about 12 chars width plus at least space for the status
250 * image and the close button */
251 gtk_widget_set_size_request (hbox,
252 12 * PANGO_PIXELS (char_width) + 2 * w, -1);
254 gtk_widget_set_size_request (button, w, h);
258 chat_window_create_label (EmpathyChatWindow *window,
260 gboolean is_tab_label)
262 EmpathyChatWindowPriv *priv;
264 GtkWidget *name_label;
265 GtkWidget *status_image;
266 GtkWidget *event_box;
267 GtkWidget *event_box_hbox;
268 PangoAttrList *attr_list;
269 PangoAttribute *attr;
271 priv = GET_PRIV (window);
273 /* The spacing between the button and the label. */
274 hbox = gtk_hbox_new (FALSE, 0);
276 event_box = gtk_event_box_new ();
277 gtk_event_box_set_visible_window (GTK_EVENT_BOX (event_box), FALSE);
279 name_label = gtk_label_new (NULL);
281 gtk_label_set_ellipsize (GTK_LABEL (name_label), PANGO_ELLIPSIZE_END);
283 attr_list = pango_attr_list_new ();
284 attr = pango_attr_scale_new (1/1.2);
285 attr->start_index = 0;
286 attr->end_index = -1;
287 pango_attr_list_insert (attr_list, attr);
288 gtk_label_set_attributes (GTK_LABEL (name_label), attr_list);
289 pango_attr_list_unref (attr_list);
291 gtk_misc_set_padding (GTK_MISC (name_label), 2, 0);
292 gtk_misc_set_alignment (GTK_MISC (name_label), 0.0, 0.5);
293 g_object_set_data (G_OBJECT (chat),
294 is_tab_label ? "chat-window-tab-label" : "chat-window-menu-label",
297 status_image = gtk_image_new ();
299 /* Spacing between the icon and label. */
300 event_box_hbox = gtk_hbox_new (FALSE, 0);
302 gtk_box_pack_start (GTK_BOX (event_box_hbox), status_image, FALSE, FALSE, 0);
303 gtk_box_pack_start (GTK_BOX (event_box_hbox), name_label, TRUE, TRUE, 0);
305 g_object_set_data (G_OBJECT (chat),
306 is_tab_label ? "chat-window-tab-image" : "chat-window-menu-image",
308 g_object_set_data (G_OBJECT (chat),
309 is_tab_label ? "chat-window-tab-tooltip-widget" : "chat-window-menu-tooltip-widget",
312 gtk_container_add (GTK_CONTAINER (event_box), event_box_hbox);
313 gtk_box_pack_start (GTK_BOX (hbox), event_box, TRUE, TRUE, 0);
316 GtkWidget *close_button;
317 GtkWidget *sending_spinner;
319 sending_spinner = gtk_spinner_new ();
321 gtk_box_pack_start (GTK_BOX (hbox), sending_spinner,
323 g_object_set_data (G_OBJECT (chat),
324 "chat-window-tab-sending-spinner",
327 close_button = gedit_close_button_new ();
328 g_object_set_data (G_OBJECT (chat), "chat-window-tab-close-button", close_button);
330 /* We don't want focus/keynav for the button to avoid clutter, and
331 * Ctrl-W works anyway.
333 gtk_widget_set_can_focus (close_button, FALSE);
334 gtk_widget_set_can_default (close_button, FALSE);
336 gtk_box_pack_end (GTK_BOX (hbox), close_button, FALSE, FALSE, 0);
338 g_signal_connect (close_button,
340 G_CALLBACK (chat_window_close_clicked_cb),
343 /* React to theme changes and also setup the size correctly. */
344 g_signal_connect (hbox,
346 G_CALLBACK (chat_tab_style_updated_cb),
350 gtk_widget_show_all (hbox);
356 _submenu_notify_visible_changed_cb (GObject *object,
360 g_signal_handlers_disconnect_by_func (object,
361 _submenu_notify_visible_changed_cb,
363 chat_window_update (EMPATHY_CHAT_WINDOW (userdata), TRUE);
367 chat_window_menu_context_update (EmpathyChatWindowPriv *priv,
372 gboolean wrap_around;
373 gboolean is_connected;
376 page_num = gtk_notebook_get_current_page (GTK_NOTEBOOK (priv->notebook));
377 first_page = (page_num == 0);
378 last_page = (page_num == (num_pages - 1));
379 g_object_get (gtk_settings_get_default (), "gtk-keynav-wrap-around",
381 is_connected = empathy_chat_get_tp_chat (priv->current_chat) != NULL;
383 gtk_action_set_sensitive (priv->menu_tabs_next, (!last_page ||
385 gtk_action_set_sensitive (priv->menu_tabs_prev, (!first_page ||
387 gtk_action_set_sensitive (priv->menu_tabs_detach, num_pages > 1);
388 gtk_action_set_sensitive (priv->menu_tabs_left, !first_page);
389 gtk_action_set_sensitive (priv->menu_tabs_right, !last_page);
390 gtk_action_set_sensitive (priv->menu_conv_insert_smiley, is_connected);
394 chat_window_conversation_menu_update (EmpathyChatWindowPriv *priv,
395 EmpathyChatWindow *self)
397 EmpathyTpChat *tp_chat;
398 TpConnection *connection;
400 gboolean sensitive = FALSE;
402 g_return_if_fail (priv->current_chat != NULL);
404 action = gtk_ui_manager_get_action (priv->ui_manager,
405 "/chats_menubar/menu_conv/menu_conv_invite_participant");
406 tp_chat = empathy_chat_get_tp_chat (priv->current_chat);
408 if (tp_chat != NULL) {
409 connection = empathy_tp_chat_get_connection (tp_chat);
411 sensitive = empathy_tp_chat_can_add_contact (tp_chat) &&
412 (tp_connection_get_status (connection, NULL) ==
413 TP_CONNECTION_STATUS_CONNECTED);
416 gtk_action_set_sensitive (action, sensitive);
420 chat_window_contact_menu_update (EmpathyChatWindowPriv *priv,
421 EmpathyChatWindow *window)
423 GtkWidget *menu, *submenu, *orig_submenu;
425 menu = gtk_ui_manager_get_widget (priv->ui_manager,
426 "/chats_menubar/menu_contact");
427 orig_submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (menu));
429 if (orig_submenu == NULL || !gtk_widget_get_visible (orig_submenu)) {
430 submenu = empathy_chat_get_contact_menu (priv->current_chat);
432 if (submenu != NULL) {
433 /* gtk_menu_attach_to_widget () doesn't behave nicely here */
434 g_object_set_data (G_OBJECT (submenu), "window", priv->dialog);
436 gtk_menu_item_set_submenu (GTK_MENU_ITEM (menu), submenu);
437 gtk_widget_show (menu);
438 gtk_widget_set_sensitive (menu, TRUE);
440 gtk_widget_set_sensitive (menu, FALSE);
443 tp_g_signal_connect_object (orig_submenu,
445 (GCallback)_submenu_notify_visible_changed_cb,
451 get_all_unread_messages (EmpathyChatWindowPriv *priv)
456 for (l = priv->chats; l != NULL; l = g_list_next (l))
457 nb += empathy_chat_get_nb_unread_messages (EMPATHY_CHAT (l->data));
463 get_window_title_name (EmpathyChatWindowPriv *priv)
465 gchar *active_name, *ret;
467 guint current_unread_msgs;
469 nb_chats = g_list_length (priv->chats);
470 g_assert (nb_chats > 0);
472 active_name = empathy_chat_dup_name (priv->current_chat);
474 current_unread_msgs = empathy_chat_get_nb_unread_messages (
479 if (current_unread_msgs == 0)
480 ret = g_strdup (active_name);
482 ret = g_strdup_printf (ngettext (
484 "%s (%d unread)", current_unread_msgs),
485 active_name, current_unread_msgs);
487 guint nb_others = nb_chats - 1;
488 guint all_unread_msgs;
490 all_unread_msgs = get_all_unread_messages (priv);
492 if (all_unread_msgs == 0) {
493 /* no unread message */
494 ret = g_strdup_printf (ngettext (
496 "%s (and %u others)", nb_others),
497 active_name, nb_others);
500 else if (all_unread_msgs == current_unread_msgs) {
501 /* unread messages are in the current tab */
502 ret = g_strdup_printf (ngettext (
504 "%s (%d unread)", current_unread_msgs),
505 active_name, current_unread_msgs);
508 else if (current_unread_msgs == 0) {
509 /* unread messages are in other tabs */
510 ret = g_strdup_printf (ngettext (
511 "%s (%d unread from others)",
512 "%s (%d unread from others)",
514 active_name, all_unread_msgs);
518 /* unread messages are in all the tabs */
519 ret = g_strdup_printf (ngettext (
520 "%s (%d unread from all)",
521 "%s (%d unread from all)",
523 active_name, all_unread_msgs);
527 g_free (active_name);
533 chat_window_title_update (EmpathyChatWindowPriv *priv)
537 name = get_window_title_name (priv);
538 gtk_window_set_title (GTK_WINDOW (priv->dialog), name);
543 chat_window_icon_update (EmpathyChatWindowPriv *priv, gboolean new_messages)
546 EmpathyContact *remote_contact;
547 gboolean avatar_in_icon;
550 n_chats = g_list_length (priv->chats);
552 /* Update window icon */
554 gtk_window_set_icon_name (GTK_WINDOW (priv->dialog),
555 EMPATHY_IMAGE_MESSAGE);
557 avatar_in_icon = g_settings_get_boolean (priv->gsettings_chat,
558 EMPATHY_PREFS_CHAT_AVATAR_IN_ICON);
560 if (n_chats == 1 && avatar_in_icon) {
561 remote_contact = empathy_chat_get_remote_contact (priv->current_chat);
562 icon = empathy_pixbuf_avatar_from_contact_scaled (remote_contact, 0, 0);
563 gtk_window_set_icon (GTK_WINDOW (priv->dialog), icon);
566 g_object_unref (icon);
569 gtk_window_set_icon_name (GTK_WINDOW (priv->dialog), NULL);
575 chat_window_close_button_update (EmpathyChatWindowPriv *priv,
579 GtkWidget *chat_close_button;
582 if (num_pages == 1) {
583 chat = gtk_notebook_get_nth_page (GTK_NOTEBOOK (priv->notebook), 0);
584 chat_close_button = g_object_get_data (G_OBJECT (chat),
585 "chat-window-tab-close-button");
586 gtk_widget_hide (chat_close_button);
588 for (i=0; i<num_pages; i++) {
589 chat = gtk_notebook_get_nth_page (GTK_NOTEBOOK (priv->notebook), i);
590 chat_close_button = g_object_get_data (G_OBJECT (chat),
591 "chat-window-tab-close-button");
592 gtk_widget_show (chat_close_button);
598 chat_window_update (EmpathyChatWindow *window,
599 gboolean update_contact_menu)
601 EmpathyChatWindowPriv *priv = GET_PRIV (window);
604 num_pages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (priv->notebook));
606 /* Update Tab menu */
607 chat_window_menu_context_update (priv,
610 chat_window_conversation_menu_update (priv, window);
612 /* If this update is due to a focus-in event, we know the menu will be
613 the same as when we last left it, so no work to do. Besides, if we
614 swap out the menu on a focus-in, we may confuse any external global
616 if (update_contact_menu) {
617 chat_window_contact_menu_update (priv,
621 chat_window_title_update (priv);
623 chat_window_icon_update (priv, get_all_unread_messages (priv) > 0);
625 chat_window_close_button_update (priv,
630 append_markup_printf (GString *string,
637 va_start (args, format);
639 tmp = g_markup_vprintf_escaped (format, args);
640 g_string_append (string, tmp);
647 chat_window_update_chat_tab_full (EmpathyChat *chat,
648 gboolean update_contact_menu)
650 EmpathyChatWindow *window;
651 EmpathyChatWindowPriv *priv;
652 EmpathyContact *remote_contact;
656 const gchar *subject;
657 const gchar *status = NULL;
661 const gchar *icon_name;
662 GtkWidget *tab_image;
663 GtkWidget *menu_image;
664 GtkWidget *sending_spinner;
667 window = chat_window_find_chat (chat);
671 priv = GET_PRIV (window);
673 /* Get information */
674 name = empathy_chat_dup_name (chat);
675 account = empathy_chat_get_account (chat);
676 subject = empathy_chat_get_subject (chat);
677 remote_contact = empathy_chat_get_remote_contact (chat);
679 DEBUG ("Updating chat tab, name=%s, account=%s, subject=%s, remote_contact=%p",
680 name, tp_proxy_get_object_path (account), subject, remote_contact);
682 /* Update tab image */
683 if (empathy_chat_get_tp_chat (chat) == NULL) {
684 /* No TpChat, we are disconnected */
687 else if (empathy_chat_get_nb_unread_messages (chat) > 0) {
688 icon_name = EMPATHY_IMAGE_MESSAGE;
690 else if (remote_contact && empathy_chat_is_composing (chat)) {
691 icon_name = EMPATHY_IMAGE_TYPING;
693 else if (empathy_chat_is_sms_channel (chat)) {
694 icon_name = EMPATHY_IMAGE_SMS;
696 else if (remote_contact) {
697 icon_name = empathy_icon_name_for_contact (remote_contact);
699 icon_name = EMPATHY_IMAGE_GROUP_MESSAGE;
702 tab_image = g_object_get_data (G_OBJECT (chat), "chat-window-tab-image");
703 menu_image = g_object_get_data (G_OBJECT (chat), "chat-window-menu-image");
704 if (icon_name != NULL) {
705 gtk_image_set_from_icon_name (GTK_IMAGE (tab_image), icon_name, GTK_ICON_SIZE_MENU);
706 gtk_widget_show (tab_image);
707 gtk_image_set_from_icon_name (GTK_IMAGE (menu_image), icon_name, GTK_ICON_SIZE_MENU);
708 gtk_widget_show (menu_image);
710 gtk_widget_hide (tab_image);
711 gtk_widget_hide (menu_image);
714 /* Update the sending spinner */
715 nb_sending = empathy_chat_get_n_messages_sending (chat);
716 sending_spinner = g_object_get_data (G_OBJECT (chat),
717 "chat-window-tab-sending-spinner");
719 g_object_set (sending_spinner,
720 "active", nb_sending > 0,
721 "visible", nb_sending > 0,
724 /* Update tab tooltip */
725 tooltip = g_string_new (NULL);
727 if (remote_contact) {
728 id = empathy_contact_get_id (remote_contact);
729 status = empathy_contact_get_presence_message (remote_contact);
734 if (empathy_chat_is_sms_channel (chat)) {
735 append_markup_printf (tooltip, "%s ", _("SMS:"));
738 append_markup_printf (tooltip,
739 "<b>%s</b><small> (%s)</small>",
741 tp_account_get_display_name (account));
743 if (nb_sending > 0) {
744 char *tmp = g_strdup_printf (
745 ngettext ("Sending %d message",
746 "Sending %d messages",
750 g_string_append (tooltip, "\n");
751 g_string_append (tooltip, tmp);
753 gtk_widget_set_tooltip_text (sending_spinner, tmp);
757 if (!EMP_STR_EMPTY (status)) {
758 append_markup_printf (tooltip, "\n<i>%s</i>", status);
762 append_markup_printf (tooltip, "\n<b>%s</b> %s",
763 _("Topic:"), subject);
766 if (remote_contact && empathy_chat_is_composing (chat)) {
767 append_markup_printf (tooltip, "\n%s", _("Typing a message."));
770 markup = g_string_free (tooltip, FALSE);
771 widget = g_object_get_data (G_OBJECT (chat), "chat-window-tab-tooltip-widget");
772 gtk_widget_set_tooltip_markup (widget, markup);
773 widget = g_object_get_data (G_OBJECT (chat), "chat-window-menu-tooltip-widget");
774 gtk_widget_set_tooltip_markup (widget, markup);
777 /* Update tab and menu label */
778 widget = g_object_get_data (G_OBJECT (chat), "chat-window-tab-label");
779 gtk_label_set_text (GTK_LABEL (widget), name);
780 widget = g_object_get_data (G_OBJECT (chat), "chat-window-menu-label");
781 gtk_label_set_text (GTK_LABEL (widget), name);
783 /* Update the window if it's the current chat */
784 if (priv->current_chat == chat) {
785 chat_window_update (window, update_contact_menu);
792 chat_window_update_chat_tab (EmpathyChat *chat)
794 chat_window_update_chat_tab_full (chat, TRUE);
798 chat_window_chat_notify_cb (EmpathyChat *chat)
800 EmpathyContact *old_remote_contact;
801 EmpathyContact *remote_contact = NULL;
803 old_remote_contact = g_object_get_data (G_OBJECT (chat), "chat-window-remote-contact");
804 remote_contact = empathy_chat_get_remote_contact (chat);
806 if (old_remote_contact != remote_contact) {
807 /* The remote-contact associated with the chat changed, we need
808 * to keep track of any change of that contact and update the
809 * window each time. */
810 if (remote_contact) {
811 g_signal_connect_swapped (remote_contact, "notify",
812 G_CALLBACK (chat_window_update_chat_tab),
815 if (old_remote_contact) {
816 g_signal_handlers_disconnect_by_func (old_remote_contact,
817 chat_window_update_chat_tab,
821 g_object_set_data_full (G_OBJECT (chat), "chat-window-remote-contact",
822 g_object_ref (remote_contact), (GDestroyNotify) g_object_unref);
825 chat_window_update_chat_tab (chat);
829 chat_window_insert_smiley_activate_cb (EmpathySmileyManager *manager,
830 EmpathySmiley *smiley,
833 EmpathyChatWindowPriv *priv = GET_PRIV (window);
835 GtkTextBuffer *buffer;
838 chat = priv->current_chat;
840 buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (chat->input_text_view));
841 gtk_text_buffer_get_end_iter (buffer, &iter);
842 gtk_text_buffer_insert (buffer, &iter, smiley->str, -1);
846 chat_window_conv_activate_cb (GtkAction *action,
847 EmpathyChatWindow *window)
849 EmpathyChatWindowPriv *priv = GET_PRIV (window);
852 EmpathyContact *remote_contact = NULL;
854 /* Favorite room menu */
855 is_room = empathy_chat_is_room (priv->current_chat);
859 gboolean found = FALSE;
860 EmpathyChatroom *chatroom;
862 room = empathy_chat_get_id (priv->current_chat);
863 account = empathy_chat_get_account (priv->current_chat);
864 chatroom = empathy_chatroom_manager_find (priv->chatroom_manager,
866 if (chatroom != NULL)
867 found = empathy_chatroom_is_favorite (chatroom);
869 DEBUG ("This room %s favorite", found ? "is" : "is not");
870 gtk_toggle_action_set_active (
871 GTK_TOGGLE_ACTION (priv->menu_conv_favorite), found);
873 if (chatroom != NULL)
874 found = empathy_chatroom_is_always_urgent (chatroom);
876 gtk_toggle_action_set_active (
877 GTK_TOGGLE_ACTION (priv->menu_conv_always_urgent),
880 gtk_action_set_visible (priv->menu_conv_favorite, is_room);
881 gtk_action_set_visible (priv->menu_conv_always_urgent, is_room);
883 /* Show contacts menu */
884 g_object_get (priv->current_chat,
885 "remote-contact", &remote_contact,
886 "show-contacts", &active,
888 if (remote_contact == NULL) {
889 gtk_toggle_action_set_active (
890 GTK_TOGGLE_ACTION (priv->menu_conv_toggle_contacts),
893 gtk_action_set_visible (priv->menu_conv_toggle_contacts,
894 (remote_contact == NULL));
895 if (remote_contact != NULL) {
896 g_object_unref (remote_contact);
901 chat_window_clear_activate_cb (GtkAction *action,
902 EmpathyChatWindow *window)
904 EmpathyChatWindowPriv *priv = GET_PRIV (window);
906 empathy_chat_clear (priv->current_chat);
910 chat_window_favorite_toggled_cb (GtkToggleAction *toggle_action,
911 EmpathyChatWindow *window)
913 EmpathyChatWindowPriv *priv = GET_PRIV (window);
918 EmpathyChatroom *chatroom;
920 active = gtk_toggle_action_get_active (toggle_action);
921 account = empathy_chat_get_account (priv->current_chat);
922 room = empathy_chat_get_id (priv->current_chat);
923 name = empathy_chat_dup_name (priv->current_chat);
925 chatroom = empathy_chatroom_manager_ensure_chatroom (
926 priv->chatroom_manager,
931 empathy_chatroom_set_favorite (chatroom, active);
932 g_object_unref (chatroom);
937 chat_window_always_urgent_toggled_cb (GtkToggleAction *toggle_action,
938 EmpathyChatWindow *window)
940 EmpathyChatWindowPriv *priv = GET_PRIV (window);
945 EmpathyChatroom *chatroom;
947 active = gtk_toggle_action_get_active (toggle_action);
948 account = empathy_chat_get_account (priv->current_chat);
949 room = empathy_chat_get_id (priv->current_chat);
950 name = empathy_chat_dup_name (priv->current_chat);
952 chatroom = empathy_chatroom_manager_ensure_chatroom (
953 priv->chatroom_manager,
958 empathy_chatroom_set_always_urgent (chatroom, active);
959 g_object_unref (chatroom);
964 chat_window_contacts_toggled_cb (GtkToggleAction *toggle_action,
965 EmpathyChatWindow *window)
967 EmpathyChatWindowPriv *priv = GET_PRIV (window);
970 active = gtk_toggle_action_get_active (toggle_action);
972 empathy_chat_set_show_contacts (priv->current_chat, active);
976 got_contact_cb (TpConnection *connection,
977 EmpathyContact *contact,
982 EmpathyTpChat *tp_chat = EMPATHY_TP_CHAT (user_data);
985 DEBUG ("Failed: %s", error->message);
988 empathy_contact_list_add (EMPATHY_CONTACT_LIST (tp_chat),
989 contact, _("Inviting you to this room"));
994 chat_window_invite_participant_activate_cb (GtkAction *action,
995 EmpathyChatWindow *window)
997 EmpathyChatWindowPriv *priv;
999 EmpathyTpChat *tp_chat;
1004 priv = GET_PRIV (window);
1006 g_return_if_fail (priv->current_chat != NULL);
1008 tp_chat = empathy_chat_get_tp_chat (priv->current_chat);
1009 channel = empathy_tp_chat_get_channel (tp_chat);
1010 account = empathy_chat_get_account (priv->current_chat);
1012 dialog = empathy_invite_participant_dialog_new (
1013 GTK_WINDOW (priv->dialog), account);
1014 gtk_widget_show (dialog);
1016 response = gtk_dialog_run (GTK_DIALOG (dialog));
1018 if (response == GTK_RESPONSE_ACCEPT) {
1019 TpConnection *connection;
1022 id = empathy_contact_selector_dialog_get_selected (
1023 EMPATHY_CONTACT_SELECTOR_DIALOG (dialog), NULL, NULL);
1024 if (EMP_STR_EMPTY (id)) goto out;
1026 connection = tp_channel_borrow_connection (channel);
1027 empathy_tp_contact_factory_get_from_id (connection, id,
1028 got_contact_cb, tp_chat, NULL, NULL);
1032 gtk_widget_destroy (dialog);
1036 chat_window_close_activate_cb (GtkAction *action,
1037 EmpathyChatWindow *window)
1039 EmpathyChatWindowPriv *priv;
1041 priv = GET_PRIV (window);
1043 g_return_if_fail (priv->current_chat != NULL);
1045 empathy_chat_window_remove_chat (window, priv->current_chat);
1049 chat_window_edit_activate_cb (GtkAction *action,
1050 EmpathyChatWindow *window)
1052 EmpathyChatWindowPriv *priv;
1053 GtkClipboard *clipboard;
1054 GtkTextBuffer *buffer;
1055 gboolean text_available;
1057 priv = GET_PRIV (window);
1059 g_return_if_fail (priv->current_chat != NULL);
1061 if (!empathy_chat_get_tp_chat (priv->current_chat)) {
1062 gtk_action_set_sensitive (priv->menu_edit_copy, FALSE);
1063 gtk_action_set_sensitive (priv->menu_edit_cut, FALSE);
1064 gtk_action_set_sensitive (priv->menu_edit_paste, FALSE);
1068 buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (priv->current_chat->input_text_view));
1069 if (gtk_text_buffer_get_has_selection (buffer)) {
1070 gtk_action_set_sensitive (priv->menu_edit_copy, TRUE);
1071 gtk_action_set_sensitive (priv->menu_edit_cut, TRUE);
1075 selection = empathy_chat_view_get_has_selection (priv->current_chat->view);
1077 gtk_action_set_sensitive (priv->menu_edit_cut, FALSE);
1078 gtk_action_set_sensitive (priv->menu_edit_copy, selection);
1081 clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
1082 text_available = gtk_clipboard_wait_is_text_available (clipboard);
1083 gtk_action_set_sensitive (priv->menu_edit_paste, text_available);
1087 chat_window_cut_activate_cb (GtkAction *action,
1088 EmpathyChatWindow *window)
1090 EmpathyChatWindowPriv *priv;
1092 g_return_if_fail (EMPATHY_IS_CHAT_WINDOW (window));
1094 priv = GET_PRIV (window);
1096 empathy_chat_cut (priv->current_chat);
1100 chat_window_copy_activate_cb (GtkAction *action,
1101 EmpathyChatWindow *window)
1103 EmpathyChatWindowPriv *priv;
1105 g_return_if_fail (EMPATHY_IS_CHAT_WINDOW (window));
1107 priv = GET_PRIV (window);
1109 empathy_chat_copy (priv->current_chat);
1113 chat_window_paste_activate_cb (GtkAction *action,
1114 EmpathyChatWindow *window)
1116 EmpathyChatWindowPriv *priv;
1118 g_return_if_fail (EMPATHY_IS_CHAT_WINDOW (window));
1120 priv = GET_PRIV (window);
1122 empathy_chat_paste (priv->current_chat);
1126 chat_window_find_activate_cb (GtkAction *action,
1127 EmpathyChatWindow *window)
1129 EmpathyChatWindowPriv *priv;
1131 g_return_if_fail (EMPATHY_IS_CHAT_WINDOW (window));
1133 priv = GET_PRIV (window);
1135 empathy_chat_find (priv->current_chat);
1139 chat_window_tabs_next_activate_cb (GtkAction *action,
1140 EmpathyChatWindow *window)
1142 EmpathyChatWindowPriv *priv;
1144 gint index_, numPages;
1145 gboolean wrap_around;
1147 priv = GET_PRIV (window);
1149 g_object_get (gtk_settings_get_default (), "gtk-keynav-wrap-around",
1150 &wrap_around, NULL);
1152 chat = priv->current_chat;
1153 index_ = gtk_notebook_get_current_page (GTK_NOTEBOOK (priv->notebook));
1154 numPages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (priv->notebook));
1156 if (index_ == (numPages - 1) && wrap_around) {
1157 gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->notebook), 0);
1161 gtk_notebook_next_page (GTK_NOTEBOOK (priv->notebook));
1165 chat_window_tabs_previous_activate_cb (GtkAction *action,
1166 EmpathyChatWindow *window)
1168 EmpathyChatWindowPriv *priv;
1170 gint index_, numPages;
1171 gboolean wrap_around;
1173 priv = GET_PRIV (window);
1175 g_object_get (gtk_settings_get_default (), "gtk-keynav-wrap-around",
1176 &wrap_around, NULL);
1178 chat = priv->current_chat;
1179 index_ = gtk_notebook_get_current_page (GTK_NOTEBOOK (priv->notebook));
1180 numPages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (priv->notebook));
1182 if (index_ <= 0 && wrap_around) {
1183 gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->notebook), numPages - 1);
1187 gtk_notebook_prev_page (GTK_NOTEBOOK (priv->notebook));
1191 chat_window_tabs_undo_close_tab_activate_cb (GtkAction *action,
1192 EmpathyChatWindow *window)
1194 EmpathyChatWindowPriv *priv = GET_PRIV (window);
1195 empathy_chat_manager_undo_closed_chat (priv->chat_manager);
1199 chat_window_tabs_left_activate_cb (GtkAction *action,
1200 EmpathyChatWindow *window)
1202 EmpathyChatWindowPriv *priv;
1204 gint index_, num_pages;
1206 priv = GET_PRIV (window);
1208 chat = priv->current_chat;
1209 index_ = gtk_notebook_get_current_page (GTK_NOTEBOOK (priv->notebook));
1214 gtk_notebook_reorder_child (GTK_NOTEBOOK (priv->notebook),
1218 num_pages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (priv->notebook));
1219 chat_window_menu_context_update (priv, num_pages);
1223 chat_window_tabs_right_activate_cb (GtkAction *action,
1224 EmpathyChatWindow *window)
1226 EmpathyChatWindowPriv *priv;
1228 gint index_, num_pages;
1230 priv = GET_PRIV (window);
1232 chat = priv->current_chat;
1233 index_ = gtk_notebook_get_current_page (GTK_NOTEBOOK (priv->notebook));
1235 gtk_notebook_reorder_child (GTK_NOTEBOOK (priv->notebook),
1239 num_pages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (priv->notebook));
1240 chat_window_menu_context_update (priv, num_pages);
1243 static EmpathyChatWindow *
1244 empathy_chat_window_new (void)
1246 return EMPATHY_CHAT_WINDOW (g_object_new (EMPATHY_TYPE_CHAT_WINDOW, NULL));
1250 chat_window_detach_activate_cb (GtkAction *action,
1251 EmpathyChatWindow *window)
1253 EmpathyChatWindowPriv *priv;
1254 EmpathyChatWindow *new_window;
1257 priv = GET_PRIV (window);
1259 chat = priv->current_chat;
1260 new_window = empathy_chat_window_new ();
1262 empathy_chat_window_move_chat (window, new_window, chat);
1264 priv = GET_PRIV (new_window);
1265 gtk_widget_show (priv->dialog);
1269 chat_window_help_contents_activate_cb (GtkAction *action,
1270 EmpathyChatWindow *window)
1272 EmpathyChatWindowPriv *priv = GET_PRIV (window);
1274 empathy_url_show (priv->dialog, "ghelp:empathy");
1278 chat_window_help_about_activate_cb (GtkAction *action,
1279 EmpathyChatWindow *window)
1281 EmpathyChatWindowPriv *priv = GET_PRIV (window);
1283 empathy_about_dialog_new (GTK_WINDOW (priv->dialog));
1287 chat_window_delete_event_cb (GtkWidget *dialog,
1289 EmpathyChatWindow *window)
1291 EmpathyChatWindowPriv *priv = GET_PRIV (window);
1293 DEBUG ("Delete event received");
1295 g_object_ref (window);
1296 while (priv->chats) {
1297 empathy_chat_window_remove_chat (window, priv->chats->data);
1299 g_object_unref (window);
1305 chat_window_composing_cb (EmpathyChat *chat,
1306 gboolean is_composing,
1307 EmpathyChatWindow *window)
1309 chat_window_update_chat_tab (chat);
1313 chat_window_set_urgency_hint (EmpathyChatWindow *window,
1316 EmpathyChatWindowPriv *priv;
1318 priv = GET_PRIV (window);
1320 gtk_window_set_urgency_hint (GTK_WINDOW (priv->dialog), urgent);
1324 chat_window_notification_closed_cb (NotifyNotification *notify,
1325 EmpathyChatWindow *self)
1327 EmpathyChatWindowPriv *priv = GET_PRIV (self);
1329 g_object_unref (notify);
1330 if (priv->notification == notify) {
1331 priv->notification = NULL;
1336 chat_window_show_or_update_notification (EmpathyChatWindow *window,
1337 EmpathyMessage *message,
1340 EmpathyContact *sender;
1341 const gchar *header;
1345 EmpathyChatWindowPriv *priv = GET_PRIV (window);
1346 gboolean res, has_x_canonical_append;
1347 NotifyNotification *notification = priv->notification;
1349 if (!empathy_notify_manager_notification_is_enabled (priv->notify_mgr)) {
1352 res = g_settings_get_boolean (priv->gsettings_notif,
1353 EMPATHY_PREFS_NOTIFICATIONS_FOCUS);
1360 sender = empathy_message_get_sender (message);
1361 header = empathy_contact_get_alias (sender);
1362 body = empathy_message_get_body (message);
1363 escaped = g_markup_escape_text (body, -1);
1364 has_x_canonical_append = empathy_notify_manager_has_capability (
1365 priv->notify_mgr, EMPATHY_NOTIFY_MANAGER_CAP_X_CANONICAL_APPEND);
1367 if (notification != NULL && !has_x_canonical_append) {
1368 /* if the notification server supports x-canonical-append, it is
1369 better to not use notify_notification_update to avoid
1370 overwriting the current notification message */
1371 notify_notification_update (notification,
1372 header, escaped, NULL);
1374 /* if the notification server supports x-canonical-append,
1375 the hint will be added, so that the message from the
1376 just created notification will be automatically appended
1377 to an existing notification with the same title.
1378 In this way the previous message will not be lost: the new
1379 message will appear below it, in the same notification */
1380 notification = notify_notification_new (header, escaped, NULL);
1382 if (priv->notification == NULL) {
1383 priv->notification = notification;
1386 notify_notification_set_timeout (notification, NOTIFY_EXPIRES_DEFAULT);
1388 tp_g_signal_connect_object (notification, "closed",
1389 G_CALLBACK (chat_window_notification_closed_cb), window, 0);
1391 if (has_x_canonical_append) {
1392 notify_notification_set_hint_string (notification,
1393 EMPATHY_NOTIFY_MANAGER_CAP_X_CANONICAL_APPEND, "");
1397 pixbuf = empathy_notify_manager_get_pixbuf_for_notification (priv->notify_mgr,
1398 sender, EMPATHY_IMAGE_NEW_MESSAGE);
1400 if (pixbuf != NULL) {
1401 notify_notification_set_icon_from_pixbuf (notification, pixbuf);
1402 g_object_unref (pixbuf);
1405 notify_notification_show (notification, NULL);
1411 chat_window_set_highlight_room_labels (EmpathyChat *chat)
1413 gchar *markup, *name;
1416 if (!empathy_chat_is_room (chat))
1419 name = empathy_chat_dup_name (chat);
1420 markup = g_markup_printf_escaped (
1421 "<span color=\"red\" weight=\"bold\">%s</span>",
1424 widget = g_object_get_data (G_OBJECT (chat), "chat-window-tab-label");
1425 gtk_label_set_markup (GTK_LABEL (widget), markup);
1427 widget = g_object_get_data (G_OBJECT (chat), "chat-window-menu-label");
1428 gtk_label_set_markup (GTK_LABEL (widget), markup);
1435 empathy_chat_window_has_focus (EmpathyChatWindow *window)
1437 EmpathyChatWindowPriv *priv;
1440 g_return_val_if_fail (EMPATHY_IS_CHAT_WINDOW (window), FALSE);
1442 priv = GET_PRIV (window);
1444 g_object_get (priv->dialog, "has-toplevel-focus", &has_focus, NULL);
1450 chat_window_new_message_cb (EmpathyChat *chat,
1451 EmpathyMessage *message,
1453 EmpathyChatWindow *window)
1455 EmpathyChatWindowPriv *priv;
1457 gboolean needs_urgency;
1458 EmpathyContact *sender;
1460 priv = GET_PRIV (window);
1462 has_focus = empathy_chat_window_has_focus (window);
1464 /* - if we're the sender, we play the sound if it's specified in the
1465 * preferences and we're not away.
1466 * - if we receive a message, we play the sound if it's specified in the
1467 * preferences and the window does not have focus on the chat receiving
1471 sender = empathy_message_get_sender (message);
1473 if (empathy_contact_is_user (sender)) {
1474 empathy_sound_manager_play (priv->sound_mgr, GTK_WIDGET (priv->dialog),
1475 EMPATHY_SOUND_MESSAGE_OUTGOING);
1478 if (has_focus && priv->current_chat == chat) {
1479 /* window and tab are focused so consider the message to be read */
1481 /* FIXME: see Bug#610994 and coments about it in EmpathyChatPriv */
1482 empathy_chat_messages_read (chat);
1486 /* Update the chat tab if this is the first unread message */
1487 if (empathy_chat_get_nb_unread_messages (chat) == 1) {
1488 chat_window_update_chat_tab (chat);
1491 /* If empathy_chat_is_room () returns TRUE, that means it's a named MUC.
1492 * If empathy_chat_get_remote_contact () returns NULL, that means it's
1493 * an unamed MUC (msn-like).
1494 * In case of a MUC, we set urgency if either:
1495 * a) the chatroom's always_urgent property is TRUE
1496 * b) the message contains our alias
1498 if (empathy_chat_is_room (chat) ||
1499 empathy_chat_get_remote_contact (chat) == NULL) {
1502 EmpathyChatroom *chatroom;
1504 account = empathy_chat_get_account (chat);
1505 room = empathy_chat_get_id (chat);
1507 chatroom = empathy_chatroom_manager_find (priv->chatroom_manager,
1510 if (chatroom != NULL && empathy_chatroom_is_always_urgent (chatroom)) {
1511 needs_urgency = TRUE;
1513 needs_urgency = empathy_message_should_highlight (message);
1516 needs_urgency = TRUE;
1519 if (needs_urgency) {
1520 chat_window_set_highlight_room_labels (chat);
1523 chat_window_set_urgency_hint (window, TRUE);
1526 /* Pending messages have already been displayed and notified in the
1527 * approver, so we don't display a notification and play a sound for those */
1529 empathy_sound_manager_play (priv->sound_mgr, GTK_WIDGET (priv->dialog),
1530 EMPATHY_SOUND_MESSAGE_INCOMING);
1532 chat_window_show_or_update_notification (window, message, chat);
1536 /* update the number of unread messages and the window icon */
1537 chat_window_title_update (priv);
1538 chat_window_icon_update (priv, TRUE);
1541 static GtkNotebook *
1542 notebook_create_window_cb (GtkNotebook *source,
1548 EmpathyChatWindowPriv *priv;
1549 EmpathyChatWindow *window, *new_window;
1552 chat = EMPATHY_CHAT (page);
1553 window = chat_window_find_chat (chat);
1555 new_window = empathy_chat_window_new ();
1556 priv = GET_PRIV (new_window);
1558 DEBUG ("Detach hook called");
1560 empathy_chat_window_move_chat (window, new_window, chat);
1562 gtk_widget_show (priv->dialog);
1563 gtk_window_move (GTK_WINDOW (priv->dialog), x, y);
1569 chat_window_page_switched_cb (GtkNotebook *notebook,
1572 EmpathyChatWindow *window)
1574 EmpathyChatWindowPriv *priv = GET_PRIV (window);
1575 EmpathyChat *chat = EMPATHY_CHAT (child);
1577 DEBUG ("Page switched");
1579 if (priv->page_added) {
1580 priv->page_added = FALSE;
1581 empathy_chat_scroll_down (chat);
1583 else if (priv->current_chat == chat) {
1587 priv->current_chat = chat;
1588 empathy_chat_messages_read (chat);
1590 chat_window_update_chat_tab (chat);
1594 chat_window_page_added_cb (GtkNotebook *notebook,
1597 EmpathyChatWindow *window)
1599 EmpathyChatWindowPriv *priv;
1602 priv = GET_PRIV (window);
1604 /* If we just received DND to the same window, we don't want
1605 * to do anything here like removing the tab and then readding
1606 * it, so we return here and in "page-added".
1608 if (priv->dnd_same_window) {
1609 DEBUG ("Page added (back to the same window)");
1610 priv->dnd_same_window = FALSE;
1614 DEBUG ("Page added");
1616 /* Get chat object */
1617 chat = EMPATHY_CHAT (child);
1619 /* Connect chat signals for this window */
1620 g_signal_connect (chat, "composing",
1621 G_CALLBACK (chat_window_composing_cb),
1623 g_signal_connect (chat, "new-message",
1624 G_CALLBACK (chat_window_new_message_cb),
1626 g_signal_connect (chat, "notify::tp-chat",
1627 G_CALLBACK (chat_window_update_chat_tab),
1630 /* Set flag so we know to perform some special operations on
1631 * switch page due to the new page being added.
1633 priv->page_added = TRUE;
1635 /* Get list of chats up to date */
1636 priv->chats = g_list_append (priv->chats, chat);
1638 chat_window_update_chat_tab (chat);
1642 chat_window_page_removed_cb (GtkNotebook *notebook,
1645 EmpathyChatWindow *window)
1647 EmpathyChatWindowPriv *priv;
1650 priv = GET_PRIV (window);
1652 /* If we just received DND to the same window, we don't want
1653 * to do anything here like removing the tab and then readding
1654 * it, so we return here and in "page-added".
1656 if (priv->dnd_same_window) {
1657 DEBUG ("Page removed (and will be readded to same window)");
1661 DEBUG ("Page removed");
1663 /* Get chat object */
1664 chat = EMPATHY_CHAT (child);
1666 /* Disconnect all signal handlers for this chat and this window */
1667 g_signal_handlers_disconnect_by_func (chat,
1668 G_CALLBACK (chat_window_composing_cb),
1670 g_signal_handlers_disconnect_by_func (chat,
1671 G_CALLBACK (chat_window_new_message_cb),
1673 g_signal_handlers_disconnect_by_func (chat,
1674 G_CALLBACK (chat_window_update_chat_tab),
1677 /* Keep list of chats up to date */
1678 priv->chats = g_list_remove (priv->chats, chat);
1679 empathy_chat_messages_read (chat);
1681 if (priv->chats == NULL) {
1682 g_object_unref (window);
1684 chat_window_update (window, TRUE);
1689 chat_window_focus_in_event_cb (GtkWidget *widget,
1691 EmpathyChatWindow *window)
1693 EmpathyChatWindowPriv *priv;
1695 priv = GET_PRIV (window);
1697 empathy_chat_messages_read (priv->current_chat);
1699 chat_window_set_urgency_hint (window, FALSE);
1701 /* Update the title, since we now mark all unread messages as read. */
1702 chat_window_update_chat_tab_full (priv->current_chat, FALSE);
1708 chat_window_drag_drop (GtkWidget *widget,
1709 GdkDragContext *context,
1713 EmpathyChatWindow *window)
1716 EmpathyChatWindowPriv *priv;
1718 priv = GET_PRIV (window);
1720 target = gtk_drag_dest_find_target (widget, context, priv->file_targets);
1721 if (target == GDK_NONE)
1722 target = gtk_drag_dest_find_target (widget, context, priv->contact_targets);
1724 if (target != GDK_NONE) {
1725 gtk_drag_get_data (widget, context, target, time_);
1733 chat_window_drag_motion (GtkWidget *widget,
1734 GdkDragContext *context,
1738 EmpathyChatWindow *window)
1741 EmpathyChatWindowPriv *priv;
1743 priv = GET_PRIV (window);
1745 target = gtk_drag_dest_find_target (widget, context, priv->file_targets);
1746 if (target != GDK_NONE) {
1747 /* This is a file drag. Ensure the contact is online and set the
1748 drag type to COPY. Note that it's possible that the tab will
1749 be switched by GTK+ after a timeout from drag_motion without
1750 getting another drag_motion to disable the drop. You have
1751 to hold your mouse really still.
1753 EmpathyContact *contact;
1755 priv = GET_PRIV (window);
1756 contact = empathy_chat_get_remote_contact (priv->current_chat);
1757 /* contact is NULL for multi-user chats. We don't do
1758 * file transfers to MUCs. We also don't send files
1759 * to offline contacts or contacts that don't support
1762 if ((contact == NULL) || !empathy_contact_is_online (contact)) {
1763 gdk_drag_status (context, 0, time_);
1766 if (!(empathy_contact_get_capabilities (contact)
1767 & EMPATHY_CAPABILITIES_FT)) {
1768 gdk_drag_status (context, 0, time_);
1771 gdk_drag_status (context, GDK_ACTION_COPY, time_);
1775 target = gtk_drag_dest_find_target (widget, context, priv->contact_targets);
1776 if (target != GDK_NONE) {
1777 /* This is a drag of a contact from a contact list. Set to COPY.
1778 FIXME: If this drag is to a MUC window, it invites the user.
1779 Otherwise, it opens a chat. Should we use a different drag
1780 type for invites? Should we allow ASK?
1782 gdk_drag_status (context, GDK_ACTION_COPY, time_);
1790 chat_window_drag_data_received (GtkWidget *widget,
1791 GdkDragContext *context,
1794 GtkSelectionData *selection,
1797 EmpathyChatWindow *window)
1799 if (info == DND_DRAG_TYPE_CONTACT_ID) {
1800 EmpathyChat *chat = NULL;
1801 EmpathyChatWindow *old_window;
1802 TpAccount *account = NULL;
1803 TpAccountManager *account_manager;
1806 const gchar *account_id;
1807 const gchar *contact_id;
1809 id = (const gchar*) gtk_selection_data_get_data (selection);
1811 /* FIXME: Perhaps should be sure that the account manager is
1812 * prepared before calling _ensure_account on it. */
1813 account_manager = tp_account_manager_dup ();
1815 DEBUG ("DND contact from roster with id:'%s'", id);
1817 strv = g_strsplit (id, ":", 2);
1818 if (g_strv_length (strv) == 2) {
1819 account_id = strv[0];
1820 contact_id = strv[1];
1822 tp_account_manager_ensure_account (account_manager, account_id);
1823 if (account != NULL)
1824 chat = empathy_chat_window_find_chat (account, contact_id, FALSE);
1827 if (account == NULL) {
1829 gtk_drag_finish (context, FALSE, FALSE, time_);
1834 empathy_chat_with_contact_id (
1835 account, contact_id, gtk_get_current_event_time ());
1840 g_object_unref (account_manager);
1843 old_window = chat_window_find_chat (chat);
1845 if (old_window == window) {
1846 gtk_drag_finish (context, TRUE, FALSE, time_);
1850 empathy_chat_window_move_chat (old_window, window, chat);
1852 empathy_chat_window_add_chat (window, chat);
1855 /* Added to take care of any outstanding chat events */
1856 empathy_chat_window_present_chat (chat,
1857 TP_USER_ACTION_TIME_NOT_USER_ACTION);
1859 /* We should return TRUE to remove the data when doing
1860 * GDK_ACTION_MOVE, but we don't here otherwise it has
1861 * weird consequences, and we handle that internally
1862 * anyway with add_chat () and remove_chat ().
1864 gtk_drag_finish (context, TRUE, FALSE, time_);
1866 else if (info == DND_DRAG_TYPE_URI_LIST) {
1867 EmpathyChatWindowPriv *priv;
1868 EmpathyContact *contact;
1871 priv = GET_PRIV (window);
1872 contact = empathy_chat_get_remote_contact (priv->current_chat);
1874 /* contact is NULL when current_chat is a multi-user chat.
1875 * We don't do file transfers to MUCs, so just cancel the drag.
1877 if (contact == NULL) {
1878 gtk_drag_finish (context, TRUE, FALSE, time_);
1882 data = (const gchar *) gtk_selection_data_get_data (selection);
1883 empathy_send_file_from_uri_list (contact, data);
1885 gtk_drag_finish (context, TRUE, FALSE, time_);
1887 else if (info == DND_DRAG_TYPE_TAB) {
1889 EmpathyChatWindow *old_window = NULL;
1893 chat = (void *) gtk_selection_data_get_data (selection);
1894 old_window = chat_window_find_chat (*chat);
1897 EmpathyChatWindowPriv *priv;
1899 priv = GET_PRIV (window);
1900 priv->dnd_same_window = (old_window == window);
1901 DEBUG ("DND tab (within same window: %s)",
1902 priv->dnd_same_window ? "Yes" : "No");
1905 DEBUG ("DND from unknown source");
1906 gtk_drag_finish (context, FALSE, FALSE, time_);
1911 chat_window_chat_manager_chats_changed_cb (EmpathyChatManager *chat_manager,
1912 guint num_chats_in_manager,
1913 EmpathyChatWindow *window)
1915 EmpathyChatWindowPriv *priv = GET_PRIV (window);
1917 gtk_action_set_sensitive (priv->menu_tabs_undo_close_tab,
1918 num_chats_in_manager > 0);
1922 chat_window_finalize (GObject *object)
1924 EmpathyChatWindow *window;
1925 EmpathyChatWindowPriv *priv;
1927 window = EMPATHY_CHAT_WINDOW (object);
1928 priv = GET_PRIV (window);
1930 DEBUG ("Finalized: %p", object);
1932 g_object_unref (priv->ui_manager);
1933 g_object_unref (priv->chatroom_manager);
1934 g_object_unref (priv->notify_mgr);
1935 g_object_unref (priv->gsettings_chat);
1936 g_object_unref (priv->gsettings_notif);
1937 g_object_unref (priv->gsettings_ui);
1938 g_object_unref (priv->sound_mgr);
1940 if (priv->notification != NULL) {
1941 notify_notification_close (priv->notification, NULL);
1942 priv->notification = NULL;
1945 if (priv->contact_targets) {
1946 gtk_target_list_unref (priv->contact_targets);
1948 if (priv->file_targets) {
1949 gtk_target_list_unref (priv->file_targets);
1952 if (priv->chat_manager) {
1953 g_signal_handler_disconnect (priv->chat_manager,
1954 priv->chat_manager_chats_changed_id);
1955 g_object_unref (priv->chat_manager);
1956 priv->chat_manager = NULL;
1959 chat_windows = g_list_remove (chat_windows, window);
1960 gtk_widget_destroy (priv->dialog);
1962 G_OBJECT_CLASS (empathy_chat_window_parent_class)->finalize (object);
1966 empathy_chat_window_class_init (EmpathyChatWindowClass *klass)
1968 GObjectClass *object_class = G_OBJECT_CLASS (klass);
1970 object_class->finalize = chat_window_finalize;
1972 g_type_class_add_private (object_class, sizeof (EmpathyChatWindowPriv));
1976 empathy_chat_window_init (EmpathyChatWindow *window)
1979 GtkAccelGroup *accel_group;
1984 GtkWidget *chat_vbox;
1986 EmpathySmileyManager *smiley_manager;
1987 EmpathyChatWindowPriv *priv = G_TYPE_INSTANCE_GET_PRIVATE (window,
1988 EMPATHY_TYPE_CHAT_WINDOW, EmpathyChatWindowPriv);
1990 window->priv = priv;
1991 filename = empathy_file_lookup ("empathy-chat-window.ui", "src");
1992 gui = empathy_builder_get_file (filename,
1993 "chat_window", &priv->dialog,
1994 "chat_vbox", &chat_vbox,
1995 "ui_manager", &priv->ui_manager,
1996 "menu_conv_insert_smiley", &priv->menu_conv_insert_smiley,
1997 "menu_conv_favorite", &priv->menu_conv_favorite,
1998 "menu_conv_always_urgent", &priv->menu_conv_always_urgent,
1999 "menu_conv_toggle_contacts", &priv->menu_conv_toggle_contacts,
2000 "menu_edit_cut", &priv->menu_edit_cut,
2001 "menu_edit_copy", &priv->menu_edit_copy,
2002 "menu_edit_paste", &priv->menu_edit_paste,
2003 "menu_edit_find", &priv->menu_edit_find,
2004 "menu_tabs_next", &priv->menu_tabs_next,
2005 "menu_tabs_prev", &priv->menu_tabs_prev,
2006 "menu_tabs_undo_close_tab", &priv->menu_tabs_undo_close_tab,
2007 "menu_tabs_left", &priv->menu_tabs_left,
2008 "menu_tabs_right", &priv->menu_tabs_right,
2009 "menu_tabs_detach", &priv->menu_tabs_detach,
2013 empathy_builder_connect (gui, window,
2014 "menu_conv", "activate", chat_window_conv_activate_cb,
2015 "menu_conv_clear", "activate", chat_window_clear_activate_cb,
2016 "menu_conv_favorite", "toggled", chat_window_favorite_toggled_cb,
2017 "menu_conv_always_urgent", "toggled", chat_window_always_urgent_toggled_cb,
2018 "menu_conv_toggle_contacts", "toggled", chat_window_contacts_toggled_cb,
2019 "menu_conv_invite_participant", "activate", chat_window_invite_participant_activate_cb,
2020 "menu_conv_close", "activate", chat_window_close_activate_cb,
2021 "menu_edit", "activate", chat_window_edit_activate_cb,
2022 "menu_edit_cut", "activate", chat_window_cut_activate_cb,
2023 "menu_edit_copy", "activate", chat_window_copy_activate_cb,
2024 "menu_edit_paste", "activate", chat_window_paste_activate_cb,
2025 "menu_edit_find", "activate", chat_window_find_activate_cb,
2026 "menu_tabs_next", "activate", chat_window_tabs_next_activate_cb,
2027 "menu_tabs_prev", "activate", chat_window_tabs_previous_activate_cb,
2028 "menu_tabs_undo_close_tab", "activate", chat_window_tabs_undo_close_tab_activate_cb,
2029 "menu_tabs_left", "activate", chat_window_tabs_left_activate_cb,
2030 "menu_tabs_right", "activate", chat_window_tabs_right_activate_cb,
2031 "menu_tabs_detach", "activate", chat_window_detach_activate_cb,
2032 "menu_help_contents", "activate", chat_window_help_contents_activate_cb,
2033 "menu_help_about", "activate", chat_window_help_about_activate_cb,
2036 g_object_ref (priv->ui_manager);
2037 g_object_unref (gui);
2039 priv->gsettings_chat = g_settings_new (EMPATHY_PREFS_CHAT_SCHEMA);
2040 priv->gsettings_notif = g_settings_new (EMPATHY_PREFS_NOTIFICATIONS_SCHEMA);
2041 priv->gsettings_ui = g_settings_new (EMPATHY_PREFS_UI_SCHEMA);
2042 priv->chatroom_manager = empathy_chatroom_manager_dup_singleton (NULL);
2044 priv->sound_mgr = empathy_sound_manager_dup_singleton ();
2046 priv->notebook = gtk_notebook_new ();
2048 g_signal_connect (priv->notebook, "create-window",
2049 G_CALLBACK (notebook_create_window_cb), window);
2051 gtk_notebook_set_group_name (GTK_NOTEBOOK (priv->notebook),
2052 "EmpathyChatWindow");
2053 gtk_notebook_set_scrollable (GTK_NOTEBOOK (priv->notebook), TRUE);
2054 gtk_notebook_popup_enable (GTK_NOTEBOOK (priv->notebook));
2055 gtk_box_pack_start (GTK_BOX (chat_vbox), priv->notebook, TRUE, TRUE, 0);
2056 gtk_widget_show (priv->notebook);
2059 accel_group = gtk_accel_group_new ();
2060 gtk_window_add_accel_group (GTK_WINDOW (priv->dialog), accel_group);
2062 for (i = 0; i < G_N_ELEMENTS (tab_accel_keys); i++) {
2063 closure = g_cclosure_new (G_CALLBACK (chat_window_accel_cb),
2066 gtk_accel_group_connect (accel_group,
2073 g_object_unref (accel_group);
2075 /* Set up drag target lists */
2076 priv->contact_targets = gtk_target_list_new (drag_types_dest_contact,
2077 G_N_ELEMENTS (drag_types_dest_contact));
2078 priv->file_targets = gtk_target_list_new (drag_types_dest_file,
2079 G_N_ELEMENTS (drag_types_dest_file));
2081 /* Set up smiley menu */
2082 smiley_manager = empathy_smiley_manager_dup_singleton ();
2083 submenu = empathy_smiley_menu_new (smiley_manager,
2084 chat_window_insert_smiley_activate_cb,
2086 menu = gtk_ui_manager_get_widget (priv->ui_manager,
2087 "/chats_menubar/menu_conv/menu_conv_insert_smiley");
2088 gtk_menu_item_set_submenu (GTK_MENU_ITEM (menu), submenu);
2089 g_object_unref (smiley_manager);
2091 /* Set up signals we can't do with ui file since we may need to
2092 * block/unblock them at some later stage.
2095 g_signal_connect (priv->dialog,
2097 G_CALLBACK (chat_window_delete_event_cb),
2099 g_signal_connect (priv->dialog,
2101 G_CALLBACK (chat_window_focus_in_event_cb),
2103 g_signal_connect_after (priv->notebook,
2105 G_CALLBACK (chat_window_page_switched_cb),
2107 g_signal_connect (priv->notebook,
2109 G_CALLBACK (chat_window_page_added_cb),
2111 g_signal_connect (priv->notebook,
2113 G_CALLBACK (chat_window_page_removed_cb),
2116 /* Set up drag and drop */
2117 gtk_drag_dest_set (GTK_WIDGET (priv->notebook),
2118 GTK_DEST_DEFAULT_HIGHLIGHT,
2120 G_N_ELEMENTS (drag_types_dest),
2121 GDK_ACTION_MOVE | GDK_ACTION_COPY);
2123 /* connect_after to allow GtkNotebook's built-in tab switching */
2124 g_signal_connect_after (priv->notebook,
2126 G_CALLBACK (chat_window_drag_motion),
2128 g_signal_connect (priv->notebook,
2129 "drag-data-received",
2130 G_CALLBACK (chat_window_drag_data_received),
2132 g_signal_connect (priv->notebook,
2134 G_CALLBACK (chat_window_drag_drop),
2137 chat_windows = g_list_prepend (chat_windows, window);
2139 /* Set up private details */
2141 priv->current_chat = NULL;
2142 priv->notification = NULL;
2144 priv->notify_mgr = empathy_notify_manager_dup_singleton ();
2146 priv->chat_manager = empathy_chat_manager_dup_singleton ();
2147 priv->chat_manager_chats_changed_id =
2148 g_signal_connect (priv->chat_manager, "closed-chats-changed",
2149 G_CALLBACK (chat_window_chat_manager_chats_changed_cb),
2152 chat_window_chat_manager_chats_changed_cb (priv->chat_manager,
2153 empathy_chat_manager_get_num_closed_chats (priv->chat_manager),
2158 empathy_chat_window_get_dialog (EmpathyChatWindow *window)
2160 EmpathyChatWindowPriv *priv;
2162 g_return_val_if_fail (window != NULL, NULL);
2164 priv = GET_PRIV (window);
2166 return priv->dialog;
2169 /* Returns the window to open a new tab in if there is a suitable window,
2170 * otherwise, returns NULL indicating that a new window should be added.
2172 static EmpathyChatWindow *
2173 empathy_chat_window_get_default (gboolean room)
2175 GSettings *gsettings = g_settings_new (EMPATHY_PREFS_UI_SCHEMA);
2177 gboolean separate_windows = TRUE;
2179 separate_windows = g_settings_get_boolean (gsettings,
2180 EMPATHY_PREFS_UI_SEPARATE_CHAT_WINDOWS);
2182 g_object_unref (gsettings);
2184 if (separate_windows) {
2185 /* Always create a new window */
2189 for (l = chat_windows; l; l = l->next) {
2190 EmpathyChatWindowPriv *priv;
2191 EmpathyChatWindow *chat_window;
2193 guint nb_rooms, nb_private;
2195 chat_window = l->data;
2196 priv = GET_PRIV (chat_window);
2198 dialog = empathy_chat_window_get_dialog (chat_window);
2200 empathy_chat_window_get_nb_chats (chat_window, &nb_rooms, &nb_private);
2202 /* Skip the window if there aren't any rooms in it */
2203 if (room && nb_rooms == 0)
2206 /* Skip the window if there aren't any 1-1 chats in it */
2207 if (!room && nb_private == 0)
2210 /* Found a window on this desktop, make it visible if necessary */
2211 if (!empathy_window_get_is_visible (GTK_WINDOW (dialog)))
2212 empathy_window_present (GTK_WINDOW (dialog));
2220 empathy_chat_window_add_chat (EmpathyChatWindow *window,
2223 EmpathyChatWindowPriv *priv;
2225 GtkWidget *popup_label;
2227 GValue value = { 0, };
2229 g_return_if_fail (window != NULL);
2230 g_return_if_fail (EMPATHY_IS_CHAT (chat));
2232 priv = GET_PRIV (window);
2234 /* Reference the chat object */
2235 g_object_ref (chat);
2237 /* If this window has just been created, position it */
2238 if (priv->chats == NULL) {
2239 const gchar *name = "chat-window";
2240 gboolean separate_windows;
2242 separate_windows = g_settings_get_boolean (priv->gsettings_ui,
2243 EMPATHY_PREFS_UI_SEPARATE_CHAT_WINDOWS);
2245 if (empathy_chat_is_room (chat))
2246 name = "room-window";
2248 if (separate_windows) {
2251 /* Save current position of the window */
2252 gtk_window_get_position (GTK_WINDOW (priv->dialog), &x, &y);
2254 /* First bind to the 'generic' name. So new window for which we didn't
2255 * save a geometry yet will have the geometry of the last saved
2256 * window (bgo #601191). */
2257 empathy_geometry_bind (GTK_WINDOW (priv->dialog), name);
2259 /* Restore previous position of the window so the newly created window
2260 * won't be in the same position as the latest saved window and so
2261 * completely hide it. */
2262 gtk_window_move (GTK_WINDOW (priv->dialog), x, y);
2264 /* Then bind it to the name of the contact/room so we'll save the
2265 * geometry specific to this window */
2266 name = empathy_chat_get_id (chat);
2269 empathy_geometry_bind (GTK_WINDOW (priv->dialog), name);
2272 child = GTK_WIDGET (chat);
2273 label = chat_window_create_label (window, chat, TRUE);
2274 popup_label = chat_window_create_label (window, chat, FALSE);
2275 gtk_widget_show (child);
2277 g_signal_connect (chat, "notify::name",
2278 G_CALLBACK (chat_window_chat_notify_cb),
2280 g_signal_connect (chat, "notify::subject",
2281 G_CALLBACK (chat_window_chat_notify_cb),
2283 g_signal_connect (chat, "notify::remote-contact",
2284 G_CALLBACK (chat_window_chat_notify_cb),
2286 g_signal_connect (chat, "notify::sms-channel",
2287 G_CALLBACK (chat_window_chat_notify_cb),
2289 g_signal_connect (chat, "notify::n-messages-sending",
2290 G_CALLBACK (chat_window_chat_notify_cb),
2292 chat_window_chat_notify_cb (chat);
2294 gtk_notebook_append_page_menu (GTK_NOTEBOOK (priv->notebook), child, label, popup_label);
2295 gtk_notebook_set_tab_reorderable (GTK_NOTEBOOK (priv->notebook), child, TRUE);
2296 gtk_notebook_set_tab_detachable (GTK_NOTEBOOK (priv->notebook), child, TRUE);
2297 g_value_init (&value, G_TYPE_BOOLEAN);
2298 g_value_set_boolean (&value, TRUE);
2299 gtk_container_child_set_property (GTK_CONTAINER (priv->notebook),
2300 child, "tab-expand" , &value);
2301 gtk_container_child_set_property (GTK_CONTAINER (priv->notebook),
2302 child, "tab-fill" , &value);
2303 g_value_unset (&value);
2305 DEBUG ("Chat added (%d references)", G_OBJECT (chat)->ref_count);
2309 empathy_chat_window_remove_chat (EmpathyChatWindow *window,
2312 EmpathyChatWindowPriv *priv;
2314 EmpathyContact *remote_contact;
2315 EmpathyChatManager *chat_manager;
2317 g_return_if_fail (window != NULL);
2318 g_return_if_fail (EMPATHY_IS_CHAT (chat));
2320 priv = GET_PRIV (window);
2322 g_signal_handlers_disconnect_by_func (chat,
2323 chat_window_chat_notify_cb,
2325 remote_contact = g_object_get_data (G_OBJECT (chat),
2326 "chat-window-remote-contact");
2327 if (remote_contact) {
2328 g_signal_handlers_disconnect_by_func (remote_contact,
2329 chat_window_update_chat_tab,
2333 chat_manager = empathy_chat_manager_dup_singleton ();
2334 empathy_chat_manager_closed_chat (chat_manager, chat);
2335 g_object_unref (chat_manager);
2337 position = gtk_notebook_page_num (GTK_NOTEBOOK (priv->notebook),
2339 gtk_notebook_remove_page (GTK_NOTEBOOK (priv->notebook), position);
2341 DEBUG ("Chat removed (%d references)", G_OBJECT (chat)->ref_count - 1);
2343 g_object_unref (chat);
2347 empathy_chat_window_move_chat (EmpathyChatWindow *old_window,
2348 EmpathyChatWindow *new_window,
2353 g_return_if_fail (EMPATHY_IS_CHAT_WINDOW (old_window));
2354 g_return_if_fail (EMPATHY_IS_CHAT_WINDOW (new_window));
2355 g_return_if_fail (EMPATHY_IS_CHAT (chat));
2357 widget = GTK_WIDGET (chat);
2359 DEBUG ("Chat moving with widget:%p (%d references)", widget,
2360 G_OBJECT (widget)->ref_count);
2362 /* We reference here to make sure we don't loose the widget
2363 * and the EmpathyChat object during the move.
2365 g_object_ref (chat);
2366 g_object_ref (widget);
2368 empathy_chat_window_remove_chat (old_window, chat);
2369 empathy_chat_window_add_chat (new_window, chat);
2371 g_object_unref (widget);
2372 g_object_unref (chat);
2376 empathy_chat_window_switch_to_chat (EmpathyChatWindow *window,
2379 EmpathyChatWindowPriv *priv;
2382 g_return_if_fail (window != NULL);
2383 g_return_if_fail (EMPATHY_IS_CHAT (chat));
2385 priv = GET_PRIV (window);
2387 page_num = gtk_notebook_page_num (GTK_NOTEBOOK (priv->notebook),
2389 gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->notebook),
2394 empathy_chat_window_find_chat (TpAccount *account,
2396 gboolean sms_channel)
2400 g_return_val_if_fail (!EMP_STR_EMPTY (id), NULL);
2402 for (l = chat_windows; l; l = l->next) {
2403 EmpathyChatWindowPriv *priv;
2404 EmpathyChatWindow *window;
2408 priv = GET_PRIV (window);
2410 for (ll = priv->chats; ll; ll = ll->next) {
2415 if (account == empathy_chat_get_account (chat) &&
2416 !tp_strdiff (id, empathy_chat_get_id (chat)) &&
2417 sms_channel == empathy_chat_is_sms_channel (chat)) {
2427 empathy_chat_window_present_chat (EmpathyChat *chat,
2430 EmpathyChatWindow *window;
2431 EmpathyChatWindowPriv *priv;
2432 guint32 x_timestamp;
2434 g_return_if_fail (EMPATHY_IS_CHAT (chat));
2436 window = chat_window_find_chat (chat);
2438 /* If the chat has no window, create one */
2439 if (window == NULL) {
2440 window = empathy_chat_window_get_default (empathy_chat_is_room (chat));
2442 window = empathy_chat_window_new ();
2445 empathy_chat_window_add_chat (window, chat);
2448 /* Don't force the window to show itself when it wasn't
2449 * an action by the user
2451 if (!tp_user_action_time_should_present (timestamp, &x_timestamp))
2454 priv = GET_PRIV (window);
2456 if (x_timestamp != GDK_CURRENT_TIME) {
2457 /* Don't present or switch tab if the action was earlier than the
2458 * last actions X time, accounting for overflow and the first ever
2461 if (priv->x_user_action_time != 0
2462 && X_EARLIER_OR_EQL (x_timestamp, priv->x_user_action_time))
2465 priv->x_user_action_time = x_timestamp;
2468 empathy_chat_window_switch_to_chat (window, chat);
2469 empathy_window_present_with_time (GTK_WINDOW (priv->dialog),
2472 gtk_widget_grab_focus (chat->input_text_view);
2476 empathy_chat_window_get_nb_chats (EmpathyChatWindow *self,
2480 EmpathyChatWindowPriv *priv = GET_PRIV (self);
2482 guint _nb_rooms = 0, _nb_private = 0;
2484 for (l = priv->chats; l != NULL; l = g_list_next (l)) {
2485 if (empathy_chat_is_room (EMPATHY_CHAT (l->data)))
2491 if (nb_rooms != NULL)
2492 *nb_rooms = _nb_rooms;
2493 if (nb_private != NULL)
2494 *nb_private = _nb_private;