~bcurtiswx/ubuntu/precise/empathy/3.4.2.1-0ubuntu1

« back to all changes in this revision

Viewing changes to libempathy-gtk/gossip-ui-utils.c

  • Committer: Bazaar Package Importer
  • Author(s): Sjoerd Simons
  • Date: 2007-05-20 15:31:42 UTC
  • Revision ID: james.westby@ubuntu.com-20070520153142-r3auwguxdgxhktqb
Tags: upstream-0.4
ImportĀ upstreamĀ versionĀ 0.4

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 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., 59 Temple Place - Suite 330,
 
19
 * Boston, MA 02111-1307, 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
 * 
 
26
 *          Part of this file is copied from GtkSourceView (gtksourceiter.c):
 
27
 *          Paolo Maggi
 
28
 *          Jeroen Zwartepoorte
 
29
 */
 
30
 
 
31
#include <string.h>
 
32
 
 
33
#include <glib/gi18n.h>
 
34
#include <gtk/gtk.h>
 
35
#include <glade/glade.h>
 
36
#include <libgnome/libgnome.h>
 
37
 
 
38
#include <libmissioncontrol/mc-profile.h>
 
39
 
 
40
#include <libempathy/gossip-paths.h>
 
41
#include <libempathy/gossip-debug.h>
 
42
 
 
43
#include "gossip-ui-utils.h"
 
44
#include "empathy-images.h"
 
45
 
 
46
#define DEBUG_DOMAIN "UiUtils"
 
47
 
 
48
struct SizeData {
 
49
        gint     width;
 
50
        gint     height;
 
51
        gboolean preserve_aspect_ratio;
 
52
};
 
53
 
 
54
static GladeXML *
 
55
get_glade_file (const gchar *filename,
 
56
                const gchar *root,
 
57
                const gchar *domain,
 
58
                const gchar *first_required_widget,
 
59
                va_list      args)
 
60
{
 
61
        gchar      *path;
 
62
        GladeXML   *gui;
 
63
        const char *name;
 
64
        GtkWidget **widget_ptr;
 
65
 
 
66
        path = gossip_paths_get_glade_path (filename);
 
67
        gui = glade_xml_new (path, root, domain);
 
68
        g_free (path);
 
69
 
 
70
        if (!gui) {
 
71
                g_warning ("Couldn't find necessary glade file '%s'", filename);
 
72
                return NULL;
 
73
        }
 
74
 
 
75
        for (name = first_required_widget; name; name = va_arg (args, char *)) {
 
76
                widget_ptr = va_arg (args, void *);
 
77
 
 
78
                *widget_ptr = glade_xml_get_widget (gui, name);
 
79
 
 
80
                if (!*widget_ptr) {
 
81
                        g_warning ("Glade file '%s' is missing widget '%s'.",
 
82
                                   filename, name);
 
83
                        continue;
 
84
                }
 
85
        }
 
86
 
 
87
        return gui;
 
88
}
 
89
 
 
90
void
 
91
gossip_glade_get_file_simple (const gchar *filename,
 
92
                              const gchar *root,
 
93
                              const gchar *domain,
 
94
                              const gchar *first_required_widget, ...)
 
95
{
 
96
        va_list   args;
 
97
        GladeXML *gui;
 
98
 
 
99
        va_start (args, first_required_widget);
 
100
 
 
101
        gui = get_glade_file (filename,
 
102
                              root,
 
103
                              domain,
 
104
                              first_required_widget,
 
105
                              args);
 
106
 
 
107
        va_end (args);
 
108
 
 
109
        if (!gui) {
 
110
                return;
 
111
        }
 
112
 
 
113
        g_object_unref (gui);
 
114
}
 
115
 
 
116
GladeXML *
 
117
gossip_glade_get_file (const gchar *filename,
 
118
                       const gchar *root,
 
119
                       const gchar *domain,
 
120
                       const gchar *first_required_widget, ...)
 
121
{
 
122
        va_list   args;
 
123
        GladeXML *gui;
 
124
 
 
125
        va_start (args, first_required_widget);
 
126
 
 
127
        gui = get_glade_file (filename,
 
128
                              root,
 
129
                              domain,
 
130
                              first_required_widget,
 
131
                              args);
 
132
 
 
133
        va_end (args);
 
134
 
 
135
        if (!gui) {
 
136
                return NULL;
 
137
        }
 
138
 
 
139
        return gui;
 
140
}
 
141
 
 
142
void
 
143
gossip_glade_connect (GladeXML *gui,
 
144
                      gpointer  user_data,
 
145
                      gchar     *first_widget, ...)
 
146
{
 
147
        va_list      args;
 
148
        const gchar *name;
 
149
        const gchar *signal;
 
150
        GtkWidget   *widget;
 
151
        gpointer    *callback;
 
152
 
 
153
        va_start (args, first_widget);
 
154
 
 
155
        for (name = first_widget; name; name = va_arg (args, char *)) {
 
156
                signal = va_arg (args, void *);
 
157
                callback = va_arg (args, void *);
 
158
 
 
159
                widget = glade_xml_get_widget (gui, name);
 
160
                if (!widget) {
 
161
                        g_warning ("Glade file is missing widget '%s', aborting",
 
162
                                   name);
 
163
                        continue;
 
164
                }
 
165
 
 
166
                g_signal_connect (widget,
 
167
                                  signal,
 
168
                                  G_CALLBACK (callback),
 
169
                                  user_data);
 
170
        }
 
171
 
 
172
        va_end (args);
 
173
}
 
174
 
 
175
void
 
176
gossip_glade_setup_size_group (GladeXML         *gui,
 
177
                               GtkSizeGroupMode  mode,
 
178
                               gchar            *first_widget, ...)
 
179
{
 
180
        va_list       args;
 
181
        GtkWidget    *widget;
 
182
        GtkSizeGroup *size_group;
 
183
        const gchar  *name;
 
184
 
 
185
        va_start (args, first_widget);
 
186
 
 
187
        size_group = gtk_size_group_new (mode);
 
188
 
 
189
        for (name = first_widget; name; name = va_arg (args, char *)) {
 
190
                widget = glade_xml_get_widget (gui, name);
 
191
                if (!widget) {
 
192
                        g_warning ("Glade file is missing widget '%s'", name);
 
193
                        continue;
 
194
                }
 
195
 
 
196
                gtk_size_group_add_widget (size_group, widget);
 
197
        }
 
198
 
 
199
        g_object_unref (size_group);
 
200
 
 
201
        va_end (args);
 
202
}
 
