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

« back to all changes in this revision

Viewing changes to .pc/31_really_raise_window.patch/libempathy-gtk/empathy-ui-utils.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) 2002-2007 Imendio AB
4
 
 * Copyright (C) 2007-2010 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
 
 *          Xavier Claessens <xclaesse@gmail.com>
25
 
 *          Jonny Lamb <jonny.lamb@collabora.co.uk>
26
 
 *          Travis Reitter <travis.reitter@collabora.co.uk>
27
 
 *
28
 
 *          Part of this file is copied from GtkSourceView (gtksourceiter.c):
29
 
 *          Paolo Maggi
30
 
 *          Jeroen Zwartepoorte
31
 
 */
32
 
 
33
 
#include <config.h>
34
 
 
35
 
#include <string.h>
36
 
#include <X11/Xatom.h>
37
 
#include <gdk/gdkx.h>
38
 
#include <glib/gi18n-lib.h>
39
 
#include <gtk/gtk.h>
40
 
#include <gio/gio.h>
41
 
 
42
 
#include <telepathy-glib/util.h>
43
 
#include <folks/folks.h>
44
 
 
45
 
#include "empathy-ui-utils.h"
46
 
#include "empathy-images.h"
47
 
#include "empathy-smiley-manager.h"
48
 
 
49
 
#define DEBUG_FLAG EMPATHY_DEBUG_OTHER
50
 
#include <libempathy/empathy-debug.h>
51
 
#include <libempathy/empathy-utils.h>
52
 
#include <libempathy/empathy-dispatcher.h>
53
 
#include <libempathy/empathy-idle.h>
54
 
#include <libempathy/empathy-ft-factory.h>
55
 
 
56
 
void
57
 
empathy_gtk_init (void)
58
 
{
59
 
        static gboolean initialized = FALSE;
60
 
 
61
 
        if (initialized)
62
 
                return;
63
 
 
64
 
        empathy_init ();
65
 
        gtk_icon_theme_append_search_path (gtk_icon_theme_get_default (),
66
 
                                           PKGDATADIR G_DIR_SEPARATOR_S "icons");
67
 
 
68
 
        initialized = TRUE;
69
 
}
70
 
 
71
 
static GtkBuilder *
72
 
builder_get_file_valist (const gchar *filename,
73
 
                         const gchar *first_object,
74
 
                         va_list      args)
75
 
{
76
 
        GtkBuilder  *gui;
77
 
        const gchar *name;
78
 
        GObject    **object_ptr;
79
 
        GError      *error = NULL;
80
 
 
81
 
        DEBUG ("Loading file %s", filename);
82
 
 
83
 
        gui = gtk_builder_new ();
84
 
        gtk_builder_set_translation_domain (gui, GETTEXT_PACKAGE);
85
 
        if (!gtk_builder_add_from_file (gui, filename, &error)) {
86
 
                g_critical ("GtkBuilder Error (%s): %s",
87
 
                                filename, error->message);
88
 
                g_clear_error (&error);
89
 
                g_object_unref (gui);
90
 
 
91
 
                /* we need to iterate and set all of the pointers to NULL */
92
 
                for (name = first_object; name;
93
 
                     name = va_arg (args, const gchar *)) {
94
 
                        object_ptr = va_arg (args, GObject**);
95
 
 
96
 
                        *object_ptr = NULL;
97
 
                }
98
 
 
99
 
                return NULL;
100
 
        }
101
 
 
102
 
        for (name = first_object; name; name = va_arg (args, const gchar *)) {
103
 
                object_ptr = va_arg (args, GObject**);
104
 
 
105
 
                *object_ptr = gtk_builder_get_object (gui, name);
106
 
 
107
 
                if (!*object_ptr) {
108
 
                        g_warning ("File is missing object '%s'.", name);
109
 
                        continue;
110
 
                }
111
 
        }
112
 
 
113
 
        return gui;
114
 
}
115
 
 
116
 
GtkBuilder *
117
 
empathy_builder_get_file (const gchar *filename,
118
 
                          const gchar *first_object,
119
 
                          ...)
120
 
{
121
 
        GtkBuilder *gui;
122
 
        va_list     args;
123
 
 
124
 
        va_start (args, first_object);
125
 
        gui = builder_get_file_valist (filename, first_object, args);
126
 
        va_end (args);
127
 
 
128
 
        return gui;
129
 
}
130
 
 
131
 
void
132
 
empathy_builder_connect (GtkBuilder  *gui,
133
 
                         gpointer     user_data,
134
 
                         const gchar *first_object,
135
 
                         ...)
136
 
{
137
 
        va_list      args;
138
 
        const gchar *name;
139
 
        const gchar *sig;
140
 
        GObject     *object;
141
 
        GCallback    callback;
142
 
 
143
 
        va_start (args, first_object);
144
 
        for (name = first_object; name; name = va_arg (args, const gchar *)) {
145
 
                sig = va_arg (args, const gchar *);
146
 
                callback = va_arg (args, GCallback);
147
 
 
148
 
                object = gtk_builder_get_object (gui, name);
149
 
                if (!object) {
150
 
                        g_warning ("File is missing object '%s'.", name);
151
 
                        continue;
152
 
                }
153
 
 
154
 
                g_signal_connect (object, sig, callback, user_data);
155
 
        }
156
 
 
157
 
        va_end (args);
158
 
}
159
 
 
160
 
GtkWidget *
161
 
empathy_builder_unref_and_keep_widget (GtkBuilder *gui,
162
 
                                       GtkWidget  *widget)
163
 
{
164
 
        /* On construction gui sinks the initial reference to widget. When gui
165
 
         * is finalized it will drop its ref to widget. We take our own ref to
166
 
         * prevent widget being finalised. The widget is forced to have a
167
 
         * floating reference, like when it was initially unowned so that it can
168
 
         * be used like any other GtkWidget. */
169
 
 
170
 
        g_object_ref (widget);
171
 
        g_object_force_floating (G_OBJECT (widget));
172
 
        g_object_unref (gui);
173
 
 
174
 
        return widget;
175
 
}
176
 
 
177
 
const gchar *
178
 
empathy_icon_name_for_presence (TpConnectionPresenceType presence)
179
 
{
180
 
        switch (presence) {
181
 
        case TP_CONNECTION_PRESENCE_TYPE_AVAILABLE:
182
 
                return EMPATHY_IMAGE_AVAILABLE;
183
 
        case TP_CONNECTION_PRESENCE_TYPE_BUSY:
184
 
                return EMPATHY_IMAGE_BUSY;
185
 
        case TP_CONNECTION_PRESENCE_TYPE_AWAY:
186
 
                return EMPATHY_IMAGE_AWAY;
187
 
        case TP_CONNECTION_PRESENCE_TYPE_EXTENDED_AWAY:
188
 
                if (gtk_icon_theme_has_icon (gtk_icon_theme_get_default (),
189
 
                                             EMPATHY_IMAGE_EXT_AWAY))
190
 
                        return EMPATHY_IMAGE_EXT_AWAY;
191
 
 
192
 
                /* The 'extended-away' icon is not an official one so we fallback to idle if
193
 
                 * it's not implemented */
194
 
                return EMPATHY_IMAGE_IDLE;
195
 
        case TP_CONNECTION_PRESENCE_TYPE_HIDDEN:
196
 
                if (gtk_icon_theme_has_icon (gtk_icon_theme_get_default (),
197
 
                                             EMPATHY_IMAGE_HIDDEN))
198
 
                        return EMPATHY_IMAGE_HIDDEN;
199
 
 
200
 
                /* The 'hidden' icon is not an official one so we fallback to offline if
201
 
                 * it's not implemented */
202
 
                return EMPATHY_IMAGE_OFFLINE;
203
 
        case TP_CONNECTION_PRESENCE_TYPE_OFFLINE:
204
 
        case TP_CONNECTION_PRESENCE_TYPE_ERROR:
205
 
                return EMPATHY_IMAGE_OFFLINE;
206
 
        case TP_CONNECTION_PRESENCE_TYPE_UNKNOWN:
207
 
                return EMPATHY_IMAGE_PENDING;
208
 
        case TP_CONNECTION_PRESENCE_TYPE_UNSET:
209
 
        default:
210
 
                return NULL;
211
 
        }
212
 
 
213
 
        return NULL;
214
 
}
215
 
 
216
 
const gchar *
217
 
empathy_icon_name_for_contact (EmpathyContact *contact)
218
 
{
219
 
        TpConnectionPresenceType presence;
220
 
 
221
 
        g_return_val_if_fail (EMPATHY_IS_CONTACT (contact),
222
 
                              EMPATHY_IMAGE_OFFLINE);
223
 
 
224
 
        presence = empathy_contact_get_presence (contact);
225
 
        return empathy_icon_name_for_presence (presence);
226
 
}
227
 
 
228
 
const gchar *
229
 
empathy_icon_name_for_individual (FolksIndividual *individual)
230
 
{
231
 
        FolksPresenceType folks_presence;
232
 
        TpConnectionPresenceType presence;
233
 
 
234
 
        folks_presence =
235
 
            folks_presence_details_get_presence_type (
236
 
                FOLKS_PRESENCE_DETAILS (individual));
237
 
        presence = empathy_folks_presence_type_to_tp (folks_presence);
238
 
 
239
 
        return empathy_icon_name_for_presence (presence);
240
 
}
241
 
 
242
 
const gchar *
243
 
empathy_protocol_name_for_contact (EmpathyContact   *contact)
244
 
{
245
 
        TpAccount     *account;
246
 
 
247
 
        g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), NULL);
248
 
 
249
 
        account = empathy_contact_get_account (contact);
250
 
        if (account == NULL) {
251
 
                return NULL;
252
 
        }
253
 
 
254
 
        return tp_account_get_icon_name (account);
255
 
}
256
 
 
257
 
GdkPixbuf *
258
 
empathy_pixbuf_from_data (gchar *data,
259
 
                          gsize  data_size)
