~ubuntu-branches/ubuntu/precise/empathy/precise-proposed-201205180810

« back to all changes in this revision

Viewing changes to .pc/70_append_messages_in_notifications.patch/src/empathy-chat-window.c

  • Committer: Bazaar Package Importer
  • Author(s): Brian Curtis, Brian Curtis, Ken VanDine
  • Date: 2011-06-01 10:35:24 UTC
  • mfrom: (1.1.70 upstream) (6.3.44 experimental)
  • Revision ID: james.westby@ubuntu.com-20110601103524-wx3wgp71394730jt
Tags: 3.1.1-1ubuntu1
[ Brian Curtis ]
* Merge with Debian experimental, remaining Ubuntu changes:
* debian/control:
  - Drop geoclue/mapping build-depends (they are in Universe)
  - Add Vcz-Bzr link
  - Add Suggests on telepathy-idle
  - Bump telepathy-butterfly, telepathy-haze to recommends
  - Don't recommend the freedesktop sound theme we have an ubuntu one
  - Add build depend for libunity-dev
* debian/rules:
  - Use autoreconf.mk
  - Disable map and location
* debian/empathy.install:
  - Install message indicator configuration
* debian/indicators/empathy:
  - Message indicator configuration
* debian/patches/01_lpi.patch:
  - Add Launchpad integration
* debian/patches/10_use_notify_osd_icons.patch:
  - Use the notify-osd image for new messages
* debian/patches/34_start_raised_execpt_in_session.patch
  - If not started with the session, we should always raise
* debian/patches/36_chat_window_default_size.patch:
  - Make the default chat window size larger
* debian/patches/37_facebook_default.patch:
  - Make facebook the default chat account type
* debian/patches/38_lp_569289.patch
  - Set freenode as default IRC network for new IRC accounts 
* debian/patches/41_unity_launcher_progress.patch
  - Display file transfer progress in the unity launcher

[ Ken VanDine ]
* debian/control
  - build depend on libgcr-3-dev instead of libgcr-dev
  - dropped build depends for libindicate, we will use telepathy-indicator
  - Depend on dconf-gsettings-backend | gsettings-backend
  - Added a Recommends for telepathy-indicator
* +debian/empathy.gsettings-override
  - Added an override for notifications-focus
* debian/patches/series
  - commented out 23_idomessagedialog_for_voip_and_ft.patch, until ido has 
    been ported to gtk3

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2
 
/*
3
 
 * Copyright (C) 2003-2007 Imendio AB
4
 
 * Copyright (C) 2007-2008 Collabora Ltd.
5
 
 *
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.
10
 
 *
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.
15
 
 *
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
20
 
 *
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>
27
 
 */
28
 
 
29
 
#include <config.h>
30
 
 
31
 
#include <string.h>
32
 
 
33
 
#include <gtk/gtk.h>
34
 
#include <gdk/gdkkeysyms.h>
35
 
#include <gdk/gdkx.h>
36
 
#include <glib/gi18n.h>
37
 
#include <libnotify/notification.h>
38
 
 
39
 
/* Add launchpad hooks */
40
 
#include <launchpad-integration.h>
41
 
 
42
 
#include <telepathy-glib/telepathy-glib.h>
43
 
 
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>
51
 
 
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>
60
 
 
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"
65
 
 
66
 
#ifdef HAVE_LIBINDICATE
67
 
#include "empathy-indicator.h"
68
 
#include "empathy-indicator-manager.h"
69
 
#endif
70
 
 
71
 
#define DEBUG_FLAG EMPATHY_DEBUG_CHAT
72
 
#include <libempathy/empathy-debug.h>
73
 
 
74
 
#ifdef HAVE_UNITY
75
 
#include <unity.h>
76
 
#endif
77
 
 
78
 
/* Macro to compare guint32 X timestamps, while accounting for wrapping around
79
 
 */
80
 
#define X_EARLIER_OR_EQL(t1, t2) \
81
 
        ((t1 <= t2 && ((t2 - t1) < G_MAXUINT32/2))  \
82
 
          || (t1 >= t2 && (t1 - t2) > (G_MAXUINT32/2)) \
83
 
        )
84
 
 
85
 
#define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyChatWindow)
86
 
typedef struct {
87
 
        EmpathyChat *current_chat;
88
 
        GList       *chats;
89
 
        GList       *chats_new_msg;
90
 
        GList       *chats_composing;
91
 
        gboolean     page_added;
92
 
        gboolean     dnd_same_window;
93
 
        EmpathyChatroomManager *chatroom_manager;
94
 
        EmpathyNotifyManager *notify_mgr;
95
 
        GtkWidget   *dialog;
96
 
        GtkWidget   *notebook;
97
 
        NotifyNotification *notification;
98
 
#ifdef HAVE_LIBINDICATE
99
 
        EmpathyIndicatorManager *indicator_manager;
100
 
        /* EmpathyChat -> EmpathyIndicator for that chat, if any */
101
 
        GHashTable  *indicators;
102
 
#endif
103
 
#ifdef HAVE_UNITY
104
 
        UnityLauncherEntry *launcher;
105
 
#endif
106
 
        GtkTargetList *contact_targets;
107
 
        GtkTargetList *file_targets;
108
 
 
109
 
        EmpathyChatManager *chat_manager;
110
 
        gulong chat_manager_chats_changed_id;
111
 
 
112
 
        /* Menu items. */
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;
118
 
 
119
 
        GtkAction   *menu_edit_cut;
120
 
        GtkAction   *menu_edit_copy;
121
 
        GtkAction   *menu_edit_paste;
122
 
        GtkAction   *menu_edit_find;
123
 
 
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;
130
 
 
131
 
        /* Last user action time we acted upon to show a tab */
132
 
        guint32    x_user_action_time;
133
 
} EmpathyChatWindowPriv;
134
 
 
135
 
static GList *chat_windows = NULL;
136
 
 
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
140
 
};
141
 
 
142
 
typedef enum {
143
 
        DND_DRAG_TYPE_CONTACT_ID,
144
 
        DND_DRAG_TYPE_URI_LIST,
145
 
        DND_DRAG_TYPE_TAB
146
 
} DndDragType;
147
 
 
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 },
153
 
};
154
 
 
155
 
static const GtkTargetEntry drag_types_dest_contact[] = {
156
 
        { "text/contact-id", 0, DND_DRAG_TYPE_CONTACT_ID },
157
 
};
158
 
 
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 },
164
 
};
165
 
 
166
 
static void chat_window_update (EmpathyChatWindow *window,
167
 
                gboolean update_contact_menu);
168
 
 
169
 
G_DEFINE_TYPE (EmpathyChatWindow, empathy_chat_window, G_TYPE_OBJECT);
170
 
 
171
 
static void
172
 
chat_window_accel_cb (GtkAccelGroup    *accelgroup,
173
 
                      GObject          *object,
174
 
                      guint             key,
175
 
                      GdkModifierType   mod,
176
 
                      EmpathyChatWindow *window)
177
 
{
178
 
        EmpathyChatWindowPriv *priv;
179
 
        gint                  num = -1;
180
 
        guint                 i;
181
 
 
182
 
        priv = GET_PRIV (window);
183
 
 
184
 
        for (i = 0; i < G_N_ELEMENTS (tab_accel_keys); i++) {
185
 
                if (tab_accel_keys[i] == key) {
186
 
                        num = i;
187
 
                        break;
188
 
                }
189
 
        }
190
 
 
191
 
        if (num != -1) {
192
 
                gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->notebook), num);
193
 
        }
194
 
}
195
 
 
196
 
static EmpathyChatWindow *
197
 
chat_window_find_chat (EmpathyChat *chat)
198
 
{
199
 
        EmpathyChatWindowPriv *priv;
200
 
        GList                 *l, *ll;
201
 
 
202
 
        for (l = chat_windows; l; l = l->next) {
203
 
                priv = GET_PRIV (l->data);
204
 
                ll = g_list_find (priv->chats, chat);
205
 
                if (ll) {
206
 
                        return l->data;
207
 
                }
208
 
        }
209
 
 
210
 
        return NULL;
211
 
}
212
 
 
213
 
static void
214
 
chat_window_close_clicked_cb (GtkAction   *action,
215
 
                              EmpathyChat *chat)
216
 
{
217
 
        EmpathyChatWindow *window;
218
 
 
219
 
        window = chat_window_find_chat (chat);
220
 
        empathy_chat_window_remove_chat (window, chat);
221
 
}
222
 
 
223
 
static void
224
 
chat_tab_style_set_cb (GtkWidget *hbox,
225
 
                                       GtkStyle  *previous_style,
226
 
                                       gpointer   user_data)
227
 
{
228
 
        GtkWidget *button;
229
 
        int char_width, h, w;
230
 
        PangoContext *context;
231
 
        PangoFontMetrics *metrics;
232
 
 
233
 
        button = g_object_get_data (G_OBJECT (user_data),
234
 
                "chat-window-tab-close-button");
235
 
        context = gtk_widget_get_pango_context (hbox);
236
 
 
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);
241
 
 
242
 
        gtk_icon_size_lookup_for_settings (gtk_widget_get_settings (button),
243
 
                                           GTK_ICON_SIZE_MENU, &w, &h);
244
 
 
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);
249
 
 
250
 
        gtk_widget_set_size_request (button, w, h);
251
 
}
252
 
 
253
 
static GtkWidget *
254
 
chat_window_create_label (EmpathyChatWindow *window,
255
 
                          EmpathyChat       *chat,
256
 
                          gboolean           is_tab_label)
257
 
{
258
 
        EmpathyChatWindowPriv *priv;
259
 
        GtkWidget            *hbox;
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;
268
 
 
269
 
        priv = GET_PRIV (window);
270
 
 
271
 
        /* The spacing between the button and the label. */
272
 
        hbox = gtk_hbox_new (FALSE, 0);
273
 
 
274
 
        event_box = gtk_event_box_new ();
275
 
        gtk_event_box_set_visible_window (GTK_EVENT_BOX (event_box), FALSE);
276
 
 
277
 
        name_label = gtk_label_new (NULL);
278
 
        if (is_tab_label)
279
 
                gtk_label_set_ellipsize (GTK_LABEL (name_label), PANGO_ELLIPSIZE_END);
280
 
 
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);
288
 
 
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",
293
 
                name_label);
294
 
 
295
 
        status_image = gtk_image_new ();
296
 
 
297
 
        /* Spacing between the icon and label. */
298
 
        event_box_hbox = gtk_hbox_new (FALSE, 0);
299
 
 
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);
302
 
 
303
 
        g_object_set_data (G_OBJECT (chat),
304
 
                is_tab_label ? "chat-window-tab-image" : "chat-window-menu-image",
305
 
                status_image);
306
 
        g_object_set_data (G_OBJECT (chat),
307
 
                is_tab_label ? "chat-window-tab-tooltip-widget" : "chat-window-menu-tooltip-widget",
308
 
                event_box);
309
 
 
310
 
        gtk_container_add (GTK_CONTAINER (event_box), event_box_hbox);
311
 
        gtk_box_pack_start (GTK_BOX (hbox), event_box, TRUE, TRUE, 0);
312
 
 
313
 
        if (is_tab_label) {
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);
317
 
 
318
 
                /* We don't want focus/keynav for the button to avoid clutter, and
319
 
                 * Ctrl-W works anyway.
320
 
                 */