203
 
 
204
GdkPixbuf *
 
205
gossip_pixbuf_from_icon_name (const gchar *icon_name,
 
206
                              GtkIconSize  icon_size)
 
207
{
 
208
        GtkIconTheme  *theme;
 
209
        GdkPixbuf     *pixbuf = NULL;
 
210
        GError        *error = NULL;
 
211
        gint           w, h;
 
212
        gint           size = 48;
 
213
 
 
214
        theme = gtk_icon_theme_get_default ();
 
215
 
 
216
        if (gtk_icon_size_lookup (icon_size, &w, &h)) {
 
217
                size = (w + h) / 2;
 
218
        }
 
219
 
 
220
        pixbuf = gtk_icon_theme_load_icon (theme,
 
221
                                           icon_name,
 
222
                                           size,
 
223
                                           0,
 
224
                                           &error);
 
225
        if (error) {
 
226
                gossip_debug (DEBUG_DOMAIN, "Error loading icon: %s", error->message);
 
227
                g_clear_error (&error);
 
228
        }
 
229
 
 
230
        return pixbuf;
 
231
}
 
232
 
 
233
GdkPixbuf *
 
234
gossip_pixbuf_from_smiley (GossipSmiley type,
 
235
                           GtkIconSize  icon_size)
 
236
{
 
237
        const gchar *icon_id;
 
238
 
 
239
        switch (type) {
 
240
        case GOSSIP_SMILEY_NORMAL:       /*  :)   */
 
241
                icon_id = "stock_smiley-1";
 
242
                break;
 
243
        case GOSSIP_SMILEY_WINK:         /*  ;)   */
 
244
                icon_id = "stock_smiley-3";
 
245
                break;
 
246
        case GOSSIP_SMILEY_BIGEYE:       /*  =)   */
 
247
                icon_id = "stock_smiley-2";
 
248
                break;
 
249
        case GOSSIP_SMILEY_NOSE:         /*  :-)  */
 
250
                icon_id = "stock_smiley-7";
 
251
                break;
 
252
        case GOSSIP_SMILEY_CRY:          /*  :'(  */
 
253
                icon_id = "stock_smiley-11";
 
254
                break;
 
255
        case GOSSIP_SMILEY_SAD:          /*  :(   */
 
256
                icon_id = "stock_smiley-4";
 
257
                break;
 
258
        case GOSSIP_SMILEY_SCEPTICAL:    /*  :/   */
 
259
                icon_id = "stock_smiley-9";
 
260
                break;
 
261
        case GOSSIP_SMILEY_BIGSMILE:     /*  :D   */
 
262
                icon_id = "stock_smiley-6";
 
263
                break;
 
264
        case GOSSIP_SMILEY_INDIFFERENT:  /*  :|   */
 
265
                icon_id = "stock_smiley-8";
 
266
                break;
 
267
        case GOSSIP_SMILEY_TOUNGE:       /*  :p   */
 
268
                icon_id = "stock_smiley-10";
 
269
                break;
 
270
        case GOSSIP_SMILEY_SHOCKED:      /*  :o   */
 
271
                icon_id = "stock_smiley-5";
 
272
                break;
 
273
        case GOSSIP_SMILEY_COOL:         /*  8)   */
 
274
                icon_id = "stock_smiley-15";
 
275
                break;
 
276
        case GOSSIP_SMILEY_SORRY:        /*  *|   */
 
277
                icon_id = "stock_smiley-12";
 
278
                break;
 
279
        case GOSSIP_SMILEY_KISS:         /*  :*   */
 
280
                icon_id = "stock_smiley-13";
 
281
                break;
 
282
        case GOSSIP_SMILEY_SHUTUP:       /*  :#   */
 
283
                icon_id = "stock_smiley-14";
 
284
                break;
 
285
        case GOSSIP_SMILEY_YAWN:         /*  |O   */
 
286
                icon_id = "";
 
287
                break;
 
288
        case GOSSIP_SMILEY_CONFUSED:     /*  :$   */
 
289
                icon_id = "stock_smiley-17";
 
290
                break;
 
291
        case GOSSIP_SMILEY_ANGEL:        /*  O)   */
 
292
                icon_id = "stock_smiley-18";
 
293
                break;
 
294
        case GOSSIP_SMILEY_OOOH:         /*  :x   */
 
295
                icon_id = "stock_smiley-19";
 
296
                break;
 
297
        case GOSSIP_SMILEY_LOOKAWAY:     /*  *)   */
 
298
                icon_id = "stock_smiley-20";
 
299
                break;
 
300
        case GOSSIP_SMILEY_BLUSH:        /*  *S   */
 
301
                icon_id = "stock_smiley-23";
 
302
                break;
 
303
        case GOSSIP_SMILEY_COOLBIGSMILE: /*  8D   */
 
304
                icon_id = "stock_smiley-25";
 
305
                break;
 
306
        case GOSSIP_SMILEY_ANGRY:        /*  :@   */
 
307
                icon_id = "stock_smiley-16";
 
308
                break;
 
309
        case GOSSIP_SMILEY_BOSS:         /*  @)   */
 
310
                icon_id = "stock_smiley-21";
 
311
                break;
 
312
        case GOSSIP_SMILEY_MONKEY:       /*  #)   */
 
313
                icon_id = "stock_smiley-22";
 
314
                break;
 
315
        case GOSSIP_SMILEY_SILLY:        /*  O)   */
 
316
                icon_id = "stock_smiley-24";
 
317
                break;
 
318
        case GOSSIP_SMILEY_SICK:         /*  +o(  */
 
319
                icon_id = "stock_smiley-26";
 
320
                break;
 
321
 
 
322
        default:
 
323
                g_assert_not_reached ();
 
324
                icon_id = NULL;
 
325
        }
 
326
 
 
327
 
 
328
        return gossip_pixbuf_from_icon_name (icon_id, icon_size);
 
329
}
 
330
 
 
331
const gchar *
 
332
gossip_icon_name_from_account (McAccount *account)
 
333
{
 
334
        McProfile *profile;
 
335
 
 
336
        profile = mc_account_get_profile (account);
 
337
 
 
338
        return mc_profile_get_icon_name (profile);
 
339
}
 