260
 
{
261
 
        return empathy_pixbuf_from_data_and_mime (data, data_size, NULL);
262
 
}
263
 
 
264
 
GdkPixbuf *
265
 
empathy_pixbuf_from_data_and_mime (gchar  *data,
266
 
                                   gsize   data_size,
267
 
                                   gchar **mime_type)
268
 
{
269
 
        GdkPixbufLoader *loader;
270
 
        GdkPixbufFormat *format;
271
 
        GdkPixbuf       *pixbuf = NULL;
272
 
        gchar          **mime_types;
273
 
        GError          *error = NULL;
274
 
 
275
 
        if (!data) {
276
 
                return NULL;
277
 
        }
278
 
 
279
 
        loader = gdk_pixbuf_loader_new ();
280
 
        if (!gdk_pixbuf_loader_write (loader, (guchar *) data, data_size, &error)) {
281
 
                DEBUG ("Failed to write to pixbuf loader: %s",
282
 
                        error ? error->message : "No error given");
283
 
                goto out;
284
 
        }
285
 
        if (!gdk_pixbuf_loader_close (loader, &error)) {
286
 
                DEBUG ("Failed to close pixbuf loader: %s",
287
 
                        error ? error->message : "No error given");
288
 
                goto out;
289
 
        }
290
 
 
291
 
        pixbuf = gdk_pixbuf_loader_get_pixbuf (loader);
292
 
        if (pixbuf) {
293
 
                g_object_ref (pixbuf);
294
 
 
295
 
                if (mime_type != NULL) {
296
 
                        format = gdk_pixbuf_loader_get_format (loader);
297
 
                        mime_types = gdk_pixbuf_format_get_mime_types (format);
298
 
 
299
 
                        *mime_type = g_strdup (*mime_types);
300
 
                        if (mime_types[1] != NULL) {
301
 
                                DEBUG ("Loader supports more than one mime "
302
 
                                        "type! Picking the first one, %s",
303
 
                                        *mime_type);
304
 
                        }
305
 
                        g_strfreev (mime_types);
306
 
                }
307
 
        }
308
 
 
309
 
out:
310
 
        g_clear_error (&error);
311
 
        g_object_unref (loader);
312
 
 
313
 
        return pixbuf;
314
 
}
315
 
 
316
 
struct SizeData {
317
 
        gint     width;
318
 
        gint     height;
319
 
        gboolean preserve_aspect_ratio;
320
 
};
321
 
 
322
 
static void
323
 
pixbuf_from_avatar_size_prepared_cb (GdkPixbufLoader *loader,
324
 
                                     int              width,
325
 
                                     int              height,
326
 
                                     struct SizeData *data)
327
 
{
328
 
        g_return_if_fail (width > 0 && height > 0);
329
 
 
330
 
        if (data->preserve_aspect_ratio && (data->width > 0 || data->height > 0)) {
331
 
                if (width < data->width && height < data->height) {
332
 
                        width = width;
333
 
                        height = height;
334
 
                }
335
 
 
336
 
                if (data->width < 0) {
337
 
                        width = width * (double) data->height / (gdouble) height;
338
 
                        height = data->height;
339
 
                } else if (data->height < 0) {
340
 
                        height = height * (double) data->width / (double) width;
341
 
                        width = data->width;
342
 
                } else if ((double) height * (double) data->width >
343
 
                           (double) width * (double) data->height) {
344
 
                        width = 0.5 + (double) width * (double) data->height / (double) height;
345
 
                        height = data->height;
346
 
                } else {
347
 
                        height = 0.5 + (double) height * (double) data->width / (double) width;
348
 
                        width = data->width;
349
 
                }
350
 
        } else {
351
 
                if (data->width > 0) {
352
 
                        width = data->width;
353
 
                }
354
 
 
355
 
                if (data->height > 0) {
356
 
                        height = data->height;
357
 
                }
358
 
        }
359
 
 
360
 
        gdk_pixbuf_loader_set_size (loader, width, height);
361
 
}
362
 
 
363
 
static void
364
 
empathy_avatar_pixbuf_roundify (GdkPixbuf *pixbuf)
365
 
{
366
 
        gint width, height, rowstride;
367
 
        guchar *pixels;
368
 
 
369
 
        width = gdk_pixbuf_get_width (pixbuf);
370
 
        height = gdk_pixbuf_get_height (pixbuf);
371
 
        rowstride = gdk_pixbuf_get_rowstride (pixbuf);
372
 
        pixels = gdk_pixbuf_get_pixels (pixbuf);
373
 
 
374
 
        if (width < 6 || height < 6) {
375
 
                return;
376
 
        }
377
 
 
378
 
        /* Top left */
379
 
        pixels[3] = 0;
380
 
        pixels[7] = 0x80;
381
 
        pixels[11] = 0xC0;
382
 
        pixels[rowstride + 3] = 0x80;
383
 
        pixels[rowstride * 2 + 3] = 0xC0;
384
 
 
385
 
        /* Top right */
386
 
        pixels[width * 4 - 1] = 0;
387
 
        pixels[width * 4 - 5] = 0x80;
388
 
        pixels[width * 4 - 9] = 0xC0;
389
 
        pixels[rowstride + (width * 4) - 1] = 0x80;
390
 
        pixels[(2 * rowstride) + (width * 4) - 1] = 0xC0;
391
 
 
392
 
        /* Bottom left */
393
 
        pixels[(height - 1) * rowstride + 3] = 0;
394
 
        pixels[(height - 1) * rowstride + 7] = 0x80;
395
 
        pixels[(height - 1) * rowstride + 11] = 0xC0;
396
 
        pixels[(height - 2) * rowstride + 3] = 0x80;
397
 
        pixels[(height - 3) * rowstride + 3] = 0xC0;
398
 
 
399
 
        /* Bottom right */
400
 
        pixels[height * rowstride - 1] = 0;
401
 
        pixels[(height - 1) * rowstride - 1] = 0x80;
402
 
        pixels[(height - 2) * rowstride - 1] = 0xC0;
403
 
        pixels[height * rowstride - 5] = 0x80;
404
 
        pixels[height * rowstride - 9] = 0xC0;
405
 
}
406
 
 
407
 
static gboolean
408
 
empathy_gdk_pixbuf_is_opaque (GdkPixbuf *pixbuf)
409
 
{
410
 
        gint width, height, rowstride, i;
411
 
        guchar *pixels;
412
 
        guchar *row;
413
 
 
414
 
        width = gdk_pixbuf_get_width (pixbuf);
415
 
        height = gdk_pixbuf_get_height (pixbuf);
416
 
        rowstride = gdk_pixbuf_get_rowstride (pixbuf);
417
 
        pixels = gdk_pixbuf_get_pixels (pixbuf);
418
 
 
419
 
        row = pixels;
420
 
        for (i = 3; i < rowstride; i+=4) {
421
 
                if (row[i] < 0xfe) {
422
 
                        return FALSE;
423
 
                }
424
 
        }
425
 
 
426
 
        for (i = 1; i < height - 1; i++) {
427
 
                row = pixels + (i*rowstride);
428
 
                if (row[3] < 0xfe || row[rowstride-1] < 0xfe) {
429
 
                        return FALSE;
430
 
                }
431
 
        }
432
 
 
433
 
        row = pixels + ((height-1) * rowstride);
434
 
        for (i = 3; i < rowstride; i+=4) {
435
 
                if (row[i] < 0xfe) {
436
 
                        return FALSE;
437
 
                }
438
 
        }
439
 
 
440
 
        return TRUE;
441
 
}
442
 
 
443
 
static GdkPixbuf *
444
 
avatar_pixbuf_from_loader (GdkPixbufLoader *loader)
445
 
{
446
 
        GdkPixbuf *pixbuf;
447
 
 
448
 
        pixbuf = gdk_pixbuf_loader_get_pixbuf (loader);
449
 
        if (!gdk_pixbuf_get_has_alpha (pixbuf)) {
450
 
                GdkPixbuf *rounded_pixbuf;
451
 
 
452
 
                rounded_pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8,
453
 
                                                 gdk_pixbuf_get_width (pixbuf),
454
 
                                                 gdk_pixbuf_get_height (pixbuf));
455
 
                gdk_pixbuf_copy_area (pixbuf, 0, 0,
456
 
                                      gdk_pixbuf_get_width (pixbuf),
457
 
                                      gdk_pixbuf_get_height (pixbuf),
458
 
                                      rounded_pixbuf,
459
 
                                      0, 0);
460
 
                pixbuf = rounded_pixbuf;
461
 
        } else {
462
 
                g_object_ref (pixbuf);
463
 
        }
464
 
 
465
 
        if (empathy_gdk_pixbuf_is_opaque (pixbuf)) {
466
 
                empathy_avatar_pixbuf_roundify (pixbuf);
467
 
        }
468
 
 
469
 
        return pixbuf;
470
 
}
471
 
 
472
 
GdkPixbuf *
473
 
empathy_pixbuf_from_avatar_scaled (EmpathyAvatar *avatar,
474
 
                                  gint          width,
475
 
                                  gint          height)
476
 
{
477
 
        GdkPixbuf        *pixbuf;
478
 
        GdkPixbufLoader  *loader;
479
 
        struct SizeData   data;
480
 
        GError           *error = NULL;
481
 
 
482
 
        if (!avatar) {
483
 
                return NULL;
484
 
        }
485
 
 
486
 
        data.width = width;
487
 
        data.height = height;
488
 
        data.preserve_aspect_ratio = TRUE;
489
 
 
490
 
        loader = gdk_pixbuf_loader_new ();
491
 
 
492
 
        g_signal_connect (loader, "size-prepared",
493
 
                          G_CALLBACK (pixbuf_from_avatar_size_prepared_cb),
494
 
                          &data);
495
 
 
496
 
        if (!gdk_pixbuf_loader_write (loader, avatar->data, avatar->len, &error)) {
497
 
                g_warning ("Couldn't write avatar image:%p with "
498
 
                           "length:%" G_GSIZE_FORMAT " to pixbuf loader: %s",
499
 
                           avatar->data, avatar->len, error->message);
500
 
                g_error_free (error);
501
 
                return NULL;
502
 
        }
503
 
 
504
 
        gdk_pixbuf_loader_close (loader, NULL);
505
 
        pixbuf = avatar_pixbuf_from_loader (loader);
506
 
 
507
 
        g_object_unref (loader);
508
 
 
509
 
        return pixbuf;
510
 
}
511
 
 
512
 