321
 
                gtk_widget_set_can_focus (close_button, FALSE);
322
 
                gtk_widget_set_can_default (close_button, FALSE);
323
 
 
324
 
                /* Set the name to make the special rc style match. */
325
 
                gtk_widget_set_name (close_button, "empathy-close-button");
326
 
 
327
 
                close_image = gtk_image_new_from_stock (GTK_STOCK_CLOSE, GTK_ICON_SIZE_MENU);
328
 
 
329
 
                gtk_container_add (GTK_CONTAINER (close_button), close_image);
330
 
 
331
 
                gtk_box_pack_end (GTK_BOX (hbox), close_button, FALSE, FALSE, 0);
332
 
 
333
 
                g_signal_connect (close_button,
334
 
                                  "clicked",
335
 
                                  G_CALLBACK (chat_window_close_clicked_cb),
336
 
                                  chat);
337
 
 
338
 
                /* React to theme changes and also setup the size correctly.  */
339
 
                g_signal_connect (hbox,
340
 
                                  "style-set",
341
 
                                  G_CALLBACK (chat_tab_style_set_cb),
342
 
                                  chat);
343
 
        }
344
 
 
345
 
        gtk_widget_show_all (hbox);
346
 
 
347
 
        return hbox;
348
 
}
349
 
 
350
 
static void
351
 
_submenu_notify_visible_changed_cb (GObject    *object,
352
 
                                    GParamSpec *pspec,
353
 
                                    gpointer    userdata)
354
 
{
355
 
        g_signal_handlers_disconnect_by_func (object,
356
 
                                              _submenu_notify_visible_changed_cb,
357
 
                                              userdata);
358
 
        chat_window_update (EMPATHY_CHAT_WINDOW (userdata), TRUE);
359
 
}
360
 
 
361
 
static void
362
 
chat_window_menu_context_update (EmpathyChatWindowPriv *priv,
363
 
                              gint num_pages)
364
 
{
365
 
        gboolean first_page;
366
 
        gboolean last_page;
367
 
        gboolean wrap_around;
368
 
        gboolean is_connected;
369
 
        gint     page_num;
370
 
 
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",
375
 
                      &wrap_around, NULL);
376
 
        is_connected = empathy_chat_get_tp_chat (priv->current_chat) != NULL;
377
 
 
378
 
        gtk_action_set_sensitive (priv->menu_tabs_next, (!last_page ||
379
 
                                                         wrap_around));
380
 
        gtk_action_set_sensitive (priv->menu_tabs_prev, (!first_page ||
381
 
                                                         wrap_around));
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);
386
 
}
387
 
 
388
 
static void
389
 
chat_window_conversation_menu_update (EmpathyChatWindowPriv *priv,
390
 
                                      EmpathyChatWindow     *self)
391
 
{
392
 
        EmpathyTpChat *tp_chat;
393
 
        TpConnection *connection;
394
 
        GtkAction *action;
395
 
        gboolean sensitive = FALSE;
396
 
 
397
 
        g_return_if_fail (priv->current_chat != NULL);
398
 
 
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);
402
 
 
403
 
        if (tp_chat != NULL) {
404
 
                connection = empathy_tp_chat_get_connection (tp_chat);
405
 
 
406
 
                sensitive = empathy_tp_chat_can_add_contact (tp_chat) &&
407
 
                        (tp_connection_get_status (connection, NULL) ==
408
 
                         TP_CONNECTION_STATUS_CONNECTED);
409
 
        }
410
 
 
411
 
        gtk_action_set_sensitive (action, sensitive);
412
 
}
413
 
 
414
 
static void
415
 
chat_window_contact_menu_update (EmpathyChatWindowPriv *priv,
416
 
                                 EmpathyChatWindow     *window)
417
 
{
418
 
        GtkWidget *menu, *submenu, *orig_submenu;
419
 
 
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));
423
 
 
424
 
        if (orig_submenu == NULL || !gtk_widget_get_visible (orig_submenu)) {
425
 
                submenu = empathy_chat_get_contact_menu (priv->current_chat);
426
 
 
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);
430
 
 
431
 
                        gtk_menu_item_set_submenu (GTK_MENU_ITEM (menu), submenu);
432
 
                        gtk_widget_show (menu);
433
 
                        gtk_widget_set_sensitive (menu, TRUE);
434
 
                } else {
435
 
                        gtk_widget_set_sensitive (menu, FALSE);
436
 
                }
437
 
 
438
 
        } else {
439
 
                tp_g_signal_connect_object (orig_submenu,
440
 
                                             "notify::visible",
441
 
                                             (GCallback)_submenu_notify_visible_changed_cb,
442
 
                                             window, 0);
443
 
        }
444
 
}
445
 
 
446
 
static guint
447
 
get_all_unread_messages (EmpathyChatWindowPriv *priv)
448
 
{
449
 
        GList *l;
450
 
        guint nb = 0;
451
 
 
452
 
        for (l = priv->chats_new_msg; l != NULL; l = g_list_next (l)) {
453
 
                EmpathyChat *chat = l->data;
454
 
 
455
 
                nb += empathy_chat_get_nb_unread_messages (chat);
456
 
        }
457
 
 
458
 
        return nb;
459
 
}
460
 
 
461
 
static gchar *
462
 
get_window_title_name (EmpathyChatWindowPriv *priv)
463
 
{
464
 
        const gchar *active_name;
465
 
        guint nb_chats;
466
 
        guint current_unread_msgs;
467
 
 
468
 
        nb_chats = g_list_length (priv->chats);
469
 
        g_assert (nb_chats > 0);
470
 
 
471
 
        active_name = empathy_chat_get_name (priv->current_chat);
472
 
 
473
 
        current_unread_msgs = empathy_chat_get_nb_unread_messages (
474
 
                        priv->current_chat);
475
 
 
476
 
        if (nb_chats == 1) {
477
 
                /* only one tab */
478
 
                if (current_unread_msgs == 0)
479
 
                        return g_strdup (active_name);
480
 
                else
481
 
                        return g_strdup_printf (ngettext (
482
 
                                "%s (%d unread)",
483
 
                                "%s (%d unread)", current_unread_msgs),
484
 
                                active_name, current_unread_msgs);
485
 
        } else {
486
 
                guint nb_others = nb_chats - 1;
487
 
                guint all_unread_msgs;
488
 
 
489
 
                all_unread_msgs = get_all_unread_messages (priv);
490
 
 
491
 
                if (all_unread_msgs == 0) {
492
 
                        /* no unread message */
493
 
                        return g_strdup_printf (ngettext (
494
 
                                "%s (and %u other)",
495
 
                                "%s (and %u others)", nb_others),
496
 
                                active_name, nb_others);
497
 
                }
498
 
 
499
 
                else if (all_unread_msgs == current_unread_msgs) {
500
 
                        /* unread messages are in the current tab */
501
 
                        return g_strdup_printf (ngettext (
502
 
                                "%s (%d unread)",
503
 
                                "%s (%d unread)", current_unread_msgs),
504
 
                                active_name, current_unread_msgs);
505
 
                }
506
 
 
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)",
512
 
                                all_unread_msgs),
513
 
                                active_name, all_unread_msgs);
514
 
                }
515
 
 
516
 
                else {
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)",
521
 
                                all_unread_msgs),
522
 
                                active_name, all_unread_msgs);
523
 
                }
524
 
        }
525
 
}
526
 
 
527
 
static void
528
 
chat_window_title_update (EmpathyChatWindowPriv *priv)
529
 
{
530
 
        gchar *name;
531
 
 
532
 
        name = get_window_title_name (priv);
533
 
        gtk_window_set_title (GTK_WINDOW (priv->dialog), name);
534
 
        g_free (name);
535
 
}
536
 
 
537
 
static void
538
 
chat_window_icon_update (EmpathyChatWindowPriv *priv)
539
 
{
540
 
        GdkPixbuf      *icon;
541
 
        EmpathyContact *remote_contact;
542
 
        gboolean        avatar_in_icon;
543
 
        guint           n_chats;
544
 
 
545
 
        n_chats = g_list_length (priv->chats);
546
 
 
547
 
        /* Update window icon */
548
 
        if (priv->chats_new_msg) {
549
 
                gtk_window_set_icon_name (GTK_WINDOW (priv->dialog),
550
 
                                          EMPATHY_IMAGE_MESSAGE);
551
 
        } else {
552
 
                GSettings *gsettings = g_settings_new (EMPATHY_PREFS_CHAT_SCHEMA);
553
 
 
554
 
                avatar_in_icon = g_settings_get_boolean (gsettings,
555
 
                                EMPATHY_PREFS_CHAT_AVATAR_IN_ICON);
556
 
 
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);
561
 
 
562
 
                        if (icon != NULL) {
563
 
                                g_object_unref (icon);
564
 
                        }
565
 
                } else {
566
 
                        gtk_window_set_icon_name (GTK_WINDOW (priv->dialog), NULL);
567
 
                }
568
 
 
569
 
                g_object_unref (gsettings);
570
 
        }
571
 
}
572
 
 
573
 
static void
574
 
chat_window_close_button_update (EmpathyChatWindowPriv *priv,
575
 
                                 gint num_pages)
576
 
{
577
 
        GtkWidget *chat;
578
 
        GtkWidget *chat_close_button;
579
 
        gint       i;
580
 
 
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);
586
 
        } else {
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);
592
 
                }
593
 
        }
594
 
}
595
 
 
596
 
static void
597
 
chat_window_update (EmpathyChatWindow *window,
598
 
                    gboolean update_contact_menu)
599
 
{
600
 
        EmpathyChatWindowPriv *priv = GET_PRIV (window);
601
 
        gint                   num_pages;
602
 
 
603
 
        num_pages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (priv->notebook));
604
 
 
605
 
        /* Update Tab menu */
606
 
        chat_window_menu_context_update (priv,
607
 
                                         num_pages);
608
 
 
609
 
        chat_window_conversation_menu_update (priv, window);
610
 
 
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
614
 
           menu watching. */
615
 
        if (update_contact_menu) {
616
 
                chat_window_contact_menu_update (priv,
617
 
                                                 window);
618
 
        }
619
 
 
620
 
        chat_window_title_update (priv);
621
 
 
622
 
        chat_window_icon_update (priv);
623
 
 
624
 
        chat_window_close_button_update (priv,
625
 
                                         num_pages);
626
 
}
627
 
 
628
 
static void
629
 
append_markup_printf (GString    *string,
630
 
                      const char *format,
631
 
                      ...)
632
 
{
633
 
        gchar *tmp;
634
 
        va_list args;
635
 
 
636
 
        va_start (args, format);
637
 
 
638
 
        tmp = g_markup_vprintf_escaped (format, args);
639
 
        g_string_append (string, tmp);
640
 
        g_free (tmp);
641
 
 
642
 
        va_end (args);
643
 
}
644
 
 
645
 
static void
646
 
chat_window_update_chat_tab_full (EmpathyChat *chat,
647
 
                                  gboolean update_contact_menu)
648
 