340
 
 
341
const gchar *
 
342
gossip_icon_name_for_presence_state (McPresence state)
 
343
{
 
344
        switch (state) {
 
345
        case MC_PRESENCE_AVAILABLE:
 
346
                return EMPATHY_IMAGE_AVAILABLE;
 
347
        case MC_PRESENCE_DO_NOT_DISTURB:
 
348
                return EMPATHY_IMAGE_BUSY;
 
349
        case MC_PRESENCE_AWAY:
 
350
                return EMPATHY_IMAGE_AWAY;
 
351
        case MC_PRESENCE_EXTENDED_AWAY:
 
352
                return EMPATHY_IMAGE_EXT_AWAY;
 
353
        case MC_PRESENCE_HIDDEN:
 
354
        case MC_PRESENCE_OFFLINE:
 
355
        case MC_PRESENCE_UNSET:
 
356
                return EMPATHY_IMAGE_OFFLINE;
 
357
        default:
 
358
                g_assert_not_reached ();
 
359
        }
 
360
 
 
361
        return NULL;
 
362
}
 
363
 
 
364
const gchar *
 
365
gossip_icon_name_for_presence (GossipPresence *presence)
 
366
{
 
367
        McPresence state;
 
368
 
 
369
        g_return_val_if_fail (GOSSIP_IS_PRESENCE (presence),
 
370
                              EMPATHY_IMAGE_OFFLINE);
 
371
 
 
372
        state = gossip_presence_get_state (presence);
 
373
 
 
374
        return gossip_icon_name_for_presence_state (state);
 
375
}
 
376
 
 
377
const gchar *
 
378
gossip_icon_name_for_contact (GossipContact *contact)
 
379
{
 
380
        GossipPresence     *presence;
 
381
        GossipSubscription  subscription;
 
382
 
 
383
        g_return_val_if_fail (GOSSIP_IS_CONTACT (contact),
 
384
                              EMPATHY_IMAGE_OFFLINE);
 
385
 
 
386
        presence = gossip_contact_get_presence (contact);
 
387
 
 
388
        if (presence) {
 
389
                return gossip_icon_name_for_presence (presence);
 
390
        }
 
391
 
 
392
        subscription = gossip_contact_get_subscription (contact);
 
393
 
 
394
        if (subscription != GOSSIP_SUBSCRIPTION_BOTH &&
 
395
            subscription != GOSSIP_SUBSCRIPTION_TO) {
 
396
                return EMPATHY_IMAGE_PENDING;
 
397
        }
 
398
 
 
399
        return EMPATHY_IMAGE_OFFLINE;
 
400
}
 
401
 
 
402
GdkPixbuf *
 
403
gossip_pixbuf_avatar_from_contact (GossipContact *contact)
 
404
{
 
405
        GdkPixbuf       *pixbuf;
 
406
        GdkPixbufLoader *loader;
 
407
        GossipAvatar    *avatar;
 
408
        GError          *error = NULL;
 
409
 
 
410
        g_return_val_if_fail (GOSSIP_IS_CONTACT (contact), NULL);
 
411
 
 
412
        avatar = gossip_contact_get_avatar (contact);
 
413
        if (!avatar) {
 
414
                return NULL;
 
415
        }
 
416
 
 
417
        loader = gdk_pixbuf_loader_new ();
 
418
 
 
419
        if (!gdk_pixbuf_loader_write (loader, avatar->data, avatar->len, &error)) {
 
420
                g_warning ("Couldn't write avatar image:%p with "
 
421
                           "length:%" G_GSIZE_FORMAT " to pixbuf loader: %s",
 
422
                           avatar->data, avatar->len, error->message);
 
423
                g_error_free (error);
 
424
                return NULL;
 
425
        }
 
426
 
 
427
        gdk_pixbuf_loader_close (loader, NULL);
 
428
 
 
429
        pixbuf = gdk_pixbuf_loader_get_pixbuf (loader);
 
430
 
 
431
        g_object_ref (pixbuf);
 
432
        g_object_unref (loader);
 
433
 
 
434
        return pixbuf;
 
435
}
 
436
 
 
437
static void
 
438
pixbuf_from_avatar_size_prepared_cb (GdkPixbufLoader *loader,
 
439
                                     int              width,
 
440
                                     int              height,
 
441
                                     struct SizeData *data)
 
442
{
 
443
        g_return_if_fail (width > 0 && height > 0);
 
444
 
 
445
        if (data->preserve_aspect_ratio && (data->width > 0 || data->height > 0)) {
 
446
                if (width < data->width && height < data->height) {
 
447
                        width = width;
 
448
                        height = height;
 
449
                }
 
450
 
 
451
                if (data->width < 0) {
 
452
                        width = width * (double) data->height / (gdouble) height;
 
453
                        height = data->height;
 
454
                } else if (data->height < 0) {
 
455
                        height = height * (double) data->width / (double) width;
 
456
                        width = data->width;
 
457
                } else if ((double) height * (double) data->width >
 
458
                           (double) width * (double) data->height) {
 
459
                        width = 0.5 + (double) width * (double) data->height / (double) height;
 
460
                        height = data->height;
 
461
                } else {
 
462
                        height = 0.5 + (double) height * (double) data->width / (double) width;
 
463
                        width = data->width;
 
464
                }
 
465
        } else {
 
466
                if (data->width > 0) {
 
467
                        width = data->width;
 
468
                }
 
469
 
 
470
                if (data->height > 0) {
 
471
                        height = data->height;
 
472
                }
 
473
        }
 
474
 
 
475
        gdk_pixbuf_loader_set_size (loader, width, height);
 
476
}
 
477
 
 
478
GdkPixbuf *
 
479
gossip_pixbuf_from_avatar_scaled (GossipAvatar *avatar,
 
480
                                  gint          width,
 
481
                                  gint          height)
 