GdkPixbuf *
513
 
empathy_pixbuf_avatar_from_contact_scaled (EmpathyContact *contact,
514
 
                                          gint           width,
515
 
                                          gint           height)
516
 
{
517
 
        EmpathyAvatar *avatar;
518
 
 
519
 
        g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), NULL);
520
 
 
521
 
        avatar = empathy_contact_get_avatar (contact);
522
 
 
523
 
        return empathy_pixbuf_from_avatar_scaled (avatar, width, height);
524
 
}
525
 
 
526
 
typedef struct {
527
 
        FolksIndividual *individual;
528
 
        GSimpleAsyncResult *result;
529
 
        guint width;
530
 
        guint height;
531
 
} PixbufAvatarFromIndividualClosure;
532
 
 
533
 
static PixbufAvatarFromIndividualClosure *
534
 
pixbuf_avatar_from_individual_closure_new (FolksIndividual    *individual,
535
 
                                           GSimpleAsyncResult *result,
536
 
                                           gint                width,
537
 
                                           gint                height)
538
 
{
539
 
        PixbufAvatarFromIndividualClosure *closure;
540
 
 
541
 
        g_return_val_if_fail (FOLKS_IS_INDIVIDUAL (individual), NULL);
542
 
        g_return_val_if_fail (G_IS_ASYNC_RESULT (result), NULL);
543
 
 
544
 
        closure = g_new0 (PixbufAvatarFromIndividualClosure, 1);
545
 
        closure->individual = g_object_ref (individual);
546
 
        closure->result = g_object_ref (result);
547
 
        closure->width = width;
548
 
        closure->height = height;
549
 
 
550
 
        return closure;
551
 
}
552
 
 
553
 
static void
554
 
pixbuf_avatar_from_individual_closure_free (
555
 
                PixbufAvatarFromIndividualClosure *closure)
556
 
{
557
 
        g_object_unref (closure->individual);
558
 
        g_object_unref (closure->result);
559
 
        g_free (closure);
560
 
}
561
 
 
562
 
static void
563
 
avatar_file_load_contents_cb (GObject      *object,
564
 
                              GAsyncResult *result,
565
 
                              gpointer      user_data)
566
 
{
567
 
        GFile *file = G_FILE (object);
568
 
        PixbufAvatarFromIndividualClosure *closure = user_data;
569
 
        char *data = NULL;
570
 
        gsize data_size;
571
 
        struct SizeData size_data;
572
 
        GError *error = NULL;
573
 
        GdkPixbufLoader *loader = NULL;
574
 
 
575
 
        if (!g_file_load_contents_finish (file, result, &data, &data_size,
576
 
                                NULL, &error)) {
577
 
                DEBUG ("failed to load avatar from file: %s",
578
 
                                error->message);
579
 
                g_simple_async_result_set_from_error (closure->result, error);
580
 
                goto out;
581
 
        }
582
 
 
583
 
        size_data.width = closure->width;
584
 
        size_data.height = closure->height;
585
 
        size_data.preserve_aspect_ratio = TRUE;
586
 
 
587
 
        loader = gdk_pixbuf_loader_new ();
588
 
 
589
 
        g_signal_connect (loader, "size-prepared",
590
 
                          G_CALLBACK (pixbuf_from_avatar_size_prepared_cb),
591
 
                          &size_data);
592
 
 
593
 
        if (!gdk_pixbuf_loader_write (loader, (guchar *) data, data_size,
594
 
                                &error)) {
595
 
                DEBUG ("Failed to write to pixbuf loader: %s",
596
 
                        error ? error->message : "No error given");
597
 
                g_simple_async_result_set_from_error (closure->result, error);
598
 
                goto out;
599
 
        }
600
 
        if (!gdk_pixbuf_loader_close (loader, &error)) {
601
 
                DEBUG ("Failed to close pixbuf loader: %s",
602
 
                        error ? error->message : "No error given");
603
 
                g_simple_async_result_set_from_error (closure->result, error);
604
 
                goto out;
605
 
        }
606
 
 
607
 
        g_simple_async_result_set_op_res_gpointer (closure->result,
608
 
                        avatar_pixbuf_from_loader (loader), g_object_unref);
609
 
 
610
 
out:
611
 
        g_simple_async_result_complete (closure->result);
612
 
 
613
 
        g_clear_error (&error);
614
 
        g_free (data);
615
 
        tp_clear_object (&loader);
616
 
        pixbuf_avatar_from_individual_closure_free (closure);
617
 
}
618
 
 
619
 
void
620
 
empathy_pixbuf_avatar_from_individual_scaled_async (
621
 
                FolksIndividual     *individual,
622
 
                gint                 width,
623
 
                gint                 height,
624
 
                GCancellable        *cancellable,
625
 
                GAsyncReadyCallback  callback,
626
 
                gpointer             user_data)
627
 
{
628
 
        GFile *avatar_file;
629
 
        GSimpleAsyncResult *result;
630
 
        PixbufAvatarFromIndividualClosure *closure;
631
 
 
632
 
        result = g_simple_async_result_new (G_OBJECT (individual),
633
 
                        callback, user_data,
634
 
                        empathy_pixbuf_avatar_from_individual_scaled_async);
635
 
 
636
 
        avatar_file =
637
 
                folks_avatar_details_get_avatar (FOLKS_AVATAR_DETAILS (individual));
638
 
        if (avatar_file == NULL)
639
 
                goto out;
640
 
 
641
 
        closure = pixbuf_avatar_from_individual_closure_new (individual, result,
642
 
                                                             width, height);
643
 
        if (closure == NULL)
644
 
                goto out;
645
 
 
646
 
        g_file_load_contents_async (avatar_file, cancellable,
647
 
                        avatar_file_load_contents_cb, closure);
648
 
 
649
 
        g_object_unref (result);
650
 
 
651
 
        return;
652
 
 
653
 
out:
654
 
        g_simple_async_result_set_op_res_gpointer (result, NULL, NULL);
655
 
        g_simple_async_result_complete (result);
656
 
        g_object_unref (result);
657
 
}
658
 
 
659
 
/* Return a ref on the GdkPixbuf */
660
 
GdkPixbuf *
661
 
empathy_pixbuf_avatar_from_individual_scaled_finish (
662
 
                FolksIndividual *individual,
663
 
                GAsyncResult *result,
664
 
                GError **error)
665
 
{
666
 
        GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (result);
667
 
        gboolean result_valid;
668
 
        GdkPixbuf *pixbuf;
669
 
 
670
 
        g_return_val_if_fail (FOLKS_IS_INDIVIDUAL (individual), NULL);
671
 
        g_return_val_if_fail (G_IS_SIMPLE_ASYNC_RESULT (simple), NULL);
672
 
 
673
 
        if (g_simple_async_result_propagate_error (simple, error))
674
 
                return NULL;
675
 
 
676
 
        result_valid = g_simple_async_result_is_valid (result,
677
 
                        G_OBJECT (individual),
678
 
                        empathy_pixbuf_avatar_from_individual_scaled_async);
679
 
        g_return_val_if_fail (result_valid, NULL);
680
 
 
681
 
        pixbuf = g_simple_async_result_get_op_res_gpointer (simple);
682
 
        return pixbuf != NULL ? g_object_ref (pixbuf) : NULL;
683
 
}
684
 
 
685
 
GdkPixbuf *
686
 
empathy_pixbuf_contact_status_icon (EmpathyContact *contact,
687
 
                                   gboolean       show_protocol)
688
 
{
689
 
        const gchar *icon_name;
690
 
 
691
 
        g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), NULL);
692
 
 
693
 
        icon_name = empathy_icon_name_for_contact (contact);
694
 
 
695
 
        if (icon_name == NULL) {
696
 
                return NULL;
697
 
        }
698
 
        return empathy_pixbuf_contact_status_icon_with_icon_name (contact,
699
 
            icon_name,
700
 
            show_protocol);
701
 
}
702
 
 
703
 
GdkPixbuf *
704
 
empathy_pixbuf_contact_status_icon_with_icon_name (EmpathyContact *contact,
705
 
                                          const gchar    *icon_name,
706
 
                                          gboolean       show_protocol)
707
 
{
708
 
        GdkPixbuf *pix_status;
709
 
        GdkPixbuf *pix_protocol;
710
 
        gchar     *icon_filename;
711
 
        gint       height, width;
712
 
        gint       numerator, denominator;
713
 
 
714
 
        g_return_val_if_fail (EMPATHY_IS_CONTACT (contact) ||
715
 
                        (show_protocol == FALSE), NULL);
716
 
        g_return_val_if_fail (icon_name != NULL, NULL);
717
 
 
718
 
        numerator = 3;
719
 
        denominator = 4;
720
 
 
721
 
        icon_filename = empathy_filename_from_icon_name (icon_name,
722
 
                                                         GTK_ICON_SIZE_MENU);
723
 
        if (icon_filename == NULL) {
724
 
                DEBUG ("icon name: %s could not be found\n", icon_name);
725
 
                return NULL;
726
 
        }
727
 
 
728
 
        pix_status = gdk_pixbuf_new_from_file (icon_filename, NULL);
729
 
 
730
 
        if (pix_status == NULL) {
731
 
                DEBUG ("Could not open icon %s\n", icon_filename);
732
 
                g_free (icon_filename);
733
 
                return NULL;
734
 
        }
735
 
 
736
 
        g_free (icon_filename);
737
 
 
738
 
        if (!show_protocol)
739
 
                return pix_status;
740
 
 
741
 
        height = gdk_pixbuf_get_height (pix_status);
742
 
        width = gdk_pixbuf_get_width (pix_status);
743
 
 
744
 
        pix_protocol = empathy_pixbuf_protocol_from_contact_scaled (contact,
745
 
                                                                    width * numerator / denominator,
746
 
                                                                    height * numerator / denominator);
747
 
 
748
 
        if (pix_protocol == NULL) {
749
 
                return pix_status;
750
 
        }
751
 
        gdk_pixbuf_composite (pix_protocol, pix_status,
752
 
            0, height - height * numerator / denominator,
753
 
            width * numerator / denominator, height * numerator / denominator,
754
 
            0, height - height * numerator / denominator,
755
 
            1, 1,
756
 
            GDK_INTERP_BILINEAR, 255);
757
 
 
758
 
        g_object_unref (pix_protocol);
759
 
 
760
 
        return pix_status;
761
 
}
762
 
 
763
 