{
649
 
        EmpathyChatWindow     *window;
650
 
        EmpathyChatWindowPriv *priv;
651
 
        EmpathyContact        *remote_contact;
652
 
        const gchar           *name;
653
 
        const gchar           *id;
654
 
        TpAccount             *account;
655
 
        const gchar           *subject;
656
 
        const gchar           *status = NULL;
657
 
        GtkWidget             *widget;
658
 
        GString               *tooltip;
659
 
        gchar                 *markup;
660
 
        const gchar           *icon_name;
661
 
        GtkWidget             *tab_image;
662
 
        GtkWidget             *menu_image;
663
 
 
664
 
        window = chat_window_find_chat (chat);
665
 
        if (!window) {
666
 
                return;
667
 
        }
668
 
        priv = GET_PRIV (window);
669
 
 
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);
675
 
 
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);
678
 
 
679
 
        /* Update tab image */
680
 
        if (empathy_chat_get_tp_chat (chat) == NULL) {
681
 
                /* No TpChat, we are disconnected */
682
 
                icon_name = NULL;
683
 
        }
684
 
        else if (g_list_find (priv->chats_new_msg, chat)) {
685
 
                icon_name = EMPATHY_IMAGE_MESSAGE;
686
 
        }
687
 
        else if (g_list_find (priv->chats_composing, chat)) {
688
 
                icon_name = EMPATHY_IMAGE_TYPING;
689
 
        }
690
 
        else if (remote_contact) {
691
 
                icon_name = empathy_icon_name_for_contact (remote_contact);
692
 
        } else {
693
 
                icon_name = EMPATHY_IMAGE_GROUP_MESSAGE;
694
 
        }
695
 
 
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);
703
 
        } else {
704
 
                gtk_widget_hide (tab_image);
705
 
                gtk_widget_hide (menu_image);
706
 
        }
707
 
 
708
 
        /* Update tab tooltip */
709
 
        tooltip = g_string_new (NULL);
710
 
 
711
 
        if (remote_contact) {
712
 
                id = empathy_contact_get_id (remote_contact);
713
 
                status = empathy_contact_get_presence_message (remote_contact);
714
 
        } else {
715
 
                id = name;
716
 
        }
717
 
 
718
 
        append_markup_printf (tooltip,
719
 
                              "<b>%s</b><small> (%s)</small>",
720
 
                              id,
721
 
                              tp_account_get_display_name (account));
722
 
 
723
 
        if (!EMP_STR_EMPTY (status)) {
724
 
                append_markup_printf (tooltip, "\n<i>%s</i>", status);
725
 
        }
726
 
 
727
 
        if (subject) {
728
 
                append_markup_printf (tooltip, "\n<b>%s</b> %s",
729
 
                                      _("Topic:"), subject);
730
 
        }
731
 
 
732
 
        if (g_list_find (priv->chats_composing, chat)) {
733
 
                append_markup_printf (tooltip, "\n%s", _("Typing a message."));
734
 
        }
735
 
 
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);
741
 
        g_free (markup);
742
 
 
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);
748
 
 
749
 
        /* Update the window if it's the current chat */
750
 
        if (priv->current_chat == chat) {
751
 
                chat_window_update (window, update_contact_menu);
752
 
        }
753
 
}
754
 
 
755
 
static void
756
 
chat_window_update_chat_tab (EmpathyChat *chat)
757
 
{
758
 
        chat_window_update_chat_tab_full (chat, TRUE);
759
 
}
760
 
 
761
 
static void
762
 
chat_window_chat_notify_cb (EmpathyChat *chat)
763
 
{
764
 
        EmpathyContact *old_remote_contact;
765
 
        EmpathyContact *remote_contact = NULL;
766
 
 
767
 
        old_remote_contact = g_object_get_data (G_OBJECT (chat), "chat-window-remote-contact");
768
 
        remote_contact = empathy_chat_get_remote_contact (chat);
769
 
 
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),
777
 
                                                  chat);
778
 
                }
779
 
                if (old_remote_contact) {
780
 
                        g_signal_handlers_disconnect_by_func (old_remote_contact,
781
 
                                                              chat_window_update_chat_tab,
782
 
                                                              chat);
783
 
                }
784
 
 
785
 
                g_object_set_data_full (G_OBJECT (chat), "chat-window-remote-contact",
786
 
                                   g_object_ref (remote_contact), (GDestroyNotify) g_object_unref);
787
 
        }
788
 
 
789
 
        chat_window_update_chat_tab (chat);
790
 
}
791
 
 
792
 
static void
793
 
chat_window_insert_smiley_activate_cb (EmpathySmileyManager *manager,
794
 
                                       EmpathySmiley        *smiley,
795
 
                                       gpointer              window)
796
 
{
797
 
        EmpathyChatWindowPriv *priv = GET_PRIV (window);
798
 
        EmpathyChat           *chat;
799
 
        GtkTextBuffer         *buffer;
800
 
        GtkTextIter            iter;
801
 
 
802
 
        chat = priv->current_chat;
803
 
 
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);
807
 
}
808
 
 
809
 
static void
810
 
chat_window_conv_activate_cb (GtkAction         *action,
811
 
                              EmpathyChatWindow *window)
812
 
{
813
 
        EmpathyChatWindowPriv *priv = GET_PRIV (window);
814
 
        gboolean               is_room;
815
 
        gboolean               active;
816
 
        EmpathyContact        *remote_contact = NULL;
817
 
 
818
 
        /* Favorite room menu */
819
 
        is_room = empathy_chat_is_room (priv->current_chat);
820
 
        if (is_room) {
821
 
                const gchar *room;
822
 
                TpAccount   *account;
823
 
                gboolean     found = FALSE;
824
 
                EmpathyChatroom *chatroom;
825
 
 
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,
829
 
                                                       account, room);
830
 
                if (chatroom != NULL)
831
 
                        found = empathy_chatroom_is_favorite (chatroom);
832
 
 
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);
836
 
 
837
 
                if (chatroom != NULL)
838
 
                        found = empathy_chatroom_is_always_urgent (chatroom);
839
 
 
840
 
                gtk_toggle_action_set_active (
841
 
                        GTK_TOGGLE_ACTION (priv->menu_conv_always_urgent),
842
 
                        found);
843
 
        }
844
 
        gtk_action_set_visible (priv->menu_conv_favorite, is_room);
845
 
        gtk_action_set_visible (priv->menu_conv_always_urgent, is_room);
846
 
 
847
 
        /* Show contacts menu */
848
 
        g_object_get (priv->current_chat,
849
 
                      "remote-contact", &remote_contact,
850
 
                      "show-contacts", &active,
851
 
                      NULL);
852
 
        if (remote_contact == NULL) {
853
 
                gtk_toggle_action_set_active (
854
 
                        GTK_TOGGLE_ACTION (priv->menu_conv_toggle_contacts),
855
 
                                           active);
856
 
        }
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);
861
 
        }
862
 
}
863
 
 
864
 
static void
865
 
chat_window_clear_activate_cb (GtkAction         *action,
866
 
                               EmpathyChatWindow *window)
867
 
{
868
 
        EmpathyChatWindowPriv *priv = GET_PRIV (window);
869
 
 
870
 
        empathy_chat_clear (priv->current_chat);
871
 
}
872
 
 
873
 
static void
874
 
chat_window_favorite_toggled_cb (GtkToggleAction   *toggle_action,
875
 
                                 EmpathyChatWindow *window)
876
 
{
877
 
        EmpathyChatWindowPriv *priv = GET_PRIV (window);
878
 
        gboolean               active;
879
 
        TpAccount             *account;
880
 
        const gchar           *room;
881
 
        EmpathyChatroom       *chatroom;
882
 
 
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);
886
 
 
887
 
        chatroom = empathy_chatroom_manager_ensure_chatroom (
888
 
                     priv->chatroom_manager,
889
 
                     account,
890
 
                     room,
891
 
                     empathy_chat_get_name (priv->current_chat));
892
 
 
893
 
        empathy_chatroom_set_favorite (chatroom, active);
894
 
        g_object_unref (chatroom);
895
 
}
896
 
 
897
 
static void
898
 
chat_window_always_urgent_toggled_cb (GtkToggleAction   *toggle_action,
899
 
                                 EmpathyChatWindow *window)
900
 
{
901
 
        EmpathyChatWindowPriv *priv = GET_PRIV (window);
902
 
        gboolean               active;
903
 
        TpAccount             *account;
904
 
        const gchar           *room;
905
 
        EmpathyChatroom       *chatroom;
906
 
 
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);
910
 
 
911
 
        chatroom = empathy_chatroom_manager_ensure_chatroom (
912
 
                     priv->chatroom_manager,
913
 
                     account,
914
 
                     room,
915
 
                     empathy_chat_get_name (priv->current_chat));
916
 
 
917
 
        empathy_chatroom_set_always_urgent (chatroom, active);
918
 
        g_object_unref (chatroom);
919
 
}
920
 
 
921
 
static void
922
 
chat_window_contacts_toggled_cb (GtkToggleAction   *toggle_action,
923
 
                                 EmpathyChatWindow *window)
924
 
{
925
 
        EmpathyChatWindowPriv *priv = GET_PRIV (window);
926
 
        gboolean               active;
927
 
 
928
 
        active = gtk_toggle_action_get_active (toggle_action);
929
 
 
930
 
        empathy_chat_set_show_contacts (priv->current_chat, active);
931
 
}
932
 
 
933
 
static void
934
 
got_contact_cb (TpConnection            *connection,
935
 
                EmpathyContact          *contact,
936
 
                const GError            *error,
937
 
                gpointer                 user_data,
938
 
                GObject                 *object)
939
 
{
940
 
        EmpathyTpChat *tp_chat = EMPATHY_TP_CHAT (user_data);
941
 
 
942
 
        if (error != NULL) {
943
 
                DEBUG ("Failed: %s", error->message);
944
 
                return;
945
 
        } else {
946
 
                empathy_contact_list_add (EMPATHY_CONTACT_LIST (tp_chat),
947
 
                                contact, _("Inviting you to this room"));
948
 
        }
949
 
}
950
 
 
951
 
static void
952
 
chat_window_invite_participant_activate_cb (GtkAction         *action,
953
 
                                            EmpathyChatWindow *window)
954
 
{
955
 
        EmpathyChatWindowPriv *priv;
956
 
        GtkWidget             *dialog;
957
 
        EmpathyTpChat         *tp_chat;
958
 
        TpChannel             *channel;
959
 
        int                    response;
960
 
        TpAccount             *account;
961
 
 
962
 
        priv = GET_PRIV (window);
963
 
 
964
 
        g_return_if_fail (priv->current_chat != NULL);
965
 
 
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);
969
 
 
970
 
        dialog = empathy_invite_participant_dialog_new (
971
 
                        GTK_WINDOW (priv->dialog), account);
972
 
        gtk_widget_show (dialog);
973
 
 
974
 
        response = gtk_dialog_run (GTK_DIALOG (dialog));
975
 
 
976
 
        if (response == GTK_RESPONSE_ACCEPT) {
977
 
                TpConnection *connection;
978
 
                const char *id;
979
 
 
980
 
                id = empathy_contact_selector_dialog_get_selected (
981
 
                                EMPATHY_CONTACT_SELECTOR_DIALOG (dialog), NULL, NULL);
982
 
                if (EMP_STR_EMPTY (id)) goto out;
983
 
 
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);
987
 
        }
988
 
 
989
 
out:
990
 
        gtk_widget_destroy (dialog);
991
 
}
992
 
 
993
 
static void
994
 