482
{
 
483
        GdkPixbuf        *pixbuf;
 
484
        GdkPixbufLoader  *loader;
 
485
        struct SizeData   data;
 
486
        GError           *error = NULL;
 
487
 
 
488
        if (!avatar) {
 
489
                return NULL;
 
490
        }
 
491
 
 
492
        data.width = width;
 
493
        data.height = height;
 
494
        data.preserve_aspect_ratio = TRUE;
 
495
 
 
496
        loader = gdk_pixbuf_loader_new ();
 
497
 
 
498
        g_signal_connect (loader, "size-prepared",
 
499
                          G_CALLBACK (pixbuf_from_avatar_size_prepared_cb),
 
500
                          &data);
 
501
 
 
502
        if (!gdk_pixbuf_loader_write (loader, avatar->data, avatar->len, &error)) {
 
503
                g_warning ("Couldn't write avatar image:%p with "
 
504
                           "length:%" G_GSIZE_FORMAT " to pixbuf loader: %s",
 
505
                           avatar->data, avatar->len, error->message);
 
506
                g_error_free (error);
 
507
                return NULL;
 
508
        }
 
509
 
 
510
        gdk_pixbuf_loader_close (loader, NULL);
 
511
 
 
512
        pixbuf = gdk_pixbuf_loader_get_pixbuf (loader);
 
513
 
 
514
        g_object_ref (pixbuf);
 
515
        g_object_unref (loader);
 
516
 
 
517
        return pixbuf;
 
518
}
 
519
 
 
520
GdkPixbuf *
 
521
gossip_pixbuf_avatar_from_contact_scaled (GossipContact *contact,
 
522
                                          gint           width,
 
523
                                          gint           height)
 
524
{
 
525
        GossipAvatar *avatar;
 
526
 
 
527
        g_return_val_if_fail (GOSSIP_IS_CONTACT (contact), NULL);
 
528
 
 
529
        avatar = gossip_contact_get_avatar (contact);
 
530
 
 
531
        return gossip_pixbuf_from_avatar_scaled (avatar, width, height);
 
532
}
 
533
/* Stolen from GtkSourceView, hence the weird intendation. Please keep it like
 
534
 * that to make it easier to apply changes from the original code.
 
535
 */
 
536
#define GTK_TEXT_UNKNOWN_CHAR 0xFFFC
 
537
 
 
538
/* this function acts like g_utf8_offset_to_pointer() except that if it finds a
 
539
 * decomposable character it consumes the decomposition length from the given
 
540
 * offset.  So it's useful when the offset was calculated for the normalized
 
541
 * version of str, but we need a pointer to str itself. */
 
542
static const gchar *
 
543
pointer_from_offset_skipping_decomp (const gchar *str, gint offset)
 
544
{
 
545
        gchar *casefold, *normal;
 
546
        const gchar *p, *q;
 
547
 
 
548
        p = str;
 
549
        while (offset > 0)
 
550
        {
 
551
                q = g_utf8_next_char (p);
 
552
                casefold = g_utf8_casefold (p, q - p);
 
553
                normal = g_utf8_normalize (casefold, -1, G_NORMALIZE_NFD);
 
554
                offset -= g_utf8_strlen (normal, -1);
 
555
                g_free (casefold);
 
556
                g_free (normal);
 
557
                p = q;
 
558
        }
 
559
        return p;
 
560
}
 
561
 
 
562
static const gchar *
 
563
g_utf8_strcasestr (const gchar *haystack, const gchar *needle)
 
564
{
 
565
        gsize needle_len;
 
566
        gsize haystack_len;
 
567
        const gchar *ret = NULL;
 
568
        gchar *p;
 
569
        gchar *casefold;
 
570
        gchar *caseless_haystack;
 
571
        gint i;
 
572
 
 
573
        g_return_val_if_fail (haystack != NULL, NULL);
 
574
        g_return_val_if_fail (needle != NULL, NULL);
 
575
 
 
576
        casefold = g_utf8_casefold (haystack, -1);
 
577
        caseless_haystack = g_utf8_normalize (casefold, -1, G_NORMALIZE_NFD);
 
578
        g_free (casefold);
 
579
 
 
580
        needle_len = g_utf8_strlen (needle, -1);
 
581
        haystack_len = g_utf8_strlen (caseless_haystack, -1);
 
582
 
 
583
        if (needle_len == 0)
 
584
        {
 
585
                ret = (gchar *)haystack;
 
586
                goto finally_1;
 
587
        }
 
588
 
 
589
        if (haystack_len < needle_len)
 
590
        {
 
591
                ret = NULL;
 
592
                goto finally_1;
 
593
        }
 
594
 
 
595
        p = (gchar*)caseless_haystack;
 
596
        needle_len = strlen (needle);
 
597
        i = 0;
 
598
 
 
599
        while (*p)
 
600
        {
 
601
                if ((strncmp (p, needle, needle_len) == 0))
 
602
                {
 
603
                        ret = pointer_from_offset_skipping_decomp (haystack, i);
 
604
                        goto finally_1;
 
605
                }
 
606
 
 
607
                p = g_utf8_next_char (p);
 
608
                i++;
 
609
        }
 
610
 
 
611
finally_1:
 
612
        g_free (caseless_haystack);
 
613
 
 
614
        return ret;
 
615
}
 
616
 
 
617
static gboolean
 
618
g_utf8_caselessnmatch (const char *s1, const char *s2,
 
619
                       gssize n1, gssize n2)
 
620
{
 
621
        gchar *casefold;
 
622
        gchar *normalized_s1;
 
623
        gchar *normalized_s2;
 
624
        gint len_s1;
 
625
        gint len_s2;
 
626
        gboolean ret = FALSE;
 
627
 
 
628
        g_return_val_if_fail (s1 != NULL, FALSE);
 
629
        g_return_val_if_fail (s2 != NULL, FALSE);
 
630
        g_return_val_if_fail (n1 > 0, FALSE);
 
631
        g_return_val_if_fail (n2 > 0, FALSE);
 
632
 
 
633
        casefold = g_utf8_casefold (s1, n1);
 
634
        normalized_s1 = g_utf8_normalize (casefold, -1, G_NORMALIZE_NFD);
 
635
        g_free (casefold);
 
636
 
 
637
        casefold = g_utf8_casefold (s2, n2);
 
638
        normalized_s2 = g_utf8_normalize (casefold, -1, G_NORMALIZE_NFD);
 
639
        g_free (casefold);
 
640
 
 
641
        len_s1 = strlen (normalized_s1);
 
642
        len_s2 = strlen (normalized_s2);
 
643
 
 
644
        if (len_s1 < len_s2)
 
645
                goto finally_2;
 
646
 
 
647
        ret = (strncmp (normalized_s1, normalized_s2, len_s2) == 0);
 
648
 
 
649
finally_2:
 
650
        g_free (normalized_s1);
 
651
        g_free (normalized_s2);
 
652
 
 
653
        return ret;
 
654
}
 
