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)
263 GtkWidget *name_label;
264 GtkWidget *status_image;
265 GtkWidget *event_box;
266 GtkWidget *event_box_hbox;
267 PangoAttrList *attr_list;
268 PangoAttribute *attr;
270 /* The spacing between the button and the label. */
271 hbox = gtk_hbox_new (FALSE, 0);
273 event_box = gtk_event_box_new ();
274 gtk_event_box_set_visible_window (GTK_EVENT_BOX (event_box), FALSE);
276 name_label = gtk_label_new (NULL);
278 gtk_label_set_ellipsize (GTK_LABEL (name_label), PANGO_ELLIPSIZE_END);
280 attr_list = pango_attr_list_new ();
281 attr = pango_attr_scale_new (1/1.2);
282 attr->start_index = 0;
283 attr->end_index = -1;
284 pango_attr_list_insert (attr_list, attr);
285 gtk_label_set_attributes (GTK_LABEL (name_label), attr_list);
286 pango_attr_list_unref (attr_list);
288 gtk_misc_set_padding (GTK_MISC (name_label), 2, 0);
289 gtk_misc_set_alignment (GTK_MISC (name_label), 0.0, 0.5);
290 g_object_set_data (G_OBJECT (chat),
291 is_tab_label ? "chat-window-tab-label" : "chat-window-menu-label",
294 status_image = gtk_image_new ();
296 /* Spacing between the icon and label. */
297 event_box_hbox = gtk_hbox_new (FALSE, 0);
299 gtk_box_pack_start (GTK_BOX (event_box_hbox), status_image, FALSE, FALSE, 0);
300 gtk_box_pack_start (GTK_BOX (event_box_hbox), name_label, TRUE, TRUE, 0);
302 g_object_set_data (G_OBJECT (chat),
303 is_tab_label ? "chat-window-tab-image" : "chat-window-menu-image",
305 g_object_set_data (G_OBJECT (chat),
306 is_tab_label ? "chat-window-tab-tooltip-widget" : "chat-window-menu-tooltip-widget",
309 gtk_container_add (GTK_CONTAINER (event_box), event_box_hbox);
310 gtk_box_pack_start (GTK_BOX (hbox), event_box, TRUE, TRUE, 0);
313 GtkWidget *close_button;
314 GtkWidget *sending_spinner;
316 sending_spinner = gtk_spinner_new ();
318 gtk_box_pack_start (GTK_BOX (hbox), sending_spinner,
320 g_object_set_data (G_OBJECT (chat),
321 "chat-window-tab-sending-spinner",
324 close_button = gedit_close_button_new ();
325 g_object_set_data (G_OBJECT (chat), "chat-window-tab-close-button", close_button);
327 /* We don't want focus/keynav for the button to avoid clutter, and
328 * Ctrl-W works anyway.
330 gtk_widget_set_can_focus (close_button, FALSE);
331 gtk_widget_set_can_default (close_button, FALSE);
333 gtk_box_pack_end (GTK_BOX (hbox), close_button, FALSE, FALSE, 0);
335 g_signal_connect (close_button,
337 G_CALLBACK (chat_window_close_clicked_cb),
340 /* React to theme changes and also setup the size correctly. */
341 g_signal_connect (hbox,
343 G_CALLBACK (chat_tab_style_updated_cb),
347 gtk_widget_show_all (hbox);
353 _submenu_notify_visible_changed_cb (GObject *object,
357 g_signal_handlers_disconnect_by_func (object,
358 _submenu_notify_visible_changed_cb,
360 chat_window_update (EMPATHY_CHAT_WINDOW (userdata), TRUE);
364 chat_window_menu_context_update (EmpathyChatWindowPriv *priv,
369 gboolean wrap_around;
370 gboolean is_connected;
373 page_num = gtk_notebook_get_current_page (GTK_NOTEBOOK (priv->notebook));
374 first_page = (page_num == 0);
375 last_page = (page_num == (num_pages - 1));
376 g_object_get (gtk_settings_get_default (), "gtk-keynav-wrap-around",
378 is_connected = empathy_chat_get_tp_chat (priv->current_chat) != NULL;
380 gtk_action_set_sensitive (priv->menu_tabs_next, (!last_page ||
382 gtk_action_set_sensitive (priv->menu_tabs_prev, (!first_page ||
384 gtk_action_set_sensitive (priv->menu_tabs_detach, num_pages > 1);
385 gtk_action_set_sensitive (priv->menu_tabs_left, !first_page);
386 gtk_action_set_sensitive (priv->menu_tabs_right, !last_page);
387 gtk_action_set_sensitive (priv->menu_conv_insert_smiley, is_connected);
391 chat_window_conversation_menu_update (EmpathyChatWindowPriv *priv,
392 EmpathyChatWindow *self)
394 EmpathyTpChat *tp_chat;
395 TpConnection *connection;
397 gboolean sensitive = FALSE;
399 g_return_if_fail (priv->current_chat != NULL);
401 action = gtk_ui_manager_get_action (priv->ui_manager,
402 "/chats_menubar/menu_conv/menu_conv_invite_participant");
403 tp_chat = empathy_chat_get_tp_chat (priv->current_chat);
405 if (tp_chat != NULL) {
406 connection = tp_channel_borrow_connection (TP_CHANNEL (tp_chat));
408 sensitive = empathy_tp_chat_can_add_contact (tp_chat) &&
409 (tp_connection_get_status (connection, NULL) ==
410 TP_CONNECTION_STATUS_CONNECTED);
413 gtk_action_set_sensitive (action, sensitive);
417 chat_window_contact_menu_update (EmpathyChatWindowPriv *priv,
418 EmpathyChatWindow *window)
420 GtkWidget *menu, *submenu, *orig_submenu;
422 menu = gtk_ui_manager_get_widget (priv->ui_manager,
423 "/chats_menubar/menu_contact");
424 orig_submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (menu));
426 if (orig_submenu == NULL || !gtk_widget_get_visible (orig_submenu)) {
427 submenu = empathy_chat_get_contact_menu (priv->current_chat);
429 if (submenu != NULL) {
430 /* gtk_menu_attach_to_widget () doesn't behave nicely here */
431 g_object_set_data (G_OBJECT (submenu), "window", priv->dialog);
433 gtk_menu_item_set_submenu (GTK_MENU_ITEM (menu), submenu);
434 gtk_widget_show (menu);
435 gtk_widget_set_sensitive (menu, TRUE);
437 gtk_widget_set_sensitive (menu, FALSE);
440 tp_g_signal_connect_object (orig_submenu,
442 (GCallback)_submenu_notify_visible_changed_cb,
448 get_all_unread_messages (EmpathyChatWindowPriv *priv)
453 for (l = priv->chats; l != NULL; l = g_list_next (l))
454 nb += empathy_chat_get_nb_unread_messages (EMPATHY_CHAT (l->data));
460 get_window_title_name (EmpathyChatWindowPriv *priv)
462 gchar *active_name, *ret;
464 guint current_unread_msgs;
466 nb_chats = g_list_length (priv->chats);
467 g_assert (nb_chats > 0);
469 active_name = empathy_chat_dup_name (priv->current_chat);
471 current_unread_msgs = empathy_chat_get_nb_unread_messages (
476 if (current_unread_msgs == 0)
477 ret = g_strdup (active_name);
479 ret = g_strdup_printf (ngettext (
481 "%s (%d unread)", current_unread_msgs),
482 active_name, current_unread_msgs);
484 guint nb_others = nb_chats - 1;
485 guint all_unread_msgs;
487 all_unread_msgs = get_all_unread_messages (priv);
489 if (all_unread_msgs == 0) {
490 /* no unread message */
491 ret = g_strdup_printf (ngettext (
493 "%s (and %u others)", nb_others),
494 active_name, nb_others);
497 else if (all_unread_msgs == current_unread_msgs) {
498 /* unread messages are in the current tab */
499 ret = g_strdup_printf (ngettext (
501 "%s (%d unread)", current_unread_msgs),
502 active_name, current_unread_msgs);
505 else if (current_unread_msgs == 0) {
506 /* unread messages are in other tabs */
507 ret = g_strdup_printf (ngettext (
508 "%s (%d unread from others)",
509 "%s (%d unread from others)",
511 active_name, all_unread_msgs);
515 /* unread messages are in all the tabs */
516 ret = g_strdup_printf (ngettext (
517 "%s (%d unread from all)",
518 "%s (%d unread from all)",
520 active_name, all_unread_msgs);
524 g_free (active_name);
530 chat_window_title_update (EmpathyChatWindowPriv *priv)
534 name = get_window_title_name (priv);
535 gtk_window_set_title (GTK_WINDOW (priv->dialog), name);
540 chat_window_icon_update (EmpathyChatWindowPriv *priv, gboolean new_messages)
543 EmpathyContact *remote_contact;
544 gboolean avatar_in_icon;
547 n_chats = g_list_length (priv->chats);
549 /* Update window icon */
551 gtk_window_set_icon_name (GTK_WINDOW (priv->dialog),
552 EMPATHY_IMAGE_MESSAGE);
554 avatar_in_icon = g_settings_get_boolean (priv->gsettings_chat,
555 EMPATHY_PREFS_CHAT_AVATAR_IN_ICON);
557 if (n_chats == 1 && avatar_in_icon) {
558 remote_contact = empathy_chat_get_remote_contact (priv->current_chat);
559 icon = empathy_pixbuf_avatar_from_contact_scaled (remote_contact, 0, 0);
560 gtk_window_set_icon (GTK_WINDOW (priv->dialog), icon);
563 g_object_unref (icon);
566 gtk_window_set_icon_name (GTK_WINDOW (priv->dialog), NULL);
572 chat_window_close_button_update (EmpathyChatWindowPriv *priv,
576 GtkWidget *chat_close_button;
579 if (num_pages == 1) {
580 chat = gtk_notebook_get_nth_page (GTK_NOTEBOOK (priv->notebook), 0);
581 chat_close_button = g_object_get_data (G_OBJECT (chat),
582 "chat-window-tab-close-button");
583 gtk_widget_hide (chat_close_button);
585 for (i=0; i<num_pages; i++) {
586 chat = gtk_notebook_get_nth_page (GTK_NOTEBOOK (priv->notebook), i);
587 chat_close_button = g_object_get_data (G_OBJECT (chat),
588 "chat-window-tab-close-button");
589 gtk_widget_show (chat_close_button);
595 chat_window_update (EmpathyChatWindow *window,
596 gboolean update_contact_menu)
598 EmpathyChatWindowPriv *priv = GET_PRIV (window);
601 num_pages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (priv->notebook));
603 /* Update Tab menu */
604 chat_window_menu_context_update (priv,
607 chat_window_conversation_menu_update (priv, window);
609 /* If this update is due to a focus-in event, we know the menu will be
610 the same as when we last left it, so no work to do. Besides, if we
611 swap out the menu on a focus-in, we may confuse any external global
613 if (update_contact_menu) {
614 chat_window_contact_menu_update (priv,
618 chat_window_title_update (priv);
620 chat_window_icon_update (priv, get_all_unread_messages (priv) > 0);
622 chat_window_close_button_update (priv,
627 append_markup_printf (GString *string,
634 va_start (args, format);
636 tmp = g_markup_vprintf_escaped (format, args);
637 g_string_append (string, tmp);
644 chat_window_update_chat_tab_full (EmpathyChat *chat,
645 gboolean update_contact_menu)
647 EmpathyChatWindow *window;
648 EmpathyChatWindowPriv *priv;
649 EmpathyContact *remote_contact;
653 const gchar *subject;
654 const gchar *status = NULL;
658 const gchar *icon_name;
659 GtkWidget *tab_image;
660 GtkWidget *menu_image;
661 GtkWidget *sending_spinner;
664 window = chat_window_find_chat (chat);
668 priv = GET_PRIV (window);
670 /* Get information */
671 name = empathy_chat_dup_name (chat);
672 account = empathy_chat_get_account (chat);
673 subject = empathy_chat_get_subject (chat);
674 remote_contact = empathy_chat_get_remote_contact (chat);
676 DEBUG ("Updating chat tab, name=%s, account=%s, subject=%s, remote_contact=%p",
677 name, tp_proxy_get_object_path (account), subject, remote_contact);
679 /* Update tab image */
680 if (empathy_chat_get_tp_chat (chat) == NULL) {
681 /* No TpChat, we are disconnected */
684 else if (empathy_chat_get_nb_unread_messages (chat) > 0) {
685 icon_name = EMPATHY_IMAGE_MESSAGE;
687 else if (remote_contact && empathy_chat_is_composing (chat)) {
688 icon_name = EMPATHY_IMAGE_TYPING;
690 else if (empathy_chat_is_sms_channel (chat)) {
691 icon_name = EMPATHY_IMAGE_SMS;
693 else if (remote_contact) {
694 icon_name = empathy_icon_name_for_contact (remote_contact);
696 icon_name = EMPATHY_IMAGE_GROUP_MESSAGE;
699 tab_image = g_object_get_data (G_OBJECT (chat), "chat-window-tab-image");
700 menu_image = g_object_get_data (G_OBJECT (chat), "chat-window-menu-image");
701 if (icon_name != NULL) {
702 gtk_image_set_from_icon_name (GTK_IMAGE (tab_image), icon_name, GTK_ICON_SIZE_MENU);
703 gtk_widget_show (tab_image);
704 gtk_image_set_from_icon_name (GTK_IMAGE (menu_image), icon_name, GTK_ICON_SIZE_MENU);
705 gtk_widget_show (menu_image);
707 gtk_widget_hide (tab_image);
708 gtk_widget_hide (menu_image);
711 /* Update the sending spinner */
712 nb_sending = empathy_chat_get_n_messages_sending (chat);
713 sending_spinner = g_object_get_data (G_OBJECT (chat),
714 "chat-window-tab-sending-spinner");
716 g_object_set (sending_spinner,
717 "active", nb_sending > 0,
718 "visible", nb_sending > 0,
721 /* Update tab tooltip */
722 tooltip = g_string_new (NULL);
724 if (remote_contact) {
725 id = empathy_contact_get_id (remote_contact);
726 status = empathy_contact_get_presence_message (remote_contact);
731 if (empathy_chat_is_sms_channel (chat)) {
732 append_markup_printf (tooltip, "%s ", _("SMS:"));
735 append_markup_printf (tooltip,
736 "<b>%s</b><small> (%s)</small>",
738 tp_account_get_display_name (account));
740 if (nb_sending > 0) {
741 char *tmp = g_strdup_printf (
742 ngettext ("Sending %d message",
743 "Sending %d messages",
747 g_string_append (tooltip, "\n");
748 g_string_append (tooltip, tmp);
750 gtk_widget_set_tooltip_text (sending_spinner, tmp);
754 if (!EMP_STR_EMPTY (status)) {
755 append_markup_printf (tooltip, "\n<i>%s</i>", status);
759 append_markup_printf (tooltip, "\n<b>%s</b> %s",
760 _("Topic:"), subject);
763 if (remote_contact && empathy_chat_is_composing (chat)) {
764 append_markup_printf (tooltip, "\n%s", _("Typing a message."));
767 markup = g_string_free (tooltip, FALSE);
768 widget = g_object_get_data (G_OBJECT (chat), "chat-window-tab-tooltip-widget");
769 gtk_widget_set_tooltip_markup (widget, markup);
770 widget = g_object_get_data (G_OBJECT (chat), "chat-window-menu-tooltip-widget");
771 gtk_widget_set_tooltip_markup (widget, markup);
774 /* Update tab and menu label */
775 widget = g_object_get_data (G_OBJECT (chat), "chat-window-tab-label");
776 gtk_label_set_text (GTK_LABEL (widget), name);
777 widget = g_object_get_data (G_OBJECT (chat), "chat-window-menu-label");
778 gtk_label_set_text (GTK_LABEL (widget), name);
780 /* Update the window if it's the current chat */
781 if (priv->current_chat == chat) {
782 chat_window_update (window, update_contact_menu);
789 chat_window_update_chat_tab (EmpathyChat *chat)
791 chat_window_update_chat_tab_full (chat, TRUE);
795 chat_window_chat_notify_cb (EmpathyChat *chat)
797 EmpathyChatWindow *window;
798 EmpathyContact *old_remote_contact;
799 EmpathyContact *remote_contact = NULL;
801 old_remote_contact = g_object_get_data (G_OBJECT (chat), "chat-window-remote-contact");
802 remote_contact = empathy_chat_get_remote_contact (chat);
804 if (old_remote_contact != remote_contact) {
805 /* The remote-contact associated with the chat changed, we need
806 * to keep track of any change of that contact and update the
807 * window each time. */
808 if (remote_contact) {
809 g_signal_connect_swapped (remote_contact, "notify",
810 G_CALLBACK (chat_window_update_chat_tab),
813 if (old_remote_contact) {
814 g_signal_handlers_disconnect_by_func (old_remote_contact,
815 chat_window_update_chat_tab,
819 g_object_set_data_full (G_OBJECT (chat), "chat-window-remote-contact",
820 g_object_ref (remote_contact), (GDestroyNotify) g_object_unref);
823 chat_window_update_chat_tab (chat);
825 window = chat_window_find_chat (chat);
826 if (window != NULL) {
827 chat_window_update (window, FALSE);
832 chat_window_insert_smiley_activate_cb (EmpathySmileyManager *manager,
833 EmpathySmiley *smiley,
836 EmpathyChatWindowPriv *priv = GET_PRIV (window);
838 GtkTextBuffer *buffer;
841 chat = priv->current_chat;
843 buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (chat->input_text_view));
844 gtk_text_buffer_get_end_iter (buffer, &iter);
845 gtk_text_buffer_insert (buffer, &iter, smiley->str, -1);
849 chat_window_conv_activate_cb (GtkAction *action,
850 EmpathyChatWindow *window)
852 EmpathyChatWindowPriv *priv = GET_PRIV (window);
855 EmpathyContact *remote_contact = NULL;
857 /* Favorite room menu */
858 is_room = empathy_chat_is_room (priv->current_chat);
862 gboolean found = FALSE;
863 EmpathyChatroom *chatroom;
865 room = empathy_chat_get_id (priv->current_chat);
866 account = empathy_chat_get_account (priv->current_chat);
867 chatroom = empathy_chatroom_manager_find (priv->chatroom_manager,
869 if (chatroom != NULL)
870 found = empathy_chatroom_is_favorite (chatroom);
872 DEBUG ("This room %s favorite", found ? "is" : "is not");
873 gtk_toggle_action_set_active (
874 GTK_TOGGLE_ACTION (priv->menu_conv_favorite), found);
876 if (chatroom != NULL)
877 found = empathy_chatroom_is_always_urgent (chatroom);
879 gtk_toggle_action_set_active (
880 GTK_TOGGLE_ACTION (priv->menu_conv_always_urgent),
883 gtk_action_set_visible (priv->menu_conv_favorite, is_room);
884 gtk_action_set_visible (priv->menu_conv_always_urgent, is_room);
886 /* Show contacts menu */
887 g_object_get (priv->current_chat,
888 "remote-contact", &remote_contact,
889 "show-contacts", &active,
891 if (remote_contact == NULL) {
892 gtk_toggle_action_set_active (
893 GTK_TOGGLE_ACTION (priv->menu_conv_toggle_contacts),
896 gtk_action_set_visible (priv->menu_conv_toggle_contacts,
897 (remote_contact == NULL));
898 if (remote_contact != NULL) {
899 g_object_unref (remote_contact);
904 chat_window_clear_activate_cb (GtkAction *action,
905 EmpathyChatWindow *window)
907 EmpathyChatWindowPriv *priv = GET_PRIV (window);
909 empathy_chat_clear (priv->current_chat);
913 chat_window_favorite_toggled_cb (GtkToggleAction *toggle_action,
914 EmpathyChatWindow *window)
916 EmpathyChatWindowPriv *priv = GET_PRIV (window);
921 EmpathyChatroom *chatroom;
923 active = gtk_toggle_action_get_active (toggle_action);
924 account = empathy_chat_get_account (priv->current_chat);
925 room = empathy_chat_get_id (priv->current_chat);
926 name = empathy_chat_dup_name (priv->current_chat);
928 chatroom = empathy_chatroom_manager_ensure_chatroom (
929 priv->chatroom_manager,
934 empathy_chatroom_set_favorite (chatroom, active);
935 g_object_unref (chatroom);
940 chat_window_always_urgent_toggled_cb (GtkToggleAction *toggle_action,
941 EmpathyChatWindow *window)
943 EmpathyChatWindowPriv *priv = GET_PRIV (window);
948 EmpathyChatroom *chatroom;
950 active = gtk_toggle_action_get_active (toggle_action);
951 account = empathy_chat_get_account (priv->current_chat);
952 room = empathy_chat_get_id (priv->current_chat);
953 name = empathy_chat_dup_name (priv->current_chat);
955 chatroom = empathy_chatroom_manager_ensure_chatroom (
956 priv->chatroom_manager,
961 empathy_chatroom_set_always_urgent (chatroom, active);
962 g_object_unref (chatroom);
967 chat_window_contacts_toggled_cb (GtkToggleAction *toggle_action,
968 EmpathyChatWindow *window)
970 EmpathyChatWindowPriv *priv = GET_PRIV (window);
973 active = gtk_toggle_action_get_active (toggle_action);
975 empathy_chat_set_show_contacts (priv->current_chat, active);
979 chat_window_invite_participant_activate_cb (GtkAction *action,
980 EmpathyChatWindow *window)
982 EmpathyChatWindowPriv *priv;
984 EmpathyTpChat *tp_chat;
987 priv = GET_PRIV (window);
989 g_return_if_fail (priv->current_chat != NULL);
991 tp_chat = empathy_chat_get_tp_chat (priv->current_chat);
993 dialog = empathy_invite_participant_dialog_new (
994 GTK_WINDOW (priv->dialog), tp_chat);
995 gtk_widget_show (dialog);
997 response = gtk_dialog_run (GTK_DIALOG (dialog));
999 if (response == GTK_RESPONSE_ACCEPT) {
1000 TpContact *tp_contact;
1001 EmpathyContact *contact;
1003 tp_contact = empathy_invite_participant_dialog_get_selected (
1004 EMPATHY_INVITE_PARTICIPANT_DIALOG (dialog));
1005 if (tp_contact == NULL) goto out;
1007 contact = empathy_contact_dup_from_tp_contact (tp_contact);
1009 empathy_contact_list_add (EMPATHY_CONTACT_LIST (tp_chat),
1010 contact, _("Inviting you to this room"));
1012 g_object_unref (contact);
1016 gtk_widget_destroy (dialog);
1020 chat_window_close_activate_cb (GtkAction *action,
1021 EmpathyChatWindow *window)
1023 EmpathyChatWindowPriv *priv;
1025 priv = GET_PRIV (window);
1027 g_return_if_fail (priv->current_chat != NULL);
1029 empathy_chat_window_remove_chat (window, priv->current_chat);
1033 chat_window_edit_activate_cb (GtkAction *action,
1034 EmpathyChatWindow *window)
1036 EmpathyChatWindowPriv *priv;
1037 GtkClipboard *clipboard;
1038 GtkTextBuffer *buffer;
1039 gboolean text_available;
1041 priv = GET_PRIV (window);
1043 g_return_if_fail (priv->current_chat != NULL);
1045 if (!empathy_chat_get_tp_chat (priv->current_chat)) {
1046 gtk_action_set_sensitive (priv->menu_edit_copy, FALSE);
1047 gtk_action_set_sensitive (priv->menu_edit_cut, FALSE);
1048 gtk_action_set_sensitive (priv->menu_edit_paste, FALSE);
1052 buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (priv->current_chat->input_text_view));
1053 if (gtk_text_buffer_get_has_selection (buffer)) {
1054 gtk_action_set_sensitive (priv->menu_edit_copy, TRUE);
1055 gtk_action_set_sensitive (priv->menu_edit_cut, TRUE);
1059 selection = empathy_chat_view_get_has_selection (priv->current_chat->view);
1061 gtk_action_set_sensitive (priv->menu_edit_cut, FALSE);
1062 gtk_action_set_sensitive (priv->menu_edit_copy, selection);
1065 clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
1066 text_available = gtk_clipboard_wait_is_text_available (clipboard);
1067 gtk_action_set_sensitive (priv->menu_edit_paste, text_available);
1071 chat_window_cut_activate_cb (GtkAction *action,
1072 EmpathyChatWindow *window)
1074 EmpathyChatWindowPriv *priv;
1076 g_return_if_fail (EMPATHY_IS_CHAT_WINDOW (window));
1078 priv = GET_PRIV (window);
1080 empathy_chat_cut (priv->current_chat);
1084 chat_window_copy_activate_cb (GtkAction *action,
1085 EmpathyChatWindow *window)
1087 EmpathyChatWindowPriv *priv;
1089 g_return_if_fail (EMPATHY_IS_CHAT_WINDOW (window));
1091 priv = GET_PRIV (window);
1093 empathy_chat_copy (priv->current_chat);
1097 chat_window_paste_activate_cb (GtkAction *action,
1098 EmpathyChatWindow *window)
1100 EmpathyChatWindowPriv *priv;
1102 g_return_if_fail (EMPATHY_IS_CHAT_WINDOW (window));
1104 priv = GET_PRIV (window);
1106 empathy_chat_paste (priv->current_chat);
1110 chat_window_find_activate_cb (GtkAction *action,
1111 EmpathyChatWindow *window)
1113 EmpathyChatWindowPriv *priv;
1115 g_return_if_fail (EMPATHY_IS_CHAT_WINDOW (window));
1117 priv = GET_PRIV (window);
1119 empathy_chat_find (priv->current_chat);
1123 chat_window_tabs_next_activate_cb (GtkAction *action,
1124 EmpathyChatWindow *window)
1126 EmpathyChatWindowPriv *priv;
1127 gint index_, numPages;
1128 gboolean wrap_around;
1130 priv = GET_PRIV (window);
1132 g_object_get (gtk_settings_get_default (), "gtk-keynav-wrap-around",
1133 &wrap_around, NULL);
1135 index_ = gtk_notebook_get_current_page (GTK_NOTEBOOK (priv->notebook));
1136 numPages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (priv->notebook));
1138 if (index_ == (numPages - 1) && wrap_around) {
1139 gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->notebook), 0);
1143 gtk_notebook_next_page (GTK_NOTEBOOK (priv->notebook));
1147 chat_window_tabs_previous_activate_cb (GtkAction *action,
1148 EmpathyChatWindow *window)
1150 EmpathyChatWindowPriv *priv;
1151 gint index_, numPages;
1152 gboolean wrap_around;
1154 priv = GET_PRIV (window);
1156 g_object_get (gtk_settings_get_default (), "gtk-keynav-wrap-around",
1157 &wrap_around, NULL);
1159 index_ = gtk_notebook_get_current_page (GTK_NOTEBOOK (priv->notebook));
1160 numPages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (priv->notebook));
1162 if (index_ <= 0 && wrap_around) {
1163 gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->notebook), numPages - 1);
1167 gtk_notebook_prev_page (GTK_NOTEBOOK (priv->notebook));
1171 chat_window_tabs_undo_close_tab_activate_cb (GtkAction *action,
1172 EmpathyChatWindow *window)
1174 EmpathyChatWindowPriv *priv = GET_PRIV (window);
1175 empathy_chat_manager_undo_closed_chat (priv->chat_manager,
1176 empathy_get_current_action_time ());
1180 chat_window_tabs_left_activate_cb (GtkAction *action,
1181 EmpathyChatWindow *window)
1183 EmpathyChatWindowPriv *priv;
1185 gint index_, num_pages;
1187 priv = GET_PRIV (window);
1189 chat = priv->current_chat;
1190 index_ = gtk_notebook_get_current_page (GTK_NOTEBOOK (priv->notebook));
1195 gtk_notebook_reorder_child (GTK_NOTEBOOK (priv->notebook),
1199 num_pages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (priv->notebook));
1200 chat_window_menu_context_update (priv, num_pages);
1204 chat_window_tabs_right_activate_cb (GtkAction *action,
1205 EmpathyChatWindow *window)
1207 EmpathyChatWindowPriv *priv;
1209 gint index_, num_pages;
1211 priv = GET_PRIV (window);
1213 chat = priv->current_chat;
1214 index_ = gtk_notebook_get_current_page (GTK_NOTEBOOK (priv->notebook));
1216 gtk_notebook_reorder_child (GTK_NOTEBOOK (priv->notebook),
1220 num_pages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (priv->notebook));
1221 chat_window_menu_context_update (priv, num_pages);
1224 static EmpathyChatWindow *
1225 empathy_chat_window_new (void)
1227 return EMPATHY_CHAT_WINDOW (g_object_new (EMPATHY_TYPE_CHAT_WINDOW, NULL));
1231 chat_window_detach_activate_cb (GtkAction *action,
1232 EmpathyChatWindow *window)
1234 EmpathyChatWindowPriv *priv;
1235 EmpathyChatWindow *new_window;
1238 priv = GET_PRIV (window);
1240 chat = priv->current_chat;
1241 new_window = empathy_chat_window_new ();
1243 empathy_chat_window_move_chat (window, new_window, chat);
1245 priv = GET_PRIV (new_window);
1246 gtk_widget_show (priv->dialog);
1250 chat_window_help_contents_activate_cb (GtkAction *action,
1251 EmpathyChatWindow *window)
1253 EmpathyChatWindowPriv *priv = GET_PRIV (window);
1255 empathy_url_show (priv->dialog, "ghelp:empathy");
1259 chat_window_help_about_activate_cb (GtkAction *action,
1260 EmpathyChatWindow *window)
1262 EmpathyChatWindowPriv *priv = GET_PRIV (window);
1264 empathy_about_dialog_new (GTK_WINDOW (priv->dialog));
1268 chat_window_delete_event_cb (GtkWidget *dialog,
1270 EmpathyChatWindow *window)
1272 EmpathyChatWindowPriv *priv = GET_PRIV (window);
1274 DEBUG ("Delete event received");
1276 g_object_ref (window);
1277 while (priv->chats) {
1278 empathy_chat_window_remove_chat (window, priv->chats->data);
1280 g_object_unref (window);
1286 chat_window_composing_cb (EmpathyChat *chat,
1287 gboolean is_composing,
1288 EmpathyChatWindow *window)
1290 chat_window_update_chat_tab (chat);
1294 chat_window_set_urgency_hint (EmpathyChatWindow *window,
1297 EmpathyChatWindowPriv *priv;
1299 priv = GET_PRIV (window);
1301 gtk_window_set_urgency_hint (GTK_WINDOW (priv->dialog), urgent);
1305 chat_window_notification_closed_cb (NotifyNotification *notify,
1306 EmpathyChatWindow *self)
1308 EmpathyChatWindowPriv *priv = GET_PRIV (self);
1310 g_object_unref (notify);
1311 if (priv->notification == notify) {
1312 priv->notification = NULL;
1317 chat_window_show_or_update_notification (EmpathyChatWindow *window,
1318 EmpathyMessage *message,
1321 EmpathyContact *sender;
1322 const gchar *header;
1326 EmpathyChatWindowPriv *priv = GET_PRIV (window);
1327 gboolean res, has_x_canonical_append;
1328 NotifyNotification *notification = priv->notification;
1330 if (!empathy_notify_manager_notification_is_enabled (priv->notify_mgr)) {
1333 res = g_settings_get_boolean (priv->gsettings_notif,
1334 EMPATHY_PREFS_NOTIFICATIONS_FOCUS);
1341 sender = empathy_message_get_sender (message);
1342 header = empathy_contact_get_alias (sender);
1343 body = empathy_message_get_body (message);
1344 escaped = g_markup_escape_text (body, -1);
1345 has_x_canonical_append = empathy_notify_manager_has_capability (
1346 priv->notify_mgr, EMPATHY_NOTIFY_MANAGER_CAP_X_CANONICAL_APPEND);
1348 if (notification != NULL && !has_x_canonical_append) {
1349 /* if the notification server supports x-canonical-append, it is
1350 better to not use notify_notification_update to avoid
1351 overwriting the current notification message */
1352 notify_notification_update (notification,
1353 header, escaped, NULL);
1355 /* if the notification server supports x-canonical-append,
1356 the hint will be added, so that the message from the
1357 just created notification will be automatically appended
1358 to an existing notification with the same title.
1359 In this way the previous message will not be lost: the new
1360 message will appear below it, in the same notification */
1361 notification = notify_notification_new (header, escaped, NULL);
1363 if (priv->notification == NULL) {
1364 priv->notification = notification;
1367 notify_notification_set_timeout (notification, NOTIFY_EXPIRES_DEFAULT);
1369 tp_g_signal_connect_object (notification, "closed",
1370 G_CALLBACK (chat_window_notification_closed_cb), window, 0);
1372 if (has_x_canonical_append) {
1373 /* We have to set a not empty string to keep libnotify happy */
1374 notify_notification_set_hint_string (notification,
1375 EMPATHY_NOTIFY_MANAGER_CAP_X_CANONICAL_APPEND, "1");
1379 pixbuf = empathy_notify_manager_get_pixbuf_for_notification (priv->notify_mgr,
1380 sender, EMPATHY_IMAGE_NEW_MESSAGE);
1382 if (pixbuf != NULL) {
1383 notify_notification_set_icon_from_pixbuf (notification, pixbuf);
1384 g_object_unref (pixbuf);
1387 notify_notification_show (notification, NULL);
1393 chat_window_set_highlight_room_labels (EmpathyChat *chat)
1395 gchar *markup, *name;
1398 if (!empathy_chat_is_room (chat))
1401 name = empathy_chat_dup_name (chat);
1402 markup = g_markup_printf_escaped (
1403 "<span color=\"red\" weight=\"bold\">%s</span>",
1406 widget = g_object_get_data (G_OBJECT (chat), "chat-window-tab-label");
1407 gtk_label_set_markup (GTK_LABEL (widget), markup);
1409 widget = g_object_get_data (G_OBJECT (chat), "chat-window-menu-label");
1410 gtk_label_set_markup (GTK_LABEL (widget), markup);
1417 empathy_chat_window_has_focus (EmpathyChatWindow *window)
1419 EmpathyChatWindowPriv *priv;
1422 g_return_val_if_fail (EMPATHY_IS_CHAT_WINDOW (window), FALSE);
1424 priv = GET_PRIV (window);
1426 g_object_get (priv->dialog, "has-toplevel-focus", &has_focus, NULL);
1432 chat_window_new_message_cb (EmpathyChat *chat,
1433 EmpathyMessage *message,
1435 EmpathyChatWindow *window)
1437 EmpathyChatWindowPriv *priv;
1439 gboolean needs_urgency;
1440 EmpathyContact *sender;
1442 priv = GET_PRIV (window);
1444 has_focus = empathy_chat_window_has_focus (window);
1446 /* - if we're the sender, we play the sound if it's specified in the
1447 * preferences and we're not away.
1448 * - if we receive a message, we play the sound if it's specified in the
1449 * preferences and the window does not have focus on the chat receiving
1453 sender = empathy_message_get_sender (message);
1455 if (empathy_contact_is_user (sender)) {
1456 empathy_sound_manager_play (priv->sound_mgr, GTK_WIDGET (priv->dialog),
1457 EMPATHY_SOUND_MESSAGE_OUTGOING);
1460 if (has_focus && priv->current_chat == chat) {
1461 /* window and tab are focused so consider the message to be read */
1463 /* FIXME: see Bug#610994 and coments about it in EmpathyChatPriv */
1464 empathy_chat_messages_read (chat);
1468 /* Update the chat tab if this is the first unread message */
1469 if (empathy_chat_get_nb_unread_messages (chat) == 1) {
1470 chat_window_update_chat_tab (chat);
1473 /* If empathy_chat_is_room () returns TRUE, that means it's a named MUC.
1474 * If empathy_chat_get_remote_contact () returns NULL, that means it's
1475 * an unamed MUC (msn-like).
1476 * In case of a MUC, we set urgency if either:
1477 * a) the chatroom's always_urgent property is TRUE
1478 * b) the message contains our alias
1480 if (empathy_chat_is_room (chat) ||
1481 empathy_chat_get_remote_contact (chat) == NULL) {
1484 EmpathyChatroom *chatroom;
1486 account = empathy_chat_get_account (chat);
1487 room = empathy_chat_get_id (chat);
1489 chatroom = empathy_chatroom_manager_find (priv->chatroom_manager,
1492 if (chatroom != NULL && empathy_chatroom_is_always_urgent (chatroom)) {
1493 needs_urgency = TRUE;
1495 needs_urgency = empathy_message_should_highlight (message);
1498 needs_urgency = TRUE;
1501 if (needs_urgency) {
1502 chat_window_set_highlight_room_labels (chat);
1505 chat_window_set_urgency_hint (window, TRUE);
1508 /* Pending messages have already been displayed and notified in the
1509 * approver, so we don't display a notification and play a sound for those */
1511 empathy_sound_manager_play (priv->sound_mgr, GTK_WIDGET (priv->dialog),
1512 EMPATHY_SOUND_MESSAGE_INCOMING);
1514 chat_window_show_or_update_notification (window, message, chat);
1518 /* update the number of unread messages and the window icon */
1519 chat_window_title_update (priv);
1520 chat_window_icon_update (priv, TRUE);
1524 chat_window_command_part (EmpathyChat *chat,
1527 EmpathyChat *chat_to_be_parted;
1528 EmpathyTpChat *tp_chat = NULL;
1530 if (strv[1] == NULL) {
1531 /* No chatroom ID specified */
1532 tp_chat = empathy_chat_get_tp_chat (chat);
1534 empathy_tp_chat_leave (tp_chat, "");
1537 chat_to_be_parted = empathy_chat_window_find_chat (
1538 empathy_chat_get_account (chat), strv[1], FALSE);
1540 if (chat_to_be_parted != NULL) {
1541 /* Found a chatroom matching the specified ID */
1542 tp_chat = empathy_chat_get_tp_chat (chat_to_be_parted);
1544 empathy_tp_chat_leave (tp_chat, strv[2]);
1548 /* Going by the syntax of PART command:
1550 * /PART [<chatroom-ID>] [<reason>]
1552 * Chatroom-ID is not a must to specify a reason.
1553 * If strv[1] (chatroom-ID) is not a valid identifier for a connected
1554 * MUC then the current chatroom should be parted and srtv[1] should
1555 * be treated as part of the optional part-message. */
1556 message = g_strconcat (strv[1], " ", strv[2], NULL);
1557 tp_chat = empathy_chat_get_tp_chat (chat);
1559 empathy_tp_chat_leave (tp_chat, message);
1565 static GtkNotebook *
1566 notebook_create_window_cb (GtkNotebook *source,
1572 EmpathyChatWindowPriv *priv;
1573 EmpathyChatWindow *window, *new_window;
1576 chat = EMPATHY_CHAT (page);
1577 window = chat_window_find_chat (chat);
1579 new_window = empathy_chat_window_new ();
1580 priv = GET_PRIV (new_window);
1582 DEBUG ("Detach hook called");
1584 empathy_chat_window_move_chat (window, new_window, chat);
1586 gtk_widget_show (priv->dialog);
1587 gtk_window_move (GTK_WINDOW (priv->dialog), x, y);
1593 chat_window_page_switched_cb (GtkNotebook *notebook,
1596 EmpathyChatWindow *window)
1598 EmpathyChatWindowPriv *priv = GET_PRIV (window);
1599 EmpathyChat *chat = EMPATHY_CHAT (child);
1601 DEBUG ("Page switched");
1603 if (priv->page_added) {
1604 priv->page_added = FALSE;
1605 empathy_chat_scroll_down (chat);
1607 else if (priv->current_chat == chat) {
1611 priv->current_chat = chat;
1612 empathy_chat_messages_read (chat);
1614 chat_window_update_chat_tab (chat);
1618 chat_window_page_added_cb (GtkNotebook *notebook,
1621 EmpathyChatWindow *window)
1623 EmpathyChatWindowPriv *priv;
1626 priv = GET_PRIV (window);
1628 /* If we just received DND to the same window, we don't want
1629 * to do anything here like removing the tab and then readding
1630 * it, so we return here and in "page-added".
1632 if (priv->dnd_same_window) {
1633 DEBUG ("Page added (back to the same window)");
1634 priv->dnd_same_window = FALSE;
1638 DEBUG ("Page added");
1640 /* Get chat object */
1641 chat = EMPATHY_CHAT (child);
1643 /* Connect chat signals for this window */
1644 g_signal_connect (chat, "composing",
1645 G_CALLBACK (chat_window_composing_cb),
1647 g_signal_connect (chat, "new-message",
1648 G_CALLBACK (chat_window_new_message_cb),
1650 g_signal_connect (chat, "part-command-entered",
1651 G_CALLBACK (chat_window_command_part),
1653 g_signal_connect (chat, "notify::tp-chat",
1654 G_CALLBACK (chat_window_update_chat_tab),
1657 /* Set flag so we know to perform some special operations on
1658 * switch page due to the new page being added.
1660 priv->page_added = TRUE;
1662 /* Get list of chats up to date */
1663 priv->chats = g_list_append (priv->chats, chat);
1665 chat_window_update_chat_tab (chat);
1669 chat_window_page_removed_cb (GtkNotebook *notebook,
1672 EmpathyChatWindow *window)
1674 EmpathyChatWindowPriv *priv;
1677 priv = GET_PRIV (window);
1679 /* If we just received DND to the same window, we don't want
1680 * to do anything here like removing the tab and then readding
1681 * it, so we return here and in "page-added".
1683 if (priv->dnd_same_window) {
1684 DEBUG ("Page removed (and will be readded to same window)");
1688 DEBUG ("Page removed");
1690 /* Get chat object */
1691 chat = EMPATHY_CHAT (child);
1693 /* Disconnect all signal handlers for this chat and this window */
1694 g_signal_handlers_disconnect_by_func (chat,
1695 G_CALLBACK (chat_window_composing_cb),
1697 g_signal_handlers_disconnect_by_func (chat,
1698 G_CALLBACK (chat_window_new_message_cb),
1700 g_signal_handlers_disconnect_by_func (chat,
1701 G_CALLBACK (chat_window_update_chat_tab),
1704 /* Keep list of chats up to date */
1705 priv->chats = g_list_remove (priv->chats, chat);
1706 empathy_chat_messages_read (chat);
1708 if (priv->chats == NULL) {
1709 g_object_unref (window);
1711 chat_window_update (window, TRUE);
1716 chat_window_focus_in_event_cb (GtkWidget *widget,
1718 EmpathyChatWindow *window)
1720 EmpathyChatWindowPriv *priv;
1722 priv = GET_PRIV (window);
1724 empathy_chat_messages_read (priv->current_chat);
1726 chat_window_set_urgency_hint (window, FALSE);
1728 /* Update the title, since we now mark all unread messages as read. */
1729 chat_window_update_chat_tab_full (priv->current_chat, FALSE);
1735 chat_window_drag_drop (GtkWidget *widget,
1736 GdkDragContext *context,
1740 EmpathyChatWindow *window)
1743 EmpathyChatWindowPriv *priv;
1745 priv = GET_PRIV (window);
1747 target = gtk_drag_dest_find_target (widget, context, priv->file_targets);
1748 if (target == GDK_NONE)
1749 target = gtk_drag_dest_find_target (widget, context, priv->contact_targets);
1751 if (target != GDK_NONE) {
1752 gtk_drag_get_data (widget, context, target, time_);
1760 chat_window_drag_motion (GtkWidget *widget,
1761 GdkDragContext *context,
1765 EmpathyChatWindow *window)
1768 EmpathyChatWindowPriv *priv;
1770 priv = GET_PRIV (window);
1772 target = gtk_drag_dest_find_target (widget, context, priv->file_targets);
1773 if (target != GDK_NONE) {
1774 /* This is a file drag. Ensure the contact is online and set the
1775 drag type to COPY. Note that it's possible that the tab will
1776 be switched by GTK+ after a timeout from drag_motion without
1777 getting another drag_motion to disable the drop. You have
1778 to hold your mouse really still.
1780 EmpathyContact *contact;
1782 priv = GET_PRIV (window);
1783 contact = empathy_chat_get_remote_contact (priv->current_chat);
1784 /* contact is NULL for multi-user chats. We don't do
1785 * file transfers to MUCs. We also don't send files
1786 * to offline contacts or contacts that don't support
1789 if ((contact == NULL) || !empathy_contact_is_online (contact)) {
1790 gdk_drag_status (context, 0, time_);
1793 if (!(empathy_contact_get_capabilities (contact)
1794 & EMPATHY_CAPABILITIES_FT)) {
1795 gdk_drag_status (context, 0, time_);
1798 gdk_drag_status (context, GDK_ACTION_COPY, time_);
1802 target = gtk_drag_dest_find_target (widget, context, priv->contact_targets);
1803 if (target != GDK_NONE) {
1804 /* This is a drag of a contact from a contact list. Set to COPY.
1805 FIXME: If this drag is to a MUC window, it invites the user.
1806 Otherwise, it opens a chat. Should we use a different drag
1807 type for invites? Should we allow ASK?
1809 gdk_drag_status (context, GDK_ACTION_COPY, time_);
1817 chat_window_drag_data_received (GtkWidget *widget,
1818 GdkDragContext *context,
1821 GtkSelectionData *selection,
1824 EmpathyChatWindow *window)
1826 if (info == DND_DRAG_TYPE_CONTACT_ID) {
1827 EmpathyChat *chat = NULL;
1828 EmpathyChatWindow *old_window;
1829 TpAccount *account = NULL;
1830 TpAccountManager *account_manager;
1833 const gchar *account_id;
1834 const gchar *contact_id;
1836 id = (const gchar*) gtk_selection_data_get_data (selection);
1838 /* FIXME: Perhaps should be sure that the account manager is
1839 * prepared before calling _ensure_account on it. */
1840 account_manager = tp_account_manager_dup ();
1842 DEBUG ("DND contact from roster with id:'%s'", id);
1844 strv = g_strsplit (id, ":", 2);
1845 if (g_strv_length (strv) == 2) {
1846 account_id = strv[0];
1847 contact_id = strv[1];
1849 tp_account_manager_ensure_account (account_manager, account_id);
1850 if (account != NULL)
1851 chat = empathy_chat_window_find_chat (account, contact_id, FALSE);
1854 if (account == NULL) {
1856 gtk_drag_finish (context, FALSE, FALSE, time_);
1861 empathy_chat_with_contact_id (
1862 account, contact_id, empathy_get_current_action_time ());
1867 g_object_unref (account_manager);
1870 old_window = chat_window_find_chat (chat);
1872 if (old_window == window) {
1873 gtk_drag_finish (context, TRUE, FALSE, time_);
1877 empathy_chat_window_move_chat (old_window, window, chat);
1879 empathy_chat_window_add_chat (window, chat);
1882 /* Added to take care of any outstanding chat events */
1883 empathy_chat_window_present_chat (chat,
1884 TP_USER_ACTION_TIME_NOT_USER_ACTION);
1886 /* We should return TRUE to remove the data when doing
1887 * GDK_ACTION_MOVE, but we don't here otherwise it has
1888 * weird consequences, and we handle that internally
1889 * anyway with add_chat () and remove_chat ().
1891 gtk_drag_finish (context, TRUE, FALSE, time_);
1893 else if (info == DND_DRAG_TYPE_URI_LIST) {
1894 EmpathyChatWindowPriv *priv;
1895 EmpathyContact *contact;
1898 priv = GET_PRIV (window);
1899 contact = empathy_chat_get_remote_contact (priv->current_chat);
1901 /* contact is NULL when current_chat is a multi-user chat.
1902 * We don't do file transfers to MUCs, so just cancel the drag.
1904 if (contact == NULL) {
1905 gtk_drag_finish (context, TRUE, FALSE, time_);
1909 data = (const gchar *) gtk_selection_data_get_data (selection);
1910 empathy_send_file_from_uri_list (contact, data);
1912 gtk_drag_finish (context, TRUE, FALSE, time_);
1914 else if (info == DND_DRAG_TYPE_TAB) {
1916 EmpathyChatWindow *old_window = NULL;
1920 chat = (void *) gtk_selection_data_get_data (selection);
1921 old_window = chat_window_find_chat (*chat);
1924 EmpathyChatWindowPriv *priv;
1926 priv = GET_PRIV (window);
1927 priv->dnd_same_window = (old_window == window);
1928 DEBUG ("DND tab (within same window: %s)",
1929 priv->dnd_same_window ? "Yes" : "No");
1932 DEBUG ("DND from unknown source");
1933 gtk_drag_finish (context, FALSE, FALSE, time_);
1938 chat_window_chat_manager_chats_changed_cb (EmpathyChatManager *chat_manager,
1939 guint num_chats_in_manager,
1940 EmpathyChatWindow *window)
1942 EmpathyChatWindowPriv *priv = GET_PRIV (window);
1944 gtk_action_set_sensitive (priv->menu_tabs_undo_close_tab,
1945 num_chats_in_manager > 0);
1949 chat_window_finalize (GObject *object)
1951 EmpathyChatWindow *window;
1952 EmpathyChatWindowPriv *priv;
1954 window = EMPATHY_CHAT_WINDOW (object);
1955 priv = GET_PRIV (window);
1957 DEBUG ("Finalized: %p", object);
1959 g_object_unref (priv->ui_manager);
1960 g_object_unref (priv->chatroom_manager);
1961 g_object_unref (priv->notify_mgr);
1962 g_object_unref (priv->gsettings_chat);
1963 g_object_unref (priv->gsettings_notif);
1964 g_object_unref (priv->gsettings_ui);
1965 g_object_unref (priv->sound_mgr);
1967 if (priv->notification != NULL) {
1968 notify_notification_close (priv->notification, NULL);
1969 priv->notification = NULL;
1972 if (priv->contact_targets) {
1973 gtk_target_list_unref (priv->contact_targets);
1975 if (priv->file_targets) {
1976 gtk_target_list_unref (priv->file_targets);
1979 if (priv->chat_manager) {
1980 g_signal_handler_disconnect (priv->chat_manager,
1981 priv->chat_manager_chats_changed_id);
1982 g_object_unref (priv->chat_manager);
1983 priv->chat_manager = NULL;
1986 chat_windows = g_list_remove (chat_windows, window);
1987 gtk_widget_destroy (priv->dialog);
1989 G_OBJECT_CLASS (empathy_chat_window_parent_class)->finalize (object);
1993 empathy_chat_window_class_init (EmpathyChatWindowClass *klass)
1995 GObjectClass *object_class = G_OBJECT_CLASS (klass);
1997 object_class->finalize = chat_window_finalize;
1999 g_type_class_add_private (object_class, sizeof (EmpathyChatWindowPriv));
2003 empathy_chat_window_init (EmpathyChatWindow *window)
2006 GtkAccelGroup *accel_group;
2011 GtkWidget *chat_vbox;
2013 EmpathySmileyManager *smiley_manager;
2014 EmpathyChatWindowPriv *priv = G_TYPE_INSTANCE_GET_PRIVATE (window,
2015 EMPATHY_TYPE_CHAT_WINDOW, EmpathyChatWindowPriv);
2017 window->priv = priv;
2018 filename = empathy_file_lookup ("empathy-chat-window.ui", "src");
2019 gui = empathy_builder_get_file (filename,
2020 "chat_window", &priv->dialog,
2021 "chat_vbox", &chat_vbox,
2022 "ui_manager", &priv->ui_manager,
2023 "menu_conv_insert_smiley", &priv->menu_conv_insert_smiley,
2024 "menu_conv_favorite", &priv->menu_conv_favorite,
2025 "menu_conv_always_urgent", &priv->menu_conv_always_urgent,
2026 "menu_conv_toggle_contacts", &priv->menu_conv_toggle_contacts,
2027 "menu_edit_cut", &priv->menu_edit_cut,
2028 "menu_edit_copy", &priv->menu_edit_copy,
2029 "menu_edit_paste", &priv->menu_edit_paste,
2030 "menu_edit_find", &priv->menu_edit_find,
2031 "menu_tabs_next", &priv->menu_tabs_next,
2032 "menu_tabs_prev", &priv->menu_tabs_prev,
2033 "menu_tabs_undo_close_tab", &priv->menu_tabs_undo_close_tab,
2034 "menu_tabs_left", &priv->menu_tabs_left,
2035 "menu_tabs_right", &priv->menu_tabs_right,
2036 "menu_tabs_detach", &priv->menu_tabs_detach,
2040 empathy_builder_connect (gui, window,
2041 "menu_conv", "activate", chat_window_conv_activate_cb,
2042 "menu_conv_clear", "activate", chat_window_clear_activate_cb,
2043 "menu_conv_favorite", "toggled", chat_window_favorite_toggled_cb,
2044 "menu_conv_always_urgent", "toggled", chat_window_always_urgent_toggled_cb,
2045 "menu_conv_toggle_contacts", "toggled", chat_window_contacts_toggled_cb,
2046 "menu_conv_invite_participant", "activate", chat_window_invite_participant_activate_cb,
2047 "menu_conv_close", "activate", chat_window_close_activate_cb,
2048 "menu_edit", "activate", chat_window_edit_activate_cb,
2049 "menu_edit_cut", "activate", chat_window_cut_activate_cb,
2050 "menu_edit_copy", "activate", chat_window_copy_activate_cb,
2051 "menu_edit_paste", "activate", chat_window_paste_activate_cb,
2052 "menu_edit_find", "activate", chat_window_find_activate_cb,
2053 "menu_tabs_next", "activate", chat_window_tabs_next_activate_cb,
2054 "menu_tabs_prev", "activate", chat_window_tabs_previous_activate_cb,
2055 "menu_tabs_undo_close_tab", "activate", chat_window_tabs_undo_close_tab_activate_cb,
2056 "menu_tabs_left", "activate", chat_window_tabs_left_activate_cb,
2057 "menu_tabs_right", "activate", chat_window_tabs_right_activate_cb,
2058 "menu_tabs_detach", "activate", chat_window_detach_activate_cb,
2059 "menu_help_contents", "activate", chat_window_help_contents_activate_cb,
2060 "menu_help_about", "activate", chat_window_help_about_activate_cb,
2063 g_object_ref (priv->ui_manager);
2064 g_object_unref (gui);
2066 priv->gsettings_chat = g_settings_new (EMPATHY_PREFS_CHAT_SCHEMA);
2067 priv->gsettings_notif = g_settings_new (EMPATHY_PREFS_NOTIFICATIONS_SCHEMA);
2068 priv->gsettings_ui = g_settings_new (EMPATHY_PREFS_UI_SCHEMA);
2069 priv->chatroom_manager = empathy_chatroom_manager_dup_singleton (NULL);
2071 priv->sound_mgr = empathy_sound_manager_dup_singleton ();
2073 priv->notebook = gtk_notebook_new ();
2075 g_signal_connect (priv->notebook, "create-window",
2076 G_CALLBACK (notebook_create_window_cb), window);
2078 gtk_notebook_set_group_name (GTK_NOTEBOOK (priv->notebook),
2079 "EmpathyChatWindow");
2080 gtk_notebook_set_scrollable (GTK_NOTEBOOK (priv->notebook), TRUE);
2081 gtk_notebook_popup_enable (GTK_NOTEBOOK (priv->notebook));
2082 gtk_box_pack_start (GTK_BOX (chat_vbox), priv->notebook, TRUE, TRUE, 0);
2083 gtk_widget_show (priv->notebook);
2086 accel_group = gtk_accel_group_new ();
2087 gtk_window_add_accel_group (GTK_WINDOW (priv->dialog), accel_group);
2089 for (i = 0; i < G_N_ELEMENTS (tab_accel_keys); i++) {
2090 closure = g_cclosure_new (G_CALLBACK (chat_window_accel_cb),
2093 gtk_accel_group_connect (accel_group,
2100 g_object_unref (accel_group);
2102 /* Set up drag target lists */
2103 priv->contact_targets = gtk_target_list_new (drag_types_dest_contact,
2104 G_N_ELEMENTS (drag_types_dest_contact));
2105 priv->file_targets = gtk_target_list_new (drag_types_dest_file,
2106 G_N_ELEMENTS (drag_types_dest_file));
2108 /* Set up smiley menu */
2109 smiley_manager = empathy_smiley_manager_dup_singleton ();
2110 submenu = empathy_smiley_menu_new (smiley_manager,
2111 chat_window_insert_smiley_activate_cb,
2113 menu = gtk_ui_manager_get_widget (priv->ui_manager,
2114 "/chats_menubar/menu_conv/menu_conv_insert_smiley");
2115 gtk_menu_item_set_submenu (GTK_MENU_ITEM (menu), submenu);
2116 g_object_unref (smiley_manager);
2118 /* Set up signals we can't do with ui file since we may need to
2119 * block/unblock them at some later stage.
2122 g_signal_connect (priv->dialog,
2124 G_CALLBACK (chat_window_delete_event_cb),
2126 g_signal_connect (priv->dialog,
2128 G_CALLBACK (chat_window_focus_in_event_cb),
2130 g_signal_connect_after (priv->notebook,
2132 G_CALLBACK (chat_window_page_switched_cb),
2134 g_signal_connect (priv->notebook,
2136 G_CALLBACK (chat_window_page_added_cb),
2138 g_signal_connect (priv->notebook,
2140 G_CALLBACK (chat_window_page_removed_cb),
2143 /* Set up drag and drop */
2144 gtk_drag_dest_set (GTK_WIDGET (priv->notebook),
2145 GTK_DEST_DEFAULT_HIGHLIGHT,
2147 G_N_ELEMENTS (drag_types_dest),
2148 GDK_ACTION_MOVE | GDK_ACTION_COPY);
2150 /* connect_after to allow GtkNotebook's built-in tab switching */
2151 g_signal_connect_after (priv->notebook,
2153 G_CALLBACK (chat_window_drag_motion),
2155 g_signal_connect (priv->notebook,
2156 "drag-data-received",
2157 G_CALLBACK (chat_window_drag_data_received),
2159 g_signal_connect (priv->notebook,
2161 G_CALLBACK (chat_window_drag_drop),
2164 chat_windows = g_list_prepend (chat_windows, window);
2166 /* Set up private details */
2168 priv->current_chat = NULL;
2169 priv->notification = NULL;
2171 priv->notify_mgr = empathy_notify_manager_dup_singleton ();
2173 priv->chat_manager = empathy_chat_manager_dup_singleton ();
2174 priv->chat_manager_chats_changed_id =
2175 g_signal_connect (priv->chat_manager, "closed-chats-changed",
2176 G_CALLBACK (chat_window_chat_manager_chats_changed_cb),
2179 chat_window_chat_manager_chats_changed_cb (priv->chat_manager,
2180 empathy_chat_manager_get_num_closed_chats (priv->chat_manager),
2184 /* Returns the window to open a new tab in if there is a suitable window,
2185 * otherwise, returns NULL indicating that a new window should be added.
2187 static EmpathyChatWindow *
2188 empathy_chat_window_get_default (gboolean room)
2190 GSettings *gsettings = g_settings_new (EMPATHY_PREFS_UI_SCHEMA);
2192 gboolean separate_windows = TRUE;
2194 separate_windows = g_settings_get_boolean (gsettings,
2195 EMPATHY_PREFS_UI_SEPARATE_CHAT_WINDOWS);
2197 g_object_unref (gsettings);
2199 if (separate_windows) {
2200 /* Always create a new window */
2204 for (l = chat_windows; l; l = l->next) {
2205 EmpathyChatWindow *chat_window;
2206 guint nb_rooms, nb_private;
2208 chat_window = l->data;
2210 empathy_chat_window_get_nb_chats (chat_window, &nb_rooms, &nb_private);
2212 /* Skip the window if there aren't any rooms in it */
2213 if (room && nb_rooms == 0)
2216 /* Skip the window if there aren't any 1-1 chats in it */
2217 if (!room && nb_private == 0)
2227 empathy_chat_window_add_chat (EmpathyChatWindow *window,
2230 EmpathyChatWindowPriv *priv;
2232 GtkWidget *popup_label;
2234 GValue value = { 0, };
2236 g_return_if_fail (window != NULL);
2237 g_return_if_fail (EMPATHY_IS_CHAT (chat));
2239 priv = GET_PRIV (window);
2241 /* Reference the chat object */
2242 g_object_ref (chat);
2244 /* If this window has just been created, position it */
2245 if (priv->chats == NULL) {
2246 const gchar *name = "chat-window";
2247 gboolean separate_windows;
2249 separate_windows = g_settings_get_boolean (priv->gsettings_ui,
2250 EMPATHY_PREFS_UI_SEPARATE_CHAT_WINDOWS);
2252 if (empathy_chat_is_room (chat))
2253 name = "room-window";
2255 if (separate_windows) {
2258 /* Save current position of the window */
2259 gtk_window_get_position (GTK_WINDOW (priv->dialog), &x, &y);
2261 /* First bind to the 'generic' name. So new window for which we didn't
2262 * save a geometry yet will have the geometry of the last saved
2263 * window (bgo #601191). */
2264 empathy_geometry_bind (GTK_WINDOW (priv->dialog), name);
2266 /* Restore previous position of the window so the newly created window
2267 * won't be in the same position as the latest saved window and so
2268 * completely hide it. */
2269 gtk_window_move (GTK_WINDOW (priv->dialog), x, y);
2271 /* Then bind it to the name of the contact/room so we'll save the
2272 * geometry specific to this window */
2273 name = empathy_chat_get_id (chat);
2276 empathy_geometry_bind (GTK_WINDOW (priv->dialog), name);
2279 child = GTK_WIDGET (chat);
2280 label = chat_window_create_label (window, chat, TRUE);
2281 popup_label = chat_window_create_label (window, chat, FALSE);
2282 gtk_widget_show (child);
2284 g_signal_connect (chat, "notify::name",
2285 G_CALLBACK (chat_window_chat_notify_cb),
2287 g_signal_connect (chat, "notify::subject",
2288 G_CALLBACK (chat_window_chat_notify_cb),
2290 g_signal_connect (chat, "notify::remote-contact",
2291 G_CALLBACK (chat_window_chat_notify_cb),
2293 g_signal_connect (chat, "notify::sms-channel",
2294 G_CALLBACK (chat_window_chat_notify_cb),
2296 g_signal_connect (chat, "notify::n-messages-sending",
2297 G_CALLBACK (chat_window_chat_notify_cb),
2299 g_signal_connect (chat, "notify::nb-unread-messages",
2300 G_CALLBACK (chat_window_chat_notify_cb),
2302 chat_window_chat_notify_cb (chat);
2304 gtk_notebook_append_page_menu (GTK_NOTEBOOK (priv->notebook), child, label, popup_label);
2305 gtk_notebook_set_tab_reorderable (GTK_NOTEBOOK (priv->notebook), child, TRUE);
2306 gtk_notebook_set_tab_detachable (GTK_NOTEBOOK (priv->notebook), child, TRUE);
2307 g_value_init (&value, G_TYPE_BOOLEAN);
2308 g_value_set_boolean (&value, TRUE);
2309 gtk_container_child_set_property (GTK_CONTAINER (priv->notebook),
2310 child, "tab-expand" , &value);
2311 gtk_container_child_set_property (GTK_CONTAINER (priv->notebook),
2312 child, "tab-fill" , &value);
2313 g_value_unset (&value);
2315 DEBUG ("Chat added (%d references)", G_OBJECT (chat)->ref_count);
2319 empathy_chat_window_remove_chat (EmpathyChatWindow *window,
2322 EmpathyChatWindowPriv *priv;
2324 EmpathyContact *remote_contact;
2325 EmpathyChatManager *chat_manager;
2327 g_return_if_fail (window != NULL);
2328 g_return_if_fail (EMPATHY_IS_CHAT (chat));
2330 priv = GET_PRIV (window);
2332 g_signal_handlers_disconnect_by_func (chat,
2333 chat_window_chat_notify_cb,
2335 remote_contact = g_object_get_data (G_OBJECT (chat),
2336 "chat-window-remote-contact");
2337 if (remote_contact) {
2338 g_signal_handlers_disconnect_by_func (remote_contact,
2339 chat_window_update_chat_tab,
2343 chat_manager = empathy_chat_manager_dup_singleton ();
2344 empathy_chat_manager_closed_chat (chat_manager, chat);
2345 g_object_unref (chat_manager);
2347 position = gtk_notebook_page_num (GTK_NOTEBOOK (priv->notebook),
2349 gtk_notebook_remove_page (GTK_NOTEBOOK (priv->notebook), position);
2351 DEBUG ("Chat removed (%d references)", G_OBJECT (chat)->ref_count - 1);
2353 g_object_unref (chat);
2357 empathy_chat_window_move_chat (EmpathyChatWindow *old_window,
2358 EmpathyChatWindow *new_window,
2363 g_return_if_fail (EMPATHY_IS_CHAT_WINDOW (old_window));
2364 g_return_if_fail (EMPATHY_IS_CHAT_WINDOW (new_window));
2365 g_return_if_fail (EMPATHY_IS_CHAT (chat));
2367 widget = GTK_WIDGET (chat);
2369 DEBUG ("Chat moving with widget:%p (%d references)", widget,
2370 G_OBJECT (widget)->ref_count);
2372 /* We reference here to make sure we don't loose the widget
2373 * and the EmpathyChat object during the move.
2375 g_object_ref (chat);
2376 g_object_ref (widget);
2378 empathy_chat_window_remove_chat (old_window, chat);
2379 empathy_chat_window_add_chat (new_window, chat);
2381 g_object_unref (widget);
2382 g_object_unref (chat);
2386 empathy_chat_window_switch_to_chat (EmpathyChatWindow *window,
2389 EmpathyChatWindowPriv *priv;
2392 g_return_if_fail (window != NULL);
2393 g_return_if_fail (EMPATHY_IS_CHAT (chat));
2395 priv = GET_PRIV (window);
2397 page_num = gtk_notebook_page_num (GTK_NOTEBOOK (priv->notebook),
2399 gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->notebook),
2404 empathy_chat_window_find_chat (TpAccount *account,
2406 gboolean sms_channel)
2410 g_return_val_if_fail (!EMP_STR_EMPTY (id), NULL);
2412 for (l = chat_windows; l; l = l->next) {
2413 EmpathyChatWindowPriv *priv;
2414 EmpathyChatWindow *window;
2418 priv = GET_PRIV (window);
2420 for (ll = priv->chats; ll; ll = ll->next) {
2425 if (account == empathy_chat_get_account (chat) &&
2426 !tp_strdiff (id, empathy_chat_get_id (chat)) &&
2427 sms_channel == empathy_chat_is_sms_channel (chat)) {
2437 empathy_chat_window_present_chat (EmpathyChat *chat,
2440 EmpathyChatWindow *window;
2441 EmpathyChatWindowPriv *priv;
2442 guint32 x_timestamp;
2444 g_return_if_fail (EMPATHY_IS_CHAT (chat));
2446 window = chat_window_find_chat (chat);
2448 /* If the chat has no window, create one */
2449 if (window == NULL) {
2450 window = empathy_chat_window_get_default (empathy_chat_is_room (chat));
2452 window = empathy_chat_window_new ();
2454 /* we want to display the newly created window even if we don't present
2456 priv = GET_PRIV (window);
2457 gtk_widget_show (priv->dialog);
2460 empathy_chat_window_add_chat (window, chat);
2463 /* Don't force the window to show itself when it wasn't
2464 * an action by the user
2466 if (!tp_user_action_time_should_present (timestamp, &x_timestamp))
2469 priv = GET_PRIV (window);
2471 if (x_timestamp != GDK_CURRENT_TIME) {
2472 /* Don't present or switch tab if the action was earlier than the
2473 * last actions X time, accounting for overflow and the first ever
2476 if (priv->x_user_action_time != 0
2477 && X_EARLIER_OR_EQL (x_timestamp, priv->x_user_action_time))
2480 priv->x_user_action_time = x_timestamp;
2483 empathy_chat_window_switch_to_chat (window, chat);
2484 empathy_window_present_with_time (GTK_WINDOW (priv->dialog),
2487 gtk_widget_grab_focus (chat->input_text_view);
2491 empathy_chat_window_get_nb_chats (EmpathyChatWindow *self,
2495 EmpathyChatWindowPriv *priv = GET_PRIV (self);
2497 guint _nb_rooms = 0, _nb_private = 0;
2499 for (l = priv->chats; l != NULL; l = g_list_next (l)) {
2500 if (empathy_chat_is_room (EMPATHY_CHAT (l->data)))
2506 if (nb_rooms != NULL)
2507 *nb_rooms = _nb_rooms;
2508 if (nb_private != NULL)
2509 *nb_private = _nb_private;