chat_window_close_activate_cb (GtkAction         *action,
995
 
                               EmpathyChatWindow *window)
996
 
{
997
 
        EmpathyChatWindowPriv *priv;
998
 
 
999
 
        priv = GET_PRIV (window);
1000
 
 
1001
 
        g_return_if_fail (priv->current_chat != NULL);
1002
 
 
1003
 
        empathy_chat_window_remove_chat (window, priv->current_chat);
1004
 
}
1005
 
 
1006
 
static void
1007
 
chat_window_edit_activate_cb (GtkAction         *action,
1008
 
                              EmpathyChatWindow *window)
1009
 
{
1010
 
        EmpathyChatWindowPriv *priv;
1011
 
        GtkClipboard         *clipboard;
1012
 
        GtkTextBuffer        *buffer;
1013
 
        gboolean              text_available;
1014
 
 
1015
 
        priv = GET_PRIV (window);
1016
 
 
1017
 
        g_return_if_fail (priv->current_chat != NULL);
1018
 
 
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);
1023
 
                return;
1024
 
        }
1025
 
 
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);
1030
 
        } else {
1031
 
                gboolean selection;
1032
 
 
1033
 
                selection = empathy_chat_view_get_has_selection (priv->current_chat->view);
1034
 
 
1035
 
                gtk_action_set_sensitive (priv->menu_edit_cut, FALSE);
1036
 
                gtk_action_set_sensitive (priv->menu_edit_copy, selection);
1037
 
        }
1038
 
 
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);
1042
 
}
1043
 
 
1044
 
static void
1045
 
chat_window_cut_activate_cb (GtkAction         *action,
1046
 
                             EmpathyChatWindow *window)
1047
 
{
1048
 
        EmpathyChatWindowPriv *priv;
1049
 
 
1050
 
        g_return_if_fail (EMPATHY_IS_CHAT_WINDOW (window));
1051
 
 
1052
 
        priv = GET_PRIV (window);
1053
 
 
1054
 
        empathy_chat_cut (priv->current_chat);
1055
 
}
1056
 
 
1057
 
static void
1058
 
chat_window_copy_activate_cb (GtkAction         *action,
1059
 
                              EmpathyChatWindow *window)
1060
 
{
1061
 
        EmpathyChatWindowPriv *priv;
1062
 
 
1063
 
        g_return_if_fail (EMPATHY_IS_CHAT_WINDOW (window));
1064
 
 
1065
 
        priv = GET_PRIV (window);
1066
 
 
1067
 
        empathy_chat_copy (priv->current_chat);
1068
 
}
1069
 
 
1070
 
static void
1071
 
chat_window_paste_activate_cb (GtkAction         *action,
1072
 
                               EmpathyChatWindow *window)
1073
 
{
1074
 
        EmpathyChatWindowPriv *priv;
1075
 
 
1076
 
        g_return_if_fail (EMPATHY_IS_CHAT_WINDOW (window));
1077
 
 
1078
 
        priv = GET_PRIV (window);
1079
 
 
1080
 
        empathy_chat_paste (priv->current_chat);
1081
 
}
1082
 
 
1083
 
static void
1084
 
chat_window_find_activate_cb (GtkAction         *action,
1085
 
                              EmpathyChatWindow *window)
1086
 
{
1087
 
        EmpathyChatWindowPriv *priv;
1088
 
 
1089
 
        g_return_if_fail (EMPATHY_IS_CHAT_WINDOW (window));
1090
 
 
1091
 
        priv = GET_PRIV (window);
1092
 
 
1093
 
        empathy_chat_find (priv->current_chat);
1094
 
}
1095
 
 
1096
 
static void
1097
 
chat_window_tabs_next_activate_cb (GtkAction         *action,
1098
 
                                   EmpathyChatWindow *window)
1099
 
{
1100
 
        EmpathyChatWindowPriv *priv;
1101
 
        EmpathyChat           *chat;
1102
 
        gint                  index_, numPages;
1103
 
        gboolean              wrap_around;
1104
 
 
1105
 
        priv = GET_PRIV (window);
1106
 
 
1107
 
        g_object_get (gtk_settings_get_default (), "gtk-keynav-wrap-around",
1108
 
                      &wrap_around, NULL);
1109
 
 
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));
1113
 
 
1114
 
        if (index_ == (numPages - 1) && wrap_around) {
1115
 
                gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->notebook), 0);
1116
 
                return;
1117
 
        }
1118
 
 
1119
 
        gtk_notebook_next_page (GTK_NOTEBOOK (priv->notebook));
1120
 
}
1121
 
 
1122
 
static void
1123
 
chat_window_tabs_previous_activate_cb (GtkAction         *action,
1124
 
                                   EmpathyChatWindow *window)
1125
 
{
1126
 
        EmpathyChatWindowPriv *priv;
1127
 
        EmpathyChat           *chat;
1128
 
        gint                  index_, numPages;
1129
 
        gboolean              wrap_around;
1130
 
 
1131
 
        priv = GET_PRIV (window);
1132
 
 
1133
 
        g_object_get (gtk_settings_get_default (), "gtk-keynav-wrap-around",
1134
 
                      &wrap_around, NULL);
1135
 
 
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));
1139
 
 
1140
 
        if (index_ <= 0 && wrap_around) {
1141
 
                gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->notebook), numPages - 1);
1142
 
                return;
1143
 
        }
1144
 
 
1145
 
        gtk_notebook_prev_page (GTK_NOTEBOOK (priv->notebook));
1146
 
}
1147
 
 
1148
 
static void
1149
 
chat_window_tabs_undo_close_tab_activate_cb (GtkAction         *action,
1150
 
                                             EmpathyChatWindow *window)
1151
 
{
1152
 
        EmpathyChatWindowPriv *priv = GET_PRIV (window);
1153
 
        empathy_chat_manager_undo_closed_chat (priv->chat_manager);
1154
 
}
1155
 
 
1156
 
static void
1157
 
chat_window_tabs_left_activate_cb (GtkAction         *action,
1158
 
                                   EmpathyChatWindow *window)
1159
 
{
1160
 
        EmpathyChatWindowPriv *priv;
1161
 
        EmpathyChat           *chat;
1162
 
        gint                  index_, num_pages;
1163
 
 
1164
 
        priv = GET_PRIV (window);
1165
 
 
1166
 
        chat = priv->current_chat;
1167
 
        index_ = gtk_notebook_get_current_page (GTK_NOTEBOOK (priv->notebook));
1168
 
        if (index_ <= 0) {
1169
 
                return;
1170
 
        }
1171
 
 
1172
 
        gtk_notebook_reorder_child (GTK_NOTEBOOK (priv->notebook),
1173
 
                                    GTK_WIDGET (chat),
1174
 
                                    index_ - 1);
1175
 
 
1176
 
        num_pages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (priv->notebook));
1177
 
        chat_window_menu_context_update (priv, num_pages);
1178
 
}
1179
 
 
1180
 
static void
1181
 
chat_window_tabs_right_activate_cb (GtkAction         *action,
1182
 
                                    EmpathyChatWindow *window)
1183
 
{
1184
 
        EmpathyChatWindowPriv *priv;
1185
 
        EmpathyChat           *chat;
1186
 
        gint                  index_, num_pages;
1187
 
 
1188
 
        priv = GET_PRIV (window);
1189
 
 
1190
 
        chat = priv->current_chat;
1191
 
        index_ = gtk_notebook_get_current_page (GTK_NOTEBOOK (priv->notebook));
1192
 
 
1193
 
        gtk_notebook_reorder_child (GTK_NOTEBOOK (priv->notebook),
1194
 
                                    GTK_WIDGET (chat),
1195
 
                                    index_ + 1);
1196
 
 
1197
 
        num_pages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (priv->notebook));
1198
 
        chat_window_menu_context_update (priv, num_pages);
1199
 
}
1200
 
 
1201
 
static void
1202
 
chat_window_detach_activate_cb (GtkAction         *action,
1203
 
                                EmpathyChatWindow *window)
1204
 
{
1205
 
        EmpathyChatWindowPriv *priv;
1206
 
        EmpathyChatWindow     *new_window;
1207
 
        EmpathyChat           *chat;
1208
 
 
1209
 
        priv = GET_PRIV (window);
1210
 
 
1211
 
        chat = priv->current_chat;
1212
 
        new_window = empathy_chat_window_new ();
1213
 
 
1214
 
        empathy_chat_window_move_chat (window, new_window, chat);
1215
 
 
1216
 
        priv = GET_PRIV (new_window);
1217
 
        gtk_widget_show (priv->dialog);
1218
 
}
1219
 
 
1220
 
static void
1221
 
chat_window_help_contents_activate_cb (GtkAction         *action,
1222
 
                                       EmpathyChatWindow *window)
1223
 
{
1224
 
        EmpathyChatWindowPriv *priv = GET_PRIV (window);
1225
 
 
1226
 
        empathy_url_show (priv->dialog, "ghelp:empathy");
1227
 
}
1228
 
 
1229
 
static void
1230
 
chat_window_help_about_activate_cb (GtkAction         *action,
1231
 
                                    EmpathyChatWindow *window)
1232
 
{
1233
 
        EmpathyChatWindowPriv *priv = GET_PRIV (window);
1234
 
 
1235
 
        empathy_about_dialog_new (GTK_WINDOW (priv->dialog));
1236
 
}
1237
 
 
1238
 
static gboolean
1239
 
chat_window_delete_event_cb (GtkWidget        *dialog,
1240
 
                             GdkEvent         *event,
1241
 
                             EmpathyChatWindow *window)
1242
 
{
1243
 
        EmpathyChatWindowPriv *priv = GET_PRIV (window);
1244
 
 
1245
 
        DEBUG ("Delete event received");
1246
 
 
1247
 
        g_object_ref (window);
1248
 
        while (priv->chats) {
1249
 
                empathy_chat_window_remove_chat (window, priv->chats->data);
1250
 
        }
1251
 
        g_object_unref (window);
1252
 
 
1253
 
        return TRUE;
1254
 
}
1255
 
 
1256
 
static void
1257
 
chat_window_composing_cb (EmpathyChat       *chat,
1258
 
                          gboolean          is_composing,
1259
 
                          EmpathyChatWindow *window)
1260
 
{
1261
 
        EmpathyChatWindowPriv *priv;
1262
 
 
1263
 
        priv = GET_PRIV (window);
1264
 
 
1265
 
        if (is_composing && !g_list_find (priv->chats_composing, chat)) {
1266
 
                priv->chats_composing = g_list_prepend (priv->chats_composing, chat);
1267
 
        } else {
1268
 
                priv->chats_composing = g_list_remove (priv->chats_composing, chat);
1269
 
        }
1270
 
 
1271
 
        chat_window_update_chat_tab (chat);
1272
 
}
1273
 
 
1274
 
static void
1275
 
chat_window_set_urgency_hint (EmpathyChatWindow *window,
1276
 
                              gboolean          urgent)
1277
 
{
1278
 
        EmpathyChatWindowPriv *priv;
1279
 
 
1280
 
        priv = GET_PRIV (window);
1281
 
 
1282
 
        gtk_window_set_urgency_hint (GTK_WINDOW (priv->dialog), urgent);
1283
 
}
1284
 
 
1285
 
static void
1286
 
chat_window_notification_closed_cb (NotifyNotification *notify,
1287
 
                                    EmpathyChatWindow *self)