655
 
 
656
static void
 
657
forward_chars_with_skipping (GtkTextIter *iter,
 
658
                             gint         count,
 
659
                             gboolean     skip_invisible,
 
660
                             gboolean     skip_nontext,
 
661
                             gboolean     skip_decomp)
 
662
{
 
663
        gint i;
 
664
 
 
665
        g_return_if_fail (count >= 0);
 
666
 
 
667
        i = count;
 
668
 
 
669
        while (i > 0)
 
670
        {
 
671
                gboolean ignored = FALSE;
 
672
 
 
673
                /* minimal workaround to avoid the infinite loop of bug #168247.
 
674
                 * It doesn't fix the problemjust the symptom...
 
675
                 */
 
676
                if (gtk_text_iter_is_end (iter))
 
677
                        return;
 
678
 
 
679
                if (skip_nontext && gtk_text_iter_get_char (iter) == GTK_TEXT_UNKNOWN_CHAR)
 
680
                        ignored = TRUE;
 
681
 
 
682
                if (!ignored && skip_invisible &&
 
683
                    /* _gtk_text_btree_char_is_invisible (iter)*/ FALSE)
 
684
                        ignored = TRUE;
 
685
 
 
686
                if (!ignored && skip_decomp)
 
687
                {
 
688
                        /* being UTF8 correct sucks; this accounts for extra
 
689
                           offsets coming from canonical decompositions of
 
690
                           UTF8 characters (e.g. accented characters) which
 
691
                           g_utf8_normalize() performs */
 
692
                        gchar *normal;
 
693
                        gchar buffer[6];
 
694
                        gint buffer_len;
 
695
 
 
696
                        buffer_len = g_unichar_to_utf8 (gtk_text_iter_get_char (iter), buffer);
 
697
                        normal = g_utf8_normalize (buffer, buffer_len, G_NORMALIZE_NFD);
 
698
                        i -= (g_utf8_strlen (normal, -1) - 1);
 
699
                        g_free (normal);
 
700
                }
 
701
 
 
702
                gtk_text_iter_forward_char (iter);
 
703
 
 
704
                if (!ignored)
 
705
                        --i;
 
706
        }
 
707
}
 
708
 
 
709
static gboolean
 
710
lines_match (const GtkTextIter *start,
 
711
             const gchar      **lines,
 
712
             gboolean           visible_only,
 
713
             gboolean           slice,
 
714
             GtkTextIter       *match_start,
 
715
             GtkTextIter       *match_end)
 
716
{
 
717
        GtkTextIter next;
 
718
        gchar *line_text;
 
719
        const gchar *found;
 
720
        gint offset;
 
721
 
 
722
        if (*lines == NULL || **lines == '\0')
 
723
        {
 
724
                if (match_start)
 
725
                        *match_start = *start;
 
726
                if (match_end)
 
727
                        *match_end = *start;
 
728
                return TRUE;
 
729
        }
 
730
 
 
731
        next = *start;
 
732
        gtk_text_iter_forward_line (&next);
 
733
 
 
734
        /* No more text in buffer, but *lines is nonempty */
 
735
        if (gtk_text_iter_equal (start, &next))
 
736
                return FALSE;
 
737
 
 
738
        if (slice)
 
739
        {
 
740
                if (visible_only)
 
741
                        line_text = gtk_text_iter_get_visible_slice (start, &next);
 
742
                else
 
743
                        line_text = gtk_text_iter_get_slice (start, &next);
 
744
        }
 
745
        else
 
746
        {
 
747
                if (visible_only)
 
748
                        line_text = gtk_text_iter_get_visible_text (start, &next);
 
749
                else
 
750
                        line_text = gtk_text_iter_get_text (start, &next);
 
751
        }
 
752
 
 
753
        if (match_start) /* if this is the first line we're matching */
 
754
        {
 
755
                found = g_utf8_strcasestr (line_text, *lines);
 
756
        }
 
757
        else
 
758
        {
 
759
                /* If it's not the first line, we have to match from the
 
760
                 * start of the line.
 
761
                 */
 
762
                if (g_utf8_caselessnmatch (line_text, *lines, strlen (line_text),
 
763
                                           strlen (*lines)))
 
764
                        found = line_text;
 
765
                else
 
766
                        found = NULL;
 
767
        }
 
768
 
 
769
        if (found == NULL)
 
770
        {
 
771
                g_free (line_text);
 
772
                return FALSE;
 
773
        }
 
774
 
 
775
        /* Get offset to start of search string */
 
776
        offset = g_utf8_strlen (line_text, found - line_text);
 
777
 
 
778
        next = *start;
 
779
 
 
780
        /* If match start needs to be returned, set it to the
 
781
         * start of the search string.
 
782
         */
 
783
        forward_chars_with_skipping (&next, offset, visible_only, !slice, FALSE);
 
784
        if (match_start)
 
785
        {
 
786
                *match_start = next;
 
787
        }
 
788
 
 
789
        /* Go to end of search string */
 
790
        forward_chars_with_skipping (&next, g_utf8_strlen (*lines, -1), visible_only, !slice, TRUE);
 
791
 
 
792
        g_free (line_text);
 
793
 
 
794
        ++lines;
 
795
 
 
796
        if (match_end)
 
797
                *match_end = next;
 
798
 
 
799
        /* pass NULL for match_start, since we don't need to find the
 
800
         * start again.
 
801
         */
 
802
        return lines_match (&next, lines, visible_only, slice, NULL, match_end);
 
803
}
 
804
 
 
805
/* strsplit () that retains the delimiter as part of the string. */
 
806
static gchar **
 
807
strbreakup (const char *string,
 
808
            const char *delimiter,
 
809
            gint        max_tokens)
 