GdkPixbuf *
764
 
empathy_pixbuf_protocol_from_contact_scaled (EmpathyContact *contact,
765
 
                                          gint           width,
766
 
                                          gint           height)
767
 
{
768
 
        TpAccount *account;
769
 
        gchar     *filename;
770
 
        GdkPixbuf *pixbuf = NULL;
771
 
 
772
 
        g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), NULL);
773
 
 
774
 
        account = empathy_contact_get_account (contact);
775
 
        filename = empathy_filename_from_icon_name (tp_account_get_icon_name (account),
776
 
                                                    GTK_ICON_SIZE_MENU);
777
 
        if (filename != NULL) {
778
 
                pixbuf = gdk_pixbuf_new_from_file_at_size (filename, width, height, NULL);
779
 
                g_free (filename);
780
 
        }
781
 
 
782
 
        return pixbuf;
783
 
}
784
 
 
785
 
GdkPixbuf *
786
 
empathy_pixbuf_scale_down_if_necessary (GdkPixbuf *pixbuf, gint max_size)
787
 
{
788
 
        gint      width, height;
789
 
        gdouble   factor;
790
 
 
791
 
        width = gdk_pixbuf_get_width (pixbuf);
792
 
        height = gdk_pixbuf_get_height (pixbuf);
793
 
 
794
 
        if (width > 0 && (width > max_size || height > max_size)) {
795
 
                factor = (gdouble) max_size / MAX (width, height);
796
 
 
797
 
                width = width * factor;
798
 
                height = height * factor;
799
 
 
800
 
                return gdk_pixbuf_scale_simple (pixbuf,
801
 
                                                width, height,
802
 
                                                GDK_INTERP_HYPER);
803
 
        }
804
 
 
805
 
        return g_object_ref (pixbuf);
806
 
}
807
 
 
808
 
GdkPixbuf *
809
 
empathy_pixbuf_from_icon_name_sized (const gchar *icon_name,
810
 
                                     gint size)
811
 
{
812
 
        GtkIconTheme *theme;
813
 
        GdkPixbuf *pixbuf;
814
 
        GError *error = NULL;
815
 
 
816
 
        if (!icon_name) {
817
 
                return NULL;
818
 
        }
819
 
 
820
 
        theme = gtk_icon_theme_get_default ();
821
 
 
822
 
        pixbuf = gtk_icon_theme_load_icon (theme,
823
 
                                           icon_name,
824
 
                                           size,
825
 
                                           0,
826
 
                                           &error);
827
 
        if (error) {
828
 
                DEBUG ("Error loading icon: %s", error->message);
829
 
                g_clear_error (&error);
830
 
        }
831
 
 
832
 
        return pixbuf;
833
 
}
834
 
 
835
 
GdkPixbuf *
836
 
empathy_pixbuf_from_icon_name (const gchar *icon_name,
837
 
                               GtkIconSize  icon_size)
838
 
{
839
 
        gint  w, h;
840
 
        gint  size = 48;
841
 
 
842
 
        if (!icon_name) {
843
 
                return NULL;
844
 
        }
845
 
 
846
 
        if (gtk_icon_size_lookup (icon_size, &w, &h)) {
847
 
                size = (w + h) / 2;
848
 
        }
849
 
 
850
 
        return empathy_pixbuf_from_icon_name_sized (icon_name, size);
851
 
}
852
 
 
853
 
gchar *
854
 
empathy_filename_from_icon_name (const gchar *icon_name,
855
 
                                 GtkIconSize  icon_size)
856
 
{
857
 
        GtkIconTheme *icon_theme;
858
 
        GtkIconInfo  *icon_info;
859
 
        gint          w, h;
860
 
        gint          size = 48;
861
 
        gchar        *ret;
862
 
 
863
 
        icon_theme = gtk_icon_theme_get_default ();
864
 
 
865
 
        if (gtk_icon_size_lookup (icon_size, &w, &h)) {
866
 
                size = (w + h) / 2;
867
 
        }
868
 
 
869
 
        icon_info = gtk_icon_theme_lookup_icon (icon_theme, icon_name, size, 0);
870
 
        if (icon_info == NULL)
871
 
                return NULL;
872
 
 
873
 
        ret = g_strdup (gtk_icon_info_get_filename (icon_info));
874
 
        gtk_icon_info_free (icon_info);
875
 
 
876
 
        return ret;
877
 
}
878
 
 
879
 
/* Stolen from GtkSourceView, hence the weird intendation. Please keep it like
880
 
 * that to make it easier to apply changes from the original code.
881
 
 */
882
 
#define GTK_TEXT_UNKNOWN_CHAR 0xFFFC
883
 
 
884
 
/* this function acts like g_utf8_offset_to_pointer() except that if it finds a
885
 
 * decomposable character it consumes the decomposition length from the given
886
 
 * offset.  So it's useful when the offset was calculated for the normalized
887
 
 * version of str, but we need a pointer to str itself. */
888
 
static const gchar *
889
 
pointer_from_offset_skipping_decomp (const gchar *str, gint offset)
890
 
{
891
 
        gchar *casefold, *normal;
892
 
        const gchar *p, *q;
893
 
 
894
 
        p = str;
895
 
        while (offset > 0)
896
 
        {
897
 
                q = g_utf8_next_char (p);
898
 
                casefold = g_utf8_casefold (p, q - p);
899
 
                normal = g_utf8_normalize (casefold, -1, G_NORMALIZE_NFD);
900
 
                offset -= g_utf8_strlen (normal, -1);
901
 
                g_free (casefold);
902
 
                g_free (normal);
903
 
                p = q;
904
 
        }
905
 
        return p;
906
 
}
907
 
 
908
 
static const gchar *
909
 
g_utf8_strcasestr (const gchar *haystack, const gchar *needle)
910
 
{
911
 
        gsize needle_len;
912
 
        gsize haystack_len;
913
 
        const gchar *ret = NULL;
914
 
        gchar *p;
915
 
        gchar *casefold;
916
 
        gchar *caseless_haystack;
917
 
        gint i;
918
 
 
919
 
        g_return_val_if_fail (haystack != NULL, NULL);
920
 
        g_return_val_if_fail (needle != NULL, NULL);
921
 
 
922
 
        casefold = g_utf8_casefold (haystack, -1);
923
 
        caseless_haystack = g_utf8_normalize (casefold, -1, G_NORMALIZE_NFD);
924
 
        g_free (casefold);
925
 
 
926
 
        needle_len = g_utf8_strlen (needle, -1);
927
 
        haystack_len = g_utf8_strlen (caseless_haystack, -1);
928
 
 
929
 
        if (needle_len == 0)
930
 
        {
931
 
                ret = (gchar *) haystack;
932
 
                goto finally_1;
933
 
        }
934
 
 
935
 
        if (haystack_len < needle_len)
936
 
        {
937
 
                ret = NULL;
938
 
                goto finally_1;
939
 
        }
940
 
 
941
 
        p = (gchar *) caseless_haystack;
942
 
        needle_len = strlen (needle);
943
 
        i = 0;
944
 
 
945
 
        while (*p)
946
 
        {
947
 
                if ((strncmp (p, needle, needle_len) == 0))
948
 
                {
949
 
                        ret = pointer_from_offset_skipping_decomp (haystack, i);
950
 
                        goto finally_1;
951
 
                }
952
 
 
953
 
                p = g_utf8_next_char (p);
954
 
                i++;
955
 
        }
956
 
 
957
 
finally_1:
958
 
        g_free (caseless_haystack);
959
 
 
960
 
        return ret;
961
 
}
962
 
 
963
 
static gboolean
964
 
g_utf8_caselessnmatch (const char *s1, const char *s2,
965
 
                       gssize n1, gssize n2)
966
 
{
967
 
        gchar *casefold;
968
 
        gchar *normalized_s1;
969
 
        gchar *normalized_s2;
970
 
        gint len_s1;
971
 
        gint len_s2;
972
 
        gboolean ret = FALSE;
973
 
 
974
 
        g_return_val_if_fail (s1 != NULL, FALSE);
975
 
        g_return_val_if_fail (s2 != NULL, FALSE);
976
 
        g_return_val_if_fail (n1 > 0, FALSE);
977
 
        g_return_val_if_fail (n2 > 0, FALSE);
978
 
 
979
 
        casefold = g_utf8_casefold (s1, n1);
980
 
        normalized_s1 = g_utf8_normalize (casefold, -1, G_NORMALIZE_NFD);
981
 
        g_free (casefold);
982
 
 
983
 
        casefold = g_utf8_casefold (s2, n2);
984
 
        normalized_s2 = g_utf8_normalize (casefold, -1, G_NORMALIZE_NFD);
985
 
        g_free (casefold);
986
 
 
987
 
        len_s1 = strlen (normalized_s1);
988
 
        len_s2 = strlen (normalized_s2);
989
 
 
990
 
        if (len_s1 < len_s2)
991
 
                goto finally_2;
992
 
 
993
 
        ret = (strncmp (normalized_s1, normalized_s2, len_s2) == 0);
994
 
 
995
 
finally_2:
996
 
        g_free (normalized_s1);
997
 
        g_free (normalized_s2);
998
 
 
999
 
        return ret;
1000
 
}
1001
 
 
1002
 