1288
 
{
1289
 
        EmpathyChatWindowPriv *priv = GET_PRIV (self);
1290
 
 
1291
 
        g_object_unref (notify);
1292
 
        if (priv->notification == notify) {
1293
 
                priv->notification = NULL;
1294
 
        }
1295
 
}
1296
 
 
1297
 
#ifdef HAVE_LIBINDICATE
1298
 
static void
1299
 
chat_window_indicator_activate_cb (EmpathyIndicator *indicator, guint timestamp,
1300
 
                                    EmpathyChat *chat)
1301
 
{
1302
 
        empathy_chat_window_present_chat (chat, timestamp);
1303
 
        empathy_indicator_hide (indicator);
1304
 
}
1305
 
 
1306
 
static void
1307
 
chat_window_add_indicator (EmpathyChatWindow *window,
1308
 
                                         EmpathyMessage *message,
1309
 
                                         EmpathyChat    *chat)
1310
 
{
1311
 
        EmpathyChatWindowPriv *priv = GET_PRIV (window);
1312
 
        EmpathyContact *sender;
1313
 
        const char *body;
1314
 
        gboolean use_libindicate;
1315
 
        EmpathyIndicator *indicator = NULL;
1316
 
        GSettings *gsettings;
1317
 
 
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);
1321
 
 
1322
 
        if (!use_libindicate) {
1323
 
                return;
1324
 
        }
1325
 
 
1326
 
        sender = empathy_message_get_sender (message);
1327
 
        body = empathy_message_get_body (message);
1328
 
 
1329
 
        indicator = g_hash_table_lookup (priv->indicators, chat);
1330
 
        if (indicator != NULL) {
1331
 
                DEBUG ("indicator exists");
1332
 
                empathy_indicator_update (indicator, body);
1333
 
        } else {
1334
 
                DEBUG ("indicator doesn't exist yet, creating a new indicator");
1335
 
                indicator = empathy_indicator_manager_create_indicator (priv->indicator_manager,
1336
 
                        sender, body);
1337
 
                g_signal_connect (indicator, "activate",
1338
 
                                  G_CALLBACK (chat_window_indicator_activate_cb), chat);
1339
 
                g_hash_table_insert (priv->indicators, chat, indicator);
1340
 
#ifdef HAVE_UNITY
1341
 
                if (priv->launcher != NULL)
1342
 
                {
1343
 
                        gint count = g_hash_table_size (priv->indicators);
1344
 
                        DEBUG ("unity launcher: count is now %d", count);
1345
 
                        if (count > 0)
1346
 
                        {
1347
 
                                unity_launcher_entry_set_count (priv->launcher, count);
1348
 
                                unity_launcher_entry_set_count_visible (priv->launcher, TRUE);
1349
 
                        }
1350
 
                }
1351
 
#endif
1352
 
        }
1353
 
        empathy_indicator_show (indicator);
1354
 
}
1355
 
 
1356
 
static void
1357
 
chat_window_remove_indicator (EmpathyChatWindow *window, EmpathyChat *chat)
1358
 
{
1359
 
        EmpathyIndicator *indicator = NULL;
1360
 
        EmpathyChatWindowPriv *priv = GET_PRIV (window);
1361
 
 
1362
 
        indicator = g_hash_table_lookup (priv->indicators, chat);
1363
 
 
1364
 
        if ((indicator) && (indicator != NULL)) {
1365
 
                DEBUG ("indicator is %p", indicator);
1366
 
                empathy_indicator_hide (indicator);
1367
 
                g_hash_table_remove (priv->indicators, chat);
1368
 
#ifdef HAVE_UNITY
1369
 
        if (priv->launcher != NULL)
1370
 
        {
1371
 
                gint count = g_hash_table_size (priv->indicators);
1372
 
                DEBUG ("unity launcher: count is %d", count);
1373
 
                if (count > 0)
1374
 
                {
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);
1378
 
                } else {
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);
1382
 
                }
1383
 
        }
1384
 
#endif
1385
 
 
1386
 
 
1387
 
        } else {
1388
 
                DEBUG ("indicator is NULL, nothing to remove");
1389
 
        }
1390
 
}
1391
 
#endif
1392
 
 
1393
 
static void
1394
 
chat_window_show_or_update_notification (EmpathyChatWindow *window,
1395
 
                                         EmpathyMessage *message,
1396
 
                                         EmpathyChat *chat)
1397
 
{
1398
 
        EmpathyContact *sender;
1399
 
        const gchar *header;
1400
 
        char *escaped;
1401
 
        const char *body;
1402
 
        GdkPixbuf *pixbuf;
1403
 
        EmpathyChatWindowPriv *priv = GET_PRIV (window);
1404
 
        gboolean res, has_x_canonical_append;
1405
 
        NotifyNotification *notification = priv->notification;
1406
 
 
1407
 
        if (!empathy_notify_manager_notification_is_enabled (priv->notify_mgr)) {
1408
 
                return;
1409
 
        } else {
1410
 
                GSettings *gsettings = g_settings_new (
1411
 
                                EMPATHY_PREFS_NOTIFICATIONS_SCHEMA);
1412
 
 
1413
 
                res = g_settings_get_boolean (gsettings,
1414
 
                                EMPATHY_PREFS_NOTIFICATIONS_FOCUS);
1415
 
 
1416
 
                g_object_unref (gsettings);
1417
 
 
1418
 
                if (!res) {
1419
 
                        return;
1420
 
                }
1421
 
        }
1422
 
 
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);
1429
 
 
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);
1436
 
        } else {
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);
1444
 
 
1445
 
                if (priv->notification == NULL) {
1446
 
                        priv->notification = notification;
1447
 
                }
1448
 
 
1449
 
                notify_notification_set_timeout (notification, NOTIFY_EXPIRES_DEFAULT);
1450
 
 
1451
 
                tp_g_signal_connect_object (notification, "closed",
1452
 
                                  G_CALLBACK (chat_window_notification_closed_cb), window, 0);
1453
 
 
1454
 
                if (has_x_canonical_append) {
1455
 
                        notify_notification_set_hint_string (notification,
1456
 
                                EMPATHY_NOTIFY_MANAGER_CAP_X_CANONICAL_APPEND, "");
1457
 
                }
1458
 
        }
1459
 
 
1460
 
        pixbuf = empathy_notify_manager_get_pixbuf_for_notification (priv->notify_mgr,
1461
 
                sender, EMPATHY_IMAGE_NEW_MESSAGE);
1462
 
 
1463
 
        if (pixbuf != NULL) {
1464
 
                notify_notification_set_icon_from_pixbuf (notification, pixbuf);
1465
 
                g_object_unref (pixbuf);
1466
 
        }
1467
 
 
1468
 
        notify_notification_show (notification, NULL);
1469
 
 
1470
 
        g_free (escaped);
1471
 
}
1472
 
 
1473
 
static void
1474
 
chat_window_set_highlight_room_tab_label (EmpathyChat *chat)
1475
 
{
1476
 
        gchar *markup;
1477
 
        GtkWidget *widget;
1478
 
 
1479
 
        if (!empathy_chat_is_room (chat))
1480
 
                return;
1481
 
 
1482
 
        markup = g_markup_printf_escaped (
1483
 
                "<span color=\"red\" weight=\"bold\">%s</span>",
1484
 
                empathy_chat_get_name (chat));
1485
 
 
1486
 
        widget = g_object_get_data (G_OBJECT (chat), "chat-window-tab-label");
1487
 
        gtk_label_set_markup (GTK_LABEL (widget), markup);
1488
 
        g_free (markup);
1489
 
}
1490
 
 
1491
 
static void
1492
 
chat_window_new_message_cb (EmpathyChat       *chat,
1493
 
                            EmpathyMessage    *message,
1494
 
                            gboolean pending,
1495
 
                            EmpathyChatWindow *window)
1496
 
{
1497
 
        EmpathyChatWindowPriv *priv;
1498
 
        gboolean              has_focus;
1499
 
        gboolean              needs_urgency;
1500
 
        EmpathyContact        *sender;
1501
 
 
1502
 
        priv = GET_PRIV (window);
1503
 
 
1504
 
        has_focus = empathy_chat_window_has_focus (window);
1505
 
 
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
1510
 
         *   the message.
1511
 
         */
1512
 
 
1513
 
        sender = empathy_message_get_sender (message);
1514
 
 
1515
 
        if (empathy_contact_is_user (sender)) {
1516
 
                empathy_sound_play (GTK_WIDGET (priv->dialog),
1517
 
                                    EMPATHY_SOUND_MESSAGE_OUTGOING);
1518
 
        }
1519
 
 
1520
 
        if (has_focus && priv->current_chat == chat) {
1521
 
                /* window and tab are focused so consider the message to be read */
1522
 
 
1523
 
                /* FIXME: see Bug#610994 and coments about it in EmpathyChatPriv */
1524
 
                empathy_chat_messages_read (chat);
1525
 
                return;
1526
 
        }
1527
 
 
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);
1531
 
        }
1532
 
 
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
1539
 
         */
1540
 
        if (empathy_chat_is_room (chat) ||
1541
 
            empathy_chat_get_remote_contact (chat) == NULL) {
1542
 
                TpAccount             *account;
1543
 
                const gchar           *room;
1544
 
                EmpathyChatroom       *chatroom;
1545
 
 
1546
 
                account = empathy_chat_get_account (chat);
1547
 
                room = empathy_chat_get_id (chat);
1548
 
 
1549
 
                chatroom = empathy_chatroom_manager_find (priv->chatroom_manager,
1550
 
                                                          account, room);
1551
 
 
1552
 
                if (chatroom != NULL && empathy_chatroom_is_always_urgent (chatroom)) {
1553
 
                        needs_urgency = TRUE;
1554
 
                } else {
1555
 
                        needs_urgency = empathy_message_should_highlight (message);
1556
 
                }
1557
 
        } else {
1558
 
                needs_urgency = TRUE;
1559
 
        }
1560
 
 
1561
 
        if (needs_urgency) {
1562
 
                if (!has_focus) {
1563
 
                        chat_window_set_urgency_hint (window, TRUE);
1564
 
                        chat_window_set_highlight_room_tab_label (chat);
1565
 
                }
1566
 
 
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);
1571
 
#endif
1572
 
 
1573
 
                /* Pending messages have already been displayed in the approver, so we don't
1574
 
                * display a notification for those. */
1575
 
                if (!pending)
1576
 
                        chat_window_show_or_update_notification (window, message, chat);
1577
 
        }
1578
 
 
1579
 
        /* update the number of unread messages and the window icon */
1580
 
        chat_window_title_update (priv);
1581
 
        chat_window_icon_update (priv);
1582
 
}
1583
 
 
1584
 
static GtkNotebook *
1585
 
chat_window_detach_hook (GtkNotebook *source,
1586
 
                         GtkWidget   *page,
1587
 
                         gint         x,
1588
 
                         gint         y,
1589
 
                         gpointer     user_data)
1590
 
