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
#ifdef HAVE_LIBINDICATE
67
#include "empathy-indicator.h"
68
#include "empathy-indicator-manager.h"
71
#define DEBUG_FLAG EMPATHY_DEBUG_CHAT
72
#include <libempathy/empathy-debug.h>
78
/* Macro to compare guint32 X timestamps, while accounting for wrapping around
80
#define X_EARLIER_OR_EQL(t1, t2) \
81
((t1 <= t2 && ((t2 - t1) < G_MAXUINT32/2)) \
82
|| (t1 >= t2 && (t1 - t2) > (G_MAXUINT32/2)) \
85
#define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyChatWindow)
87
EmpathyChat *current_chat;
90
GList *chats_composing;
92
gboolean dnd_same_window;
93
EmpathyChatroomManager *chatroom_manager;
94
EmpathyNotifyManager *notify_mgr;
97
NotifyNotification *notification;
98
#ifdef HAVE_LIBINDICATE
99
EmpathyIndicatorManager *indicator_manager;
100
/* EmpathyChat -> EmpathyIndicator for that chat, if any */
101
GHashTable *indicators;
104
UnityLauncherEntry *launcher;
106
GtkTargetList *contact_targets;
107
GtkTargetList *file_targets;
109
EmpathyChatManager *chat_manager;
110
gulong chat_manager_chats_changed_id;
113
GtkUIManager *ui_manager;
114
GtkAction *menu_conv_insert_smiley;
115
GtkAction *menu_conv_favorite;
116
GtkAction *menu_conv_always_urgent;
117
GtkAction *menu_conv_toggle_contacts;
119
GtkAction *menu_edit_cut;
120
GtkAction *menu_edit_copy;
121
GtkAction *menu_edit_paste;
122
GtkAction *menu_edit_find;
124
GtkAction *menu_tabs_next;
125
GtkAction *menu_tabs_prev;
126
GtkAction *menu_tabs_undo_close_tab;
127
GtkAction *menu_tabs_left;
128
GtkAction *menu_tabs_right;
129
GtkAction *menu_tabs_detach;
131
/* Last user action time we acted upon to show a tab */
132
guint32 x_user_action_time;
133
} EmpathyChatWindowPriv;
135
static GList *chat_windows = NULL;
137
static const guint tab_accel_keys[] = {
138
GDK_1, GDK_2, GDK_3, GDK_4, GDK_5,
139
GDK_6, GDK_7, GDK_8, GDK_9, GDK_0
143
DND_DRAG_TYPE_CONTACT_ID,
144
DND_DRAG_TYPE_URI_LIST,
148
static const GtkTargetEntry drag_types_dest[] = {
149
{ "text/contact-id", 0, DND_DRAG_TYPE_CONTACT_ID },
150
{ "GTK_NOTEBOOK_TAB", GTK_TARGET_SAME_APP, DND_DRAG_TYPE_TAB },
151
{ "text/uri-list", 0, DND_DRAG_TYPE_URI_LIST },
152
{ "text/path-list", 0, DND_DRAG_TYPE_URI_LIST },
155
static const GtkTargetEntry drag_types_dest_contact[] = {
156
{ "text/contact-id", 0, DND_DRAG_TYPE_CONTACT_ID },
159
static const GtkTargetEntry drag_types_dest_file[] = {
160
/* must be first to be prioritized, in order to receive the
161
* note's file path from Tomboy instead of an URI */
162
{ "text/path-list", 0, DND_DRAG_TYPE_URI_LIST },
163
{ "text/uri-list", 0, DND_DRAG_TYPE_URI_LIST },
166
static void chat_window_update (EmpathyChatWindow *window,
167
gboolean update_contact_menu);
169
G_DEFINE_TYPE (EmpathyChatWindow, empathy_chat_window, G_TYPE_OBJECT);
172
chat_window_accel_cb (GtkAccelGroup *accelgroup,
176
EmpathyChatWindow *window)
178
EmpathyChatWindowPriv *priv;
182
priv = GET_PRIV (window);
184
for (i = 0; i < G_N_ELEMENTS (tab_accel_keys); i++) {
185
if (tab_accel_keys[i] == key) {
192
gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->notebook), num);
196
static EmpathyChatWindow *
197
chat_window_find_chat (EmpathyChat *chat)
199
EmpathyChatWindowPriv *priv;
202
for (l = chat_windows; l; l = l->next) {
203
priv = GET_PRIV (l->data);
204
ll = g_list_find (priv->chats, chat);
214
chat_window_close_clicked_cb (GtkAction *action,
217
EmpathyChatWindow *window;
219
window = chat_window_find_chat (chat);
220
empathy_chat_window_remove_chat (window, chat);
224
chat_tab_style_set_cb (GtkWidget *hbox,
225
GtkStyle *previous_style,
229
int char_width, h, w;
230
PangoContext *context;
231
PangoFontMetrics *metrics;
233
button = g_object_get_data (G_OBJECT (user_data),
234
"chat-window-tab-close-button");
235
context = gtk_widget_get_pango_context (hbox);
237
metrics = pango_context_get_metrics (context, gtk_widget_get_style (hbox)->font_desc,
238
pango_context_get_language (context));
239
char_width = pango_font_metrics_get_approximate_char_width (metrics);
240
pango_font_metrics_unref (metrics);
242
gtk_icon_size_lookup_for_settings (gtk_widget_get_settings (button),
243
GTK_ICON_SIZE_MENU, &w, &h);
245
/* Request at least about 12 chars width plus at least space for the status
246
* image and the close button */
247
gtk_widget_set_size_request (hbox,
248
12 * PANGO_PIXELS (char_width) + 2 * w, -1);
250
gtk_widget_set_size_request (button, w, h);
254
chat_window_create_label (EmpathyChatWindow *window,
256
gboolean is_tab_label)
258
EmpathyChatWindowPriv *priv;
260
GtkWidget *name_label;
261
GtkWidget *status_image;
262
GtkWidget *close_button;
263
GtkWidget *close_image;
264
GtkWidget *event_box;
265
GtkWidget *event_box_hbox;
266
PangoAttrList *attr_list;
267
PangoAttribute *attr;
269
priv = GET_PRIV (window);
271
/* The spacing between the button and the label. */
272
hbox = gtk_hbox_new (FALSE, 0);
274
event_box = gtk_event_box_new ();
275
gtk_event_box_set_visible_window (GTK_EVENT_BOX (event_box), FALSE);
277
name_label = gtk_label_new (NULL);
279
gtk_label_set_ellipsize (GTK_LABEL (name_label), PANGO_ELLIPSIZE_END);
281
attr_list = pango_attr_list_new ();
282
attr = pango_attr_scale_new (1/1.2);
283
attr->start_index = 0;
284
attr->end_index = -1;
285
pango_attr_list_insert (attr_list, attr);
286
gtk_label_set_attributes (GTK_LABEL (name_label), attr_list);
287
pango_attr_list_unref (attr_list);
289
gtk_misc_set_padding (GTK_MISC (name_label), 2, 0);
290
gtk_misc_set_alignment (GTK_MISC (name_label), 0.0, 0.5);
291
g_object_set_data (G_OBJECT (chat),
292
is_tab_label ? "chat-window-tab-label" : "chat-window-menu-label",
295
status_image = gtk_image_new ();
297
/* Spacing between the icon and label. */
298
event_box_hbox = gtk_hbox_new (FALSE, 0);
300
gtk_box_pack_start (GTK_BOX (event_box_hbox), status_image, FALSE, FALSE, 0);
301
gtk_box_pack_start (GTK_BOX (event_box_hbox), name_label, TRUE, TRUE, 0);
303
g_object_set_data (G_OBJECT (chat),
304
is_tab_label ? "chat-window-tab-image" : "chat-window-menu-image",
306
g_object_set_data (G_OBJECT (chat),
307
is_tab_label ? "chat-window-tab-tooltip-widget" : "chat-window-menu-tooltip-widget",
310
gtk_container_add (GTK_CONTAINER (event_box), event_box_hbox);
311
gtk_box_pack_start (GTK_BOX (hbox), event_box, TRUE, TRUE, 0);
314
close_button = gtk_button_new ();
315
gtk_button_set_relief (GTK_BUTTON (close_button), GTK_RELIEF_NONE);
316
g_object_set_data (G_OBJECT (chat), "chat-window-tab-close-button", close_button);
318
/* We don't want focus/keynav for the button to avoid clutter, and
319
* Ctrl-W works anyway.
321
gtk_widget_set_can_focus (close_button, FALSE);
322
gtk_widget_set_can_default (close_button, FALSE);
324
/* Set the name to make the special rc style match. */
325
gtk_widget_set_name (close_button, "empathy-close-button");
327
close_image = gtk_image_new_from_stock (GTK_STOCK_CLOSE, GTK_ICON_SIZE_MENU);
329
gtk_container_add (GTK_CONTAINER (close_button), close_image);
331
gtk_box_pack_end (GTK_BOX (hbox), close_button, FALSE, FALSE, 0);
333
g_signal_connect (close_button,
335
G_CALLBACK (chat_window_close_clicked_cb),
338
/* React to theme changes and also setup the size correctly. */
339
g_signal_connect (hbox,
341
G_CALLBACK (chat_tab_style_set_cb),
345
gtk_widget_show_all (hbox);
351
_submenu_notify_visible_changed_cb (GObject *object,
355
g_signal_handlers_disconnect_by_func (object,
356
_submenu_notify_visible_changed_cb,
358
chat_window_update (EMPATHY_CHAT_WINDOW (userdata), TRUE);
362
chat_window_menu_context_update (EmpathyChatWindowPriv *priv,
367
gboolean wrap_around;
368
gboolean is_connected;
371
page_num = gtk_notebook_get_current_page (GTK_NOTEBOOK (priv->notebook));
372
first_page = (page_num == 0);
373
last_page = (page_num == (num_pages - 1));
374
g_object_get (gtk_settings_get_default (), "gtk-keynav-wrap-around",
376
is_connected = empathy_chat_get_tp_chat (priv->current_chat) != NULL;
378
gtk_action_set_sensitive (priv->menu_tabs_next, (!last_page ||
380
gtk_action_set_sensitive (priv->menu_tabs_prev, (!first_page ||
382
gtk_action_set_sensitive (priv->menu_tabs_detach, num_pages > 1);
383
gtk_action_set_sensitive (priv->menu_tabs_left, !first_page);
384
gtk_action_set_sensitive (priv->menu_tabs_right, !last_page);
385
gtk_action_set_sensitive (priv->menu_conv_insert_smiley, is_connected);
389
chat_window_conversation_menu_update (EmpathyChatWindowPriv *priv,
390
EmpathyChatWindow *self)
392
EmpathyTpChat *tp_chat;
393
TpConnection *connection;
395
gboolean sensitive = FALSE;
397
g_return_if_fail (priv->current_chat != NULL);
399
action = gtk_ui_manager_get_action (priv->ui_manager,
400
"/chats_menubar/menu_conv/menu_conv_invite_participant");
401
tp_chat = empathy_chat_get_tp_chat (priv->current_chat);
403
if (tp_chat != NULL) {
404
connection = empathy_tp_chat_get_connection (tp_chat);
406
sensitive = empathy_tp_chat_can_add_contact (tp_chat) &&
407
(tp_connection_get_status (connection, NULL) ==
408
TP_CONNECTION_STATUS_CONNECTED);
411
gtk_action_set_sensitive (action, sensitive);
415
chat_window_contact_menu_update (EmpathyChatWindowPriv *priv,
416
EmpathyChatWindow *window)
418
GtkWidget *menu, *submenu, *orig_submenu;
420
menu = gtk_ui_manager_get_widget (priv->ui_manager,
421
"/chats_menubar/menu_contact");
422
orig_submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (menu));
424
if (orig_submenu == NULL || !gtk_widget_get_visible (orig_submenu)) {
425
submenu = empathy_chat_get_contact_menu (priv->current_chat);
427
if (submenu != NULL) {
428
/* gtk_menu_attach_to_widget () doesn't behave nicely here */
429
g_object_set_data (G_OBJECT (submenu), "window", priv->dialog);
431
gtk_menu_item_set_submenu (GTK_MENU_ITEM (menu), submenu);
432
gtk_widget_show (menu);
433
gtk_widget_set_sensitive (menu, TRUE);
435
gtk_widget_set_sensitive (menu, FALSE);
439
tp_g_signal_connect_object (orig_submenu,
441
(GCallback)_submenu_notify_visible_changed_cb,
447
get_all_unread_messages (EmpathyChatWindowPriv *priv)
452
for (l = priv->chats_new_msg; l != NULL; l = g_list_next (l)) {
453
EmpathyChat *chat = l->data;
455
nb += empathy_chat_get_nb_unread_messages (chat);
462
get_window_title_name (EmpathyChatWindowPriv *priv)
464
const gchar *active_name;
466
guint current_unread_msgs;
468
nb_chats = g_list_length (priv->chats);
469
g_assert (nb_chats > 0);
471
active_name = empathy_chat_get_name (priv->current_chat);
473
current_unread_msgs = empathy_chat_get_nb_unread_messages (
478
if (current_unread_msgs == 0)
479
return g_strdup (active_name);
481
return g_strdup_printf (ngettext (
483
"%s (%d unread)", current_unread_msgs),
484
active_name, current_unread_msgs);
486
guint nb_others = nb_chats - 1;
487
guint all_unread_msgs;
489
all_unread_msgs = get_all_unread_messages (priv);
491
if (all_unread_msgs == 0) {
492
/* no unread message */
493
return g_strdup_printf (ngettext (
495
"%s (and %u others)", nb_others),
496
active_name, nb_others);
499
else if (all_unread_msgs == current_unread_msgs) {
500
/* unread messages are in the current tab */
501
return g_strdup_printf (ngettext (
503
"%s (%d unread)", current_unread_msgs),
504
active_name, current_unread_msgs);
507
else if (current_unread_msgs == 0) {
508
/* unread messages are in other tabs */
509
return g_strdup_printf (ngettext (
510
"%s (%d unread from others)",
511
"%s (%d unread from others)",
513
active_name, all_unread_msgs);
517
/* unread messages are in all the tabs */
518
return g_strdup_printf (ngettext (
519
"%s (%d unread from all)",
520
"%s (%d unread from all)",
522
active_name, all_unread_msgs);
528
chat_window_title_update (EmpathyChatWindowPriv *priv)
532
name = get_window_title_name (priv);
533
gtk_window_set_title (GTK_WINDOW (priv->dialog), name);
538
chat_window_icon_update (EmpathyChatWindowPriv *priv)
541
EmpathyContact *remote_contact;
542
gboolean avatar_in_icon;
545
n_chats = g_list_length (priv->chats);
547
/* Update window icon */
548
if (priv->chats_new_msg) {
549
gtk_window_set_icon_name (GTK_WINDOW (priv->dialog),
550
EMPATHY_IMAGE_MESSAGE);
552
GSettings *gsettings = g_settings_new (EMPATHY_PREFS_CHAT_SCHEMA);
554
avatar_in_icon = g_settings_get_boolean (gsettings,
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);
569
g_object_unref (gsettings);
574
chat_window_close_button_update (EmpathyChatWindowPriv *priv,
578
GtkWidget *chat_close_button;
581
if (num_pages == 1) {
582
chat = gtk_notebook_get_nth_page (GTK_NOTEBOOK (priv->notebook), 0);
583
chat_close_button = g_object_get_data (G_OBJECT (chat),
584
"chat-window-tab-close-button");
585
gtk_widget_hide (chat_close_button);
587
for (i=0; i<num_pages; i++) {
588
chat = gtk_notebook_get_nth_page (GTK_NOTEBOOK (priv->notebook), i);
589
chat_close_button = g_object_get_data (G_OBJECT (chat),
590
"chat-window-tab-close-button");
591
gtk_widget_show (chat_close_button);
597
chat_window_update (EmpathyChatWindow *window,
598
gboolean update_contact_menu)
600
EmpathyChatWindowPriv *priv = GET_PRIV (window);
603
num_pages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (priv->notebook));
605
/* Update Tab menu */
606
chat_window_menu_context_update (priv,
609
chat_window_conversation_menu_update (priv, window);
611
/* If this update is due to a focus-in event, we know the menu will be
612
the same as when we last left it, so no work to do. Besides, if we
613
swap out the menu on a focus-in, we may confuse any external global
615
if (update_contact_menu) {
616
chat_window_contact_menu_update (priv,
620
chat_window_title_update (priv);
622
chat_window_icon_update (priv);
624
chat_window_close_button_update (priv,
629
append_markup_printf (GString *string,
636
va_start (args, format);
638
tmp = g_markup_vprintf_escaped (format, args);
639
g_string_append (string, tmp);
646
chat_window_update_chat_tab_full (EmpathyChat *chat,
647
gboolean update_contact_menu)
649
EmpathyChatWindow *window;
650
EmpathyChatWindowPriv *priv;
651
EmpathyContact *remote_contact;
655
const gchar *subject;
656
const gchar *status = NULL;
660
const gchar *icon_name;
661
GtkWidget *tab_image;
662
GtkWidget *menu_image;
664
window = chat_window_find_chat (chat);
668
priv = GET_PRIV (window);
670
/* Get information */
671
name = empathy_chat_get_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 (g_list_find (priv->chats_new_msg, chat)) {
685
icon_name = EMPATHY_IMAGE_MESSAGE;
687
else if (g_list_find (priv->chats_composing, chat)) {
688
icon_name = EMPATHY_IMAGE_TYPING;
690
else if (remote_contact) {
691
icon_name = empathy_icon_name_for_contact (remote_contact);
693
icon_name = EMPATHY_IMAGE_GROUP_MESSAGE;
696
tab_image = g_object_get_data (G_OBJECT (chat), "chat-window-tab-image");
697
menu_image = g_object_get_data (G_OBJECT (chat), "chat-window-menu-image");
698
if (icon_name != NULL) {
699
gtk_image_set_from_icon_name (GTK_IMAGE (tab_image), icon_name, GTK_ICON_SIZE_MENU);
700
gtk_widget_show (tab_image);
701
gtk_image_set_from_icon_name (GTK_IMAGE (menu_image), icon_name, GTK_ICON_SIZE_MENU);
702
gtk_widget_show (menu_image);
704
gtk_widget_hide (tab_image);
705
gtk_widget_hide (menu_image);
708
/* Update tab tooltip */
709
tooltip = g_string_new (NULL);
711
if (remote_contact) {
712
id = empathy_contact_get_id (remote_contact);
713
status = empathy_contact_get_presence_message (remote_contact);
718
append_markup_printf (tooltip,
719
"<b>%s</b><small> (%s)</small>",
721
tp_account_get_display_name (account));
723
if (!EMP_STR_EMPTY (status)) {
724
append_markup_printf (tooltip, "\n<i>%s</i>", status);
728
append_markup_printf (tooltip, "\n<b>%s</b> %s",
729
_("Topic:"), subject);
732
if (g_list_find (priv->chats_composing, chat)) {
733
append_markup_printf (tooltip, "\n%s", _("Typing a message."));
736
markup = g_string_free (tooltip, FALSE);
737
widget = g_object_get_data (G_OBJECT (chat), "chat-window-tab-tooltip-widget");
738
gtk_widget_set_tooltip_markup (widget, markup);
739
widget = g_object_get_data (G_OBJECT (chat), "chat-window-menu-tooltip-widget");
740
gtk_widget_set_tooltip_markup (widget, markup);
743
/* Update tab and menu label */
744
widget = g_object_get_data (G_OBJECT (chat), "chat-window-tab-label");
745
gtk_label_set_text (GTK_LABEL (widget), name);
746
widget = g_object_get_data (G_OBJECT (chat), "chat-window-menu-label");
747
gtk_label_set_text (GTK_LABEL (widget), name);
749
/* Update the window if it's the current chat */
750
if (priv->current_chat == chat) {
751
chat_window_update (window, update_contact_menu);
756
chat_window_update_chat_tab (EmpathyChat *chat)
758
chat_window_update_chat_tab_full (chat, TRUE);
762
chat_window_chat_notify_cb (EmpathyChat *chat)
764
EmpathyContact *old_remote_contact;
765
EmpathyContact *remote_contact = NULL;
767
old_remote_contact = g_object_get_data (G_OBJECT (chat), "chat-window-remote-contact");
768
remote_contact = empathy_chat_get_remote_contact (chat);
770
if (old_remote_contact != remote_contact) {
771
/* The remote-contact associated with the chat changed, we need
772
* to keep track of any change of that contact and update the
773
* window each time. */
774
if (remote_contact) {
775
g_signal_connect_swapped (remote_contact, "notify",
776
G_CALLBACK (chat_window_update_chat_tab),
779
if (old_remote_contact) {
780
g_signal_handlers_disconnect_by_func (old_remote_contact,
781
chat_window_update_chat_tab,
785
g_object_set_data_full (G_OBJECT (chat), "chat-window-remote-contact",
786
g_object_ref (remote_contact), (GDestroyNotify) g_object_unref);
789
chat_window_update_chat_tab (chat);
793
chat_window_insert_smiley_activate_cb (EmpathySmileyManager *manager,
794
EmpathySmiley *smiley,
797
EmpathyChatWindowPriv *priv = GET_PRIV (window);
799
GtkTextBuffer *buffer;
802
chat = priv->current_chat;
804
buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (chat->input_text_view));
805
gtk_text_buffer_get_end_iter (buffer, &iter);
806
gtk_text_buffer_insert (buffer, &iter, smiley->str, -1);
810
chat_window_conv_activate_cb (GtkAction *action,
811
EmpathyChatWindow *window)
813
EmpathyChatWindowPriv *priv = GET_PRIV (window);
816
EmpathyContact *remote_contact = NULL;
818
/* Favorite room menu */
819
is_room = empathy_chat_is_room (priv->current_chat);
823
gboolean found = FALSE;
824
EmpathyChatroom *chatroom;
826
room = empathy_chat_get_id (priv->current_chat);
827
account = empathy_chat_get_account (priv->current_chat);
828
chatroom = empathy_chatroom_manager_find (priv->chatroom_manager,
830
if (chatroom != NULL)
831
found = empathy_chatroom_is_favorite (chatroom);
833
DEBUG ("This room %s favorite", found ? "is" : "is not");
834
gtk_toggle_action_set_active (
835
GTK_TOGGLE_ACTION (priv->menu_conv_favorite), found);
837
if (chatroom != NULL)
838
found = empathy_chatroom_is_always_urgent (chatroom);
840
gtk_toggle_action_set_active (
841
GTK_TOGGLE_ACTION (priv->menu_conv_always_urgent),
844
gtk_action_set_visible (priv->menu_conv_favorite, is_room);
845
gtk_action_set_visible (priv->menu_conv_always_urgent, is_room);
847
/* Show contacts menu */
848
g_object_get (priv->current_chat,
849
"remote-contact", &remote_contact,
850
"show-contacts", &active,
852
if (remote_contact == NULL) {
853
gtk_toggle_action_set_active (
854
GTK_TOGGLE_ACTION (priv->menu_conv_toggle_contacts),
857
gtk_action_set_visible (priv->menu_conv_toggle_contacts,
858
(remote_contact == NULL));
859
if (remote_contact != NULL) {
860
g_object_unref (remote_contact);
865
chat_window_clear_activate_cb (GtkAction *action,
866
EmpathyChatWindow *window)
868
EmpathyChatWindowPriv *priv = GET_PRIV (window);
870
empathy_chat_clear (priv->current_chat);
874
chat_window_favorite_toggled_cb (GtkToggleAction *toggle_action,
875
EmpathyChatWindow *window)
877
EmpathyChatWindowPriv *priv = GET_PRIV (window);
881
EmpathyChatroom *chatroom;
883
active = gtk_toggle_action_get_active (toggle_action);
884
account = empathy_chat_get_account (priv->current_chat);
885
room = empathy_chat_get_id (priv->current_chat);
887
chatroom = empathy_chatroom_manager_ensure_chatroom (
888
priv->chatroom_manager,
891
empathy_chat_get_name (priv->current_chat));
893
empathy_chatroom_set_favorite (chatroom, active);
894
g_object_unref (chatroom);
898
chat_window_always_urgent_toggled_cb (GtkToggleAction *toggle_action,
899
EmpathyChatWindow *window)
901
EmpathyChatWindowPriv *priv = GET_PRIV (window);
905
EmpathyChatroom *chatroom;
907
active = gtk_toggle_action_get_active (toggle_action);
908
account = empathy_chat_get_account (priv->current_chat);
909
room = empathy_chat_get_id (priv->current_chat);
911
chatroom = empathy_chatroom_manager_ensure_chatroom (
912
priv->chatroom_manager,
915
empathy_chat_get_name (priv->current_chat));
917
empathy_chatroom_set_always_urgent (chatroom, active);
918
g_object_unref (chatroom);
922
chat_window_contacts_toggled_cb (GtkToggleAction *toggle_action,
923
EmpathyChatWindow *window)
925
EmpathyChatWindowPriv *priv = GET_PRIV (window);
928
active = gtk_toggle_action_get_active (toggle_action);
930
empathy_chat_set_show_contacts (priv->current_chat, active);
934
got_contact_cb (TpConnection *connection,
935
EmpathyContact *contact,
940
EmpathyTpChat *tp_chat = EMPATHY_TP_CHAT (user_data);
943
DEBUG ("Failed: %s", error->message);
946
empathy_contact_list_add (EMPATHY_CONTACT_LIST (tp_chat),
947
contact, _("Inviting you to this room"));
952
chat_window_invite_participant_activate_cb (GtkAction *action,
953
EmpathyChatWindow *window)
955
EmpathyChatWindowPriv *priv;
957
EmpathyTpChat *tp_chat;
962
priv = GET_PRIV (window);
964
g_return_if_fail (priv->current_chat != NULL);
966
tp_chat = empathy_chat_get_tp_chat (priv->current_chat);
967
channel = empathy_tp_chat_get_channel (tp_chat);
968
account = empathy_chat_get_account (priv->current_chat);
970
dialog = empathy_invite_participant_dialog_new (
971
GTK_WINDOW (priv->dialog), account);
972
gtk_widget_show (dialog);
974
response = gtk_dialog_run (GTK_DIALOG (dialog));
976
if (response == GTK_RESPONSE_ACCEPT) {
977
TpConnection *connection;
980
id = empathy_contact_selector_dialog_get_selected (
981
EMPATHY_CONTACT_SELECTOR_DIALOG (dialog), NULL, NULL);
982
if (EMP_STR_EMPTY (id)) goto out;
984
connection = tp_channel_borrow_connection (channel);
985
empathy_tp_contact_factory_get_from_id (connection, id,
986
got_contact_cb, tp_chat, NULL, NULL);
990
gtk_widget_destroy (dialog);
994
chat_window_close_activate_cb (GtkAction *action,
995
EmpathyChatWindow *window)
997
EmpathyChatWindowPriv *priv;
999
priv = GET_PRIV (window);
1001
g_return_if_fail (priv->current_chat != NULL);
1003
empathy_chat_window_remove_chat (window, priv->current_chat);
1007
chat_window_edit_activate_cb (GtkAction *action,
1008
EmpathyChatWindow *window)
1010
EmpathyChatWindowPriv *priv;
1011
GtkClipboard *clipboard;
1012
GtkTextBuffer *buffer;
1013
gboolean text_available;
1015
priv = GET_PRIV (window);
1017
g_return_if_fail (priv->current_chat != NULL);
1019
if (!empathy_chat_get_tp_chat (priv->current_chat)) {
1020
gtk_action_set_sensitive (priv->menu_edit_copy, FALSE);
1021
gtk_action_set_sensitive (priv->menu_edit_cut, FALSE);
1022
gtk_action_set_sensitive (priv->menu_edit_paste, FALSE);
1026
buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (priv->current_chat->input_text_view));
1027
if (gtk_text_buffer_get_has_selection (buffer)) {
1028
gtk_action_set_sensitive (priv->menu_edit_copy, TRUE);
1029
gtk_action_set_sensitive (priv->menu_edit_cut, TRUE);
1033
selection = empathy_chat_view_get_has_selection (priv->current_chat->view);
1035
gtk_action_set_sensitive (priv->menu_edit_cut, FALSE);
1036
gtk_action_set_sensitive (priv->menu_edit_copy, selection);
1039
clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
1040
text_available = gtk_clipboard_wait_is_text_available (clipboard);
1041
gtk_action_set_sensitive (priv->menu_edit_paste, text_available);
1045
chat_window_cut_activate_cb (GtkAction *action,
1046
EmpathyChatWindow *window)
1048
EmpathyChatWindowPriv *priv;
1050
g_return_if_fail (EMPATHY_IS_CHAT_WINDOW (window));
1052
priv = GET_PRIV (window);
1054
empathy_chat_cut (priv->current_chat);
1058
chat_window_copy_activate_cb (GtkAction *action,
1059
EmpathyChatWindow *window)
1061
EmpathyChatWindowPriv *priv;
1063
g_return_if_fail (EMPATHY_IS_CHAT_WINDOW (window));
1065
priv = GET_PRIV (window);
1067
empathy_chat_copy (priv->current_chat);
1071
chat_window_paste_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_paste (priv->current_chat);
1084
chat_window_find_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_find (priv->current_chat);
1097
chat_window_tabs_next_activate_cb (GtkAction *action,
1098
EmpathyChatWindow *window)
1100
EmpathyChatWindowPriv *priv;
1102
gint index_, numPages;
1103
gboolean wrap_around;
1105
priv = GET_PRIV (window);
1107
g_object_get (gtk_settings_get_default (), "gtk-keynav-wrap-around",
1108
&wrap_around, NULL);
1110
chat = priv->current_chat;
1111
index_ = gtk_notebook_get_current_page (GTK_NOTEBOOK (priv->notebook));
1112
numPages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (priv->notebook));
1114
if (index_ == (numPages - 1) && wrap_around) {
1115
gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->notebook), 0);
1119
gtk_notebook_next_page (GTK_NOTEBOOK (priv->notebook));
1123
chat_window_tabs_previous_activate_cb (GtkAction *action,
1124
EmpathyChatWindow *window)
1126
EmpathyChatWindowPriv *priv;
1128
gint index_, numPages;
1129
gboolean wrap_around;
1131
priv = GET_PRIV (window);
1133
g_object_get (gtk_settings_get_default (), "gtk-keynav-wrap-around",
1134
&wrap_around, NULL);
1136
chat = priv->current_chat;
1137
index_ = gtk_notebook_get_current_page (GTK_NOTEBOOK (priv->notebook));
1138
numPages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (priv->notebook));
1140
if (index_ <= 0 && wrap_around) {
1141
gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->notebook), numPages - 1);
1145
gtk_notebook_prev_page (GTK_NOTEBOOK (priv->notebook));
1149
chat_window_tabs_undo_close_tab_activate_cb (GtkAction *action,
1150
EmpathyChatWindow *window)
1152
EmpathyChatWindowPriv *priv = GET_PRIV (window);
1153
empathy_chat_manager_undo_closed_chat (priv->chat_manager);
1157
chat_window_tabs_left_activate_cb (GtkAction *action,
1158
EmpathyChatWindow *window)
1160
EmpathyChatWindowPriv *priv;
1162
gint index_, num_pages;
1164
priv = GET_PRIV (window);
1166
chat = priv->current_chat;
1167
index_ = gtk_notebook_get_current_page (GTK_NOTEBOOK (priv->notebook));
1172
gtk_notebook_reorder_child (GTK_NOTEBOOK (priv->notebook),
1176
num_pages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (priv->notebook));
1177
chat_window_menu_context_update (priv, num_pages);
1181
chat_window_tabs_right_activate_cb (GtkAction *action,
1182
EmpathyChatWindow *window)
1184
EmpathyChatWindowPriv *priv;
1186
gint index_, num_pages;
1188
priv = GET_PRIV (window);
1190
chat = priv->current_chat;
1191
index_ = gtk_notebook_get_current_page (GTK_NOTEBOOK (priv->notebook));
1193
gtk_notebook_reorder_child (GTK_NOTEBOOK (priv->notebook),
1197
num_pages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (priv->notebook));
1198
chat_window_menu_context_update (priv, num_pages);
1202
chat_window_detach_activate_cb (GtkAction *action,
1203
EmpathyChatWindow *window)
1205
EmpathyChatWindowPriv *priv;
1206
EmpathyChatWindow *new_window;
1209
priv = GET_PRIV (window);
1211
chat = priv->current_chat;
1212
new_window = empathy_chat_window_new ();
1214
empathy_chat_window_move_chat (window, new_window, chat);
1216
priv = GET_PRIV (new_window);
1217
gtk_widget_show (priv->dialog);
1221
chat_window_help_contents_activate_cb (GtkAction *action,
1222
EmpathyChatWindow *window)
1224
EmpathyChatWindowPriv *priv = GET_PRIV (window);
1226
empathy_url_show (priv->dialog, "ghelp:empathy");
1230
chat_window_help_about_activate_cb (GtkAction *action,
1231
EmpathyChatWindow *window)
1233
EmpathyChatWindowPriv *priv = GET_PRIV (window);
1235
empathy_about_dialog_new (GTK_WINDOW (priv->dialog));
1239
chat_window_delete_event_cb (GtkWidget *dialog,
1241
EmpathyChatWindow *window)
1243
EmpathyChatWindowPriv *priv = GET_PRIV (window);
1245
DEBUG ("Delete event received");
1247
g_object_ref (window);
1248
while (priv->chats) {
1249
empathy_chat_window_remove_chat (window, priv->chats->data);
1251
g_object_unref (window);
1257
chat_window_composing_cb (EmpathyChat *chat,
1258
gboolean is_composing,
1259
EmpathyChatWindow *window)
1261
EmpathyChatWindowPriv *priv;
1263
priv = GET_PRIV (window);
1265
if (is_composing && !g_list_find (priv->chats_composing, chat)) {
1266
priv->chats_composing = g_list_prepend (priv->chats_composing, chat);
1268
priv->chats_composing = g_list_remove (priv->chats_composing, chat);
1271
chat_window_update_chat_tab (chat);
1275
chat_window_set_urgency_hint (EmpathyChatWindow *window,
1278
EmpathyChatWindowPriv *priv;
1280
priv = GET_PRIV (window);
1282
gtk_window_set_urgency_hint (GTK_WINDOW (priv->dialog), urgent);
1286
chat_window_notification_closed_cb (NotifyNotification *notify,
1287
EmpathyChatWindow *self)
1289
EmpathyChatWindowPriv *priv = GET_PRIV (self);
1291
g_object_unref (notify);
1292
if (priv->notification == notify) {
1293
priv->notification = NULL;
1297
#ifdef HAVE_LIBINDICATE
1299
chat_window_indicator_activate_cb (EmpathyIndicator *indicator, guint timestamp,
1302
empathy_chat_window_present_chat (chat, timestamp);
1303
empathy_indicator_hide (indicator);
1307
chat_window_add_indicator (EmpathyChatWindow *window,
1308
EmpathyMessage *message,
1311
EmpathyChatWindowPriv *priv = GET_PRIV (window);
1312
EmpathyContact *sender;
1314
gboolean use_libindicate;
1315
EmpathyIndicator *indicator = NULL;
1316
GSettings *gsettings;
1318
gsettings = g_settings_new (EMPATHY_PREFS_UI_SCHEMA);
1319
use_libindicate = g_settings_get_boolean (gsettings, EMPATHY_PREFS_UI_USE_LIBINDICATE);
1320
g_object_unref (gsettings);
1322
if (!use_libindicate) {
1326
sender = empathy_message_get_sender (message);
1327
body = empathy_message_get_body (message);
1329
indicator = g_hash_table_lookup (priv->indicators, chat);
1330
if (indicator != NULL) {
1331
DEBUG ("indicator exists");
1332
empathy_indicator_update (indicator, body);
1334
DEBUG ("indicator doesn't exist yet, creating a new indicator");
1335
indicator = empathy_indicator_manager_create_indicator (priv->indicator_manager,
1337
g_signal_connect (indicator, "activate",
1338
G_CALLBACK (chat_window_indicator_activate_cb), chat);
1339
g_hash_table_insert (priv->indicators, chat, indicator);
1341
if (priv->launcher != NULL)
1343
gint count = g_hash_table_size (priv->indicators);
1344
DEBUG ("unity launcher: count is now %d", count);
1347
unity_launcher_entry_set_count (priv->launcher, count);
1348
unity_launcher_entry_set_count_visible (priv->launcher, TRUE);
1353
empathy_indicator_show (indicator);
1357
chat_window_remove_indicator (EmpathyChatWindow *window, EmpathyChat *chat)
1359
EmpathyIndicator *indicator = NULL;
1360
EmpathyChatWindowPriv *priv = GET_PRIV (window);
1362
indicator = g_hash_table_lookup (priv->indicators, chat);
1364
if ((indicator) && (indicator != NULL)) {
1365
DEBUG ("indicator is %p", indicator);
1366
empathy_indicator_hide (indicator);
1367
g_hash_table_remove (priv->indicators, chat);
1369
if (priv->launcher != NULL)
1371
gint count = g_hash_table_size (priv->indicators);
1372
DEBUG ("unity launcher: count is %d", count);
1375
DEBUG ("unity launcher: setting count to %d", count);
1376
unity_launcher_entry_set_count (priv->launcher, count);
1377
unity_launcher_entry_set_count_visible (priv->launcher, TRUE);
1379
unity_launcher_entry_set_count (priv->launcher, count);
1380
DEBUG ("unity launcher: hiding count");
1381
unity_launcher_entry_set_count_visible (priv->launcher, FALSE);
1388
DEBUG ("indicator is NULL, nothing to remove");
1394
chat_window_show_or_update_notification (EmpathyChatWindow *window,
1395
EmpathyMessage *message,
1398
EmpathyContact *sender;
1399
const gchar *header;
1403
EmpathyChatWindowPriv *priv = GET_PRIV (window);
1404
gboolean res, has_x_canonical_append;
1405
NotifyNotification *notification = priv->notification;
1407
if (!empathy_notify_manager_notification_is_enabled (priv->notify_mgr)) {
1410
GSettings *gsettings = g_settings_new (
1411
EMPATHY_PREFS_NOTIFICATIONS_SCHEMA);
1413
res = g_settings_get_boolean (gsettings,
1414
EMPATHY_PREFS_NOTIFICATIONS_FOCUS);
1416
g_object_unref (gsettings);
1423
sender = empathy_message_get_sender (message);
1424
header = empathy_contact_get_alias (sender);
1425
body = empathy_message_get_body (message);
1426
escaped = g_markup_escape_text (body, -1);
1427
has_x_canonical_append = empathy_notify_manager_has_capability (
1428
priv->notify_mgr, EMPATHY_NOTIFY_MANAGER_CAP_X_CANONICAL_APPEND);
1430
if (notification != NULL && !has_x_canonical_append) {
1431
/* if the notification server supports x-canonical-append, it is
1432
better to not use notify_notification_update to avoid
1433
overwriting the current notification message */
1434
notify_notification_update (notification,
1435
header, escaped, NULL);
1437
/* if the notification server supports x-canonical-append,
1438
the hint will be added, so that the message from the
1439
just created notification will be automatically appended
1440
to an existing notification with the same title.
1441
In this way the previous message will not be lost: the new
1442
message will appear below it, in the same notification */
1443
notification = notify_notification_new (header, escaped, NULL);
1445
if (priv->notification == NULL) {
1446
priv->notification = notification;
1449
notify_notification_set_timeout (notification, NOTIFY_EXPIRES_DEFAULT);
1451
tp_g_signal_connect_object (notification, "closed",
1452
G_CALLBACK (chat_window_notification_closed_cb), window, 0);
1454
if (has_x_canonical_append) {
1455
notify_notification_set_hint_string (notification,
1456
EMPATHY_NOTIFY_MANAGER_CAP_X_CANONICAL_APPEND, "");
1460
pixbuf = empathy_notify_manager_get_pixbuf_for_notification (priv->notify_mgr,
1461
sender, EMPATHY_IMAGE_NEW_MESSAGE);
1463
if (pixbuf != NULL) {
1464
notify_notification_set_icon_from_pixbuf (notification, pixbuf);
1465
g_object_unref (pixbuf);
1468
notify_notification_show (notification, NULL);
1474
chat_window_set_highlight_room_tab_label (EmpathyChat *chat)
1479
if (!empathy_chat_is_room (chat))
1482
markup = g_markup_printf_escaped (
1483
"<span color=\"red\" weight=\"bold\">%s</span>",
1484
empathy_chat_get_name (chat));
1486
widget = g_object_get_data (G_OBJECT (chat), "chat-window-tab-label");
1487
gtk_label_set_markup (GTK_LABEL (widget), markup);
1492
chat_window_new_message_cb (EmpathyChat *chat,
1493
EmpathyMessage *message,
1495
EmpathyChatWindow *window)
1497
EmpathyChatWindowPriv *priv;
1499
gboolean needs_urgency;
1500
EmpathyContact *sender;
1502
priv = GET_PRIV (window);
1504
has_focus = empathy_chat_window_has_focus (window);
1506
/* - if we're the sender, we play the sound if it's specified in the
1507
* preferences and we're not away.
1508
* - if we receive a message, we play the sound if it's specified in the
1509
* preferences and the window does not have focus on the chat receiving
1513
sender = empathy_message_get_sender (message);
1515
if (empathy_contact_is_user (sender)) {
1516
empathy_sound_play (GTK_WIDGET (priv->dialog),
1517
EMPATHY_SOUND_MESSAGE_OUTGOING);
1520
if (has_focus && priv->current_chat == chat) {
1521
/* window and tab are focused so consider the message to be read */
1523
/* FIXME: see Bug#610994 and coments about it in EmpathyChatPriv */
1524
empathy_chat_messages_read (chat);
1528
if (!g_list_find (priv->chats_new_msg, chat)) {
1529
priv->chats_new_msg = g_list_prepend (priv->chats_new_msg, chat);
1530
chat_window_update_chat_tab (chat);
1533
/* If empathy_chat_is_room () returns TRUE, that means it's a named MUC.
1534
* If empathy_chat_get_remote_contact () returns NULL, that means it's
1535
* an unamed MUC (msn-like).
1536
* In case of a MUC, we set urgency if either:
1537
* a) the chatroom's always_urgent property is TRUE
1538
* b) the message contains our alias
1540
if (empathy_chat_is_room (chat) ||
1541
empathy_chat_get_remote_contact (chat) == NULL) {
1544
EmpathyChatroom *chatroom;
1546
account = empathy_chat_get_account (chat);
1547
room = empathy_chat_get_id (chat);
1549
chatroom = empathy_chatroom_manager_find (priv->chatroom_manager,
1552
if (chatroom != NULL && empathy_chatroom_is_always_urgent (chatroom)) {
1553
needs_urgency = TRUE;
1555
needs_urgency = empathy_message_should_highlight (message);
1558
needs_urgency = TRUE;
1561
if (needs_urgency) {
1563
chat_window_set_urgency_hint (window, TRUE);
1564
chat_window_set_highlight_room_tab_label (chat);
1567
empathy_sound_play (GTK_WIDGET (priv->dialog),
1568
EMPATHY_SOUND_MESSAGE_INCOMING);
1569
#ifdef HAVE_LIBINDICATE
1570
chat_window_add_indicator (window, message, chat);
1573
/* Pending messages have already been displayed in the approver, so we don't
1574
* display a notification for those. */
1576
chat_window_show_or_update_notification (window, message, chat);
1579
/* update the number of unread messages and the window icon */
1580
chat_window_title_update (priv);
1581
chat_window_icon_update (priv);
1584
static GtkNotebook *
1585
chat_window_detach_hook (GtkNotebook *source,
1591
EmpathyChatWindowPriv *priv;
1592
EmpathyChatWindow *window, *new_window;
1595
chat = EMPATHY_CHAT (page);
1596
window = chat_window_find_chat (chat);
1598
new_window = empathy_chat_window_new ();
1599
priv = GET_PRIV (new_window);
1601
DEBUG ("Detach hook called");
1603
empathy_chat_window_move_chat (window, new_window, chat);
1605
gtk_widget_show (priv->dialog);
1606
gtk_window_move (GTK_WINDOW (priv->dialog), x, y);
1612
chat_window_page_switched_cb (GtkNotebook *notebook,
1613
gpointer ignore, /* see note below */
1615
EmpathyChatWindow *window)
1617
EmpathyChatWindowPriv *priv;
1621
DEBUG ("Page switched");
1623
priv = GET_PRIV (window);
1625
/* N.B. in GTK+ 3 child is passed as the first argument to the signal,
1626
* but we can't use that while trying to retain GTK+ 2.x compatibility.
1628
child = gtk_notebook_get_nth_page (notebook, page_num);
1629
chat = EMPATHY_CHAT (child);
1631
if (priv->page_added) {
1632
priv->page_added = FALSE;
1633
empathy_chat_scroll_down (chat);
1635
else if (priv->current_chat == chat) {
1639
priv->current_chat = chat;
1640
priv->chats_new_msg = g_list_remove (priv->chats_new_msg, chat);
1641
empathy_chat_messages_read (chat);
1643
chat_window_update_chat_tab (chat);
1645
#ifdef HAVE_LIBINDICATE
1646
chat_window_remove_indicator (window, chat);
1651
chat_window_page_added_cb (GtkNotebook *notebook,
1654
EmpathyChatWindow *window)
1656
EmpathyChatWindowPriv *priv;
1659
priv = GET_PRIV (window);
1661
/* If we just received DND to the same window, we don't want
1662
* to do anything here like removing the tab and then readding
1663
* it, so we return here and in "page-added".
1665
if (priv->dnd_same_window) {
1666
DEBUG ("Page added (back to the same window)");
1667
priv->dnd_same_window = FALSE;
1671
DEBUG ("Page added");
1673
/* Get chat object */
1674
chat = EMPATHY_CHAT (child);
1676
/* Connect chat signals for this window */
1677
g_signal_connect (chat, "composing",
1678
G_CALLBACK (chat_window_composing_cb),
1680
g_signal_connect (chat, "new-message",
1681
G_CALLBACK (chat_window_new_message_cb),
1683
g_signal_connect (chat, "notify::tp-chat",
1684
G_CALLBACK (chat_window_update_chat_tab),
1687
/* Set flag so we know to perform some special operations on
1688
* switch page due to the new page being added.
1690
priv->page_added = TRUE;
1692
/* Get list of chats up to date */
1693
priv->chats = g_list_append (priv->chats, chat);
1695
chat_window_update_chat_tab (chat);
1699
chat_window_page_removed_cb (GtkNotebook *notebook,
1702
EmpathyChatWindow *window)
1704
EmpathyChatWindowPriv *priv;
1707
priv = GET_PRIV (window);
1709
/* If we just received DND to the same window, we don't want
1710
* to do anything here like removing the tab and then readding
1711
* it, so we return here and in "page-added".
1713
if (priv->dnd_same_window) {
1714
DEBUG ("Page removed (and will be readded to same window)");
1718
DEBUG ("Page removed");
1720
/* Get chat object */
1721
chat = EMPATHY_CHAT (child);
1723
/* Disconnect all signal handlers for this chat and this window */
1724
g_signal_handlers_disconnect_by_func (chat,
1725
G_CALLBACK (chat_window_composing_cb),
1727
g_signal_handlers_disconnect_by_func (chat,
1728
G_CALLBACK (chat_window_new_message_cb),
1730
g_signal_handlers_disconnect_by_func (chat,
1731
G_CALLBACK (chat_window_update_chat_tab),
1734
/* Keep list of chats up to date */
1735
priv->chats = g_list_remove (priv->chats, chat);
1736
priv->chats_new_msg = g_list_remove (priv->chats_new_msg, chat);
1737
empathy_chat_messages_read (chat);
1738
priv->chats_composing = g_list_remove (priv->chats_composing, chat);
1740
if (priv->chats == NULL) {
1741
g_object_unref (window);
1743
chat_window_update (window, TRUE);
1748
chat_window_focus_in_event_cb (GtkWidget *widget,
1750
EmpathyChatWindow *window)
1752
EmpathyChatWindowPriv *priv;
1754
priv = GET_PRIV (window);
1756
priv->chats_new_msg = g_list_remove (priv->chats_new_msg, priv->current_chat);
1757
empathy_chat_messages_read (priv->current_chat);
1759
chat_window_set_urgency_hint (window, FALSE);
1761
/* Update the title, since we now mark all unread messages as read. */
1762
chat_window_update_chat_tab_full (priv->current_chat, FALSE);
1764
#ifdef HAVE_LIBINDICATE
1765
/* Remove the indicator for the active chat */
1766
chat_window_remove_indicator (window, priv->current_chat);
1773
chat_window_drag_drop (GtkWidget *widget,
1774
GdkDragContext *context,
1778
EmpathyChatWindow *window)
1781
EmpathyChatWindowPriv *priv;
1783
priv = GET_PRIV (window);
1785
target = gtk_drag_dest_find_target (widget, context, priv->file_targets);
1786
if (target == GDK_NONE)
1787
target = gtk_drag_dest_find_target (widget, context, priv->contact_targets);
1789
if (target != GDK_NONE) {
1790
gtk_drag_get_data (widget, context, target, time_);
1798
chat_window_drag_motion (GtkWidget *widget,
1799
GdkDragContext *context,
1803
EmpathyChatWindow *window)
1806
EmpathyChatWindowPriv *priv;
1808
priv = GET_PRIV (window);
1810
target = gtk_drag_dest_find_target (widget, context, priv->file_targets);
1811
if (target != GDK_NONE) {
1812
/* This is a file drag. Ensure the contact is online and set the
1813
drag type to COPY. Note that it's possible that the tab will
1814
be switched by GTK+ after a timeout from drag_motion without
1815
getting another drag_motion to disable the drop. You have
1816
to hold your mouse really still.
1818
EmpathyContact *contact;
1820
priv = GET_PRIV (window);
1821
contact = empathy_chat_get_remote_contact (priv->current_chat);
1822
/* contact is NULL for multi-user chats. We don't do
1823
* file transfers to MUCs. We also don't send files
1824
* to offline contacts or contacts that don't support
1827
if ((contact == NULL) || !empathy_contact_is_online (contact)) {
1828
gdk_drag_status (context, 0, time_);
1831
if (!(empathy_contact_get_capabilities (contact)
1832
& EMPATHY_CAPABILITIES_FT)) {
1833
gdk_drag_status (context, 0, time_);
1836
gdk_drag_status (context, GDK_ACTION_COPY, time_);
1840
target = gtk_drag_dest_find_target (widget, context, priv->contact_targets);
1841
if (target != GDK_NONE) {
1842
/* This is a drag of a contact from a contact list. Set to COPY.
1843
FIXME: If this drag is to a MUC window, it invites the user.
1844
Otherwise, it opens a chat. Should we use a different drag
1845
type for invites? Should we allow ASK?
1847
gdk_drag_status (context, GDK_ACTION_COPY, time_);
1855
chat_window_drag_data_received (GtkWidget *widget,
1856
GdkDragContext *context,
1859
GtkSelectionData *selection,
1862
EmpathyChatWindow *window)
1864
if (info == DND_DRAG_TYPE_CONTACT_ID) {
1865
EmpathyChat *chat = NULL;
1866
EmpathyChatWindow *old_window;
1867
TpAccount *account = NULL;
1868
TpAccountManager *account_manager;
1871
const gchar *account_id;
1872
const gchar *contact_id;
1874
id = (const gchar*) gtk_selection_data_get_data (selection);
1876
/* FIXME: Perhaps should be sure that the account manager is
1877
* prepared before calling _ensure_account on it. */
1878
account_manager = tp_account_manager_dup ();
1880
DEBUG ("DND contact from roster with id:'%s'", id);
1882
strv = g_strsplit (id, ":", 2);
1883
if (g_strv_length (strv) == 2) {
1884
account_id = strv[0];
1885
contact_id = strv[1];
1887
tp_account_manager_ensure_account (account_manager, account_id);
1888
if (account != NULL)
1889
chat = empathy_chat_window_find_chat (account, contact_id);
1892
if (account == NULL) {
1894
gtk_drag_finish (context, FALSE, FALSE, time_);
1899
empathy_dispatcher_chat_with_contact_id (
1900
account, contact_id, gtk_get_current_event_time ());
1905
g_object_unref (account_manager);
1908
old_window = chat_window_find_chat (chat);
1910
if (old_window == window) {
1911
gtk_drag_finish (context, TRUE, FALSE, time_);
1915
empathy_chat_window_move_chat (old_window, window, chat);
1917
empathy_chat_window_add_chat (window, chat);
1920
/* Added to take care of any outstanding chat events */
1921
empathy_chat_window_present_chat (chat,
1922
TP_USER_ACTION_TIME_NOT_USER_ACTION);
1924
/* We should return TRUE to remove the data when doing
1925
* GDK_ACTION_MOVE, but we don't here otherwise it has
1926
* weird consequences, and we handle that internally
1927
* anyway with add_chat () and remove_chat ().
1929
gtk_drag_finish (context, TRUE, FALSE, time_);
1931
else if (info == DND_DRAG_TYPE_URI_LIST) {
1932
EmpathyChatWindowPriv *priv;
1933
EmpathyContact *contact;
1936
priv = GET_PRIV (window);
1937
contact = empathy_chat_get_remote_contact (priv->current_chat);
1939
/* contact is NULL when current_chat is a multi-user chat.
1940
* We don't do file transfers to MUCs, so just cancel the drag.
1942
if (contact == NULL) {
1943
gtk_drag_finish (context, TRUE, FALSE, time_);
1947
data = (const gchar *) gtk_selection_data_get_data (selection);
1948
empathy_send_file_from_uri_list (contact, data);
1950
gtk_drag_finish (context, TRUE, FALSE, time_);
1952
else if (info == DND_DRAG_TYPE_TAB) {
1954
EmpathyChatWindow *old_window = NULL;
1958
chat = (void *) gtk_selection_data_get_data (selection);
1959
old_window = chat_window_find_chat (*chat);
1962
EmpathyChatWindowPriv *priv;
1964
priv = GET_PRIV (window);
1965
priv->dnd_same_window = (old_window == window);
1966
DEBUG ("DND tab (within same window: %s)",
1967
priv->dnd_same_window ? "Yes" : "No");
1970
DEBUG ("DND from unknown source");
1971
gtk_drag_finish (context, FALSE, FALSE, time_);
1976
chat_window_chat_manager_chats_changed_cb (EmpathyChatManager *chat_manager,
1977
guint num_chats_in_manager,
1978
EmpathyChatWindow *window)
1980
EmpathyChatWindowPriv *priv = GET_PRIV (window);
1982
gtk_action_set_sensitive (priv->menu_tabs_undo_close_tab,
1983
num_chats_in_manager > 0);
1987
chat_window_finalize (GObject *object)
1989
EmpathyChatWindow *window;
1990
EmpathyChatWindowPriv *priv;
1992
window = EMPATHY_CHAT_WINDOW (object);
1993
priv = GET_PRIV (window);
1995
DEBUG ("Finalized: %p", object);
1997
g_object_unref (priv->ui_manager);
1998
g_object_unref (priv->chatroom_manager);
1999
g_object_unref (priv->notify_mgr);
2001
if (priv->notification != NULL) {
2002
notify_notification_close (priv->notification, NULL);
2003
priv->notification = NULL;
2006
if (priv->contact_targets) {
2007
gtk_target_list_unref (priv->contact_targets);
2009
if (priv->file_targets) {
2010
gtk_target_list_unref (priv->file_targets);
2013
if (priv->chat_manager) {
2014
g_signal_handler_disconnect (priv->chat_manager,
2015
priv->chat_manager_chats_changed_id);
2016
g_object_unref (priv->chat_manager);
2017
priv->chat_manager = NULL;
2020
if (priv->launcher) {
2021
g_object_unref (priv->launcher);
2022
priv->launcher = NULL;
2025
chat_windows = g_list_remove (chat_windows, window);
2026
gtk_widget_destroy (priv->dialog);
2028
G_OBJECT_CLASS (empathy_chat_window_parent_class)->finalize (object);
2032
empathy_chat_window_class_init (EmpathyChatWindowClass *klass)
2034
GObjectClass *object_class = G_OBJECT_CLASS (klass);
2036
object_class->finalize = chat_window_finalize;
2038
g_type_class_add_private (object_class, sizeof (EmpathyChatWindowPriv));
2040
/* Set up a style for the close button with no focus padding. */
2041
gtk_rc_parse_string (
2042
"style \"empathy-close-button-style\"\n"
2044
" GtkWidget::focus-padding = 0\n"
2048
"widget \"*.empathy-close-button\" style \"empathy-close-button-style\"");
2050
gtk_notebook_set_window_creation_hook (chat_window_detach_hook, NULL, NULL);
2054
empathy_chat_window_init (EmpathyChatWindow *window)
2057
GtkAccelGroup *accel_group;
2062
GtkWidget *chat_vbox;
2064
EmpathySmileyManager *smiley_manager;
2065
EmpathyChatWindowPriv *priv = G_TYPE_INSTANCE_GET_PRIVATE (window,
2066
EMPATHY_TYPE_CHAT_WINDOW, EmpathyChatWindowPriv);
2068
window->priv = priv;
2069
filename = empathy_file_lookup ("empathy-chat-window.ui", "src");
2070
gui = empathy_builder_get_file (filename,
2071
"chat_window", &priv->dialog,
2072
"chat_vbox", &chat_vbox,
2073
"ui_manager", &priv->ui_manager,
2074
"menu_conv_insert_smiley", &priv->menu_conv_insert_smiley,
2075
"menu_conv_favorite", &priv->menu_conv_favorite,
2076
"menu_conv_always_urgent", &priv->menu_conv_always_urgent,
2077
"menu_conv_toggle_contacts", &priv->menu_conv_toggle_contacts,
2078
"menu_edit_cut", &priv->menu_edit_cut,
2079
"menu_edit_copy", &priv->menu_edit_copy,
2080
"menu_edit_paste", &priv->menu_edit_paste,
2081
"menu_edit_find", &priv->menu_edit_find,
2082
"menu_tabs_next", &priv->menu_tabs_next,
2083
"menu_tabs_prev", &priv->menu_tabs_prev,
2084
"menu_tabs_undo_close_tab", &priv->menu_tabs_undo_close_tab,
2085
"menu_tabs_left", &priv->menu_tabs_left,
2086
"menu_tabs_right", &priv->menu_tabs_right,
2087
"menu_tabs_detach", &priv->menu_tabs_detach,
2091
empathy_builder_connect (gui, window,
2092
"menu_conv", "activate", chat_window_conv_activate_cb,
2093
"menu_conv_clear", "activate", chat_window_clear_activate_cb,
2094
"menu_conv_favorite", "toggled", chat_window_favorite_toggled_cb,
2095
"menu_conv_always_urgent", "toggled", chat_window_always_urgent_toggled_cb,
2096
"menu_conv_toggle_contacts", "toggled", chat_window_contacts_toggled_cb,
2097
"menu_conv_invite_participant", "activate", chat_window_invite_participant_activate_cb,
2098
"menu_conv_close", "activate", chat_window_close_activate_cb,
2099
"menu_edit", "activate", chat_window_edit_activate_cb,
2100
"menu_edit_cut", "activate", chat_window_cut_activate_cb,
2101
"menu_edit_copy", "activate", chat_window_copy_activate_cb,
2102
"menu_edit_paste", "activate", chat_window_paste_activate_cb,
2103
"menu_edit_find", "activate", chat_window_find_activate_cb,
2104
"menu_tabs_next", "activate", chat_window_tabs_next_activate_cb,
2105
"menu_tabs_prev", "activate", chat_window_tabs_previous_activate_cb,
2106
"menu_tabs_undo_close_tab", "activate", chat_window_tabs_undo_close_tab_activate_cb,
2107
"menu_tabs_left", "activate", chat_window_tabs_left_activate_cb,
2108
"menu_tabs_right", "activate", chat_window_tabs_right_activate_cb,
2109
"menu_tabs_detach", "activate", chat_window_detach_activate_cb,
2110
"menu_help_contents", "activate", chat_window_help_contents_activate_cb,
2111
"menu_help_about", "activate", chat_window_help_about_activate_cb,
2114
g_object_ref (priv->ui_manager);
2115
g_object_unref (gui);
2117
priv->chatroom_manager = empathy_chatroom_manager_dup_singleton (NULL);
2118
#ifdef HAVE_LIBINDICATE
2119
priv->indicator_manager = empathy_indicator_manager_dup_singleton ();
2120
priv->indicators = g_hash_table_new_full (g_direct_hash, g_direct_equal,
2121
NULL, g_object_unref);
2124
priv->launcher = unity_launcher_entry_get_for_desktop_id ("empathy.desktop");
2127
priv->notebook = gtk_notebook_new ();
2128
gtk_notebook_set_group (GTK_NOTEBOOK (priv->notebook), "EmpathyChatWindow");
2129
gtk_notebook_set_scrollable (GTK_NOTEBOOK (priv->notebook), TRUE);
2130
gtk_notebook_popup_enable (GTK_NOTEBOOK (priv->notebook));
2131
gtk_box_pack_start (GTK_BOX (chat_vbox), priv->notebook, TRUE, TRUE, 0);
2132
gtk_widget_show (priv->notebook);
2135
accel_group = gtk_accel_group_new ();
2136
gtk_window_add_accel_group (GTK_WINDOW (priv->dialog), accel_group);
2138
for (i = 0; i < G_N_ELEMENTS (tab_accel_keys); i++) {
2139
closure = g_cclosure_new (G_CALLBACK (chat_window_accel_cb),
2142
gtk_accel_group_connect (accel_group,
2149
g_object_unref (accel_group);
2151
/* Set up drag target lists */
2152
priv->contact_targets = gtk_target_list_new (drag_types_dest_contact,
2153
G_N_ELEMENTS (drag_types_dest_contact));
2154
priv->file_targets = gtk_target_list_new (drag_types_dest_file,
2155
G_N_ELEMENTS (drag_types_dest_file));
2157
/* Set up smiley menu */
2158
smiley_manager = empathy_smiley_manager_dup_singleton ();
2159
submenu = empathy_smiley_menu_new (smiley_manager,
2160
chat_window_insert_smiley_activate_cb,
2162
menu = gtk_ui_manager_get_widget (priv->ui_manager,
2163
"/chats_menubar/menu_conv/menu_conv_insert_smiley");
2164
gtk_menu_item_set_submenu (GTK_MENU_ITEM (menu), submenu);
2165
g_object_unref (smiley_manager);
2167
/* Set up signals we can't do with ui file since we may need to
2168
* block/unblock them at some later stage.
2171
g_signal_connect (priv->dialog,
2173
G_CALLBACK (chat_window_delete_event_cb),
2175
g_signal_connect (priv->dialog,
2177
G_CALLBACK (chat_window_focus_in_event_cb),
2179
g_signal_connect_after (priv->notebook,
2181
G_CALLBACK (chat_window_page_switched_cb),
2183
g_signal_connect (priv->notebook,
2185
G_CALLBACK (chat_window_page_added_cb),
2187
g_signal_connect (priv->notebook,
2189
G_CALLBACK (chat_window_page_removed_cb),
2192
/* Set up drag and drop */
2193
gtk_drag_dest_set (GTK_WIDGET (priv->notebook),
2194
GTK_DEST_DEFAULT_HIGHLIGHT,
2196
G_N_ELEMENTS (drag_types_dest),
2197
GDK_ACTION_MOVE | GDK_ACTION_COPY);
2199
/* connect_after to allow GtkNotebook's built-in tab switching */
2200
g_signal_connect_after (priv->notebook,
2202
G_CALLBACK (chat_window_drag_motion),
2204
g_signal_connect (priv->notebook,
2205
"drag-data-received",
2206
G_CALLBACK (chat_window_drag_data_received),
2208
g_signal_connect (priv->notebook,
2210
G_CALLBACK (chat_window_drag_drop),
2213
chat_windows = g_list_prepend (chat_windows, window);
2215
/* Set up private details */
2217
priv->chats_new_msg = NULL;
2218
priv->chats_composing = NULL;
2219
priv->current_chat = NULL;
2220
priv->notification = NULL;
2222
priv->notify_mgr = empathy_notify_manager_dup_singleton ();
2224
priv->chat_manager = empathy_chat_manager_dup_singleton ();
2225
priv->chat_manager_chats_changed_id =
2226
g_signal_connect (priv->chat_manager, "chats-changed",
2227
G_CALLBACK (chat_window_chat_manager_chats_changed_cb),
2230
chat_window_chat_manager_chats_changed_cb (priv->chat_manager,
2231
empathy_chat_manager_get_num_chats (priv->chat_manager),
2234
/* Add launchpad hooks */
2235
launchpad_integration_add_ui (priv->ui_manager, "/chats_menubar/menu_help/LaunchpadItems");
2239
empathy_chat_window_new (void)
2241
return EMPATHY_CHAT_WINDOW (g_object_new (EMPATHY_TYPE_CHAT_WINDOW, NULL));
2244
/* Returns the window to open a new tab in if there is only one window
2245
* visble, otherwise, returns NULL indicating that a new window should
2249
empathy_chat_window_get_default (gboolean room)
2251
GSettings *gsettings = g_settings_new (EMPATHY_PREFS_UI_SCHEMA);
2253
gboolean separate_windows = TRUE;
2255
separate_windows = g_settings_get_boolean (gsettings,
2256
EMPATHY_PREFS_UI_SEPARATE_CHAT_WINDOWS);
2258
g_object_unref (gsettings);
2260
if (separate_windows) {
2261
/* Always create a new window */
2265
for (l = chat_windows; l; l = l->next) {
2266
EmpathyChatWindowPriv *priv;
2267
EmpathyChatWindow *chat_window;
2270
chat_window = l->data;
2271
priv = GET_PRIV (chat_window);
2273
dialog = empathy_chat_window_get_dialog (chat_window);
2274
if (empathy_window_get_is_visible (GTK_WINDOW (dialog))) {
2275
guint nb_rooms, nb_private;
2276
empathy_chat_window_get_nb_chats (chat_window, &nb_rooms, &nb_private);
2278
/* Skip the window if there aren't any rooms in it */
2279
if (room && nb_rooms == 0)
2282
/* Skip the window if there aren't any 1-1 chats in it */
2283
if (!room && nb_private == 0)
2286
/* Found a visible window on this desktop */
2295
empathy_chat_window_get_dialog (EmpathyChatWindow *window)
2297
EmpathyChatWindowPriv *priv;
2299
g_return_val_if_fail (window != NULL, NULL);
2301
priv = GET_PRIV (window);
2303
return priv->dialog;
2307
empathy_chat_window_add_chat (EmpathyChatWindow *window,
2310
EmpathyChatWindowPriv *priv;
2312
GtkWidget *popup_label;
2314
GValue value = { 0, };
2316
g_return_if_fail (window != NULL);
2317
g_return_if_fail (EMPATHY_IS_CHAT (chat));
2319
priv = GET_PRIV (window);
2321
/* Reference the chat object */
2322
g_object_ref (chat);
2324
/* If this window has just been created, position it */
2325
if (priv->chats == NULL) {
2326
GSettings *gsettings = g_settings_new (EMPATHY_PREFS_UI_SCHEMA);
2327
const gchar *name = "chat-window";
2328
gboolean separate_windows;
2330
separate_windows = g_settings_get_boolean (gsettings,
2331
EMPATHY_PREFS_UI_SEPARATE_CHAT_WINDOWS);
2333
g_object_unref (gsettings);
2335
if (separate_windows) {
2336
name = empathy_chat_get_id (chat);
2338
else if (empathy_chat_is_room (chat)) {
2339
name = "room-window";
2342
empathy_geometry_bind (GTK_WINDOW (priv->dialog), name);
2345
child = GTK_WIDGET (chat);
2346
label = chat_window_create_label (window, chat, TRUE);
2347
popup_label = chat_window_create_label (window, chat, FALSE);
2348
gtk_widget_show (child);
2350
g_signal_connect (chat, "notify::name",
2351
G_CALLBACK (chat_window_chat_notify_cb),
2353
g_signal_connect (chat, "notify::subject",
2354
G_CALLBACK (chat_window_chat_notify_cb),
2356
g_signal_connect (chat, "notify::remote-contact",
2357
G_CALLBACK (chat_window_chat_notify_cb),
2359
chat_window_chat_notify_cb (chat);
2361
gtk_notebook_append_page_menu (GTK_NOTEBOOK (priv->notebook), child, label, popup_label);
2362
gtk_notebook_set_tab_reorderable (GTK_NOTEBOOK (priv->notebook), child, TRUE);
2363
gtk_notebook_set_tab_detachable (GTK_NOTEBOOK (priv->notebook), child, TRUE);
2364
g_value_init (&value, G_TYPE_BOOLEAN);
2365
g_value_set_boolean (&value, TRUE);
2366
gtk_container_child_set_property (GTK_CONTAINER (priv->notebook),
2367
child, "tab-expand" , &value);
2368
gtk_container_child_set_property (GTK_CONTAINER (priv->notebook),
2369
child, "tab-fill" , &value);
2370
g_value_unset (&value);
2372
DEBUG ("Chat added (%d references)", G_OBJECT (chat)->ref_count);
2376
empathy_chat_window_remove_chat (EmpathyChatWindow *window,
2379
EmpathyChatWindowPriv *priv;
2381
EmpathyContact *remote_contact;
2382
EmpathyChatManager *chat_manager;
2384
g_return_if_fail (window != NULL);
2385
g_return_if_fail (EMPATHY_IS_CHAT (chat));
2387
priv = GET_PRIV (window);
2389
g_signal_handlers_disconnect_by_func (chat,
2390
chat_window_chat_notify_cb,
2392
remote_contact = g_object_get_data (G_OBJECT (chat),
2393
"chat-window-remote-contact");
2394
if (remote_contact) {
2395
g_signal_handlers_disconnect_by_func (remote_contact,
2396
chat_window_update_chat_tab,
2400
chat_manager = empathy_chat_manager_dup_singleton ();
2401
empathy_chat_manager_closed_chat (chat_manager, chat);
2402
g_object_unref (chat_manager);
2404
position = gtk_notebook_page_num (GTK_NOTEBOOK (priv->notebook),
2406
gtk_notebook_remove_page (GTK_NOTEBOOK (priv->notebook), position);
2408
DEBUG ("Chat removed (%d references)", G_OBJECT (chat)->ref_count - 1);
2410
g_object_unref (chat);
2414
empathy_chat_window_move_chat (EmpathyChatWindow *old_window,
2415
EmpathyChatWindow *new_window,
2420
g_return_if_fail (EMPATHY_IS_CHAT_WINDOW (old_window));
2421
g_return_if_fail (EMPATHY_IS_CHAT_WINDOW (new_window));
2422
g_return_if_fail (EMPATHY_IS_CHAT (chat));
2424
widget = GTK_WIDGET (chat);
2426
DEBUG ("Chat moving with widget:%p (%d references)", widget,
2427
G_OBJECT (widget)->ref_count);
2429
/* We reference here to make sure we don't loose the widget
2430
* and the EmpathyChat object during the move.
2432
g_object_ref (chat);
2433
g_object_ref (widget);
2435
empathy_chat_window_remove_chat (old_window, chat);
2436
empathy_chat_window_add_chat (new_window, chat);
2438
g_object_unref (widget);
2439
g_object_unref (chat);
2443
empathy_chat_window_switch_to_chat (EmpathyChatWindow *window,
2446
EmpathyChatWindowPriv *priv;
2449
g_return_if_fail (window != NULL);
2450
g_return_if_fail (EMPATHY_IS_CHAT (chat));
2452
priv = GET_PRIV (window);
2454
page_num = gtk_notebook_page_num (GTK_NOTEBOOK (priv->notebook),
2456
gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->notebook),
2461
empathy_chat_window_has_focus (EmpathyChatWindow *window)
2463
EmpathyChatWindowPriv *priv;
2466
g_return_val_if_fail (EMPATHY_IS_CHAT_WINDOW (window), FALSE);
2468
priv = GET_PRIV (window);
2470
g_object_get (priv->dialog, "has-toplevel-focus", &has_focus, NULL);
2476
empathy_chat_window_find_chat (TpAccount *account,
2481
g_return_val_if_fail (!EMP_STR_EMPTY (id), NULL);
2483
for (l = chat_windows; l; l = l->next) {
2484
EmpathyChatWindowPriv *priv;
2485
EmpathyChatWindow *window;
2489
priv = GET_PRIV (window);
2491
for (ll = priv->chats; ll; ll = ll->next) {
2496
if (account == empathy_chat_get_account (chat) &&
2497
!tp_strdiff (id, empathy_chat_get_id (chat))) {
2507
empathy_chat_window_present_chat (EmpathyChat *chat,
2510
EmpathyChatWindow *window;
2511
EmpathyChatWindowPriv *priv;
2512
guint32 x_timestamp;
2514
g_return_if_fail (EMPATHY_IS_CHAT (chat));
2516
window = chat_window_find_chat (chat);
2518
/* If the chat has no window, create one */
2519
if (window == NULL) {
2520
window = empathy_chat_window_get_default (empathy_chat_is_room (chat));
2522
window = empathy_chat_window_new ();
2523
gtk_widget_show_all (GET_PRIV (window)->dialog);
2526
empathy_chat_window_add_chat (window, chat);
2529
/* Don't force the window to show itself when it wasn't
2530
* an action by the user
2532
if (!tp_user_action_time_should_present (timestamp, &x_timestamp))
2535
priv = GET_PRIV (window);
2537
if (x_timestamp != GDK_CURRENT_TIME) {
2538
/* Don't present or switch tab if the action was earlier than the
2539
* last actions X time, accounting for overflow and the first ever
2542
if (priv->x_user_action_time != 0
2543
&& X_EARLIER_OR_EQL (x_timestamp, priv->x_user_action_time))
2546
priv->x_user_action_time = x_timestamp;
2549
empathy_chat_window_switch_to_chat (window, chat);
2550
empathy_window_present_with_time (GTK_WINDOW (priv->dialog),
2553
gtk_widget_grab_focus (chat->input_text_view);
2557
empathy_chat_window_get_nb_chats (EmpathyChatWindow *self,
2561
EmpathyChatWindowPriv *priv = GET_PRIV (self);
2563
guint _nb_rooms = 0, _nb_private = 0;
2565
for (l = priv->chats; l != NULL; l = g_list_next (l)) {
2566
if (empathy_chat_is_room (EMPATHY_CHAT (l->data)))
2572
if (nb_rooms != NULL)
2573
*nb_rooms = _nb_rooms;
2574
if (nb_private != NULL)
2575
*nb_private = _nb_private;