static void
1003
 
forward_chars_with_skipping (GtkTextIter *iter,
1004
 
                             gint         count,
1005
 
                             gboolean     skip_invisible,
1006
 
                             gboolean     skip_nontext,
1007
 
                             gboolean     skip_decomp)
1008
 
{
1009
 
        gint i;
1010
 
 
1011
 
        g_return_if_fail (count >= 0);
1012
 
 
1013
 
        i = count;
1014
 
 
1015
 
        while (i > 0)
1016
 
        {
1017
 
                gboolean ignored = FALSE;
1018
 
 
1019
 
                /* minimal workaround to avoid the infinite loop of bug #168247.
1020
 
                 * It doesn't fix the problemjust the symptom...
1021
 
                 */
1022
 
                if (gtk_text_iter_is_end (iter))
1023
 
                        return;
1024
 
 
1025
 
                if (skip_nontext && gtk_text_iter_get_char (iter) == GTK_TEXT_UNKNOWN_CHAR)
1026
 
                        ignored = TRUE;
1027
 
 
1028
 
                if (!ignored && skip_invisible &&
1029
 
                    /* _gtk_text_btree_char_is_invisible (iter)*/ FALSE)
1030
 
                        ignored = TRUE;
1031
 
 
1032
 
                if (!ignored && skip_decomp)
1033
 
                {
1034
 
                        /* being UTF8 correct sucks; this accounts for extra
1035
 
                           offsets coming from canonical decompositions of
1036
 
                           UTF8 characters (e.g. accented characters) which
1037
 
                           g_utf8_normalize () performs */
1038
 
                        gchar *normal;
1039
 
                        gchar buffer[6];
1040
 
                        gint buffer_len;
1041
 
 
1042
 
                        buffer_len = g_unichar_to_utf8 (gtk_text_iter_get_char (iter), buffer);
1043
 
                        normal = g_utf8_normalize (buffer, buffer_len, G_NORMALIZE_NFD);
1044
 
                        i -= (g_utf8_strlen (normal, -1) - 1);
1045
 
                        g_free (normal);
1046
 
                }
1047
 
 
1048
 
                gtk_text_iter_forward_char (iter);
1049
 
 
1050
 
                if (!ignored)
1051
 
                        --i;
1052
 
        }
1053
 
}
1054
 
 
1055
 
static gboolean
1056
 
lines_match (const GtkTextIter *start,
1057
 
             const gchar      **lines,
1058
 
             gboolean           visible_only,
1059
 
             gboolean           slice,
1060
 
             GtkTextIter       *match_start,
1061
 
             GtkTextIter       *match_end)
1062
 
{
1063
 
        GtkTextIter next;
1064
 
        gchar *line_text;
1065
 
        const gchar *found;
1066
 
        gint offset;
1067
 
 
1068
 
        if (*lines == NULL || **lines == '\0')
1069
 
        {
1070
 
                if (match_start)
1071
 
                        *match_start = *start;
1072
 
                if (match_end)
1073
 
                        *match_end = *start;
1074
 
                return TRUE;
1075
 
        }
1076
 
 
1077
 
        next = *start;
1078
 
        gtk_text_iter_forward_line (&next);
1079
 
 
1080
 
        /* No more text in buffer, but *lines is nonempty */
1081
 
        if (gtk_text_iter_equal (start, &next))
1082
 
                return FALSE;
1083
 
 
1084
 
        if (slice)
1085
 
        {
1086
 
                if (visible_only)
1087
 
                        line_text = gtk_text_iter_get_visible_slice (start, &next);
1088
 
                else
1089
 
                        line_text = gtk_text_iter_get_slice (start, &next);
1090
 
        }
1091
 
        else
1092
 
        {
1093
 
                if (visible_only)
1094
 
                        line_text = gtk_text_iter_get_visible_text (start, &next);
1095
 
                else
1096
 
                        line_text = gtk_text_iter_get_text (start, &next);
1097
 
        }
1098
 
 
1099
 
        if (match_start) /* if this is the first line we're matching */
1100
 
        {
1101
 
                found = g_utf8_strcasestr (line_text, *lines);
1102
 
        }
1103
 
        else
1104
 
        {
1105
 
                /* If it's not the first line, we have to match from the
1106
 
                 * start of the line.
1107
 
                 */
1108
 
                if (g_utf8_caselessnmatch (line_text, *lines, strlen (line_text),
1109
 
                                           strlen (*lines)))
1110
 
                        found = line_text;
1111
 
                else
1112
 
                        found = NULL;
1113
 
        }
1114
 
 
1115
 
        if (found == NULL)
1116
 
        {
1117
 
                g_free (line_text);
1118
 
                return FALSE;
1119
 
        }
1120
 
 
1121
 
        /* Get offset to start of search string */
1122
 
        offset = g_utf8_strlen (line_text, found - line_text);
1123
 
 
1124
 
        next = *start;
1125
 
 
1126
 
        /* If match start needs to be returned, set it to the
1127
 
         * start of the search string.
1128
 
         */
1129
 
        forward_chars_with_skipping (&next, offset, visible_only, !slice, FALSE);
1130
 
        if (match_start)
1131
 
        {
1132
 
                *match_start = next;
1133
 
        }
1134
 
 
1135
 
        /* Go to end of search string */
1136
 
        forward_chars_with_skipping (&next, g_utf8_strlen (*lines, -1), visible_only, !slice, TRUE);
1137
 
 
1138
 
        g_free (line_text);
1139
 
 
1140
 
        ++lines;
1141
 
 
1142
 
        if (match_end)
1143
 
                *match_end = next;
1144
 
 
1145
 
        /* pass NULL for match_start, since we don't need to find the
1146
 
         * start again.
1147
 
         */
1148
 
        return lines_match (&next, lines, visible_only, slice, NULL, match_end);
1149
 
}
1150
 
 
1151
 
/* strsplit () that retains the delimiter as part of the string. */
1152
 
static gchar **
1153
 
strbreakup (const char *string,
1154
 
            const char *delimiter,
1155
 
            gint        max_tokens)
1156
 
{
1157
 
        GSList *string_list = NULL, *slist;
1158
 
        gchar **str_array, *s, *casefold, *new_string;
1159
 
        guint i, n = 1;
1160
 
 
1161
 
        g_return_val_if_fail (string != NULL, NULL);
1162
 
        g_return_val_if_fail (delimiter != NULL, NULL);
1163
 
 
1164
 
        if (max_tokens < 1)
1165
 
                max_tokens = G_MAXINT;
1166
 
 
1167
 
        s = strstr (string, delimiter);
1168
 
        if (s)
1169
 
        {
1170
 
                guint delimiter_len = strlen (delimiter);
1171
 
 
1172
 
                do
1173
 
                {
1174
 
                        guint len;
1175
 
 
1176
 
                        len = s - string + delimiter_len;
1177
 
                        new_string = g_new (gchar, len + 1);
1178
 
                        strncpy (new_string, string, len);
1179
 
                        new_string[len] = 0;
1180
 
                        casefold = g_utf8_casefold (new_string, -1);
1181
 
                        g_free (new_string);
1182
 
                        new_string = g_utf8_normalize (casefold, -1, G_NORMALIZE_NFD);
1183
 
                        g_free (casefold);
1184
 
                        string_list = g_slist_prepend (string_list, new_string);
1185
 
                        n++;
1186
 
                        string = s + delimiter_len;
1187
 
                        s = strstr (string, delimiter);
1188
 
                } while (--max_tokens && s);
1189
 
        }
1190
 
 
1191
 
        if (*string)
1192
 
        {
1193
 
                n++;
1194
 
                casefold = g_utf8_casefold (string, -1);
1195
 
                new_string = g_utf8_normalize (casefold, -1, G_NORMALIZE_NFD);
1196
 
                g_free (casefold);
1197
 
                string_list = g_slist_prepend (string_list, new_string);
1198
 
        }
1199
 
 
1200
 
        str_array = g_new (gchar*, n);
1201
 
 
1202
 
        i = n - 1;
1203
 
 
1204
 
        str_array[i--] = NULL;
1205
 
        for (slist = string_list; slist; slist = slist->next)
1206
 
                str_array[i--] = slist->data;
1207
 
 
1208
 
        g_slist_free (string_list);
1209
 
 
1210
 
        return str_array;
1211
 
}
1212
 
 
1213
 
gboolean
1214
 
empathy_text_iter_forward_search (const GtkTextIter   *iter,
1215
 
                                 const gchar         *str,
1216
 
                                 GtkTextIter         *match_start,
1217
 
                                 GtkTextIter         *match_end,
1218
 
                                 const GtkTextIter   *limit)
1219
 
