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-2008 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
/* Add launchpad hooks */
40
#include <launchpad-integration.h>
42
#include <telepathy-glib/telepathy-glib.h>
44
#include <libempathy/empathy-contact.h>
45
#include <libempathy/empathy-message.h>
46
#include <libempathy/empathy-chatroom-manager.h>
47
#include <libempathy/empathy-gsettings.h>
48
#include <libempathy/empathy-utils.h>
49
#include <libempathy/empathy-tp-contact-factory.h>
50
#include <libempathy/empathy-contact-list.h>
52
#include <libempathy-gtk/empathy-images.h>
53
#include <libempathy-gtk/empathy-contact-dialogs.h>
54
#include <libempathy-gtk/empathy-log-window.h>
55
#include <libempathy-gtk/empathy-geometry.h>
56
#include <libempathy-gtk/empathy-smiley-manager.h>
57
#include <libempathy-gtk/empathy-sound.h>
58
#include <libempathy-gtk/empathy-ui-utils.h>
59
#include <libempathy-gtk/empathy-notify-manager.h>
61
#include "empathy-chat-manager.h"
62
#include "empathy-chat-window.h"
63
#include "empathy-about-dialog.h"
64
#include "empathy-invite-participant-dialog.h"
66
#define DEBUG_FLAG EMPATHY_DEBUG_CHAT
67
#include <libempathy/empathy-debug.h>
69
/* Macro to compare guint32 X timestamps, while accounting for wrapping around
71
#define X_EARLIER_OR_EQL(t1, t2) \
72
((t1 <= t2 && ((t2 - t1) < G_MAXUINT32/2)) \
73
|| (t1 >= t2 && (t1 - t2) > (G_MAXUINT32/2)) \
76
#define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyChatWindow)
78
EmpathyChat *current_chat;
81
GList *chats_composing;
83
gboolean dnd_same_window;
84
EmpathyChatroomManager *chatroom_manager;
85
EmpathyNotifyManager *notify_mgr;
88
NotifyNotification *notification;
90
GtkTargetList *contact_targets;
91
GtkTargetList *file_targets;
93
EmpathyChatManager *chat_manager;
94
gulong chat_manager_chats_changed_id;
97
GtkUIManager *ui_manager;
98
GtkAction *menu_conv_insert_smiley;
99
GtkAction *menu_conv_favorite;
100
GtkAction *menu_conv_always_urgent;
101
GtkAction *menu_conv_toggle_contacts;
103
GtkAction *menu_edit_cut;
104
GtkAction *menu_edit_copy;
105
GtkAction *menu_edit_paste;
106
GtkAction *menu_edit_find;
108
GtkAction *menu_tabs_next;
109
GtkAction *menu_tabs_prev;
110
GtkAction *menu_tabs_undo_close_tab;
111
GtkAction *menu_tabs_left;
112
GtkAction *menu_tabs_right;
113
GtkAction *menu_tabs_detach;
115
/* Last user action time we acted upon to show a tab */
116
guint32 x_user_action_time;
117
} EmpathyChatWindowPriv;
119
static GList *chat_windows = NULL;
121
static const guint tab_accel_keys[] = {
122
GDK_1, GDK_2, GDK_3, GDK_4, GDK_5,
123
GDK_6, GDK_7, GDK_8, GDK_9, GDK_0
127
DND_DRAG_TYPE_CONTACT_ID,
128
DND_DRAG_TYPE_URI_LIST,
132
static const GtkTargetEntry drag_types_dest[] = {
133
{ "text/contact-id", 0, DND_DRAG_TYPE_CONTACT_ID },
134
{ "GTK_NOTEBOOK_TAB", GTK_TARGET_SAME_APP, DND_DRAG_TYPE_TAB },
135
{ "text/uri-list", 0, DND_DRAG_TYPE_URI_LIST },
136
{ "text/path-list", 0, DND_DRAG_TYPE_URI_LIST },
139
static const GtkTargetEntry drag_types_dest_contact[] = {
140
{ "text/contact-id", 0, DND_DRAG_TYPE_CONTACT_ID },
143
static const GtkTargetEntry drag_types_dest_file[] = {
144
/* must be first to be prioritized, in order to receive the
145
* note's file path from Tomboy instead of an URI */
146
{ "text/path-list", 0, DND_DRAG_TYPE_URI_LIST },
147
{ "text/uri-list", 0, DND_DRAG_TYPE_URI_LIST },
150
static void chat_window_update (EmpathyChatWindow *window,
151
gboolean update_contact_menu);
153
G_DEFINE_TYPE (EmpathyChatWindow, empathy_chat_window, G_TYPE_OBJECT);
156
chat_window_accel_cb (GtkAccelGroup *accelgroup,
160
EmpathyChatWindow *window)
162
EmpathyChatWindowPriv *priv;
166
priv = GET_PRIV (window);
168
for (i = 0; i < G_N_ELEMENTS (tab_accel_keys); i++) {
169
if (tab_accel_keys[i] == key) {
176
gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->notebook), num);
180
static EmpathyChatWindow *
181
chat_window_find_chat (EmpathyChat *chat)
183
EmpathyChatWindowPriv *priv;
186
for (l = chat_windows; l; l = l->next) {
187
priv = GET_PRIV (l->data);
188
ll = g_list_find (priv->chats, chat);
198
chat_window_close_clicked_cb (GtkAction *action,
201
EmpathyChatWindow *window;
203
window = chat_window_find_chat (chat);
204
empathy_chat_window_remove_chat (window, chat);
208
chat_tab_style_set_cb (GtkWidget *hbox,
209
GtkStyle *previous_style,
213
int char_width, h, w;
214
PangoContext *context;
215
PangoFontMetrics *metrics;
217
button = g_object_get_data (G_OBJECT (user_data),
218
"chat-window-tab-close-button");
219
context = gtk_widget_get_pango_context (hbox);
221
metrics = pango_context_get_metrics (context, gtk_widget_get_style (hbox)->font_desc,
222
pango_context_get_language (context));
223
char_width = pango_font_metrics_get_approximate_char_width (metrics);
224
pango_font_metrics_unref (metrics);
226
gtk_icon_size_lookup_for_settings (gtk_widget_get_settings (button),
227
GTK_ICON_SIZE_MENU, &w, &h);
229
/* Request at least about 12 chars width plus at least space for the status
230
* image and the close button */
231
gtk_widget_set_size_request (hbox,
232
12 * PANGO_PIXELS (char_width) + 2 * w, -1);
234
gtk_widget_set_size_request (button, w, h);
238
chat_window_create_label (EmpathyChatWindow *window,
240
gboolean is_tab_label)
242
EmpathyChatWindowPriv *priv;
244
GtkWidget *name_label;
245
GtkWidget *status_image;
246
GtkWidget *close_button;
247
GtkWidget *close_image;
248
GtkWidget *event_box;
249
GtkWidget *event_box_hbox;
250
PangoAttrList *attr_list;
251
PangoAttribute *attr;
253
priv = GET_PRIV (window);
255
/* The spacing between the button and the label. */
256
hbox = gtk_hbox_new (FALSE, 0);
258
event_box = gtk_event_box_new ();
259
gtk_event_box_set_visible_window (GTK_EVENT_BOX (event_box), FALSE);
261
name_label = gtk_label_new (NULL);
263
gtk_label_set_ellipsize (GTK_LABEL (name_label), PANGO_ELLIPSIZE_END);
265
attr_list = pango_attr_list_new ();
266
attr = pango_attr_scale_new (1/1.2);
267
attr->start_index = 0;
268
attr->end_index = -1;
269
pango_attr_list_insert (attr_list, attr);
270
gtk_label_set_attributes (GTK_LABEL (name_label), attr_list);
271
pango_attr_list_unref (attr_list);
273
gtk_misc_set_padding (GTK_MISC (name_label), 2, 0);
274
gtk_misc_set_alignment (GTK_MISC (name_label), 0.0, 0.5);
275
g_object_set_data (G_OBJECT (chat),
276
is_tab_label ? "chat-window-tab-label" : "chat-window-menu-label",
279
status_image = gtk_image_new ();
281
/* Spacing between the icon and label. */
282
event_box_hbox = gtk_hbox_new (FALSE, 0);
284
gtk_box_pack_start (GTK_BOX (event_box_hbox), status_image, FALSE, FALSE, 0);
285
gtk_box_pack_start (GTK_BOX (event_box_hbox), name_label, TRUE, TRUE, 0);
287
g_object_set_data (G_OBJECT (chat),
288
is_tab_label ? "chat-window-tab-image" : "chat-window-menu-image",
290
g_object_set_data (G_OBJECT (chat),
291
is_tab_label ? "chat-window-tab-tooltip-widget" : "chat-window-menu-tooltip-widget",
294
gtk_container_add (GTK_CONTAINER (event_box), event_box_hbox);
295
gtk_box_pack_start (GTK_BOX (hbox), event_box, TRUE, TRUE, 0);
298
close_button = gtk_button_new ();
299
gtk_button_set_relief (GTK_BUTTON (close_button), GTK_RELIEF_NONE);
300
g_object_set_data (G_OBJECT (chat), "chat-window-tab-close-button", close_button);
302
/* We don't want focus/keynav for the button to avoid clutter, and
303
* Ctrl-W works anyway.
305
gtk_widget_set_can_focus (close_button, FALSE);
306
gtk_widget_set_can_default (close_button, FALSE);
308
/* Set the name to make the special rc style match. */
309
gtk_widget_set_name (close_button, "empathy-close-button");
311
close_image = gtk_image_new_from_stock (GTK_STOCK_CLOSE, GTK_ICON_SIZE_MENU);
313
gtk_container_add (GTK_CONTAINER (close_button), close_image);
315
gtk_box_pack_end (GTK_BOX (hbox), close_button, FALSE, FALSE, 0);
317
g_signal_connect (close_button,
319
G_CALLBACK (chat_window_close_clicked_cb),
322
/* React to theme changes and also setup the size correctly. */
323
g_signal_connect (hbox,
325
G_CALLBACK (chat_tab_style_set_cb),
329
gtk_widget_show_all (hbox);
335
_submenu_notify_visible_changed_cb (GObject *object,
339
g_signal_handlers_disconnect_by_func (object,
340
_submenu_notify_visible_changed_cb,
342
chat_window_update (EMPATHY_CHAT_WINDOW (userdata), TRUE);
346
chat_window_menu_context_update (EmpathyChatWindowPriv *priv,
351
gboolean wrap_around;
352
gboolean is_connected;
355
page_num = gtk_notebook_get_current_page (GTK_NOTEBOOK (priv->notebook));
356
first_page = (page_num == 0);
357
last_page = (page_num == (num_pages - 1));
358
g_object_get (gtk_settings_get_default (), "gtk-keynav-wrap-around",
360
is_connected = empathy_chat_get_tp_chat (priv->current_chat) != NULL;
362
gtk_action_set_sensitive (priv->menu_tabs_next, (!last_page ||
364
gtk_action_set_sensitive (priv->menu_tabs_prev, (!first_page ||
366
gtk_action_set_sensitive (priv->menu_tabs_detach, num_pages > 1);
367
gtk_action_set_sensitive (priv->menu_tabs_left, !first_page);
368
gtk_action_set_sensitive (priv->menu_tabs_right, !last_page);
369
gtk_action_set_sensitive (priv->menu_conv_insert_smiley, is_connected);
373
chat_window_conversation_menu_update (EmpathyChatWindowPriv *priv,
374
EmpathyChatWindow *self)
376
EmpathyTpChat *tp_chat;
377
TpConnection *connection;
379
gboolean sensitive = FALSE;
381
g_return_if_fail (priv->current_chat != NULL);
383
action = gtk_ui_manager_get_action (priv->ui_manager,
384
"/chats_menubar/menu_conv/menu_conv_invite_participant");
385
tp_chat = empathy_chat_get_tp_chat (priv->current_chat);
387
if (tp_chat != NULL) {
388
connection = empathy_tp_chat_get_connection (tp_chat);
390
sensitive = empathy_tp_chat_can_add_contact (tp_chat) &&
391
(tp_connection_get_status (connection, NULL) ==
392
TP_CONNECTION_STATUS_CONNECTED);
395
gtk_action_set_sensitive (action, sensitive);
399
chat_window_contact_menu_update (EmpathyChatWindowPriv *priv,
400
EmpathyChatWindow *window)
402
GtkWidget *menu, *submenu, *orig_submenu;
404
menu = gtk_ui_manager_get_widget (priv->ui_manager,
405
"/chats_menubar/menu_contact");
406
orig_submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (menu));
408
if (orig_submenu == NULL || !gtk_widget_get_visible (orig_submenu)) {
409
submenu = empathy_chat_get_contact_menu (priv->current_chat);
411
if (submenu != NULL) {
412
/* gtk_menu_attach_to_widget () doesn't behave nicely here */
413
g_object_set_data (G_OBJECT (submenu), "window", priv->dialog);
415
gtk_menu_item_set_submenu (GTK_MENU_ITEM (menu), submenu);
416
gtk_widget_show (menu);
417
gtk_widget_set_sensitive (menu, TRUE);
419
gtk_widget_set_sensitive (menu, FALSE);
423
tp_g_signal_connect_object (orig_submenu,
425
(GCallback)_submenu_notify_visible_changed_cb,
431
get_all_unread_messages (EmpathyChatWindowPriv *priv)
436
for (l = priv->chats_new_msg; l != NULL; l = g_list_next (l)) {
437
EmpathyChat *chat = l->data;
439
nb += empathy_chat_get_nb_unread_messages (chat);
446
get_window_title_name (EmpathyChatWindowPriv *priv)
448
const gchar *active_name;
450
guint current_unread_msgs;
452
nb_chats = g_list_length (priv->chats);
453
g_assert (nb_chats > 0);
455
active_name = empathy_chat_get_name (priv->current_chat);
457
current_unread_msgs = empathy_chat_get_nb_unread_messages (
462
if (current_unread_msgs == 0)
463
return g_strdup (active_name);
465
return g_strdup_printf (ngettext (
467
"%s (%d unread)", current_unread_msgs),
468
active_name, current_unread_msgs);
470
guint nb_others = nb_chats - 1;
471
guint all_unread_msgs;
473
all_unread_msgs = get_all_unread_messages (priv);
475
if (all_unread_msgs == 0) {
476
/* no unread message */
477
return g_strdup_printf (ngettext (
479
"%s (and %u others)", nb_others),
480
active_name, nb_others);
483
else if (all_unread_msgs == current_unread_msgs) {
484
/* unread messages are in the current tab */
485
return g_strdup_printf (ngettext (
487
"%s (%d unread)", current_unread_msgs),
488
active_name, current_unread_msgs);
491
else if (current_unread_msgs == 0) {
492
/* unread messages are in other tabs */
493
return g_strdup_printf (ngettext (
494
"%s (%d unread from others)",
495
"%s (%d unread from others)",
497
active_name, all_unread_msgs);
501
/* unread messages are in all the tabs */
502
return g_strdup_printf (ngettext (
503
"%s (%d unread from all)",
504
"%s (%d unread from all)",
506
active_name, all_unread_msgs);
512
chat_window_title_update (EmpathyChatWindowPriv *priv)
516
name = get_window_title_name (priv);
517
gtk_window_set_title (GTK_WINDOW (priv->dialog), name);
522
chat_window_icon_update (EmpathyChatWindowPriv *priv)
525
EmpathyContact *remote_contact;
526
gboolean avatar_in_icon;
529
n_chats = g_list_length (priv->chats);
531
/* Update window icon */
532
if (priv->chats_new_msg) {
533
gtk_window_set_icon_name (GTK_WINDOW (priv->dialog),
534
EMPATHY_IMAGE_MESSAGE);
536
GSettings *gsettings = g_settings_new (EMPATHY_PREFS_CHAT_SCHEMA);
538
avatar_in_icon = g_settings_get_boolean (gsettings,
539
EMPATHY_PREFS_CHAT_AVATAR_IN_ICON);
541
if (n_chats == 1 && avatar_in_icon) {
542
remote_contact = empathy_chat_get_remote_contact (priv->current_chat);
543
icon = empathy_pixbuf_avatar_from_contact_scaled (remote_contact, 0, 0);
544
gtk_window_set_icon (GTK_WINDOW (priv->dialog), icon);
547
g_object_unref (icon);
550
gtk_window_set_icon_name (GTK_WINDOW (priv->dialog), NULL);
553
g_object_unref (gsettings);
558
chat_window_close_button_update (EmpathyChatWindowPriv *priv,
562
GtkWidget *chat_close_button;
565
if (num_pages == 1) {
566
chat = gtk_notebook_get_nth_page (GTK_NOTEBOOK (priv->notebook), 0);
567
chat_close_button = g_object_get_data (G_OBJECT (chat),
568
"chat-window-tab-close-button");
569
gtk_widget_hide (chat_close_button);
571
for (i=0; i<num_pages; i++) {
572
chat = gtk_notebook_get_nth_page (GTK_NOTEBOOK (priv->notebook), i);
573
chat_close_button = g_object_get_data (G_OBJECT (chat),
574
"chat-window-tab-close-button");
575
gtk_widget_show (chat_close_button);
581
chat_window_update (EmpathyChatWindow *window,
582
gboolean update_contact_menu)
584
EmpathyChatWindowPriv *priv = GET_PRIV (window);
587
num_pages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (priv->notebook));
589
/* Update Tab menu */
590
chat_window_menu_context_update (priv,
593
chat_window_conversation_menu_update (priv, window);
595
/* If this update is due to a focus-in event, we know the menu will be
596
the same as when we last left it, so no work to do. Besides, if we
597
swap out the menu on a focus-in, we may confuse any external global
599
if (update_contact_menu) {
600
chat_window_contact_menu_update (priv,
604
chat_window_title_update (priv);
606
chat_window_icon_update (priv);
608
chat_window_close_button_update (priv,
613
append_markup_printf (GString *string,
620
va_start (args, format);
622
tmp = g_markup_vprintf_escaped (format, args);
623
g_string_append (string, tmp);
630
chat_window_update_chat_tab_full (EmpathyChat *chat,
631
gboolean update_contact_menu)
633
EmpathyChatWindow *window;
634
EmpathyChatWindowPriv *priv;
635
EmpathyContact *remote_contact;
639
const gchar *subject;
640
const gchar *status = NULL;
644
const gchar *icon_name;
645
GtkWidget *tab_image;
646
GtkWidget *menu_image;
648
window = chat_window_find_chat (chat);
652
priv = GET_PRIV (window);
654
/* Get information */
655
name = empathy_chat_get_name (chat);
656
account = empathy_chat_get_account (chat);
657
subject = empathy_chat_get_subject (chat);
658
remote_contact = empathy_chat_get_remote_contact (chat);
660
DEBUG ("Updating chat tab, name=%s, account=%s, subject=%s, remote_contact=%p",
661
name, tp_proxy_get_object_path (account), subject, remote_contact);
663
/* Update tab image */
664
if (empathy_chat_get_tp_chat (chat) == NULL) {
665
/* No TpChat, we are disconnected */
668
else if (g_list_find (priv->chats_new_msg, chat)) {
669
icon_name = EMPATHY_IMAGE_MESSAGE;
671
else if (g_list_find (priv->chats_composing, chat)) {
672
icon_name = EMPATHY_IMAGE_TYPING;
674
else if (remote_contact) {
675
icon_name = empathy_icon_name_for_contact (remote_contact);
677
icon_name = EMPATHY_IMAGE_GROUP_MESSAGE;
680
tab_image = g_object_get_data (G_OBJECT (chat), "chat-window-tab-image");
681
menu_image = g_object_get_data (G_OBJECT (chat), "chat-window-menu-image");
682
if (icon_name != NULL) {
683
gtk_image_set_from_icon_name (GTK_IMAGE (tab_image), icon_name, GTK_ICON_SIZE_MENU);
684
gtk_widget_show (tab_image);
685
gtk_image_set_from_icon_name (GTK_IMAGE (menu_image), icon_name, GTK_ICON_SIZE_MENU);
686
gtk_widget_show (menu_image);
688
gtk_widget_hide (tab_image);
689
gtk_widget_hide (menu_image);
692
/* Update tab tooltip */
693
tooltip = g_string_new (NULL);
695
if (remote_contact) {
696
id = empathy_contact_get_id (remote_contact);
697
status = empathy_contact_get_presence_message (remote_contact);
702
append_markup_printf (tooltip,
703
"<b>%s</b><small> (%s)</small>",
705
tp_account_get_display_name (account));
707
if (!EMP_STR_EMPTY (status)) {
708
append_markup_printf (tooltip, "\n<i>%s</i>", status);
712
append_markup_printf (tooltip, "\n<b>%s</b> %s",
713
_("Topic:"), subject);
716
if (g_list_find (priv->chats_composing, chat)) {
717
append_markup_printf (tooltip, "\n%s", _("Typing a message."));
720
markup = g_string_free (tooltip, FALSE);
721
widget = g_object_get_data (G_OBJECT (chat), "chat-window-tab-tooltip-widget");
722
gtk_widget_set_tooltip_markup (widget, markup);
723
widget = g_object_get_data (G_OBJECT (chat), "chat-window-menu-tooltip-widget");
724
gtk_widget_set_tooltip_markup (widget, markup);
727
/* Update tab and menu label */
728
widget = g_object_get_data (G_OBJECT (chat), "chat-window-tab-label");
729
gtk_label_set_text (GTK_LABEL (widget), name);
730
widget = g_object_get_data (G_OBJECT (chat), "chat-window-menu-label");
731
gtk_label_set_text (GTK_LABEL (widget), name);
733
/* Update the window if it's the current chat */
734
if (priv->current_chat == chat) {
735
chat_window_update (window, update_contact_menu);
740
chat_window_update_chat_tab (EmpathyChat *chat)
742
chat_window_update_chat_tab_full (chat, TRUE);
746
chat_window_chat_notify_cb (EmpathyChat *chat)
748
EmpathyContact *old_remote_contact;
749
EmpathyContact *remote_contact = NULL;
751
old_remote_contact = g_object_get_data (G_OBJECT (chat), "chat-window-remote-contact");
752
remote_contact = empathy_chat_get_remote_contact (chat);
754
if (old_remote_contact != remote_contact) {
755
/* The remote-contact associated with the chat changed, we need
756
* to keep track of any change of that contact and update the
757
* window each time. */
758
if (remote_contact) {
759
g_signal_connect_swapped (remote_contact, "notify",
760
G_CALLBACK (chat_window_update_chat_tab),
763
if (old_remote_contact) {
764
g_signal_handlers_disconnect_by_func (old_remote_contact,
765
chat_window_update_chat_tab,
769
g_object_set_data_full (G_OBJECT (chat), "chat-window-remote-contact",
770
g_object_ref (remote_contact), (GDestroyNotify) g_object_unref);
773
chat_window_update_chat_tab (chat);
777
chat_window_insert_smiley_activate_cb (EmpathySmileyManager *manager,
778
EmpathySmiley *smiley,
781
EmpathyChatWindowPriv *priv = GET_PRIV (window);
783
GtkTextBuffer *buffer;
786
chat = priv->current_chat;
788
buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (chat->input_text_view));
789
gtk_text_buffer_get_end_iter (buffer, &iter);
790
gtk_text_buffer_insert (buffer, &iter, smiley->str, -1);
794
chat_window_conv_activate_cb (GtkAction *action,
795
EmpathyChatWindow *window)
797
EmpathyChatWindowPriv *priv = GET_PRIV (window);
800
EmpathyContact *remote_contact = NULL;
802
/* Favorite room menu */
803
is_room = empathy_chat_is_room (priv->current_chat);
807
gboolean found = FALSE;
808
EmpathyChatroom *chatroom;
810
room = empathy_chat_get_id (priv->current_chat);
811
account = empathy_chat_get_account (priv->current_chat);
812
chatroom = empathy_chatroom_manager_find (priv->chatroom_manager,
814
if (chatroom != NULL)
815
found = empathy_chatroom_is_favorite (chatroom);
817
DEBUG ("This room %s favorite", found ? "is" : "is not");
818
gtk_toggle_action_set_active (
819
GTK_TOGGLE_ACTION (priv->menu_conv_favorite), found);
821
if (chatroom != NULL)
822
found = empathy_chatroom_is_always_urgent (chatroom);
824
gtk_toggle_action_set_active (
825
GTK_TOGGLE_ACTION (priv->menu_conv_always_urgent),
828
gtk_action_set_visible (priv->menu_conv_favorite, is_room);
829
gtk_action_set_visible (priv->menu_conv_always_urgent, is_room);
831
/* Show contacts menu */
832
g_object_get (priv->current_chat,
833
"remote-contact", &remote_contact,
834
"show-contacts", &active,
836
if (remote_contact == NULL) {
837
gtk_toggle_action_set_active (
838
GTK_TOGGLE_ACTION (priv->menu_conv_toggle_contacts),
841
gtk_action_set_visible (priv->menu_conv_toggle_contacts,
842
(remote_contact == NULL));
843
if (remote_contact != NULL) {
844
g_object_unref (remote_contact);
849
chat_window_clear_activate_cb (GtkAction *action,
850
EmpathyChatWindow *window)
852
EmpathyChatWindowPriv *priv = GET_PRIV (window);
854
empathy_chat_clear (priv->current_chat);
858
chat_window_favorite_toggled_cb (GtkToggleAction *toggle_action,
859
EmpathyChatWindow *window)
861
EmpathyChatWindowPriv *priv = GET_PRIV (window);
865
EmpathyChatroom *chatroom;
867
active = gtk_toggle_action_get_active (toggle_action);
868
account = empathy_chat_get_account (priv->current_chat);
869
room = empathy_chat_get_id (priv->current_chat);
871
chatroom = empathy_chatroom_manager_ensure_chatroom (
872
priv->chatroom_manager,
875
empathy_chat_get_name (priv->current_chat));
877
empathy_chatroom_set_favorite (chatroom, active);
878
g_object_unref (chatroom);
882
chat_window_always_urgent_toggled_cb (GtkToggleAction *toggle_action,
883
EmpathyChatWindow *window)
885
EmpathyChatWindowPriv *priv = GET_PRIV (window);
889
EmpathyChatroom *chatroom;
891
active = gtk_toggle_action_get_active (toggle_action);
892
account = empathy_chat_get_account (priv->current_chat);
893
room = empathy_chat_get_id (priv->current_chat);
895
chatroom = empathy_chatroom_manager_ensure_chatroom (
896
priv->chatroom_manager,
899
empathy_chat_get_name (priv->current_chat));
901
empathy_chatroom_set_always_urgent (chatroom, active);
902
g_object_unref (chatroom);
906
chat_window_contacts_toggled_cb (GtkToggleAction *toggle_action,
907
EmpathyChatWindow *window)
909
EmpathyChatWindowPriv *priv = GET_PRIV (window);
912
active = gtk_toggle_action_get_active (toggle_action);
914
empathy_chat_set_show_contacts (priv->current_chat, active);
918
got_contact_cb (TpConnection *connection,
919
EmpathyContact *contact,
924
EmpathyTpChat *tp_chat = EMPATHY_TP_CHAT (user_data);
927
DEBUG ("Failed: %s", error->message);
930
empathy_contact_list_add (EMPATHY_CONTACT_LIST (tp_chat),
931
contact, _("Inviting you to this room"));
936
chat_window_invite_participant_activate_cb (GtkAction *action,
937
EmpathyChatWindow *window)
939
EmpathyChatWindowPriv *priv;
941
EmpathyTpChat *tp_chat;
946
priv = GET_PRIV (window);
948
g_return_if_fail (priv->current_chat != NULL);
950
tp_chat = empathy_chat_get_tp_chat (priv->current_chat);
951
channel = empathy_tp_chat_get_channel (tp_chat);
952
account = empathy_chat_get_account (priv->current_chat);
954
dialog = empathy_invite_participant_dialog_new (
955
GTK_WINDOW (priv->dialog), account);
956
gtk_widget_show (dialog);
958
response = gtk_dialog_run (GTK_DIALOG (dialog));
960
if (response == GTK_RESPONSE_ACCEPT) {
961
TpConnection *connection;
964
id = empathy_contact_selector_dialog_get_selected (
965
EMPATHY_CONTACT_SELECTOR_DIALOG (dialog), NULL, NULL);
966
if (EMP_STR_EMPTY (id)) goto out;
968
connection = tp_channel_borrow_connection (channel);
969
empathy_tp_contact_factory_get_from_id (connection, id,
970
got_contact_cb, tp_chat, NULL, NULL);
974
gtk_widget_destroy (dialog);
978
chat_window_close_activate_cb (GtkAction *action,
979
EmpathyChatWindow *window)
981
EmpathyChatWindowPriv *priv;
983
priv = GET_PRIV (window);
985
g_return_if_fail (priv->current_chat != NULL);
987
empathy_chat_window_remove_chat (window, priv->current_chat);
991
chat_window_edit_activate_cb (GtkAction *action,
992
EmpathyChatWindow *window)
994
EmpathyChatWindowPriv *priv;
995
GtkClipboard *clipboard;
996
GtkTextBuffer *buffer;
997
gboolean text_available;
999
priv = GET_PRIV (window);
1001
g_return_if_fail (priv->current_chat != NULL);
1003
if (!empathy_chat_get_tp_chat (priv->current_chat)) {
1004
gtk_action_set_sensitive (priv->menu_edit_copy, FALSE);
1005
gtk_action_set_sensitive (priv->menu_edit_cut, FALSE);
1006
gtk_action_set_sensitive (priv->menu_edit_paste, FALSE);
1010
buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (priv->current_chat->input_text_view));
1011
if (gtk_text_buffer_get_has_selection (buffer)) {
1012
gtk_action_set_sensitive (priv->menu_edit_copy, TRUE);
1013
gtk_action_set_sensitive (priv->menu_edit_cut, TRUE);
1017
selection = empathy_chat_view_get_has_selection (priv->current_chat->view);
1019
gtk_action_set_sensitive (priv->menu_edit_cut, FALSE);
1020
gtk_action_set_sensitive (priv->menu_edit_copy, selection);
1023
clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
1024
text_available = gtk_clipboard_wait_is_text_available (clipboard);
1025
gtk_action_set_sensitive (priv->menu_edit_paste, text_available);
1029
chat_window_cut_activate_cb (GtkAction *action,
1030
EmpathyChatWindow *window)
1032
EmpathyChatWindowPriv *priv;
1034
g_return_if_fail (EMPATHY_IS_CHAT_WINDOW (window));
1036
priv = GET_PRIV (window);
1038
empathy_chat_cut (priv->current_chat);
1042
chat_window_copy_activate_cb (GtkAction *action,
1043
EmpathyChatWindow *window)
1045
EmpathyChatWindowPriv *priv;
1047
g_return_if_fail (EMPATHY_IS_CHAT_WINDOW (window));
1049
priv = GET_PRIV (window);
1051
empathy_chat_copy (priv->current_chat);
1055
chat_window_paste_activate_cb (GtkAction *action,
1056
EmpathyChatWindow *window)
1058
EmpathyChatWindowPriv *priv;
1060
g_return_if_fail (EMPATHY_IS_CHAT_WINDOW (window));
1062
priv = GET_PRIV (window);
1064
empathy_chat_paste (priv->current_chat);
1068
chat_window_find_activate_cb (GtkAction *action,
1069
EmpathyChatWindow *window)
1071
EmpathyChatWindowPriv *priv;
1073
g_return_if_fail (EMPATHY_IS_CHAT_WINDOW (window));
1075
priv = GET_PRIV (window);
1077
empathy_chat_find (priv->current_chat);
1081
chat_window_tabs_next_activate_cb (GtkAction *action,
1082
EmpathyChatWindow *window)
1084
EmpathyChatWindowPriv *priv;
1086
gint index_, numPages;
1087
gboolean wrap_around;
1089
priv = GET_PRIV (window);
1091
g_object_get (gtk_settings_get_default (), "gtk-keynav-wrap-around",
1092
&wrap_around, NULL);
1094
chat = priv->current_chat;
1095
index_ = gtk_notebook_get_current_page (GTK_NOTEBOOK (priv->notebook));
1096
numPages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (priv->notebook));
1098
if (index_ == (numPages - 1) && wrap_around) {
1099
gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->notebook), 0);
1103
gtk_notebook_next_page (GTK_NOTEBOOK (priv->notebook));
1107
chat_window_tabs_previous_activate_cb (GtkAction *action,
1108
EmpathyChatWindow *window)
1110
EmpathyChatWindowPriv *priv;
1112
gint index_, numPages;
1113
gboolean wrap_around;
1115
priv = GET_PRIV (window);
1117
g_object_get (gtk_settings_get_default (), "gtk-keynav-wrap-around",
1118
&wrap_around, NULL);
1120
chat = priv->current_chat;
1121
index_ = gtk_notebook_get_current_page (GTK_NOTEBOOK (priv->notebook));
1122
numPages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (priv->notebook));
1124
if (index_ <= 0 && wrap_around) {
1125
gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->notebook), numPages - 1);
1129
gtk_notebook_prev_page (GTK_NOTEBOOK (priv->notebook));
1133
chat_window_tabs_undo_close_tab_activate_cb (GtkAction *action,
1134
EmpathyChatWindow *window)
1136
EmpathyChatWindowPriv *priv = GET_PRIV (window);
1137
empathy_chat_manager_undo_closed_chat (priv->chat_manager);
1141
chat_window_tabs_left_activate_cb (GtkAction *action,
1142
EmpathyChatWindow *window)
1144
EmpathyChatWindowPriv *priv;
1146
gint index_, num_pages;
1148
priv = GET_PRIV (window);
1150
chat = priv->current_chat;
1151
index_ = gtk_notebook_get_current_page (GTK_NOTEBOOK (priv->notebook));
1156
gtk_notebook_reorder_child (GTK_NOTEBOOK (priv->notebook),
1160
num_pages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (priv->notebook));
1161
chat_window_menu_context_update (priv, num_pages);
1165
chat_window_tabs_right_activate_cb (GtkAction *action,
1166
EmpathyChatWindow *window)
1168
EmpathyChatWindowPriv *priv;
1170
gint index_, num_pages;
1172
priv = GET_PRIV (window);
1174
chat = priv->current_chat;
1175
index_ = gtk_notebook_get_current_page (GTK_NOTEBOOK (priv->notebook));
1177
gtk_notebook_reorder_child (GTK_NOTEBOOK (priv->notebook),
1181
num_pages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (priv->notebook));
1182
chat_window_menu_context_update (priv, num_pages);
1186
chat_window_detach_activate_cb (GtkAction *action,
1187
EmpathyChatWindow *window)
1189
EmpathyChatWindowPriv *priv;
1190
EmpathyChatWindow *new_window;
1193
priv = GET_PRIV (window);
1195
chat = priv->current_chat;
1196
new_window = empathy_chat_window_new ();
1198
empathy_chat_window_move_chat (window, new_window, chat);
1200
priv = GET_PRIV (new_window);
1201
gtk_widget_show (priv->dialog);
1205
chat_window_help_contents_activate_cb (GtkAction *action,
1206
EmpathyChatWindow *window)
1208
EmpathyChatWindowPriv *priv = GET_PRIV (window);
1210
empathy_url_show (priv->dialog, "ghelp:empathy");
1214
chat_window_help_about_activate_cb (GtkAction *action,
1215
EmpathyChatWindow *window)
1217
EmpathyChatWindowPriv *priv = GET_PRIV (window);
1219
empathy_about_dialog_new (GTK_WINDOW (priv->dialog));
1223
chat_window_delete_event_cb (GtkWidget *dialog,
1225
EmpathyChatWindow *window)
1227
EmpathyChatWindowPriv *priv = GET_PRIV (window);
1229
DEBUG ("Delete event received");
1231
g_object_ref (window);
1232
while (priv->chats) {
1233
empathy_chat_window_remove_chat (window, priv->chats->data);
1235
g_object_unref (window);
1241
chat_window_composing_cb (EmpathyChat *chat,
1242
gboolean is_composing,
1243
EmpathyChatWindow *window)
1245
EmpathyChatWindowPriv *priv;
1247
priv = GET_PRIV (window);
1249
if (is_composing && !g_list_find (priv->chats_composing, chat)) {
1250
priv->chats_composing = g_list_prepend (priv->chats_composing, chat);
1252
priv->chats_composing = g_list_remove (priv->chats_composing, chat);
1255
chat_window_update_chat_tab (chat);
1259
chat_window_set_urgency_hint (EmpathyChatWindow *window,
1262
EmpathyChatWindowPriv *priv;
1264
priv = GET_PRIV (window);
1266
gtk_window_set_urgency_hint (GTK_WINDOW (priv->dialog), urgent);
1270
chat_window_notification_closed_cb (NotifyNotification *notify,
1271
EmpathyChatWindow *self)
1273
EmpathyChatWindowPriv *priv = GET_PRIV (self);
1275
g_object_unref (notify);
1276
if (priv->notification == notify) {
1277
priv->notification = NULL;
1282
chat_window_show_or_update_notification (EmpathyChatWindow *window,
1283
EmpathyMessage *message,
1286
EmpathyContact *sender;
1287
const gchar *header;
1291
EmpathyChatWindowPriv *priv = GET_PRIV (window);
1292
gboolean res, has_x_canonical_append;
1293
NotifyNotification *notification = priv->notification;
1295
if (!empathy_notify_manager_notification_is_enabled (priv->notify_mgr)) {
1298
GSettings *gsettings = g_settings_new (
1299
EMPATHY_PREFS_NOTIFICATIONS_SCHEMA);
1301
res = g_settings_get_boolean (gsettings,
1302
EMPATHY_PREFS_NOTIFICATIONS_FOCUS);
1304
g_object_unref (gsettings);
1311
sender = empathy_message_get_sender (message);
1312
header = empathy_contact_get_alias (sender);
1313
body = empathy_message_get_body (message);
1314
escaped = g_markup_escape_text (body, -1);
1315
has_x_canonical_append = empathy_notify_manager_has_capability (
1316
priv->notify_mgr, EMPATHY_NOTIFY_MANAGER_CAP_X_CANONICAL_APPEND);
1318
if (notification != NULL && !has_x_canonical_append) {
1319
/* if the notification server supports x-canonical-append, it is
1320
better to not use notify_notification_update to avoid
1321
overwriting the current notification message */
1322
notify_notification_update (notification,
1323
header, escaped, NULL);
1325
/* if the notification server supports x-canonical-append,
1326
the hint will be added, so that the message from the
1327
just created notification will be automatically appended
1328
to an existing notification with the same title.
1329
In this way the previous message will not be lost: the new
1330
message will appear below it, in the same notification */
1331
notification = notify_notification_new (header, escaped, NULL);
1333
if (priv->notification == NULL) {
1334
priv->notification = notification;
1337
notify_notification_set_timeout (notification, NOTIFY_EXPIRES_DEFAULT);
1339
tp_g_signal_connect_object (notification, "closed",
1340
G_CALLBACK (chat_window_notification_closed_cb), window, 0);
1342
if (has_x_canonical_append) {
1343
notify_notification_set_hint_string (notification,
1344
EMPATHY_NOTIFY_MANAGER_CAP_X_CANONICAL_APPEND, "");
1348
pixbuf = empathy_notify_manager_get_pixbuf_for_notification (priv->notify_mgr,
1349
sender, EMPATHY_IMAGE_NEW_MESSAGE);
1351
if (pixbuf != NULL) {
1352
notify_notification_set_icon_from_pixbuf (notification, pixbuf);
1353
g_object_unref (pixbuf);
1356
notify_notification_show (notification, NULL);
1362
chat_window_set_highlight_room_tab_label (EmpathyChat *chat)
1367
if (!empathy_chat_is_room (chat))
1370
markup = g_markup_printf_escaped (
1371
"<span color=\"red\" weight=\"bold\">%s</span>",
1372
empathy_chat_get_name (chat));
1374
widget = g_object_get_data (G_OBJECT (chat), "chat-window-tab-label");
1375
gtk_label_set_markup (GTK_LABEL (widget), markup);
1380
chat_window_new_message_cb (EmpathyChat *chat,
1381
EmpathyMessage *message,
1383
EmpathyChatWindow *window)
1385
EmpathyChatWindowPriv *priv;
1387
gboolean needs_urgency;
1388
EmpathyContact *sender;
1390
priv = GET_PRIV (window);
1392
has_focus = empathy_chat_window_has_focus (window);
1394
/* - if we're the sender, we play the sound if it's specified in the
1395
* preferences and we're not away.
1396
* - if we receive a message, we play the sound if it's specified in the
1397
* preferences and the window does not have focus on the chat receiving
1401
sender = empathy_message_get_sender (message);
1403
if (empathy_contact_is_user (sender)) {
1404
empathy_sound_play (GTK_WIDGET (priv->dialog),
1405
EMPATHY_SOUND_MESSAGE_OUTGOING);
1408
if (has_focus && priv->current_chat == chat) {
1409
/* window and tab are focused so consider the message to be read */
1411
/* FIXME: see Bug#610994 and coments about it in EmpathyChatPriv */
1412
empathy_chat_messages_read (chat);
1416
if (!g_list_find (priv->chats_new_msg, chat)) {
1417
priv->chats_new_msg = g_list_prepend (priv->chats_new_msg, chat);
1418
chat_window_update_chat_tab (chat);
1421
/* If empathy_chat_is_room () returns TRUE, that means it's a named MUC.
1422
* If empathy_chat_get_remote_contact () returns NULL, that means it's
1423
* an unamed MUC (msn-like).
1424
* In case of a MUC, we set urgency if either:
1425
* a) the chatroom's always_urgent property is TRUE
1426
* b) the message contains our alias
1428
if (empathy_chat_is_room (chat) ||
1429
empathy_chat_get_remote_contact (chat) == NULL) {
1432
EmpathyChatroom *chatroom;
1434
account = empathy_chat_get_account (chat);
1435
room = empathy_chat_get_id (chat);
1437
chatroom = empathy_chatroom_manager_find (priv->chatroom_manager,
1440
if (chatroom != NULL && empathy_chatroom_is_always_urgent (chatroom)) {
1441
needs_urgency = TRUE;
1443
needs_urgency = empathy_message_should_highlight (message);
1446
needs_urgency = TRUE;
1449
if (needs_urgency) {
1451
chat_window_set_urgency_hint (window, TRUE);
1452
chat_window_set_highlight_room_tab_label (chat);
1455
empathy_sound_play (GTK_WIDGET (priv->dialog),
1456
EMPATHY_SOUND_MESSAGE_INCOMING);
1458
/* Pending messages have already been displayed in the approver, so we don't
1459
* display a notification for those. */
1461
chat_window_show_or_update_notification (window, message, chat);
1464
/* update the number of unread messages and the window icon */
1465
chat_window_title_update (priv);
1466
chat_window_icon_update (priv);
1469
static GtkNotebook *
1470
chat_window_detach_hook (GtkNotebook *source,
1476
EmpathyChatWindowPriv *priv;
1477
EmpathyChatWindow *window, *new_window;
1480
chat = EMPATHY_CHAT (page);
1481
window = chat_window_find_chat (chat);
1483
new_window = empathy_chat_window_new ();
1484
priv = GET_PRIV (new_window);
1486
DEBUG ("Detach hook called");
1488
empathy_chat_window_move_chat (window, new_window, chat);
1490
gtk_widget_show (priv->dialog);
1491
gtk_window_move (GTK_WINDOW (priv->dialog), x, y);
1497
chat_window_page_switched_cb (GtkNotebook *notebook,
1498
gpointer ignore, /* see note below */
1500
EmpathyChatWindow *window)
1502
EmpathyChatWindowPriv *priv;
1506
DEBUG ("Page switched");
1508
priv = GET_PRIV (window);
1510
/* N.B. in GTK+ 3 child is passed as the first argument to the signal,
1511
* but we can't use that while trying to retain GTK+ 2.x compatibility.
1513
child = gtk_notebook_get_nth_page (notebook, page_num);
1514
chat = EMPATHY_CHAT (child);
1516
if (priv->page_added) {
1517
priv->page_added = FALSE;
1518
empathy_chat_scroll_down (chat);
1520
else if (priv->current_chat == chat) {
1524
priv->current_chat = chat;
1525
priv->chats_new_msg = g_list_remove (priv->chats_new_msg, chat);
1526
empathy_chat_messages_read (chat);
1528
chat_window_update_chat_tab (chat);
1532
chat_window_page_added_cb (GtkNotebook *notebook,
1535
EmpathyChatWindow *window)
1537
EmpathyChatWindowPriv *priv;
1540
priv = GET_PRIV (window);
1542
/* If we just received DND to the same window, we don't want
1543
* to do anything here like removing the tab and then readding
1544
* it, so we return here and in "page-added".
1546
if (priv->dnd_same_window) {
1547
DEBUG ("Page added (back to the same window)");
1548
priv->dnd_same_window = FALSE;
1552
DEBUG ("Page added");
1554
/* Get chat object */
1555
chat = EMPATHY_CHAT (child);
1557
/* Connect chat signals for this window */
1558
g_signal_connect (chat, "composing",
1559
G_CALLBACK (chat_window_composing_cb),
1561
g_signal_connect (chat, "new-message",
1562
G_CALLBACK (chat_window_new_message_cb),
1564
g_signal_connect (chat, "notify::tp-chat",
1565
G_CALLBACK (chat_window_update_chat_tab),
1568
/* Set flag so we know to perform some special operations on
1569
* switch page due to the new page being added.
1571
priv->page_added = TRUE;
1573
/* Get list of chats up to date */
1574
priv->chats = g_list_append (priv->chats, chat);
1576
chat_window_update_chat_tab (chat);
1580
chat_window_page_removed_cb (GtkNotebook *notebook,
1583
EmpathyChatWindow *window)
1585
EmpathyChatWindowPriv *priv;
1588
priv = GET_PRIV (window);
1590
/* If we just received DND to the same window, we don't want
1591
* to do anything here like removing the tab and then readding
1592
* it, so we return here and in "page-added".
1594
if (priv->dnd_same_window) {
1595
DEBUG ("Page removed (and will be readded to same window)");
1599
DEBUG ("Page removed");
1601
/* Get chat object */
1602
chat = EMPATHY_CHAT (child);
1604
/* Disconnect all signal handlers for this chat and this window */
1605
g_signal_handlers_disconnect_by_func (chat,
1606
G_CALLBACK (chat_window_composing_cb),
1608
g_signal_handlers_disconnect_by_func (chat,
1609
G_CALLBACK (chat_window_new_message_cb),
1611
g_signal_handlers_disconnect_by_func (chat,
1612
G_CALLBACK (chat_window_update_chat_tab),
1615
/* Keep list of chats up to date */
1616
priv->chats = g_list_remove (priv->chats, chat);
1617
priv->chats_new_msg = g_list_remove (priv->chats_new_msg, chat);
1618
empathy_chat_messages_read (chat);
1619
priv->chats_composing = g_list_remove (priv->chats_composing, chat);
1621
if (priv->chats == NULL) {
1622
g_object_unref (window);
1624
chat_window_update (window, TRUE);
1629
chat_window_focus_in_event_cb (GtkWidget *widget,
1631
EmpathyChatWindow *window)
1633
EmpathyChatWindowPriv *priv;
1635
priv = GET_PRIV (window);
1637
priv->chats_new_msg = g_list_remove (priv->chats_new_msg, priv->current_chat);
1638
empathy_chat_messages_read (priv->current_chat);
1640
chat_window_set_urgency_hint (window, FALSE);
1642
/* Update the title, since we now mark all unread messages as read. */
1643
chat_window_update_chat_tab_full (priv->current_chat, FALSE);
1649
chat_window_drag_drop (GtkWidget *widget,
1650
GdkDragContext *context,
1654
EmpathyChatWindow *window)
1657
EmpathyChatWindowPriv *priv;
1659
priv = GET_PRIV (window);
1661
target = gtk_drag_dest_find_target (widget, context, priv->file_targets);
1662
if (target == GDK_NONE)
1663
target = gtk_drag_dest_find_target (widget, context, priv->contact_targets);
1665
if (target != GDK_NONE) {
1666
gtk_drag_get_data (widget, context, target, time_);
1674
chat_window_drag_motion (GtkWidget *widget,
1675
GdkDragContext *context,
1679
EmpathyChatWindow *window)
1682
EmpathyChatWindowPriv *priv;
1684
priv = GET_PRIV (window);
1686
target = gtk_drag_dest_find_target (widget, context, priv->file_targets);
1687
if (target != GDK_NONE) {
1688
/* This is a file drag. Ensure the contact is online and set the
1689
drag type to COPY. Note that it's possible that the tab will
1690
be switched by GTK+ after a timeout from drag_motion without
1691
getting another drag_motion to disable the drop. You have
1692
to hold your mouse really still.
1694
EmpathyContact *contact;
1696
priv = GET_PRIV (window);
1697
contact = empathy_chat_get_remote_contact (priv->current_chat);
1698
/* contact is NULL for multi-user chats. We don't do
1699
* file transfers to MUCs. We also don't send files
1700
* to offline contacts or contacts that don't support
1703
if ((contact == NULL) || !empathy_contact_is_online (contact)) {
1704
gdk_drag_status (context, 0, time_);
1707
if (!(empathy_contact_get_capabilities (contact)
1708
& EMPATHY_CAPABILITIES_FT)) {
1709
gdk_drag_status (context, 0, time_);
1712
gdk_drag_status (context, GDK_ACTION_COPY, time_);
1716
target = gtk_drag_dest_find_target (widget, context, priv->contact_targets);
1717
if (target != GDK_NONE) {
1718
/* This is a drag of a contact from a contact list. Set to COPY.
1719
FIXME: If this drag is to a MUC window, it invites the user.
1720
Otherwise, it opens a chat. Should we use a different drag
1721
type for invites? Should we allow ASK?
1723
gdk_drag_status (context, GDK_ACTION_COPY, time_);
1731
chat_window_drag_data_received (GtkWidget *widget,
1732
GdkDragContext *context,
1735
GtkSelectionData *selection,
1738
EmpathyChatWindow *window)
1740
if (info == DND_DRAG_TYPE_CONTACT_ID) {
1741
EmpathyChat *chat = NULL;
1742
EmpathyChatWindow *old_window;
1743
TpAccount *account = NULL;
1744
TpAccountManager *account_manager;
1747
const gchar *account_id;
1748
const gchar *contact_id;
1750
id = (const gchar*) gtk_selection_data_get_data (selection);
1752
/* FIXME: Perhaps should be sure that the account manager is
1753
* prepared before calling _ensure_account on it. */
1754
account_manager = tp_account_manager_dup ();
1756
DEBUG ("DND contact from roster with id:'%s'", id);
1758
strv = g_strsplit (id, ":", 2);
1759
if (g_strv_length (strv) == 2) {
1760
account_id = strv[0];
1761
contact_id = strv[1];
1763
tp_account_manager_ensure_account (account_manager, account_id);
1764
if (account != NULL)
1765
chat = empathy_chat_window_find_chat (account, contact_id);
1768
if (account == NULL) {
1770
gtk_drag_finish (context, FALSE, FALSE, time_);
1775
empathy_dispatcher_chat_with_contact_id (
1776
account, contact_id, gtk_get_current_event_time ());
1781
g_object_unref (account_manager);
1784
old_window = chat_window_find_chat (chat);
1786
if (old_window == window) {
1787
gtk_drag_finish (context, TRUE, FALSE, time_);
1791
empathy_chat_window_move_chat (old_window, window, chat);
1793
empathy_chat_window_add_chat (window, chat);
1796
/* Added to take care of any outstanding chat events */
1797
empathy_chat_window_present_chat (chat,
1798
TP_USER_ACTION_TIME_NOT_USER_ACTION);
1800
/* We should return TRUE to remove the data when doing
1801
* GDK_ACTION_MOVE, but we don't here otherwise it has
1802
* weird consequences, and we handle that internally
1803
* anyway with add_chat () and remove_chat ().
1805
gtk_drag_finish (context, TRUE, FALSE, time_);
1807
else if (info == DND_DRAG_TYPE_URI_LIST) {
1808
EmpathyChatWindowPriv *priv;
1809
EmpathyContact *contact;
1812
priv = GET_PRIV (window);
1813
contact = empathy_chat_get_remote_contact (priv->current_chat);
1815
/* contact is NULL when current_chat is a multi-user chat.
1816
* We don't do file transfers to MUCs, so just cancel the drag.
1818
if (contact == NULL) {
1819
gtk_drag_finish (context, TRUE, FALSE, time_);
1823
data = (const gchar *) gtk_selection_data_get_data (selection);
1824
empathy_send_file_from_uri_list (contact, data);
1826
gtk_drag_finish (context, TRUE, FALSE, time_);
1828
else if (info == DND_DRAG_TYPE_TAB) {
1830
EmpathyChatWindow *old_window = NULL;
1834
chat = (void *) gtk_selection_data_get_data (selection);
1835
old_window = chat_window_find_chat (*chat);
1838
EmpathyChatWindowPriv *priv;
1840
priv = GET_PRIV (window);
1841
priv->dnd_same_window = (old_window == window);
1842
DEBUG ("DND tab (within same window: %s)",
1843
priv->dnd_same_window ? "Yes" : "No");
1846
DEBUG ("DND from unknown source");
1847
gtk_drag_finish (context, FALSE, FALSE, time_);
1852
chat_window_chat_manager_chats_changed_cb (EmpathyChatManager *chat_manager,
1853
guint num_chats_in_manager,
1854
EmpathyChatWindow *window)
1856
EmpathyChatWindowPriv *priv = GET_PRIV (window);
1858
gtk_action_set_sensitive (priv->menu_tabs_undo_close_tab,
1859
num_chats_in_manager > 0);
1863
chat_window_finalize (GObject *object)
1865
EmpathyChatWindow *window;
1866
EmpathyChatWindowPriv *priv;
1868
window = EMPATHY_CHAT_WINDOW (object);
1869
priv = GET_PRIV (window);
1871
DEBUG ("Finalized: %p", object);
1873
g_object_unref (priv->ui_manager);
1874
g_object_unref (priv->chatroom_manager);
1875
g_object_unref (priv->notify_mgr);
1877
if (priv->notification != NULL) {
1878
notify_notification_close (priv->notification, NULL);
1879
priv->notification = NULL;
1882
if (priv->contact_targets) {
1883
gtk_target_list_unref (priv->contact_targets);
1885
if (priv->file_targets) {
1886
gtk_target_list_unref (priv->file_targets);
1889
if (priv->chat_manager) {
1890
g_signal_handler_disconnect (priv->chat_manager,
1891
priv->chat_manager_chats_changed_id);
1892
g_object_unref (priv->chat_manager);
1893
priv->chat_manager = NULL;
1896
chat_windows = g_list_remove (chat_windows, window);
1897
gtk_widget_destroy (priv->dialog);
1899
G_OBJECT_CLASS (empathy_chat_window_parent_class)->finalize (object);
1903
empathy_chat_window_class_init (EmpathyChatWindowClass *klass)
1905
GObjectClass *object_class = G_OBJECT_CLASS (klass);
1907
object_class->finalize = chat_window_finalize;
1909
g_type_class_add_private (object_class, sizeof (EmpathyChatWindowPriv));
1911
/* Set up a style for the close button with no focus padding. */
1912
gtk_rc_parse_string (
1913
"style \"empathy-close-button-style\"\n"
1915
" GtkWidget::focus-padding = 0\n"
1919
"widget \"*.empathy-close-button\" style \"empathy-close-button-style\"");
1921
gtk_notebook_set_window_creation_hook (chat_window_detach_hook, NULL, NULL);
1925
empathy_chat_window_init (EmpathyChatWindow *window)
1928
GtkAccelGroup *accel_group;
1933
GtkWidget *chat_vbox;
1935
EmpathySmileyManager *smiley_manager;
1936
EmpathyChatWindowPriv *priv = G_TYPE_INSTANCE_GET_PRIVATE (window,
1937
EMPATHY_TYPE_CHAT_WINDOW, EmpathyChatWindowPriv);
1939
window->priv = priv;
1940
filename = empathy_file_lookup ("empathy-chat-window.ui", "src");
1941
gui = empathy_builder_get_file (filename,
1942
"chat_window", &priv->dialog,
1943
"chat_vbox", &chat_vbox,
1944
"ui_manager", &priv->ui_manager,
1945
"menu_conv_insert_smiley", &priv->menu_conv_insert_smiley,
1946
"menu_conv_favorite", &priv->menu_conv_favorite,
1947
"menu_conv_always_urgent", &priv->menu_conv_always_urgent,
1948
"menu_conv_toggle_contacts", &priv->menu_conv_toggle_contacts,
1949
"menu_edit_cut", &priv->menu_edit_cut,
1950
"menu_edit_copy", &priv->menu_edit_copy,
1951
"menu_edit_paste", &priv->menu_edit_paste,
1952
"menu_edit_find", &priv->menu_edit_find,
1953
"menu_tabs_next", &priv->menu_tabs_next,
1954
"menu_tabs_prev", &priv->menu_tabs_prev,
1955
"menu_tabs_undo_close_tab", &priv->menu_tabs_undo_close_tab,
1956
"menu_tabs_left", &priv->menu_tabs_left,
1957
"menu_tabs_right", &priv->menu_tabs_right,
1958
"menu_tabs_detach", &priv->menu_tabs_detach,
1962
empathy_builder_connect (gui, window,
1963
"menu_conv", "activate", chat_window_conv_activate_cb,
1964
"menu_conv_clear", "activate", chat_window_clear_activate_cb,
1965
"menu_conv_favorite", "toggled", chat_window_favorite_toggled_cb,
1966
"menu_conv_always_urgent", "toggled", chat_window_always_urgent_toggled_cb,
1967
"menu_conv_toggle_contacts", "toggled", chat_window_contacts_toggled_cb,
1968
"menu_conv_invite_participant", "activate", chat_window_invite_participant_activate_cb,
1969
"menu_conv_close", "activate", chat_window_close_activate_cb,
1970
"menu_edit", "activate", chat_window_edit_activate_cb,
1971
"menu_edit_cut", "activate", chat_window_cut_activate_cb,
1972
"menu_edit_copy", "activate", chat_window_copy_activate_cb,
1973
"menu_edit_paste", "activate", chat_window_paste_activate_cb,
1974
"menu_edit_find", "activate", chat_window_find_activate_cb,
1975
"menu_tabs_next", "activate", chat_window_tabs_next_activate_cb,
1976
"menu_tabs_prev", "activate", chat_window_tabs_previous_activate_cb,
1977
"menu_tabs_undo_close_tab", "activate", chat_window_tabs_undo_close_tab_activate_cb,
1978
"menu_tabs_left", "activate", chat_window_tabs_left_activate_cb,
1979
"menu_tabs_right", "activate", chat_window_tabs_right_activate_cb,
1980
"menu_tabs_detach", "activate", chat_window_detach_activate_cb,
1981
"menu_help_contents", "activate", chat_window_help_contents_activate_cb,
1982
"menu_help_about", "activate", chat_window_help_about_activate_cb,
1985
g_object_ref (priv->ui_manager);
1986
g_object_unref (gui);
1988
priv->chatroom_manager = empathy_chatroom_manager_dup_singleton (NULL);
1990
priv->notebook = gtk_notebook_new ();
1991
gtk_notebook_set_group (GTK_NOTEBOOK (priv->notebook), "EmpathyChatWindow");
1992
gtk_notebook_set_scrollable (GTK_NOTEBOOK (priv->notebook), TRUE);
1993
gtk_notebook_popup_enable (GTK_NOTEBOOK (priv->notebook));
1994
gtk_box_pack_start (GTK_BOX (chat_vbox), priv->notebook, TRUE, TRUE, 0);
1995
gtk_widget_show (priv->notebook);
1998
accel_group = gtk_accel_group_new ();
1999
gtk_window_add_accel_group (GTK_WINDOW (priv->dialog), accel_group);
2001
for (i = 0; i < G_N_ELEMENTS (tab_accel_keys); i++) {
2002
closure = g_cclosure_new (G_CALLBACK (chat_window_accel_cb),
2005
gtk_accel_group_connect (accel_group,
2012
g_object_unref (accel_group);
2014
/* Set up drag target lists */
2015
priv->contact_targets = gtk_target_list_new (drag_types_dest_contact,
2016
G_N_ELEMENTS (drag_types_dest_contact));
2017
priv->file_targets = gtk_target_list_new (drag_types_dest_file,
2018
G_N_ELEMENTS (drag_types_dest_file));
2020
/* Set up smiley menu */
2021
smiley_manager = empathy_smiley_manager_dup_singleton ();
2022
submenu = empathy_smiley_menu_new (smiley_manager,
2023
chat_window_insert_smiley_activate_cb,
2025
menu = gtk_ui_manager_get_widget (priv->ui_manager,
2026
"/chats_menubar/menu_conv/menu_conv_insert_smiley");
2027
gtk_menu_item_set_submenu (GTK_MENU_ITEM (menu), submenu);
2028
g_object_unref (smiley_manager);
2030
/* Set up signals we can't do with ui file since we may need to
2031
* block/unblock them at some later stage.
2034
g_signal_connect (priv->dialog,
2036
G_CALLBACK (chat_window_delete_event_cb),
2038
g_signal_connect (priv->dialog,
2040
G_CALLBACK (chat_window_focus_in_event_cb),
2042
g_signal_connect_after (priv->notebook,
2044
G_CALLBACK (chat_window_page_switched_cb),
2046
g_signal_connect (priv->notebook,
2048
G_CALLBACK (chat_window_page_added_cb),
2050
g_signal_connect (priv->notebook,
2052
G_CALLBACK (chat_window_page_removed_cb),
2055
/* Set up drag and drop */
2056
gtk_drag_dest_set (GTK_WIDGET (priv->notebook),
2057
GTK_DEST_DEFAULT_HIGHLIGHT,
2059
G_N_ELEMENTS (drag_types_dest),
2060
GDK_ACTION_MOVE | GDK_ACTION_COPY);
2062
/* connect_after to allow GtkNotebook's built-in tab switching */
2063
g_signal_connect_after (priv->notebook,
2065
G_CALLBACK (chat_window_drag_motion),
2067
g_signal_connect (priv->notebook,
2068
"drag-data-received",
2069
G_CALLBACK (chat_window_drag_data_received),
2071
g_signal_connect (priv->notebook,
2073
G_CALLBACK (chat_window_drag_drop),
2076
chat_windows = g_list_prepend (chat_windows, window);
2078
/* Set up private details */
2080
priv->chats_new_msg = NULL;
2081
priv->chats_composing = NULL;
2082
priv->current_chat = NULL;
2083
priv->notification = NULL;
2085
priv->notify_mgr = empathy_notify_manager_dup_singleton ();
2087
priv->chat_manager = empathy_chat_manager_dup_singleton ();
2088
priv->chat_manager_chats_changed_id =
2089
g_signal_connect (priv->chat_manager, "chats-changed",
2090
G_CALLBACK (chat_window_chat_manager_chats_changed_cb),
2093
chat_window_chat_manager_chats_changed_cb (priv->chat_manager,
2094
empathy_chat_manager_get_num_chats (priv->chat_manager),
2097
/* Add launchpad hooks */
2098
launchpad_integration_add_ui (priv->ui_manager, "/chats_menubar/menu_help/LaunchpadItems");
2102
empathy_chat_window_new (void)
2104
return EMPATHY_CHAT_WINDOW (g_object_new (EMPATHY_TYPE_CHAT_WINDOW, NULL));
2107
/* Returns the window to open a new tab in if there is only one window
2108
* visble, otherwise, returns NULL indicating that a new window should
2112
empathy_chat_window_get_default (gboolean room)
2114
GSettings *gsettings = g_settings_new (EMPATHY_PREFS_UI_SCHEMA);
2116
gboolean separate_windows = TRUE;
2118
separate_windows = g_settings_get_boolean (gsettings,
2119
EMPATHY_PREFS_UI_SEPARATE_CHAT_WINDOWS);
2121
g_object_unref (gsettings);
2123
if (separate_windows) {
2124
/* Always create a new window */
2128
for (l = chat_windows; l; l = l->next) {
2129
EmpathyChatWindowPriv *priv;
2130
EmpathyChatWindow *chat_window;
2133
chat_window = l->data;
2134
priv = GET_PRIV (chat_window);
2136
dialog = empathy_chat_window_get_dialog (chat_window);
2137
if (empathy_window_get_is_visible (GTK_WINDOW (dialog))) {
2138
guint nb_rooms, nb_private;
2139
empathy_chat_window_get_nb_chats (chat_window, &nb_rooms, &nb_private);
2141
/* Skip the window if there aren't any rooms in it */
2142
if (room && nb_rooms == 0)
2145
/* Skip the window if there aren't any 1-1 chats in it */
2146
if (!room && nb_private == 0)
2149
/* Found a visible window on this desktop */
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;
2170
empathy_chat_window_add_chat (EmpathyChatWindow *window,
2173
EmpathyChatWindowPriv *priv;
2175
GtkWidget *popup_label;
2177
GValue value = { 0, };
2179
g_return_if_fail (window != NULL);
2180
g_return_if_fail (EMPATHY_IS_CHAT (chat));
2182
priv = GET_PRIV (window);
2184
/* Reference the chat object */
2185
g_object_ref (chat);
2187
/* If this window has just been created, position it */
2188
if (priv->chats == NULL) {
2189
GSettings *gsettings = g_settings_new (EMPATHY_PREFS_UI_SCHEMA);
2190
const gchar *name = "chat-window";
2191
gboolean separate_windows;
2193
separate_windows = g_settings_get_boolean (gsettings,
2194
EMPATHY_PREFS_UI_SEPARATE_CHAT_WINDOWS);
2196
g_object_unref (gsettings);
2198
if (separate_windows) {
2199
name = empathy_chat_get_id (chat);
2201
else if (empathy_chat_is_room (chat)) {
2202
name = "room-window";
2205
empathy_geometry_bind (GTK_WINDOW (priv->dialog), name);
2208
child = GTK_WIDGET (chat);
2209
label = chat_window_create_label (window, chat, TRUE);
2210
popup_label = chat_window_create_label (window, chat, FALSE);
2211
gtk_widget_show (child);
2213
g_signal_connect (chat, "notify::name",
2214
G_CALLBACK (chat_window_chat_notify_cb),
2216
g_signal_connect (chat, "notify::subject",
2217
G_CALLBACK (chat_window_chat_notify_cb),
2219
g_signal_connect (chat, "notify::remote-contact",
2220
G_CALLBACK (chat_window_chat_notify_cb),
2222
chat_window_chat_notify_cb (chat);
2224
gtk_notebook_append_page_menu (GTK_NOTEBOOK (priv->notebook), child, label, popup_label);
2225
gtk_notebook_set_tab_reorderable (GTK_NOTEBOOK (priv->notebook), child, TRUE);
2226
gtk_notebook_set_tab_detachable (GTK_NOTEBOOK (priv->notebook), child, TRUE);
2227
g_value_init (&value, G_TYPE_BOOLEAN);
2228
g_value_set_boolean (&value, TRUE);
2229
gtk_container_child_set_property (GTK_CONTAINER (priv->notebook),
2230
child, "tab-expand" , &value);
2231
gtk_container_child_set_property (GTK_CONTAINER (priv->notebook),
2232
child, "tab-fill" , &value);
2233
g_value_unset (&value);
2235
DEBUG ("Chat added (%d references)", G_OBJECT (chat)->ref_count);
2239
empathy_chat_window_remove_chat (EmpathyChatWindow *window,
2242
EmpathyChatWindowPriv *priv;
2244
EmpathyContact *remote_contact;
2245
EmpathyChatManager *chat_manager;
2247
g_return_if_fail (window != NULL);
2248
g_return_if_fail (EMPATHY_IS_CHAT (chat));
2250
priv = GET_PRIV (window);
2252
g_signal_handlers_disconnect_by_func (chat,
2253
chat_window_chat_notify_cb,
2255
remote_contact = g_object_get_data (G_OBJECT (chat),
2256
"chat-window-remote-contact");
2257
if (remote_contact) {
2258
g_signal_handlers_disconnect_by_func (remote_contact,
2259
chat_window_update_chat_tab,
2263
chat_manager = empathy_chat_manager_dup_singleton ();
2264
empathy_chat_manager_closed_chat (chat_manager, chat);
2265
g_object_unref (chat_manager);
2267
position = gtk_notebook_page_num (GTK_NOTEBOOK (priv->notebook),
2269
gtk_notebook_remove_page (GTK_NOTEBOOK (priv->notebook), position);
2271
DEBUG ("Chat removed (%d references)", G_OBJECT (chat)->ref_count - 1);
2273
g_object_unref (chat);
2277
empathy_chat_window_move_chat (EmpathyChatWindow *old_window,
2278
EmpathyChatWindow *new_window,
2283
g_return_if_fail (EMPATHY_IS_CHAT_WINDOW (old_window));
2284
g_return_if_fail (EMPATHY_IS_CHAT_WINDOW (new_window));
2285
g_return_if_fail (EMPATHY_IS_CHAT (chat));
2287
widget = GTK_WIDGET (chat);
2289
DEBUG ("Chat moving with widget:%p (%d references)", widget,
2290
G_OBJECT (widget)->ref_count);
2292
/* We reference here to make sure we don't loose the widget
2293
* and the EmpathyChat object during the move.
2295
g_object_ref (chat);
2296
g_object_ref (widget);
2298
empathy_chat_window_remove_chat (old_window, chat);
2299
empathy_chat_window_add_chat (new_window, chat);
2301
g_object_unref (widget);
2302
g_object_unref (chat);
2306
empathy_chat_window_switch_to_chat (EmpathyChatWindow *window,
2309
EmpathyChatWindowPriv *priv;
2312
g_return_if_fail (window != NULL);
2313
g_return_if_fail (EMPATHY_IS_CHAT (chat));
2315
priv = GET_PRIV (window);
2317
page_num = gtk_notebook_page_num (GTK_NOTEBOOK (priv->notebook),
2319
gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->notebook),
2324
empathy_chat_window_has_focus (EmpathyChatWindow *window)
2326
EmpathyChatWindowPriv *priv;
2329
g_return_val_if_fail (EMPATHY_IS_CHAT_WINDOW (window), FALSE);
2331
priv = GET_PRIV (window);
2333
g_object_get (priv->dialog, "has-toplevel-focus", &has_focus, NULL);
2339
empathy_chat_window_find_chat (TpAccount *account,
2344
g_return_val_if_fail (!EMP_STR_EMPTY (id), NULL);
2346
for (l = chat_windows; l; l = l->next) {
2347
EmpathyChatWindowPriv *priv;
2348
EmpathyChatWindow *window;
2352
priv = GET_PRIV (window);
2354
for (ll = priv->chats; ll; ll = ll->next) {
2359
if (account == empathy_chat_get_account (chat) &&
2360
!tp_strdiff (id, empathy_chat_get_id (chat))) {
2370
empathy_chat_window_present_chat (EmpathyChat *chat,
2373
EmpathyChatWindow *window;
2374
EmpathyChatWindowPriv *priv;
2375
guint32 x_timestamp;
2377
g_return_if_fail (EMPATHY_IS_CHAT (chat));
2379
window = chat_window_find_chat (chat);
2381
/* If the chat has no window, create one */
2382
if (window == NULL) {
2383
window = empathy_chat_window_get_default (empathy_chat_is_room (chat));
2385
window = empathy_chat_window_new ();
2386
gtk_widget_show_all (GET_PRIV (window)->dialog);
2389
empathy_chat_window_add_chat (window, chat);
2392
/* Don't force the window to show itself when it wasn't
2393
* an action by the user
2395
if (!tp_user_action_time_should_present (timestamp, &x_timestamp))
2398
priv = GET_PRIV (window);
2400
if (x_timestamp != GDK_CURRENT_TIME) {
2401
/* Don't present or switch tab if the action was earlier than the
2402
* last actions X time, accounting for overflow and the first ever
2405
if (priv->x_user_action_time != 0
2406
&& X_EARLIER_OR_EQL (x_timestamp, priv->x_user_action_time))
2409
priv->x_user_action_time = x_timestamp;
2412
empathy_chat_window_switch_to_chat (window, chat);
2413
empathy_window_present_with_time (GTK_WINDOW (priv->dialog),
2416
gtk_widget_grab_focus (chat->input_text_view);
2420
empathy_chat_window_get_nb_chats (EmpathyChatWindow *self,
2424
EmpathyChatWindowPriv *priv = GET_PRIV (self);
2426
guint _nb_rooms = 0, _nb_private = 0;
2428
for (l = priv->chats; l != NULL; l = g_list_next (l)) {
2429
if (empathy_chat_is_room (EMPATHY_CHAT (l->data)))
2435
if (nb_rooms != NULL)
2436
*nb_rooms = _nb_rooms;
2437
if (nb_private != NULL)
2438
*nb_private = _nb_private;