{
1591
 
        EmpathyChatWindowPriv *priv;
1592
 
        EmpathyChatWindow     *window, *new_window;
1593
 
        EmpathyChat           *chat;
1594
 
 
1595
 
        chat = EMPATHY_CHAT (page);
1596
 
        window = chat_window_find_chat (chat);
1597
 
 
1598
 
        new_window = empathy_chat_window_new ();
1599
 
        priv = GET_PRIV (new_window);
1600
 
 
1601
 
        DEBUG ("Detach hook called");
1602
 
 
1603
 
        empathy_chat_window_move_chat (window, new_window, chat);
1604
 
 
1605
 
        gtk_widget_show (priv->dialog);
1606
 
        gtk_window_move (GTK_WINDOW (priv->dialog), x, y);
1607
 
 
1608
 
        return NULL;
1609
 
}
1610
 
 
1611
 
static void
1612
 
chat_window_page_switched_cb (GtkNotebook      *notebook,
1613
 
                              gpointer          ignore, /* see note below */
1614
 
                              gint              page_num,
1615
 
                              EmpathyChatWindow *window)
1616
 
{
1617
 
        EmpathyChatWindowPriv *priv;
1618
 
        EmpathyChat           *chat;
1619
 
        GtkWidget            *child;
1620
 
 
1621
 
        DEBUG ("Page switched");
1622
 
 
1623
 
        priv = GET_PRIV (window);
1624
 
 
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.
1627
 
         */
1628
 
        child = gtk_notebook_get_nth_page (notebook, page_num);
1629
 
        chat = EMPATHY_CHAT (child);
1630
 
 
1631
 
        if (priv->page_added) {
1632
 
                priv->page_added = FALSE;
1633
 
                empathy_chat_scroll_down (chat);
1634
 
        }
1635
 
        else if (priv->current_chat == chat) {
1636
 
                return;
1637
 
        }
1638
 
 
1639
 
        priv->current_chat = chat;
1640
 
        priv->chats_new_msg = g_list_remove (priv->chats_new_msg, chat);
1641
 
        empathy_chat_messages_read (chat);
1642
 
 
1643
 
        chat_window_update_chat_tab (chat);
1644
 
 
1645
 
#ifdef HAVE_LIBINDICATE
1646
 
        chat_window_remove_indicator (window, chat);
1647
 
#endif
1648
 
}
1649
 
 
1650
 
static void
1651
 
chat_window_page_added_cb (GtkNotebook      *notebook,
1652
 
                           GtkWidget        *child,
1653
 
                           guint             page_num,
1654
 
                           EmpathyChatWindow *window)
1655
 
{
1656
 
        EmpathyChatWindowPriv *priv;
1657
 
        EmpathyChat           *chat;
1658
 
 
1659
 
        priv = GET_PRIV (window);
1660
 
 
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".
1664
 
         */
1665
 
        if (priv->dnd_same_window) {
1666
 
                DEBUG ("Page added (back to the same window)");
1667
 
                priv->dnd_same_window = FALSE;
1668
 
                return;
1669
 
        }
1670
 
 
1671
 
        DEBUG ("Page added");
1672
 
 
1673
 
        /* Get chat object */
1674
 
        chat = EMPATHY_CHAT (child);
1675
 
 
1676
 
        /* Connect chat signals for this window */
1677
 
        g_signal_connect (chat, "composing",
1678
 
                          G_CALLBACK (chat_window_composing_cb),
1679
 
                          window);
1680
 
        g_signal_connect (chat, "new-message",
1681
 
                          G_CALLBACK (chat_window_new_message_cb),
1682
 
                          window);
1683
 
        g_signal_connect (chat, "notify::tp-chat",
1684
 
                          G_CALLBACK (chat_window_update_chat_tab),
1685
 
                          window);
1686
 
 
1687
 
        /* Set flag so we know to perform some special operations on
1688
 
         * switch page due to the new page being added.
1689
 
         */
1690
 
        priv->page_added = TRUE;
1691
 
 
1692
 
        /* Get list of chats up to date */
1693
 
        priv->chats = g_list_append (priv->chats, chat);
1694
 
 
1695
 
        chat_window_update_chat_tab (chat);
1696
 
}
1697
 
 
1698
 
static void
1699
 
chat_window_page_removed_cb (GtkNotebook      *notebook,
1700
 
                             GtkWidget        *child,
1701
 
                             guint             page_num,
1702
 
                             EmpathyChatWindow *window)
1703
 
{
1704
 
        EmpathyChatWindowPriv *priv;
1705
 
        EmpathyChat           *chat;
1706
 
 
1707
 
        priv = GET_PRIV (window);
1708
 
 
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".
1712
 
         */
1713
 
        if (priv->dnd_same_window) {
1714
 
                DEBUG ("Page removed (and will be readded to same window)");
1715
 
                return;
1716
 
        }
1717
 
 
1718
 
        DEBUG ("Page removed");
1719
 
 
1720
 
        /* Get chat object */
1721
 
        chat = EMPATHY_CHAT (child);
1722
 
 
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),
1726
 
                                              window);
1727
 
        g_signal_handlers_disconnect_by_func (chat,
1728
 
                                              G_CALLBACK (chat_window_new_message_cb),
1729
 
                                              window);
1730
 
        g_signal_handlers_disconnect_by_func (chat,
1731
 
                                              G_CALLBACK (chat_window_update_chat_tab),
1732
 
                                              window);
1733
 
 
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);
1739
 
 
1740
 
        if (priv->chats == NULL) {
1741
 
                g_object_unref (window);
1742
 
        } else {
1743
 
                chat_window_update (window, TRUE);
1744
 
        }
1745
 
}
1746
 
 
1747
 
static gboolean
1748
 
chat_window_focus_in_event_cb (GtkWidget        *widget,
1749
 
                               GdkEvent         *event,
1750
 
                               EmpathyChatWindow *window)
1751
 
{
1752
 
        EmpathyChatWindowPriv *priv;
1753
 
 
1754
 
        priv = GET_PRIV (window);
1755
 
 
1756
 
        priv->chats_new_msg = g_list_remove (priv->chats_new_msg, priv->current_chat);
1757
 
        empathy_chat_messages_read (priv->current_chat);
1758
 
 
1759
 
        chat_window_set_urgency_hint (window, FALSE);
1760
 
 
1761
 
        /* Update the title, since we now mark all unread messages as read. */
1762
 
        chat_window_update_chat_tab_full (priv->current_chat, FALSE);
1763
 
 
1764
 
#ifdef HAVE_LIBINDICATE
1765
 
        /* Remove the indicator for the active chat */
1766
 
        chat_window_remove_indicator (window, priv->current_chat);
1767
 
#endif
1768
 
 
1769
 
        return FALSE;
1770
 
}
1771
 
 
1772
 
static gboolean
1773
 
chat_window_drag_drop (GtkWidget        *widget,
1774
 
                         GdkDragContext   *context,
1775
 
                         int               x,
1776
 
                         int               y,
1777
 
                         guint             time_,
1778
 
                         EmpathyChatWindow *window)
1779
 
{
1780
 
        GdkAtom target;
1781
 
        EmpathyChatWindowPriv *priv;
1782
 
 
1783
 
        priv = GET_PRIV (window);
1784
 
 
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);
1788
 
 
1789
 
        if (target != GDK_NONE) {
1790
 
                gtk_drag_get_data (widget, context, target, time_);
1791
 
                return TRUE;
1792
 
        }
1793
 
 
1794
 
        return FALSE;
1795
 
}
1796
 
 
1797
 
static gboolean
1798
 
chat_window_drag_motion (GtkWidget        *widget,
1799
 
                         GdkDragContext   *context,
1800
 
                         int               x,
1801
 
                         int               y,
1802
 
                         guint             time_,
1803
 
                         EmpathyChatWindow *window)
1804
 
{
1805
 
        GdkAtom target;
1806
 
        EmpathyChatWindowPriv *priv;
1807
 
 
1808
 
        priv = GET_PRIV (window);
1809
 
 
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.
1817
 
                 */
1818
 
                EmpathyContact *contact;
1819
 
 
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
1825
 
                 * file transfer.
1826
 
                 */
1827
 
                if ((contact == NULL) || !empathy_contact_is_online (contact)) {
1828
 
                        gdk_drag_status (context, 0, time_);
1829
 
                        return FALSE;
1830
 
                }
1831
 
                if (!(empathy_contact_get_capabilities (contact)
1832
 
                           & EMPATHY_CAPABILITIES_FT)) {
1833
 
                        gdk_drag_status (context, 0, time_);
1834
 
                        return FALSE;
1835
 
                }
1836
 
                gdk_drag_status (context, GDK_ACTION_COPY, time_);
1837
 
                return TRUE;
1838
 
        }
1839
 
 
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?
1846
 
                 */
1847
 
                gdk_drag_status (context, GDK_ACTION_COPY, time_);
1848
 
                return TRUE;
1849
 
        }
1850
 
 
1851
 
        return FALSE;
1852
 
}
1853
 
 
1854
 
static void
1855
 
chat_window_drag_data_received (GtkWidget        *widget,
1856
 
                                GdkDragContext   *context,
1857
 
                                int               x,
1858
 
                                int               y,
1859
 
                                GtkSelectionData *selection,
1860
 
                                guint             info,
1861
 
                                guint             time_,
1862
 
                                EmpathyChatWindow *window)
1863
 
{
1864
 
        if (info == DND_DRAG_TYPE_CONTACT_ID) {
1865
 
                EmpathyChat           *chat = NULL;
1866
 
                EmpathyChatWindow     *old_window;
1867
 
                TpAccount             *account = NULL;
1868
 
                TpAccountManager      *account_manager;
1869
 
                const gchar           *id;
1870
 
                gchar                **strv;
1871
 
                const gchar           *account_id;
1872
 
                const gchar           *contact_id;
1873
 
 
1874
 
                id = (const gchar*) gtk_selection_data_get_data (selection);
1875
 
 
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 ();
1879
 
 
1880
 
                DEBUG ("DND contact from roster with id:'%s'", id);
1881
 
 
1882
 
                strv = g_strsplit (id, ":", 2);
1883
 
                if (g_strv_length (strv) == 2) {
1884
 
                        account_id = strv[0];
1885
 
                        contact_id = strv[1];
1886
 
                        account =
1887
 
                                tp_account_manager_ensure_account (account_manager, account_id);
1888
 
                        if (account != NULL)
1889
 
                                chat = empathy_chat_window_find_chat (account, contact_id);
1890
 
                }
1891
 
 
1892
 
                if (account == NULL) {
1893
 
                        g_strfreev (strv);
1894
 
                        gtk_drag_finish (context, FALSE, FALSE, time_);
1895
 
                        return;
1896
 
                }
1897
 
 
1898
 
                if (!chat) {
1899
 
                        empathy_dispatcher_chat_with_contact_id (
1900
 
                                account, contact_id, gtk_get_current_event_time ());
1901
 
 
1902
 
                        g_strfreev (strv);
1903
 
                        return;
1904
 
                }
1905
 
                g_object_unref (account_manager);
1906
 
                g_strfreev (strv);
1907
 
 
1908
 
                old_window = chat_window_find_chat (chat);
1909
 
                if (old_window) {
1910
 
                        if (old_window == window) {
1911
 
                                gtk_drag_finish (context, TRUE, FALSE, time_);
1912
 
                                return;
1913
 
                        }
1914
 
 
1915
 
                        empathy_chat_window_move_chat (old_window, window, chat);
1916
 
                } else {
1917
 
                        empathy_chat_window_add_chat (window, chat);
1918
 
                }
1919
 
 
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);
1923
 
 
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 ().
1928
 
                 */
1929
 
                gtk_drag_finish (context, TRUE, FALSE, time_);
1930
 
        }