{
1220
 
        gchar **lines = NULL;
1221
 
        GtkTextIter match;
1222
 
        gboolean retval = FALSE;
1223
 
        GtkTextIter search;
1224
 
        gboolean visible_only;
1225
 
        gboolean slice;
1226
 
 
1227
 
        g_return_val_if_fail (iter != NULL, FALSE);
1228
 
        g_return_val_if_fail (str != NULL, FALSE);
1229
 
 
1230
 
        if (limit && gtk_text_iter_compare (iter, limit) >= 0)
1231
 
                return FALSE;
1232
 
 
1233
 
        if (*str == '\0') {
1234
 
                /* If we can move one char, return the empty string there */
1235
 
                match = *iter;
1236
 
 
1237
 
                if (gtk_text_iter_forward_char (&match)) {
1238
 
                        if (limit && gtk_text_iter_equal (&match, limit)) {
1239
 
                                return FALSE;
1240
 
                        }
1241
 
 
1242
 
                        if (match_start) {
1243
 
                                *match_start = match;
1244
 
                        }
1245
 
                        if (match_end) {
1246
 
                                *match_end = match;
1247
 
                        }
1248
 
                        return TRUE;
1249
 
                } else {
1250
 
                        return FALSE;
1251
 
                }
1252
 
        }
1253
 
 
1254
 
        visible_only = TRUE;
1255
 
        slice = FALSE;
1256
 
 
1257
 
        /* locate all lines */
1258
 
        lines = strbreakup (str, "\n", -1);
1259
 
 
1260
 
        search = *iter;
1261
 
 
1262
 
        do {
1263
 
                /* This loop has an inefficient worst-case, where
1264
 
                 * gtk_text_iter_get_text () is called repeatedly on
1265
 
                 * a single line.
1266
 
                 */
1267
 
                GtkTextIter end;
1268
 
 
1269
 
                if (limit && gtk_text_iter_compare (&search, limit) >= 0) {
1270
 
                        break;
1271
 
                }
1272
 
 
1273
 
                if (lines_match (&search, (const gchar**)lines,
1274
 
                                 visible_only, slice, &match, &end)) {
1275
 
                        if (limit == NULL ||
1276
 
                            (limit && gtk_text_iter_compare (&end, limit) <= 0)) {
1277
 
                                retval = TRUE;
1278
 
 
1279
 
                                if (match_start) {
1280
 
                                        *match_start = match;
1281
 
                                }
1282
 
                                if (match_end) {
1283
 
                                        *match_end = end;
1284
 
                                }
1285
 
                        }
1286
 
                        break;
1287
 
                }
1288
 
        } while (gtk_text_iter_forward_line (&search));
1289
 
 
1290
 
        g_strfreev ((gchar **) lines);
1291
 
 
1292
 
        return retval;
1293
 
}
1294
 
 
1295
 
static const gchar *
1296
 
g_utf8_strrcasestr (const gchar *haystack, const gchar *needle)
1297
 
{
1298
 
        gsize needle_len;
1299
 
        gsize haystack_len;
1300
 
        const gchar *ret = NULL;
1301
 
        gchar *p;
1302
 
        gchar *casefold;
1303
 
        gchar *caseless_haystack;
1304
 
        gint i;
1305
 
 
1306
 
        g_return_val_if_fail (haystack != NULL, NULL);
1307
 
        g_return_val_if_fail (needle != NULL, NULL);
1308
 
 
1309
 
        casefold = g_utf8_casefold (haystack, -1);
1310
 
        caseless_haystack = g_utf8_normalize (casefold, -1, G_NORMALIZE_NFD);
1311
 
        g_free (casefold);
1312
 
 
1313
 
        needle_len = g_utf8_strlen (needle, -1);
1314
 
        haystack_len = g_utf8_strlen (caseless_haystack, -1);
1315
 
 
1316
 
        if (needle_len == 0)
1317
 
        {
1318
 
                ret = (gchar *) haystack;
1319
 
                goto finally_1;
1320
 
        }
1321
 
 
1322
 
        if (haystack_len < needle_len)
1323
 
        {
1324
 
                ret = NULL;
1325
 
                goto finally_1;
1326
 
        }
1327
 
 
1328
 
        i = haystack_len - needle_len;
1329
 
        p = g_utf8_offset_to_pointer (caseless_haystack, i);
1330
 
        needle_len = strlen (needle);
1331
 
 
1332
 
        while (p >= caseless_haystack)
1333
 
        {
1334
 
                if (strncmp (p, needle, needle_len) == 0)
1335
 
                {
1336
 
                        ret = pointer_from_offset_skipping_decomp (haystack, i);
1337
 
                        goto finally_1;
1338
 
                }
1339
 
 
1340
 
                p = g_utf8_prev_char (p);
1341
 
                i--;
1342
 
        }
1343
 
 
1344
 
finally_1:
1345
 
        g_free (caseless_haystack);
1346
 
 
1347
 
        return ret;
1348
 
}
1349
 
 
1350
 
static gboolean
1351
 
backward_lines_match (const GtkTextIter *start,
1352
 
                      const gchar      **lines,
1353
 
                      gboolean           visible_only,
1354
 
                      gboolean           slice,
1355
 
                      GtkTextIter       *match_start,
1356
 
                      GtkTextIter       *match_end)
1357
 
{
1358
 
        GtkTextIter line, next;
1359
 
        gchar *line_text;
1360
 
        const gchar *found;
1361
 
        gint offset;
1362
 
 
1363
 
        if (*lines == NULL || **lines == '\0')
1364
 
        {
1365
 
                if (match_start)
1366
 
                        *match_start = *start;
1367
 
                if (match_end)
1368
 
                        *match_end = *start;
1369
 
                return TRUE;
1370
 
        }
1371
 
 
1372
 
        line = next = *start;
1373
 
        if (gtk_text_iter_get_line_offset (&next) == 0)
1374
 
        {
1375
 
                if (!gtk_text_iter_backward_line (&next))
1376
 
                        return FALSE;
1377
 
        }
1378
 
        else
1379
 
                gtk_text_iter_set_line_offset (&next, 0);
1380
 
 
1381
 
        if (slice)
1382
 
        {
1383
 
                if (visible_only)
1384
 
                        line_text = gtk_text_iter_get_visible_slice (&next, &line);
1385
 
                else
1386
 
                        line_text = gtk_text_iter_get_slice (&next, &line);
1387
 
        }
1388
 
        else
1389
 
        {
1390
 
                if (visible_only)
1391
 
                        line_text = gtk_text_iter_get_visible_text (&next, &line);
1392
 
                else
1393
 
                        line_text = gtk_text_iter_get_text (&next, &line);
1394
 
        }
1395
 
 
1396
 
        if (match_start) /* if this is the first line we're matching */
1397
 
        {
1398
 
                found = g_utf8_strrcasestr (line_text, *lines);
1399
 
        }
1400
 
        else
1401
 
        {
1402
 
                /* If it's not the first line, we have to match from the
1403
 
                 * start of the line.
1404
 
                 */
1405
 
                if (g_utf8_caselessnmatch (line_text, *lines, strlen (line_text),
1406
 
                                           strlen (*lines)))
1407
 
                        found = line_text;
1408
 
                else
1409
 
                        found = NULL;
1410
 
        }
1411
 
 
1412
 
        if (found == NULL)
1413
 
        {
1414
 
                g_free (line_text);
1415
 
                return FALSE;
1416
 
        }
1417
 
 
1418
 
        /* Get offset to start of search string */
1419
 
        offset = g_utf8_strlen (line_text, found - line_text);
1420
 
 
1421
 
        forward_chars_with_skipping (&next, offset, visible_only, !slice, FALSE);
1422
 
 
1423
 
        /* If match start needs to be returned, set it to the
1424
 
         * start of the search string.
1425
 
         */
1426
 
        if (match_start)
1427
 
        {
1428
 
                *match_start = next;
1429
 
        }
1430
 
 
1431
 
        /* Go to end of search string */
1432
 
        forward_chars_with_skipping (&next, g_utf8_strlen (*lines, -1), visible_only, !slice, TRUE);
1433
 
 
1434
 
        g_free (line_text);
1435
 
 
1436
 
        ++lines;
1437
 
 
1438
 
        if (match_end)
1439
 
                *match_end = next;
1440
 
 
1441
 
        /* try to match the rest of the lines forward, passing NULL
1442
 
         * for match_start so lines_match will try to match the entire
1443
 
         * line */
1444
 
        return lines_match (&next, lines, visible_only,
1445
 
                            slice, NULL, match_end);
1446
 
}
1447
 
 
1448
 
gboolean
1449
 
empathy_text_iter_backward_search (const GtkTextIter   *iter,
1450
 
                                  const gchar         *str,
1451
 
                                  GtkTextIter         *match_start,
1452
 
                                  GtkTextIter         *match_end,
1453
 
                                  const GtkTextIter   *limit)
1454
 
{
1455
 
        gchar **lines = NULL;
1456
 
        GtkTextIter match;
1457
 
        gboolean retval = FALSE;
1458
 
        GtkTextIter search;
1459
 
        gboolean visible_only;
1460
 
        gboolean slice;
1461
 
 
1462
 
        g_return_val_if_fail (iter != NULL, FALSE);
1463
 
        g_return_val_if_fail (str != NULL, FALSE);
1464
 
 
1465
 
        if (limit && gtk_text_iter_compare (iter, limit) <= 0)
1466
 
                return FALSE;
1467
 
 
1468
 
        if (*str == '\0')
1469
 
        {
1470
 
                /* If we can move one char, return the empty string there */
1471
 
                match = *iter;
1472
 
 
1473
 
                if (gtk_text_iter_backward_char (&match))
1474
 
                {
1475
 
                        if (limit && gtk_text_iter_equal (&match, limit))
1476
 
                                return FALSE;
1477
 
 
1478
 
                        if (match_start)
1479
 
                                *match_start = match;
1480
 
                        if (match_end)
1481
 
                                *match_end = match;
1482
 
                        return TRUE;
1483
 
                }
1484
 
                else
1485
 
                {
1486
 
                        return FALSE;
1487
 
                }
1488
 
        }
1489
 
 
1490
 
        visible_only = TRUE;
1491
 
        slice = TRUE;
1492
 
 
1493
 
        /* locate all lines */
1494
 
        lines = strbreakup (str, "\n", -1);
1495
 
 
1496
 
        search = *iter;
1497
 
 
1498
 
        while (TRUE)
1499
 
        {
1500
 
                /* This loop has an inefficient worst-case, where
1501
 
                 * gtk_text_iter_get_text () is called repeatedly on
1502
 
                 * a single line.
1503
 
                 */
1504
 
                GtkTextIter end;
1505
 
 
1506
 
                if (limit && gtk_text_iter_compare (&search, limit) <= 0)
1507
 
                        break;
1508
 
 
1509
 
                if (backward_lines_match (&search, (const gchar**)lines,
1510
 
                                          visible_only, slice, &match, &end))
1511
 
                {
1512
 
                        if (limit == NULL || (limit &&
1513
 
                                              gtk_text_iter_compare (&end, limit) > 0))
1514
 
                        {
1515
 
                                retval = TRUE;
1516
 
 
1517
 
                                if (match_start)
1518
 
                                        *match_start = match;
1519
 
                                if (match_end)
1520
 
                                        *match_end = end;
1521
 
                        }
1522
 
                        break;
1523
 
                }
1524
 
 
1525
 
                if (gtk_text_iter_get_line_offset (&search) == 0)
1526
 
                {
1527
 
                        if (!gtk_text_iter_backward_line (&search))
1528
 
                                break;
1529
 
                }
1530
 
                else
1531
 
                {
1532
 
                        gtk_text_iter_set_line_offset (&search, 0);
1533
 
                }
1534
 
        }
1535
 
 
1536
 
        g_strfreev ((gchar **) lines);
1537
 
 
1538
 
        return retval;
1539
 
}
1540
 
 
1541
 