810
{
 
811
        GSList *string_list = NULL, *slist;
 
812
        gchar **str_array, *s, *casefold, *new_string;
 
813
        guint i, n = 1;
 
814
 
 
815
        g_return_val_if_fail (string != NULL, NULL);
 
816
        g_return_val_if_fail (delimiter != NULL, NULL);
 
817
 
 
818
        if (max_tokens < 1)
 
819
                max_tokens = G_MAXINT;
 
820
 
 
821
        s = strstr (string, delimiter);
 
822
        if (s)
 
823
        {
 
824
                guint delimiter_len = strlen (delimiter);
 
825
 
 
826
                do
 
827
                {
 
828
                        guint len;
 
829
 
 
830
                        len = s - string + delimiter_len;
 
831
                        new_string = g_new (gchar, len + 1);
 
832
                        strncpy (new_string, string, len);
 
833
                        new_string[len] = 0;
 
834
                        casefold = g_utf8_casefold (new_string, -1);
 
835
                        g_free (new_string);
 
836
                        new_string = g_utf8_normalize (casefold, -1, G_NORMALIZE_NFD);
 
837
                        g_free (casefold);
 
838
                        string_list = g_slist_prepend (string_list, new_string);
 
839
                        n++;
 
840
                        string = s + delimiter_len;
 
841
                        s = strstr (string, delimiter);
 
842
                } while (--max_tokens && s);
 
843
        }
 
844
 
 
845
        if (*string)
 
846
        {
 
847
                n++;
 
848
                casefold = g_utf8_casefold (string, -1);
 
849
                new_string = g_utf8_normalize (casefold, -1, G_NORMALIZE_NFD);
 
850
                g_free (casefold);
 
851
                string_list = g_slist_prepend (string_list, new_string);
 
852
        }
 
853
 
 
854
        str_array = g_new (gchar*, n);
 
855
 
 
856
        i = n - 1;
 
857
 
 
858
        str_array[i--] = NULL;
 
859
        for (slist = string_list; slist; slist = slist->next)
 
860
                str_array[i--] = slist->data;
 
861
 
 
862
        g_slist_free (string_list);
 
863
 
 
864
        return str_array;
 
865
}
 
866
 
 
867
gboolean
 
868
gossip_text_iter_forward_search (const GtkTextIter   *iter,
 
869
                                 const gchar         *str,
 
870
                                 GtkTextIter         *match_start,
 
871
                                 GtkTextIter         *match_end,
 
872
                                 const GtkTextIter   *limit)
 
873
{
 
874
        gchar **lines = NULL;
 
875
        GtkTextIter match;
 
876
        gboolean retval = FALSE;
 
877
        GtkTextIter search;
 
878
        gboolean visible_only;
 
879
        gboolean slice;
 
880
 
 
881
        g_return_val_if_fail (iter != NULL, FALSE);
 
882
        g_return_val_if_fail (str != NULL, FALSE);
 
883
 
 
884
        if (limit && gtk_text_iter_compare (iter, limit) >= 0)
 
885
                return FALSE;
 
886
 
 
887
        if (*str == '\0') {
 
888
                /* If we can move one char, return the empty string there */
 
889
                match = *iter;
 
890
 
 
891
                if (gtk_text_iter_forward_char (&match)) {
 
892
                        if (limit && gtk_text_iter_equal (&match, limit)) {
 
893
                                return FALSE;
 
894
                        }
 
895
 
 
896
                        if (match_start) {
 
897
                                *match_start = match;
 
898
                        }
 
899
                        if (match_end) {
 
900
                                *match_end = match;
 
901
                        }
 
902
                        return TRUE;
 
903
                } else {
 
904
                        return FALSE;
 
905
                }
 
906
        }
 
907
 
 
908
        visible_only = TRUE;
 
909
        slice = FALSE;
 
910
 
 
911
        /* locate all lines */
 
912
        lines = strbreakup (str, "\n", -1);
 
913
 
 
914
        search = *iter;
 
915
 
 
916
        do {
 
917
                /* This loop has an inefficient worst-case, where
 
918
                 * gtk_text_iter_get_text () is called repeatedly on
 
919
                 * a single line.
 
920
                 */
 
921
                GtkTextIter end;
 
922
 
 
923
                if (limit && gtk_text_iter_compare (&search, limit) >= 0) {
 
924
                        break;
 
925
                }
 
926
 
 
927
                if (lines_match (&search, (const gchar**)lines,
 
928
                                 visible_only, slice, &match, &end)) {
 
929
                        if (limit == NULL ||
 
930
                            (limit && gtk_text_iter_compare (&end, limit) <= 0)) {
 
931
                                retval = TRUE;
 
932
 
 
933
                                if (match_start) {
 
934
                                        *match_start = match;
 
935
                                }
 
936
                                if (match_end) {
 
937
                                        *match_end = end;
 
938
                                }
 
939
                        }
 
940
                        break;
 
941
                }
 
942
        } while (gtk_text_iter_forward_line (&search));
 
943
 
 
944
        g_strfreev ((gchar**)lines);
 
945
 
 
946
        return retval;
 
947
}
 
948
 
 
949
static const gchar *
 
950
g_utf8_strrcasestr (const gchar *haystack, const gchar *needle)
 
951
{
 
952
        gsize needle_len;
 
953
        gsize haystack_len;
 
954
        const gchar *ret = NULL;
 
955
        gchar *p;
 
956
        gchar *casefold;
 
957
        gchar *caseless_haystack;
 
958
        gint i;
 
959
 
 
960
        g_return_val_if_fail (haystack != NULL, NULL);
 
961
        g_return_val_if_fail (needle != NULL, NULL);
 
962
 
 
963
        casefold = g_utf8_casefold (haystack, -1);
 
964
        caseless_haystack = g_utf8_normalize (casefold, -1, G_NORMALIZE_NFD);
 
965
        g_free (casefold);
 
966
 
 
967
        needle_len = g_utf8_strlen (needle, -1);
 
968
        haystack_len = g_utf8_strlen (caseless_haystack, -1);
 
969
 
 
970
        if (needle_len == 0)
 
971
        {
 
972
                ret = (gchar *)haystack;
 
973
                goto finally_1;
 
974
        }
 
975
 
 
976
        if (haystack_len < needle_len)
 
977
        {
 
978
                ret = NULL;
 
979
                goto finally_1;
 
980
        }
 
981
 
 
982
        i = haystack_len - needle_len;
 
983
        p = g_utf8_offset_to_pointer (caseless_haystack, i);
 
984
        needle_len = strlen (needle);
 
985
 
 
986
        while (p >= caseless_haystack)
 
987
        {
 
988
                if (strncmp (p, needle, needle_len) == 0)
 
989
                {
 
990
                        ret = pointer_from_offset_skipping_decomp (haystack, i);
 
991
                        goto finally_1;
 
992
                }
 
993
 
 
994
                p = g_utf8_prev_char (p);
 
995
                i--;
 
996
        }
 
997
 
 
998
finally_1:
 
999
        g_free (caseless_haystack);
 
1000
 
 
1001
        return ret;
 
1002
}
 