1931
 
        else if (info == DND_DRAG_TYPE_URI_LIST) {
1932
 
                EmpathyChatWindowPriv *priv;
1933
 
                EmpathyContact *contact;
1934
 
                const gchar *data;
1935
 
 
1936
 
                priv = GET_PRIV (window);
1937
 
                contact = empathy_chat_get_remote_contact (priv->current_chat);
1938
 
 
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.
1941
 
                 */
1942
 
                if (contact == NULL) {
1943
 
                        gtk_drag_finish (context, TRUE, FALSE, time_);
1944
 
                        return;
1945
 
                }
1946
 
 
1947
 
                data = (const gchar *) gtk_selection_data_get_data (selection);
1948
 
                empathy_send_file_from_uri_list (contact, data);
1949
 
 
1950
 
                gtk_drag_finish (context, TRUE, FALSE, time_);
1951
 
        }
1952
 
        else if (info == DND_DRAG_TYPE_TAB) {
1953
 
                EmpathyChat        **chat;
1954
 
                EmpathyChatWindow   *old_window = NULL;
1955
 
 
1956
 
                DEBUG ("DND tab");
1957
 
 
1958
 
                chat = (void *) gtk_selection_data_get_data (selection);
1959
 
                old_window = chat_window_find_chat (*chat);
1960
 
 
1961
 
                if (old_window) {
1962
 
                        EmpathyChatWindowPriv *priv;
1963
 
 
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");
1968
 
                }
1969
 
        } else {
1970
 
                DEBUG ("DND from unknown source");
1971
 
                gtk_drag_finish (context, FALSE, FALSE, time_);
1972
 
        }
1973
 
}
1974
 
 
1975
 
static void
1976
 
chat_window_chat_manager_chats_changed_cb (EmpathyChatManager *chat_manager,
1977
 
                                           guint num_chats_in_manager,
1978
 
                                           EmpathyChatWindow *window)
1979
 
{
1980
 
        EmpathyChatWindowPriv *priv = GET_PRIV (window);
1981
 
 
1982
 
        gtk_action_set_sensitive (priv->menu_tabs_undo_close_tab,
1983
 
                                  num_chats_in_manager > 0);
1984
 
}
1985
 
 
1986
 
static void
1987
 
chat_window_finalize (GObject *object)
1988
 
{
1989
 
        EmpathyChatWindow     *window;
1990
 
        EmpathyChatWindowPriv *priv;
1991
 
 
1992
 
        window = EMPATHY_CHAT_WINDOW (object);
1993
 
        priv = GET_PRIV (window);
1994
 
 
1995
 
        DEBUG ("Finalized: %p", object);
1996
 
 
1997
 
        g_object_unref (priv->ui_manager);
1998
 
        g_object_unref (priv->chatroom_manager);
1999
 
        g_object_unref (priv->notify_mgr);
2000
 
 
2001
 
        if (priv->notification != NULL) {
2002
 
                notify_notification_close (priv->notification, NULL);
2003
 
                priv->notification = NULL;
2004
 
        }
2005
 
 
2006
 
        if (priv->contact_targets) {
2007
 
                gtk_target_list_unref (priv->contact_targets);
2008
 
        }
2009
 
        if (priv->file_targets) {
2010
 
                gtk_target_list_unref (priv->file_targets);
2011
 
        }
2012
 
 
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;
2018
 
        }
2019
 
 
2020
 
        if (priv->launcher) {
2021
 
                g_object_unref (priv->launcher);
2022
 
                priv->launcher = NULL;
2023
 
        }
2024
 
 
2025
 
        chat_windows = g_list_remove (chat_windows, window);
2026
 
        gtk_widget_destroy (priv->dialog);
2027
 
 
2028
 
        G_OBJECT_CLASS (empathy_chat_window_parent_class)->finalize (object);
2029
 
}
2030
 
 
2031
 
static void
2032
 
empathy_chat_window_class_init (EmpathyChatWindowClass *klass)
2033
 
{
2034
 
        GObjectClass *object_class = G_OBJECT_CLASS (klass);
2035
 
 
2036
 
        object_class->finalize = chat_window_finalize;
2037
 
 
2038
 
        g_type_class_add_private (object_class, sizeof (EmpathyChatWindowPriv));
2039
 
 
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"
2043
 
                "{\n"
2044
 
                "  GtkWidget::focus-padding = 0\n"
2045
 
                "  xthickness = 0\n"
2046
 
                "  ythickness = 0\n"
2047
 
                "}\n"
2048
 
                "widget \"*.empathy-close-button\" style \"empathy-close-button-style\"");
2049
 
 
2050
 
        gtk_notebook_set_window_creation_hook (chat_window_detach_hook, NULL, NULL);
2051
 
}
2052
 
 
2053
 
static void
2054
 
empathy_chat_window_init (EmpathyChatWindow *window)
2055
 
{
2056
 
        GtkBuilder            *gui;
2057
 
        GtkAccelGroup         *accel_group;
2058
 
        GClosure              *closure;
2059
 
        GtkWidget             *menu;
2060
 
        GtkWidget             *submenu;
2061
 
        guint                  i;
2062
 
        GtkWidget             *chat_vbox;
2063
 
        gchar                 *filename;
2064
 
        EmpathySmileyManager  *smiley_manager;
2065
 
        EmpathyChatWindowPriv *priv = G_TYPE_INSTANCE_GET_PRIVATE (window,
2066
 
                EMPATHY_TYPE_CHAT_WINDOW, EmpathyChatWindowPriv);
2067
 
 
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,
2088
 
                                       NULL);
2089
 
        g_free (filename);
2090
 
 
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,
2112
 
                              NULL);
2113
 
 
2114
 
        g_object_ref (priv->ui_manager);
2115
 
        g_object_unref (gui);
2116
 
 
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);
2122
 
#endif
2123
 
#ifdef HAVE_UNITY
2124
 
        priv->launcher = unity_launcher_entry_get_for_desktop_id ("empathy.desktop");
2125
 
#endif
2126
 
 
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);
2133
 
 
2134
 
        /* Set up accels */
2135
 
        accel_group = gtk_accel_group_new ();
2136
 
        gtk_window_add_accel_group (GTK_WINDOW (priv->dialog), accel_group);
2137
 
 
2138
 
        for (i = 0; i < G_N_ELEMENTS (tab_accel_keys); i++) {
2139
 
                closure =  g_cclosure_new (G_CALLBACK (chat_window_accel_cb),
2140
 
                                           window,
2141
 
                                           NULL);
2142
 
                gtk_accel_group_connect (accel_group,
2143
 
                                         tab_accel_keys[i],
2144
 
                                         GDK_MOD1_MASK,
2145
 
                                         0,
2146
 
                                         closure);
2147
 
        }
2148
 
 
2149
 
        g_object_unref (accel_group);
2150
 
 
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));
2156
 
 
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,
2161
 
                                           window);
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);
2166
 
 
2167
 
        /* Set up signals we can't do with ui file since we may need to
2168
 
         * block/unblock them at some later stage.
2169
 
         */
2170
 
 
2171
 
        g_signal_connect (priv->dialog,
2172
 
                          "delete_event",
2173
 
                          G_CALLBACK (chat_window_delete_event_cb),
2174
 
                          window);
2175
 
        g_signal_connect (priv->dialog,
2176
 
                          "focus_in_event",
2177
 
                          G_CALLBACK (chat_window_focus_in_event_cb),
2178
 
                          window);
2179
 
        g_signal_connect_after (priv->notebook,
2180
 
                                "switch_page",
2181
 
                                G_CALLBACK (chat_window_page_switched_cb),
2182
 
                                window);
2183
 
        g_signal_connect (priv->notebook,
2184
 
                          "page_added",
2185
 
                          G_CALLBACK (chat_window_page_added_cb),
2186
 
                          window);
2187
 
        g_signal_connect (priv->notebook,
2188
 
                          "page_removed",
2189
 
                          G_CALLBACK (chat_window_page_removed_cb),
2190
 
                          window);
2191
 
 
2192
 
        /* Set up drag and drop */
2193
 
        gtk_drag_dest_set (GTK_WIDGET (priv->notebook),
2194
 
                           GTK_DEST_DEFAULT_HIGHLIGHT,
2195
 
                           drag_types_dest,
2196
 
                           G_N_ELEMENTS (drag_types_dest),
2197
 
                           GDK_ACTION_MOVE | GDK_ACTION_COPY);
2198
 
 
2199
 
        /* connect_after to allow GtkNotebook's built-in tab switching */
2200
 
        g_signal_connect_after (priv->notebook,
2201
 
                                "drag-motion",
2202
 
                                G_CALLBACK (chat_window_drag_motion),
2203
 
                                window);
2204
 
        g_signal_connect (priv->notebook,
2205
 
                          "drag-data-received",
2206
 
                          G_CALLBACK (chat_window_drag_data_received),
2207
 
                          window);
2208
 
        g_signal_connect (priv->notebook,
2209
 
                          "drag-drop",
2210
 
                          G_CALLBACK (chat_window_drag_drop),
2211
 
                          window);
2212
 
 
2213
 
        chat_windows = g_list_prepend (chat_windows, window);
2214
 
 
2215
 
        /* Set up private details */
2216
 
        priv->chats = NULL;
2217
 
        priv->chats_new_msg = NULL;
2218
 
        priv->chats_composing = NULL;
2219
 
        priv->current_chat = NULL;
2220
 
        priv->notification = NULL;
2221
 
 
2222
 
        priv->notify_mgr = empathy_notify_manager_dup_singleton ();
2223
 
 
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),
2228
 
                                  window);
2229
 
 
2230
 
        chat_window_chat_manager_chats_changed_cb (priv->chat_manager,
2231
 
                                                   empathy_chat_manager_get_num_chats (priv->chat_manager),
2232
 
                                                   window);
2233
 
 
2234
 
        /* Add launchpad hooks */
2235
 
        launchpad_integration_add_ui (priv->ui_manager, "/chats_menubar/menu_help/LaunchpadItems");
2236
 
}
2237
 
 
2238
 
EmpathyChatWindow *
2239
 
empathy_chat_window_new (void)
2240
 
{
2241
 
        return EMPATHY_CHAT_WINDOW (g_object_new (EMPATHY_TYPE_CHAT_WINDOW, NULL));
2242
 
}
2243
 
 
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
2246
 
 * be added.
2247
 
 */
2248
 
EmpathyChatWindow *
2249
 
empathy_chat_window_get_default (gboolean room)
2250
 
{
2251
 
        GSettings *gsettings = g_settings_new (EMPATHY_PREFS_UI_SCHEMA);
2252
 
        GList    *l;
2253
 
        gboolean  separate_windows = TRUE;
2254
 
 
2255
 
        separate_windows = g_settings_get_boolean (gsettings,
2256
 
                        EMPATHY_PREFS_UI_SEPARATE_CHAT_WINDOWS);
2257
 
 
2258
 
        g_object_unref (gsettings);
2259
 
 
2260
 
        if (separate_windows) {
2261
 
                /* Always create a new window */
2262
 
                return NULL;
2263
 
        }
2264
 
 
2265
 
        for (l = chat_windows; l; l = l->next) {
2266
 
                EmpathyChatWindowPriv *priv;
2267
 
                EmpathyChatWindow *chat_window;
2268
 
                GtkWidget         *dialog;
2269
 
 
2270
 
                chat_window = l->data;
2271
 
                priv = GET_PRIV (chat_window);
2272
 
 
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);
2277
 
 
2278
 
                        /* Skip the window if there aren't any rooms in it */
2279
 
                        if (room && nb_rooms == 0)
2280
 
                                continue;
2281
 
 
2282
 
                        /* Skip the window if there aren't any 1-1 chats in it */
2283
 
                        if (!room && nb_private == 0)
2284
 
                                continue;
2285
 
 
2286
 
                        /* Found a visible window on this desktop */
2287
 
                        return chat_window;
2288
 
                }