gboolean
1542
 
empathy_window_get_is_visible (GtkWindow *window)
1543
 
{
1544
 
        GdkWindowState  state;
1545
 
        GdkWindow      *gdk_window;
1546
 
 
1547
 
        g_return_val_if_fail (GTK_IS_WINDOW (window), FALSE);
1548
 
 
1549
 
        gdk_window = gtk_widget_get_window (GTK_WIDGET (window));
1550
 
        if (!gdk_window) {
1551
 
                return FALSE;
1552
 
        }
1553
 
 
1554
 
        state = gdk_window_get_state (gdk_window);
1555
 
        if (state & (GDK_WINDOW_STATE_WITHDRAWN | GDK_WINDOW_STATE_ICONIFIED)) {
1556
 
                return FALSE;
1557
 
        }
1558
 
 
1559
 
        return TRUE;
1560
 
}
1561
 
 
1562
 
void
1563
 
empathy_window_iconify (GtkWindow *window, GtkStatusIcon *status_icon)
1564
 
{
1565
 
        GdkRectangle  icon_location;
1566
 
        gulong        data[4];
1567
 
        Display      *dpy;
1568
 
        GdkWindow    *gdk_window;
1569
 
 
1570
 
        // If the status icon isn't visible (because indicators are used) then
1571
 
        // attempting to change the properties of the status icon doesn't work.
1572
 
        if (gtk_status_icon_get_visible (status_icon)) {
1573
 
                gtk_status_icon_get_geometry (status_icon, NULL, &icon_location, NULL);
1574
 
                gdk_window = gtk_widget_get_window (GTK_WIDGET (window));
1575
 
                dpy = gdk_x11_drawable_get_xdisplay (gdk_window);
1576
 
 
1577
 
                data[0] = icon_location.x;
1578
 
                data[1] = icon_location.y;
1579
 
                data[2] = icon_location.width;
1580
 
                data[3] = icon_location.height;
1581
 
 
1582
 
                XChangeProperty (dpy,
1583
 
                                 GDK_WINDOW_XID (gdk_window),
1584
 
                                 gdk_x11_get_xatom_by_name_for_display (
1585
 
                                        gdk_drawable_get_display (gdk_window),
1586
 
                                 "_NET_WM_ICON_GEOMETRY"),
1587
 
                                 XA_CARDINAL, 32, PropModeReplace,
1588
 
                                 (guchar *)&data, 4);
1589
 
        }
1590
 
 
1591
 
        gtk_window_set_skip_taskbar_hint (window, TRUE);
1592
 
        // If the status icon isn't present then the WM will probably choose to
1593
 
        // iconfy to the taskbar, which doesn't look as good as the taskbar
1594
 
        // entry has just been removed. Just hide instead.
1595
 
        if (gtk_status_icon_get_visible (status_icon)) {
1596
 
                gtk_window_iconify (window);
1597
 
        } else {
1598
 
                gtk_widget_hide (GTK_WIDGET(window));
1599
 
        }
1600
 
}
1601
 
 
1602
 
/* Takes care of moving the window to the current workspace. */
1603
 
void
1604
 
empathy_window_present_with_time (GtkWindow *window,
1605
 
                        guint32 timestamp)
1606
 
{
1607
 
        GdkWindow *gdk_window;
1608
 
 
1609
 
        g_return_if_fail (GTK_IS_WINDOW (window));
1610
 
 
1611
 
        /* Move the window to the current workspace before trying to show it.
1612
 
         * This is the behaviour people expect when clicking on the statusbar icon. */
1613
 
        gdk_window = gtk_widget_get_window (GTK_WIDGET (window));
1614
 
        if (gdk_window) {
1615
 
                gint x, y;
1616
 
                gint w, h;
1617
 
 
1618
 
                /* Has no effect if the WM has viewports, like compiz */
1619
 
                gdk_x11_window_move_to_current_desktop (gdk_window);
1620
 
 
1621
 
                /* If window is still off-screen, hide it to force it to
1622
 
                 * reposition on the current workspace. */
1623
 
                gtk_window_get_position (window, &x, &y);
1624
 
                gtk_window_get_size (window, &w, &h);
1625
 
                if (!EMPATHY_RECT_IS_ON_SCREEN (x, y, w, h))
1626
 
                        gtk_widget_hide (GTK_WIDGET (window));
1627
 
        }
1628
 
 
1629
 
        if (timestamp == GDK_CURRENT_TIME)
1630
 
                gtk_window_present (window);
1631
 
        else
1632
 
                gtk_window_present_with_time (window, timestamp);
1633
 
 
1634
 
        gtk_window_set_skip_taskbar_hint (window, FALSE);
1635
 
        gtk_window_deiconify (window);
1636
 
}
1637
 
 
1638
 
void
1639
 
empathy_window_present (GtkWindow *window)
1640
 
{
1641
 
  empathy_window_present_with_time (window, gtk_get_current_event_time ());
1642
 
}
1643
 
 
1644
 
GtkWindow *
1645
 
empathy_get_toplevel_window (GtkWidget *widget)
1646
 
{
1647
 
        GtkWidget *toplevel;
1648
 
 
1649
 
        g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
1650
 
 
1651
 
        toplevel = gtk_widget_get_toplevel (widget);
1652
 
        if (GTK_IS_WINDOW (toplevel) &&
1653
 
            gtk_widget_is_toplevel (toplevel)) {
1654
 
                return GTK_WINDOW (toplevel);
1655
 
        }
1656
 
 
1657
 
        return NULL;
1658
 
}
1659
 
 
1660
 
/** empathy_make_absolute_url_len:
1661
 
 * @url: an url
1662
 
 * @len: a length
1663
 
 *
1664
 
 * Same as #empathy_make_absolute_url but for a limited string length
1665
 
 */
1666
 
gchar *
1667
 
empathy_make_absolute_url_len (const gchar *url,
1668
 
                               guint len)
1669
 
{
1670
 
        g_return_val_if_fail (url != NULL, NULL);
1671
 
 
1672
 
        if (g_str_has_prefix (url, "ghelp:") ||
1673
 
            g_str_has_prefix (url, "mailto:") ||
1674
 
            strstr (url, ":/")) {
1675
 
                return g_strndup (url, len);
1676
 
        }
1677
 
 
1678
 
        if (strstr (url, "@")) {
1679
 
                return g_strdup_printf ("mailto:%.*s", len, url);
1680
 
        }
1681
 
 
1682
 
        return g_strdup_printf ("http://%.*s", len, url);
1683
 
}
1684
 
 
1685
 
/** empathy_make_absolute_url:
1686
 
 * @url: an url
1687
 
 *
1688
 
 * The URL opening code can't handle schemeless strings, so we try to be
1689
 
 * smart and add http if there is no scheme or doesn't look like a mail
1690
 
 * address. This should work in most cases, and let us click on strings
1691
 
 * like "www.gnome.org".
1692
 
 *
1693
 
 * Returns: a newly allocated url with proper mailto: or http:// prefix, use
1694
 
 * g_free when your are done with it
1695
 
 */
1696
 
gchar *
1697
 
empathy_make_absolute_url (const gchar *url)
1698
 
{
1699
 
        return empathy_make_absolute_url_len (url, strlen (url));
1700
 
}
1701
 
 
1702
 
void
1703
 
empathy_url_show (GtkWidget *parent,
1704
 
                  const char *url)
1705
 
{
1706
 
        gchar  *real_url;
1707
 
        GError *error = NULL;
1708
 
 
1709
 
        g_return_if_fail (parent == NULL || GTK_IS_WIDGET (parent));
1710
 
        g_return_if_fail (url != NULL);
1711
 
 
1712
 
        real_url = empathy_make_absolute_url (url);
1713
 
 
1714
 
        gtk_show_uri (parent ? gtk_widget_get_screen (parent) : NULL, real_url,
1715
 
                      gtk_get_current_event_time (), &error);
1716
 
 
1717
 
        if (error) {
1718
 
                GtkWidget *dialog;
1719
 
 
1720
 
                dialog = gtk_message_dialog_new (NULL, 0,
1721
 
                                                 GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE,
1722
 
                                                 _("Unable to open URI"));
1723
 
                gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
1724
 
                                                          "%s", error->message);
1725
 
 
1726
 
                g_signal_connect (dialog, "response",
1727
 
                                  G_CALLBACK (gtk_widget_destroy),
1728
 
                                  NULL);
1729
 
                gtk_window_present (GTK_WINDOW (dialog));
1730
 
 
1731
 
                g_clear_error (&error);
1732
 
        }
1733
 
 
1734
 
        g_free (real_url);
1735
 
}
1736
 
 
1737
 
void
1738
 
empathy_send_file (EmpathyContact *contact, GFile *file)
1739
 