1003
 
 
1004
static gboolean
 
1005
backward_lines_match (const GtkTextIter *start,
 
1006
                      const gchar      **lines,
 
1007
                      gboolean           visible_only,
 
1008
                      gboolean           slice,
 
1009
                      GtkTextIter       *match_start,
 
1010
                      GtkTextIter       *match_end)
 
1011
{
 
1012
        GtkTextIter line, next;
 
1013
        gchar *line_text;
 
1014
        const gchar *found;
 
1015
        gint offset;
 
1016
 
 
1017
        if (*lines == NULL || **lines == '\0')
 
1018
        {
 
1019
                if (match_start)
 
1020
                        *match_start = *start;
 
1021
                if (match_end)
 
1022
                        *match_end = *start;
 
1023
                return TRUE;
 
1024
        }
 
1025
 
 
1026
        line = next = *start;
 
1027
        if (gtk_text_iter_get_line_offset (&next) == 0)
 
1028
        {
 
1029
                if (!gtk_text_iter_backward_line (&next))
 
1030
                        return FALSE;
 
1031
        }
 
1032
        else
 
1033
                gtk_text_iter_set_line_offset (&next, 0);
 
1034
 
 
1035
        if (slice)
 
1036
        {
 
1037
                if (visible_only)
 
1038
                        line_text = gtk_text_iter_get_visible_slice (&next, &line);
 
1039
                else
 
1040
                        line_text = gtk_text_iter_get_slice (&next, &line);
 
1041
        }
 
1042
        else
 
1043
        {
 
1044
                if (visible_only)
 
1045
                        line_text = gtk_text_iter_get_visible_text (&next, &line);
 
1046
                else
 
1047
                        line_text = gtk_text_iter_get_text (&next, &line);
 
1048
        }
 
1049
 
 
1050
        if (match_start) /* if this is the first line we're matching */
 
1051
        {
 
1052
                found = g_utf8_strrcasestr (line_text, *lines);
 
1053
        }
 
1054
        else
 
1055
        {
 
1056
                /* If it's not the first line, we have to match from the
 
1057
                 * start of the line.
 
1058
                 */
 
1059
                if (g_utf8_caselessnmatch (line_text, *lines, strlen (line_text),
 
1060
                                           strlen (*lines)))
 
1061
                        found = line_text;
 
1062
                else
 
1063
                        found = NULL;
 
1064
        }
 
1065
 
 
1066
        if (found == NULL)
 
1067
        {
 
1068
                g_free (line_text);
 
1069
                return FALSE;
 
1070
        }
 
1071
 
 
1072
        /* Get offset to start of search string */
 
1073
        offset = g_utf8_strlen (line_text, found - line_text);
 
1074
 
 
1075
        forward_chars_with_skipping (&next, offset, visible_only, !slice, FALSE);
 
1076
 
 
1077
        /* If match start needs to be returned, set it to the
 
1078
         * start of the search string.
 
1079
         */
 
1080
        if (match_start)
 
1081
        {
 
1082
                *match_start = next;
 
1083
        }
 
1084
 
 
1085
        /* Go to end of search string */
 
1086
        forward_chars_with_skipping (&next, g_utf8_strlen (*lines, -1), visible_only, !slice, TRUE);
 
1087
 
 
1088
        g_free (line_text);
 
1089
 
 
1090
        ++lines;
 
1091
 
 
1092
        if (match_end)
 
1093
                *match_end = next;
 
1094
 
 
1095
        /* try to match the rest of the lines forward, passing NULL
 
1096
         * for match_start so lines_match will try to match the entire
 
1097
         * line */
 
1098
        return lines_match (&next, lines, visible_only,
 
1099
                            slice, NULL, match_end);
 
1100
}
 
1101
 
 
1102
gboolean
 
1103
gossip_text_iter_backward_search (const GtkTextIter   *iter,
 
1104
                                  const gchar         *str,
 
1105
                                  GtkTextIter         *match_start,
 
1106
                                  GtkTextIter         *match_end,
 
1107
                                  const GtkTextIter   *limit)
 
1108
{
 
1109
        gchar **lines = NULL;
 
1110
        GtkTextIter match;
 
1111
        gboolean retval = FALSE;
 
1112
        GtkTextIter search;
 
1113
        gboolean visible_only;
 
1114
        gboolean slice;
 
1115
 
 
1116
        g_return_val_if_fail (iter != NULL, FALSE);
 
1117
        g_return_val_if_fail (str != NULL, FALSE);
 
1118
 
 
1119
        if (limit && gtk_text_iter_compare (iter, limit) <= 0)
 
1120
                return FALSE;
 
1121
 
 
1122
        if (*str == '\0')
 
1123
        {
 
1124
                /* If we can move one char, return the empty string there */
 
1125
                match = *iter;
 
1126
 
 
1127
                if (gtk_text_iter_backward_char (&match))
 
1128
                {
 
1129
                        if (limit && gtk_text_iter_equal (&match, limit))
 
1130
                                return FALSE;
 
1131
 
 
1132
                        if (match_start)
 
1133
                                *match_start = match;
 
1134
                        if (match_end)
 
1135
                                *match_end = match;
 
1136
                        return TRUE;
 
1137
                }
 
1138
                else
 
1139
                {
 
1140
                        return FALSE;
 
1141
                }
 
1142
        }
 
1143
 
 
1144
        visible_only = TRUE;
 
1145
        slice = TRUE;
 
1146
 
 
1147
        /* locate all lines */
 
1148
        lines = strbreakup (str, "\n", -1);
 
1149
 
 
1150
        search = *iter;
 
1151
 
 
1152
        while (TRUE)
 
1153
        {
 
1154
                /* This loop has an inefficient worst-case, where
 
1155
                 * gtk_text_iter_get_text () is called repeatedly on
 
1156
                 * a single line.
 
1157
                 */
 
1158
                GtkTextIter end;
 
1159
 
 
1160
                if (limit && gtk_text_iter_compare (&search, limit) <= 0)
 
1161
                        break;
 
1162
 
 
1163
                if (backward_lines_match (&search, (const gchar**)lines,
 
1164
                                          visible_only, slice, &match, &end))
 
1165
                {
 
1166
                        if (limit == NULL || (limit &&
 
1167
                                              gtk_text_iter_compare (&end, limit) > 0))
 
1168
                        {
 
1169
                                retval = TRUE;
 
1170
 
 
1171
                                if (match_start)
 
1172
                                        *match_start = match;
 
1173
                                if (match_end)
 
1174
                                        *match_end = end;
 
1175
                        }
 
1176
                        break;
 
1177
                }
 
1178
 
 
1179
                if (gtk_text_iter_get_line_offset (&search) == 0)
 
1180
                {
 
1181
                        if (!gtk_text_iter_backward_line (&search))
 
1182
                                break;
 
1183
                }
 
1184
                else
 
1185
                {
 
1186
                        gtk_text_iter_set_line_offset (&search, 0);
 
1187
                }
 
1188
        }
 
1189
 
 
1190
        g_strfreev ((gchar**)lines);
 
1191
 
 
1192
        return retval;
 
1193
}
 