2289
 
        }
2290
 
 
2291
 
        return NULL;
2292
 
}
2293
 
 
2294
 
GtkWidget *
2295
 
empathy_chat_window_get_dialog (EmpathyChatWindow *window)
2296
 
{
2297
 
        EmpathyChatWindowPriv *priv;
2298
 
 
2299
 
        g_return_val_if_fail (window != NULL, NULL);
2300
 
 
2301
 
        priv = GET_PRIV (window);
2302
 
 
2303
 
        return priv->dialog;
2304
 
}
2305
 
 
2306
 
void
2307
 
empathy_chat_window_add_chat (EmpathyChatWindow *window,
2308
 
                              EmpathyChat       *chat)
2309
 
{
2310
 
        EmpathyChatWindowPriv *priv;
2311
 
        GtkWidget             *label;
2312
 
        GtkWidget             *popup_label;
2313
 
        GtkWidget             *child;
2314
 
        GValue                value = { 0, };
2315
 
 
2316
 
        g_return_if_fail (window != NULL);
2317
 
        g_return_if_fail (EMPATHY_IS_CHAT (chat));
2318
 
 
2319
 
        priv = GET_PRIV (window);
2320
 
 
2321
 
        /* Reference the chat object */
2322
 
        g_object_ref (chat);
2323
 
 
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;
2329
 
 
2330
 
                separate_windows = g_settings_get_boolean (gsettings,
2331
 
                                EMPATHY_PREFS_UI_SEPARATE_CHAT_WINDOWS);
2332
 
 
2333
 
                g_object_unref (gsettings);
2334
 
 
2335
 
                if (separate_windows) {
2336
 
                        name = empathy_chat_get_id (chat);
2337
 
                }
2338
 
                else if (empathy_chat_is_room (chat)) {
2339
 
                        name = "room-window";
2340
 
                }
2341
 
 
2342
 
                empathy_geometry_bind (GTK_WINDOW (priv->dialog), name);
2343
 
        }
2344
 
 
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);
2349
 
 
2350
 
        g_signal_connect (chat, "notify::name",
2351
 
                          G_CALLBACK (chat_window_chat_notify_cb),
2352
 
                          NULL);
2353
 
        g_signal_connect (chat, "notify::subject",
2354
 
                          G_CALLBACK (chat_window_chat_notify_cb),
2355
 
                          NULL);
2356
 
        g_signal_connect (chat, "notify::remote-contact",
2357
 
                          G_CALLBACK (chat_window_chat_notify_cb),
2358
 
                          NULL);
2359
 
        chat_window_chat_notify_cb (chat);
2360
 
 
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);
2371
 
 
2372
 
        DEBUG ("Chat added (%d references)", G_OBJECT (chat)->ref_count);
2373
 
}
2374
 
 
2375
 
void
2376
 
empathy_chat_window_remove_chat (EmpathyChatWindow *window,
2377
 
                                 EmpathyChat       *chat)
2378
 
{
2379
 
        EmpathyChatWindowPriv *priv;
2380
 
        gint                   position;
2381
 
        EmpathyContact        *remote_contact;
2382
 
        EmpathyChatManager    *chat_manager;
2383
 
 
2384
 
        g_return_if_fail (window != NULL);
2385
 
        g_return_if_fail (EMPATHY_IS_CHAT (chat));
2386
 
 
2387
 
        priv = GET_PRIV (window);
2388
 
 
2389
 
        g_signal_handlers_disconnect_by_func (chat,
2390
 
                                              chat_window_chat_notify_cb,
2391
 
                                              NULL);
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,
2397
 
                                                      chat);
2398
 
        }
2399
 
 
2400
 
        chat_manager = empathy_chat_manager_dup_singleton ();
2401
 
        empathy_chat_manager_closed_chat (chat_manager, chat);
2402
 
        g_object_unref (chat_manager);
2403
 
 
2404
 
        position = gtk_notebook_page_num (GTK_NOTEBOOK (priv->notebook),
2405
 
                                          GTK_WIDGET (chat));
2406
 
        gtk_notebook_remove_page (GTK_NOTEBOOK (priv->notebook), position);
2407
 
 
2408
 
        DEBUG ("Chat removed (%d references)", G_OBJECT (chat)->ref_count - 1);
2409
 
 
2410
 
        g_object_unref (chat);
2411
 
}
2412
 
 
2413
 
void
2414
 
empathy_chat_window_move_chat (EmpathyChatWindow *old_window,
2415
 
                               EmpathyChatWindow *new_window,
2416
 
                               EmpathyChat       *chat)
2417
 
{
2418
 
        GtkWidget *widget;
2419
 
 
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));
2423
 
 
2424
 
        widget = GTK_WIDGET (chat);
2425
 
 
2426
 
        DEBUG ("Chat moving with widget:%p (%d references)", widget,
2427
 
                G_OBJECT (widget)->ref_count);
2428
 
 
2429
 
        /* We reference here to make sure we don't loose the widget
2430
 
         * and the EmpathyChat object during the move.
2431
 
         */
2432
 
        g_object_ref (chat);
2433
 
        g_object_ref (widget);
2434
 
 
2435
 
        empathy_chat_window_remove_chat (old_window, chat);
2436
 
        empathy_chat_window_add_chat (new_window, chat);
2437
 
 
2438
 
        g_object_unref (widget);
2439
 
        g_object_unref (chat);
2440
 
}
2441
 
 
2442
 
void
2443
 
empathy_chat_window_switch_to_chat (EmpathyChatWindow *window,
2444
 
                                    EmpathyChat       *chat)
2445
 
{
2446
 
        EmpathyChatWindowPriv *priv;
2447
 
        gint                  page_num;
2448
 
 
2449
 
        g_return_if_fail (window != NULL);
2450
 
        g_return_if_fail (EMPATHY_IS_CHAT (chat));
2451
 
 
2452
 
        priv = GET_PRIV (window);
2453
 
 
2454
 
        page_num = gtk_notebook_page_num (GTK_NOTEBOOK (priv->notebook),
2455
 
                                          GTK_WIDGET (chat));
2456
 
        gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->notebook),
2457
 
                                       page_num);
2458
 
}
2459
 
 
2460
 
gboolean
2461
 
empathy_chat_window_has_focus (EmpathyChatWindow *window)
2462
 
{
2463
 
        EmpathyChatWindowPriv *priv;
2464
 
        gboolean              has_focus;
2465
 
 
2466
 
        g_return_val_if_fail (EMPATHY_IS_CHAT_WINDOW (window), FALSE);
2467
 
 
2468
 
        priv = GET_PRIV (window);
2469
 
 
2470
 
        g_object_get (priv->dialog, "has-toplevel-focus", &has_focus, NULL);
2471
 
 
2472
 
        return has_focus;
2473
 
}
2474
 
 
2475
 
EmpathyChat *
2476
 
empathy_chat_window_find_chat (TpAccount   *account,
2477
 
                               const gchar *id)
2478
 
{
2479
 
        GList *l;
2480
 
 
2481
 
        g_return_val_if_fail (!EMP_STR_EMPTY (id), NULL);
2482
 
 
2483
 
        for (l = chat_windows; l; l = l->next) {
2484
 
                EmpathyChatWindowPriv *priv;
2485
 
                EmpathyChatWindow     *window;
2486
 
                GList                *ll;
2487
 
 
2488
 
                window = l->data;
2489
 
                priv = GET_PRIV (window);
2490
 
 
2491
 
                for (ll = priv->chats; ll; ll = ll->next) {
2492
 
                        EmpathyChat *chat;
2493
 
 
2494
 
                        chat = ll->data;
2495
 
 
2496
 
                        if (account == empathy_chat_get_account (chat) &&
2497
 
                            !tp_strdiff (id, empathy_chat_get_id (chat))) {
2498
 
                                return chat;
2499
 
                        }
2500
 
                }
2501
 
        }
2502
 
 
2503
 
        return NULL;
2504
 
}
2505
 
 
2506
 
void
2507
 
empathy_chat_window_present_chat (EmpathyChat *chat,
2508
 
                                  gint64 timestamp)
2509
 
{
2510
 
        EmpathyChatWindow     *window;
2511
 
        EmpathyChatWindowPriv *priv;
2512
 
        guint32 x_timestamp;
2513
 
 
2514
 
        g_return_if_fail (EMPATHY_IS_CHAT (chat));
2515
 
 
2516
 
        window = chat_window_find_chat (chat);
2517
 
 
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));
2521
 
                if (!window) {
2522
 
                        window = empathy_chat_window_new ();
2523
 
                        gtk_widget_show_all (GET_PRIV (window)->dialog);
2524
 
                }
2525
 
 
2526
 
                empathy_chat_window_add_chat (window, chat);
2527
 
        }
2528
 
 
2529
 
        /* Don't force the window to show itself when it wasn't
2530
 
         * an action by the user
2531
 
         */
2532
 
        if (!tp_user_action_time_should_present (timestamp, &x_timestamp))
2533
 
                return;
2534
 
 
2535
 
        priv = GET_PRIV (window);
2536
 
 
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
2540
 
                * presentation */
2541
 
 
2542
 
                if (priv->x_user_action_time != 0
2543
 
                        && X_EARLIER_OR_EQL (x_timestamp, priv->x_user_action_time))
2544
 
                        return;
2545
 
 
2546
 
                priv->x_user_action_time = x_timestamp;
2547
 
        }
2548
 
 
2549
 
        empathy_chat_window_switch_to_chat (window, chat);
2550
 
        empathy_window_present_with_time (GTK_WINDOW (priv->dialog),
2551
 
          x_timestamp);
2552
 
 
2553
 
        gtk_widget_grab_focus (chat->input_text_view);
2554
 
}
2555
 
 
2556
 
void
2557
 
empathy_chat_window_get_nb_chats (EmpathyChatWindow *self,
2558
 
                               guint *nb_rooms,
2559
 
                               guint *nb_private)
2560
 
{
2561
 
        EmpathyChatWindowPriv *priv = GET_PRIV (self);
2562
 
        GList *l;
2563
 
        guint _nb_rooms = 0, _nb_private = 0;
2564
 
 
2565
 
        for (l = priv->chats; l != NULL; l = g_list_next (l)) {
2566
 
                if (empathy_chat_is_room (EMPATHY_CHAT (l->data)))
2567
 
                        _nb_rooms++;
2568
 
                else
2569
 
                        _nb_private++;
2570
 
        }
2571
 
 
2572
 
        if (nb_rooms != NULL)
2573
 
                *nb_rooms = _nb_rooms;
2574
 
        if (nb_private != NULL)
2575
 
                *nb_private = _nb_private;
2576
 
}