{
1740
 
        EmpathyFTFactory *factory;
1741
 
        GtkRecentManager *manager;
1742
 
        gchar *uri;
1743
 
 
1744
 
        g_return_if_fail (EMPATHY_IS_CONTACT (contact));
1745
 
        g_return_if_fail (G_IS_FILE (file));
1746
 
 
1747
 
        factory = empathy_ft_factory_dup_singleton ();
1748
 
 
1749
 
        empathy_ft_factory_new_transfer_outgoing (factory, contact, file);
1750
 
 
1751
 
        uri = g_file_get_uri (file);
1752
 
        manager = gtk_recent_manager_get_default ();
1753
 
        gtk_recent_manager_add_item (manager, uri);
1754
 
        g_free (uri);
1755
 
 
1756
 
        g_object_unref (factory);
1757
 
}
1758
 
 
1759
 
void
1760
 
empathy_send_file_from_uri_list (EmpathyContact *contact, const gchar *uri_list)
1761
 
{
1762
 
        const gchar *nl;
1763
 
        GFile *file;
1764
 
 
1765
 
        /* Only handle a single file for now.  It would be wicked cool to be
1766
 
           able to do multiple files, offering to zip them or whatever like
1767
 
           nautilus-sendto does.  Note that text/uri-list is defined to have
1768
 
           each line terminated by \r\n, but we can be tolerant of applications
1769
 
           that only use \n or don't terminate single-line entries.
1770
 
        */
1771
 
        nl = strstr (uri_list, "\r\n");
1772
 
        if (!nl) {
1773
 
                nl = strchr (uri_list, '\n');
1774
 
        }
1775
 
        if (nl) {
1776
 
                gchar *uri = g_strndup (uri_list, nl - uri_list);
1777
 
                file = g_file_new_for_uri (uri);
1778
 
                g_free (uri);
1779
 
        }
1780
 
        else {
1781
 
                file = g_file_new_for_uri (uri_list);
1782
 
        }
1783
 
 
1784
 
        empathy_send_file (contact, file);
1785
 
 
1786
 
        g_object_unref (file);
1787
 
}
1788
 
 
1789
 
static void
1790
 
file_manager_send_file_response_cb (GtkDialog      *widget,
1791
 
                                    gint            response_id,
1792
 
                                    EmpathyContact *contact)
1793
 
{
1794
 
        GFile *file;
1795
 
 
1796
 
        if (response_id == GTK_RESPONSE_OK) {
1797
 
                file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (widget));
1798
 
 
1799
 
                empathy_send_file (contact, file);
1800
 
 
1801
 
                g_object_unref (file);
1802
 
        }
1803
 
 
1804
 
        gtk_widget_destroy (GTK_WIDGET (widget));
1805
 
}
1806
 
 
1807
 
void
1808
 
empathy_send_file_with_file_chooser (EmpathyContact *contact)
1809
 
{
1810
 
        GtkWidget               *widget;
1811
 
        GtkWidget               *button;
1812
 
 
1813
 
        g_return_if_fail (EMPATHY_IS_CONTACT (contact));
1814
 
 
1815
 
        DEBUG ("Creating selection file chooser");
1816
 
 
1817
 
        widget = gtk_file_chooser_dialog_new (_("Select a file"),
1818
 
                                              NULL,
1819
 
                                              GTK_FILE_CHOOSER_ACTION_OPEN,
1820
 
                                              GTK_STOCK_CANCEL,
1821
 
                                              GTK_RESPONSE_CANCEL,
1822
 
                                              NULL);
1823
 
 
1824
 
        /* send button */
1825
 
        button = gtk_button_new_with_mnemonic (_("_Send"));
1826
 
        gtk_button_set_image (GTK_BUTTON (button),
1827
 
                gtk_image_new_from_icon_name (EMPATHY_IMAGE_DOCUMENT_SEND,
1828
 
                                              GTK_ICON_SIZE_BUTTON));
1829
 
        gtk_widget_show (button);
1830
 
        gtk_dialog_add_action_widget (GTK_DIALOG (widget), button,
1831
 
                                      GTK_RESPONSE_OK);
1832
 
        gtk_widget_set_can_default (button, TRUE);
1833
 
        gtk_dialog_set_default_response (GTK_DIALOG (widget),
1834
 
                                         GTK_RESPONSE_OK);
1835
 
 
1836
 
        gtk_file_chooser_set_local_only (GTK_FILE_CHOOSER (widget), FALSE);
1837
 
 
1838
 
        gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (widget),
1839
 
                g_get_home_dir ());
1840
 
 
1841
 
        g_signal_connect (widget, "response",
1842
 
                          G_CALLBACK (file_manager_send_file_response_cb),
1843
 
                          contact);
1844
 
 
1845
 
        gtk_widget_show (widget);
1846
 
}
1847
 
 
1848
 
static void
1849
 
file_manager_receive_file_response_cb (GtkDialog *dialog,
1850
 
                                       GtkResponseType response,
1851
 
                                       EmpathyFTHandler *handler)
1852
 
{
1853
 
        EmpathyFTFactory *factory;
1854
 
        GFile *file;
1855
 
 
1856
 
        if (response == GTK_RESPONSE_OK) {
1857
 
                GFile *parent;
1858
 
                GFileInfo *info;
1859
 
                guint64 free_space, file_size;
1860
 
                GError *error = NULL;
1861
 
 
1862
 
                file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (dialog));
1863
 
                parent = g_file_get_parent (file);
1864
 
                info = g_file_query_filesystem_info (parent,
1865
 
                                G_FILE_ATTRIBUTE_FILESYSTEM_FREE,
1866
 
                                NULL, &error);
1867
 
 
1868
 
                g_object_unref (parent);
1869
 
 
1870
 
                if (error != NULL) {
1871
 
                        g_warning ("Error: %s", error->message);
1872
 
 
1873
 
                        g_object_unref (file);
1874
 
                        return;
1875
 
                }
1876
 
 
1877
 
                free_space = g_file_info_get_attribute_uint64 (info,
1878
 
                                G_FILE_ATTRIBUTE_FILESYSTEM_FREE);
1879
 
                file_size = empathy_ft_handler_get_total_bytes (handler);
1880
 
 
1881
 
                g_object_unref (info);
1882
 
 
1883
 
                if (file_size > free_space) {
1884
 
                        GtkWidget *message = gtk_message_dialog_new (
1885
 
                                GTK_WINDOW (dialog),
1886
 
                                GTK_DIALOG_MODAL,
1887
 
                                GTK_MESSAGE_ERROR,
1888
 
                                GTK_BUTTONS_CLOSE,
1889
 
                                _("Insufficient free space to save file"));
1890
 
                        char *file_size_str, *free_space_str;
1891
 
 
1892
 
                        file_size_str = g_format_size_for_display (file_size);
1893
 
                        free_space_str = g_format_size_for_display (free_space);
1894
 
 
1895
 
                        gtk_message_dialog_format_secondary_text (
1896
 
                                GTK_MESSAGE_DIALOG (message),
1897
 
                                _("%s of free space are required to save this "
1898
 
                                  "file, but only %s is available. Please "
1899
 
                                  "choose another location."),
1900
 
                                file_size_str, free_space_str);
1901
 
 
1902
 
                        gtk_dialog_run (GTK_DIALOG (message));
1903
 
 
1904
 
                        g_free (file_size_str);
1905
 
                        g_free (free_space_str);
1906
 
                        gtk_widget_destroy (message);
1907
 
 
1908
 
                        g_object_unref (file);
1909
 
 
1910
 
                        return;
1911
 
                }
1912
 
 
1913
 
                factory = empathy_ft_factory_dup_singleton ();
1914
 
 
1915
 
                empathy_ft_factory_set_destination_for_incoming_handler (
1916
 
                                factory, handler, file);
1917
 
 
1918
 
                g_object_unref (factory);
1919
 
                g_object_unref (file);
1920
 
        } else {
1921
 
                /* unref the handler, as we dismissed the file chooser,
1922
 
                 * and refused the transfer.
1923
 
                 */
1924
 
                g_object_unref (handler);
1925
 
        }
1926
 
 
1927
 
        gtk_widget_destroy (GTK_WIDGET (dialog));
1928
 
}
1929
 
 
1930
 
void
1931
 
empathy_receive_file_with_file_chooser (EmpathyFTHandler *handler)
1932
 
{
1933
 
        GtkWidget *widget;
1934
 
        const gchar *dir;
1935
 
        EmpathyContact *contact;
1936
 
        gchar *title;
1937
 
 
1938
 
        contact = empathy_ft_handler_get_contact (handler);
1939
 
        g_assert (contact != NULL);
1940
 
 
1941
 
        title = g_strdup_printf (_("Incoming file from %s"),
1942
 
                empathy_contact_get_alias (contact));
1943
 
 
1944
 
        widget = gtk_file_chooser_dialog_new (title,
1945
 
                                              NULL,
1946
 
                                              GTK_FILE_CHOOSER_ACTION_SAVE,
1947
 
                                              GTK_STOCK_CANCEL,
1948
 
                                              GTK_RESPONSE_CANCEL,
1949
 
                                              GTK_STOCK_SAVE,
1950
 
                                              GTK_RESPONSE_OK,
1951
 
                                              NULL);
1952
 
        gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (widget),
1953
 
                empathy_ft_handler_get_filename (handler));
1954
 
        gtk_file_chooser_set_do_overwrite_confirmation
1955
 
                (GTK_FILE_CHOOSER (widget), TRUE);
1956
 
 
1957
 
        dir = g_get_user_special_dir (G_USER_DIRECTORY_DOWNLOAD);
1958
 
        if (dir == NULL)
1959
 
                /* Fallback to $HOME if $XDG_DOWNLOAD_DIR is not set */
1960
 
                dir = g_get_home_dir ();
1961
 
 
1962
 
        gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (widget), dir);
1963
 
 
1964
 
        g_signal_connect (widget, "response",
1965
 
                G_CALLBACK (file_manager_receive_file_response_cb), handler);
1966
 
 
1967
 
        gtk_widget_show (widget);
1968
 
        g_free (title);
1969
 
}