1194
 
 
1195
static gboolean
 
1196
window_get_is_on_current_workspace (GtkWindow *window)
 
1197
{
 
1198
        GdkWindow *gdk_window;
 
1199
 
 
1200
        gdk_window = GTK_WIDGET (window)->window;
 
1201
        if (gdk_window) {
 
1202
                return !(gdk_window_get_state (gdk_window) &
 
1203
                         GDK_WINDOW_STATE_ICONIFIED);
 
1204
        } else {
 
1205
                return FALSE;
 
1206
        }
 
1207
}
 
1208
 
 
1209
/* Checks if the window is visible as in visible on the current workspace. */
 
1210
gboolean
 
1211
gossip_window_get_is_visible (GtkWindow *window)
 
1212
{
 
1213
        gboolean visible;
 
1214
 
 
1215
        g_return_val_if_fail (window != NULL, FALSE);
 
1216
 
 
1217
        g_object_get (window,
 
1218
                      "visible", &visible,
 
1219
                      NULL);
 
1220
 
 
1221
        return visible && window_get_is_on_current_workspace (window);
 
1222
}
 
1223
 
 
1224
/* Takes care of moving the window to the current workspace. */
 
1225
void
 
1226
gossip_window_present (GtkWindow *window,
 
1227
                       gboolean   steal_focus)
 
1228
{
 
1229
        gboolean visible;
 
1230
        gboolean on_current;
 
1231
        guint32  timestamp;
 
1232
 
 
1233
        g_return_if_fail (window != NULL);
 
1234
 
 
1235
        /* There are three cases: hidden, visible, visible on another
 
1236
         * workspace.
 
1237
         */
 
1238
 
 
1239
        g_object_get (window,
 
1240
                      "visible", &visible,
 
1241
                      NULL);
 
1242
 
 
1243
        on_current = window_get_is_on_current_workspace (window);
 
1244
 
 
1245
        if (visible && !on_current) {
 
1246
                /* Hide it so present brings it to the current workspace. */
 
1247
                gtk_widget_hide (GTK_WIDGET (window));
 
1248
        }
 
1249
 
 
1250
        timestamp = gtk_get_current_event_time ();
 
1251
        if (steal_focus && timestamp != GDK_CURRENT_TIME) {
 
1252
                gtk_window_present_with_time (window, timestamp);
 
1253
        } else {
 
1254
                gtk_window_present (window);
 
1255
        }
 
1256
}
 
1257
 
 
1258
/* The URL opening code can't handle schemeless strings, so we try to be
 
1259
 * smart and add http if there is no scheme or doesn't look like a mail
 
1260
 * address. This should work in most cases, and let us click on strings
 
1261
 * like "www.gnome.org".
 
1262
 */
 
1263
static gchar *
 
1264
fixup_url (const gchar *url)
 
1265
{
 
1266
        gchar *real_url;
 
1267
 
 
1268
        if (!g_str_has_prefix (url, "http://") &&
 
1269
            !strstr (url, ":/") &&
 
1270
            !strstr (url, "@")) {
 
1271
                real_url = g_strdup_printf ("http://%s", url);
 
1272
        } else {
 
1273
                real_url = g_strdup (url);
 
1274
        }
 
1275
 
 
1276
        return real_url;
 
1277
}
 
1278
 
 
1279
void
 
1280
gossip_url_show (const char *url)
 
1281
{
 
1282
        gchar  *real_url;
 
1283
        GError *error = NULL;
 
1284
 
 
1285
        real_url = fixup_url (url);
 
1286
        gnome_url_show (real_url, &error);
 
1287
        if (error) {
 
1288
                g_warning ("Couldn't show URL:'%s'", real_url);
 
1289
                g_error_free (error);
 
1290
        }
 
1291
 
 
1292
        g_free (real_url);
 
1293
}
 
1294
 
 
1295
static void
 
1296
link_button_hook (GtkLinkButton *button,
 
1297
                  const gchar *link,
 
1298
                  gpointer user_data)
 
1299
{
 
1300
        gossip_url_show (link);
 
1301
}
 
1302
 
 
1303
GtkWidget *
 
1304
gossip_link_button_new (const gchar *url,
 
1305
                        const gchar *title)
 
1306
{
 
1307
        static gboolean hook = FALSE;
 
1308
 
 
1309
        if (!hook) {
 
1310
                hook = TRUE;
 
1311
                gtk_link_button_set_uri_hook (link_button_hook, NULL, NULL);
 
1312
        }
 
1313
 
 
1314
        return gtk_link_button_new_with_label (url, title);
 
1315
}
 
1316
 
 
1317
/* FIXME: Do this in a proper way at some point, probably in GTK+? */
 
1318
void
 
1319
gossip_window_set_default_icon_name (const gchar *name)
 
1320
{
 
1321
        gtk_window_set_default_icon_name (name);
 
1322
}
 
1323
 
 
1324
void
 
1325
gossip_toggle_button_set_state_quietly (GtkWidget *widget,
 
1326
                                        GCallback  callback,
 
1327
                                        gpointer   user_data,
 
1328
                                        gboolean   active)
 
1329
{
 
1330
        g_return_if_fail (GTK_IS_TOGGLE_BUTTON (widget));
 
1331
 
 
1332
        g_signal_handlers_block_by_func (widget, callback, user_data);
 
1333
        g_object_set (widget, "active", active, NULL);
 
1334
        g_signal_handlers_unblock_by_func (widget, callback, user_data);
 
1335
}
 
1336