~ubuntu-branches/ubuntu/trusty/gnome-documents/trusty

« back to all changes in this revision

Viewing changes to src/lib/gd-places-links.c

  • Committer: Package Import Robot
  • Author(s): Andreas Henriksson, Thomas Bechtold
  • Date: 2013-04-04 13:32:08 UTC
  • mfrom: (3.1.4 experimental)
  • Revision ID: package-import@ubuntu.com-20130404133208-n19gqczi05z31ogb
Tags: 3.8.0-1
[ Thomas Bechtold ]
New upstream release

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8;  -*-
 
2
 *
 
3
 *  Copyright (C) 2004 Red Hat, Inc.
 
4
 *  Copyright (C) 2013 Red Hat, Inc.
 
5
 *
 
6
 *  This program is free software; you can redistribute it and/or modify
 
7
 *  it under the terms of the GNU General Public License as published by
 
8
 *  the Free Software Foundation; either version 2, or (at your option)
 
9
 *  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
 
14
 *  GNU General Public License for more details.
 
15
 *
 
16
 *  You should have received a copy of the GNU General Public License
 
17
 *  along with this program; if not, write to the Free Software
 
18
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 
19
 */
 
20
 
 
21
#ifdef HAVE_CONFIG_H
 
22
#include "config.h"
 
23
#endif
 
24
 
 
25
#include <string.h>
 
26
 
 
27
#include <glib/gi18n.h>
 
28
#include <gtk/gtk.h>
 
29
 
 
30
#include <evince-document.h>
 
31
#include <evince-view.h>
 
32
 
 
33
#include <libgd/gd.h>
 
34
 
 
35
#include "gd-places-links.h"
 
36
#include "gd-places-page.h"
 
37
 
 
38
struct _GdPlacesLinksPrivate {
 
39
        GtkWidget *tree_view;
 
40
 
 
41
        guint page_changed_id;
 
42
        guint link_activated_id;
 
43
 
 
44
        EvJob *job;
 
45
        GtkTreeModel *model;
 
46
        EvDocument *document;
 
47
        EvDocumentModel *document_model;
 
48
        const char *name;
 
49
};
 
50
 
 
51
enum {
 
52
        PROP_0,
 
53
        PROP_NAME,
 
54
        PROP_DOCUMENT_MODEL,
 
55
};
 
56
 
 
57
enum {
 
58
        LINK_ACTIVATED,
 
59
        N_SIGNALS
 
60
};
 
61
 
 
62
static guint signals[N_SIGNALS];
 
63
 
 
64
static void gd_places_links_page_iface_init (GdPlacesPageInterface *iface);
 
65
 
 
66
G_DEFINE_TYPE_EXTENDED (GdPlacesLinks,
 
67
                        gd_places_links,
 
68
                        GTK_TYPE_BOX,
 
69
                        0,
 
70
                        G_IMPLEMENT_INTERFACE (GD_TYPE_PLACES_PAGE,
 
71
                                               gd_places_links_page_iface_init))
 
72
 
 
73
 
 
74
#define GD_PLACES_LINKS_GET_PRIVATE(object) \
 
75
        (G_TYPE_INSTANCE_GET_PRIVATE ((object), GD_TYPE_PLACES_LINKS, GdPlacesLinksPrivate))
 
76
 
 
77
static gboolean
 
78
emit_link_activated (GdPlacesLinks *self)
 
79
{
 
80
        GtkTreeSelection *selection;
 
81
        GtkTreeModel *model;
 
82
        GtkTreeIter iter;
 
83
 
 
84
        selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self->priv->tree_view));
 
85
        if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
 
86
                EvLink *link;
 
87
                EvDocumentModel *document_model;
 
88
 
 
89
                gtk_tree_model_get (model, &iter,
 
90
                                    EV_DOCUMENT_LINKS_COLUMN_LINK, &link,
 
91
                                    -1);
 
92
 
 
93
                if (link == NULL) {
 
94
                        return FALSE;
 
95
                }
 
96
 
 
97
 
 
98
                document_model = g_object_ref (self->priv->document_model);
 
99
                if (self->priv->page_changed_id > 0) {
 
100
                        g_signal_handler_block (document_model,
 
101
                                                self->priv->page_changed_id);
 
102
                }
 
103
                g_signal_emit (self, signals[LINK_ACTIVATED], 0, link);
 
104
                if (self->priv->page_changed_id > 0) {
 
105
                        g_signal_handler_unblock (document_model,
 
106
                                                  self->priv->page_changed_id);
 
107
                }
 
108
                g_object_unref (document_model);
 
109
 
 
110
                g_object_unref (link);
 
111
        }
 
112
 
 
113
        self->priv->link_activated_id = 0;
 
114
 
 
115
        return FALSE;
 
116
}
 
117
 
 
118
static void
 
119
schedule_emit_link_activated (GdPlacesLinks *self)
 
120
{
 
121
        /* jump through some hoops to avoid destroying in the middle
 
122
           of a button release handler */
 
123
        if (self->priv->link_activated_id == 0) {
 
124
                self->priv->link_activated_id = g_idle_add ((GSourceFunc) emit_link_activated, self);
 
125
        }
 
126
}
 
127
 
 
128
static gboolean
 
129
update_page_cb_foreach (GtkTreeModel  *model,
 
130
                        GtkTreePath   *path,
 
131
                        GtkTreeIter   *iter,
 
132
                        GdPlacesLinks *self)
 
133
{
 
134
        EvLink *link;
 
135
        int current_page;
 
136
        int dest_page;
 
137
        EvDocumentLinks *document_links;
 
138
 
 
139
        gtk_tree_model_get (model, iter,
 
140
                            EV_DOCUMENT_LINKS_COLUMN_LINK, &link,
 
141
                            -1);
 
142
        if (link == NULL) {
 
143
                return FALSE;
 
144
        }
 
145
 
 
146
        document_links = EV_DOCUMENT_LINKS (self->priv->document);
 
147
 
 
148
        dest_page = ev_document_links_get_link_page (document_links, link);
 
149
        g_object_unref (link);
 
150
 
 
151
        current_page = ev_document_model_get_page (self->priv->document_model);
 
152
 
 
153
        if (dest_page == current_page) {
 
154
                gtk_tree_view_expand_to_path (GTK_TREE_VIEW (self->priv->tree_view),
 
155
                                              path);
 
156
                gtk_tree_view_set_cursor (GTK_TREE_VIEW (self->priv->tree_view),
 
157
                                          path, NULL, FALSE);
 
158
 
 
159
                return TRUE;
 
160
        }
 
161
 
 
162
        return FALSE;
 
163
}
 
164
 
 
165
static void
 
166
gd_places_links_set_current_page (GdPlacesLinks *self,
 
167
                                  int            current_page)
 
168
{
 
169
        GtkTreeSelection *selection;
 
170
        GtkTreeModel *model;
 
171
        GtkTreeIter iter;
 
172
 
 
173
        selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self->priv->tree_view));
 
174
 
 
175
        if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
 
176
                EvLink *link;
 
177
 
 
178
                gtk_tree_model_get (model, &iter,
 
179
                                    EV_DOCUMENT_LINKS_COLUMN_LINK, &link,
 
180
                                    -1);
 
181
                if (link != NULL) {
 
182
                        int dest_page;
 
183
                        EvDocumentLinks *document_links = EV_DOCUMENT_LINKS (self->priv->document);
 
184
 
 
185
                        dest_page = ev_document_links_get_link_page (document_links, link);
 
186
                        g_object_unref (link);
 
187
 
 
188
                        if (dest_page == current_page) {
 
189
                                return;
 
190
                        }
 
191
                }
 
192
        }
 
193
 
 
194
        /* We go through the tree linearly looking for the first page that
 
195
         * matches.  This is pretty inefficient.  We can do something neat with
 
196
         * a GtkTreeModelSort here to make it faster, if it turns out to be
 
197
         * slow.
 
198
         */
 
199
        gtk_tree_model_foreach (model,
 
200
                                (GtkTreeModelForeachFunc)update_page_cb_foreach,
 
201
                                self);
 
202
}
 
203
 
 
204
static void
 
205
update_page_cb (GdPlacesLinks *self,
 
206
                int            old_page,
 
207
                int            new_page)
 
208
{
 
209
        gd_places_links_set_current_page (self, new_page);
 
210
}
 
211
 
 
212
static void
 
213
job_finished_cb (EvJobLinks     *job,
 
214
                 GdPlacesLinks *self)
 
215
{
 
216
        GdPlacesLinksPrivate *priv = self->priv;
 
217
        GtkTreeSelection *selection;
 
218
 
 
219
        g_clear_object (&priv->model);
 
220
        priv->model = g_object_ref (job->model);
 
221
 
 
222
        gtk_tree_view_set_model (GTK_TREE_VIEW (priv->tree_view), job->model);
 
223
 
 
224
        g_clear_object (&priv->job);
 
225
 
 
226
        selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->tree_view));
 
227
        gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE);
 
228
 
 
229
        gtk_tree_view_expand_all (GTK_TREE_VIEW (priv->tree_view));
 
230
 
 
231
        if (priv->page_changed_id <= 0) {
 
232
                priv->page_changed_id =
 
233
                        g_signal_connect_swapped (priv->document_model, "page-changed",
 
234
                                                  G_CALLBACK (update_page_cb),
 
235
                                                  self);
 
236
        }
 
237
 
 
238
        gd_places_links_set_current_page (self,
 
239
                                          ev_document_model_get_page (priv->document_model));
 
240
}
 
241
 
 
242
static GtkTreeModel *
 
243
create_loading_model (void)
 
244
{
 
245
        GtkTreeModel *retval;
 
246
        GtkTreeIter iter;
 
247
 
 
248
        /* Creates a fake model to indicate that we're loading */
 
249
        retval = (GtkTreeModel *)gtk_list_store_new (EV_DOCUMENT_LINKS_COLUMN_NUM_COLUMNS,
 
250
                                                     G_TYPE_STRING,
 
251
                                                     G_TYPE_OBJECT,
 
252
                                                     G_TYPE_BOOLEAN,
 
253
                                                     G_TYPE_STRING);
 
254
 
 
255
        gtk_list_store_append (GTK_LIST_STORE (retval), &iter);
 
256
        gtk_list_store_set (GTK_LIST_STORE (retval), &iter,
 
257
                            EV_DOCUMENT_LINKS_COLUMN_MARKUP, _("Loading…"),
 
258
                            EV_DOCUMENT_LINKS_COLUMN_EXPAND, FALSE,
 
259
                            EV_DOCUMENT_LINKS_COLUMN_LINK, NULL,
 
260
                            -1);
 
261
 
 
262
        return retval;
 
263
}
 
264
 
 
265
static void
 
266
gd_places_links_construct (GdPlacesLinks *self)
 
267
{
 
268
        GdPlacesLinksPrivate *priv;
 
269
        GtkWidget *swindow;
 
270
        GtkTreeViewColumn *column;
 
271
        GtkCellRenderer *renderer;
 
272
        GtkTreeSelection *selection;
 
273
 
 
274
        priv = self->priv;
 
275
 
 
276
        swindow = gtk_scrolled_window_new (NULL, NULL);
 
277
 
 
278
        gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (swindow),
 
279
                                             GTK_SHADOW_IN);
 
280
 
 
281
        /* Create tree view */
 
282
        priv->tree_view = gtk_tree_view_new ();
 
283
        gtk_tree_view_set_activate_on_single_click (GTK_TREE_VIEW (priv->tree_view), TRUE);
 
284
        g_signal_connect_swapped (priv->tree_view, "row-activated",
 
285
                                  G_CALLBACK (schedule_emit_link_activated), self);
 
286
 
 
287
        gtk_tree_view_set_show_expanders (GTK_TREE_VIEW (priv->tree_view), FALSE);
 
288
        gtk_tree_view_set_level_indentation (GTK_TREE_VIEW (priv->tree_view), 20);
 
289
 
 
290
        selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->tree_view));
 
291
        gtk_tree_selection_set_mode (selection, GTK_SELECTION_NONE);
 
292
        gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (priv->tree_view), FALSE);
 
293
        gtk_container_add (GTK_CONTAINER (swindow), priv->tree_view);
 
294
 
 
295
        gtk_box_pack_start (GTK_BOX (self), swindow, TRUE, TRUE, 0);
 
296
        gtk_widget_show_all (GTK_WIDGET (self));
 
297
 
 
298
        column = gtk_tree_view_column_new ();
 
299
        gtk_tree_view_column_set_expand (GTK_TREE_VIEW_COLUMN (column), TRUE);
 
300
        gtk_tree_view_append_column (GTK_TREE_VIEW (priv->tree_view), column);
 
301
 
 
302
        renderer = (GtkCellRenderer *)
 
303
                g_object_new (GTK_TYPE_CELL_RENDERER_TEXT,
 
304
                              "ellipsize", PANGO_ELLIPSIZE_END,
 
305
                              "weight", PANGO_WEIGHT_BOLD,
 
306
                              "xpad", 10,
 
307
                              NULL);
 
308
        gtk_tree_view_column_pack_start (GTK_TREE_VIEW_COLUMN (column), renderer, TRUE);
 
309
        gtk_tree_view_column_set_attributes (GTK_TREE_VIEW_COLUMN (column), renderer,
 
310
                                             "markup", EV_DOCUMENT_LINKS_COLUMN_MARKUP,
 
311
                                             NULL);
 
312
 
 
313
        renderer = gd_styled_text_renderer_new ();
 
314
        gd_styled_text_renderer_add_class (GD_STYLED_TEXT_RENDERER (renderer), "dim-label");
 
315
        g_object_set (renderer,
 
316
                      "max-width-chars", 12,
 
317
                      "scale", PANGO_SCALE_SMALL,
 
318
                      "xalign", 1.0,
 
319
                      "xpad", 10,
 
320
                      NULL);
 
321
        gtk_tree_view_column_pack_end (GTK_TREE_VIEW_COLUMN (column), renderer, FALSE);
 
322
        gtk_tree_view_column_set_attributes (GTK_TREE_VIEW_COLUMN (column), renderer,
 
323
                                             "text", EV_DOCUMENT_LINKS_COLUMN_PAGE_LABEL,
 
324
                                             NULL);
 
325
}
 
326
 
 
327
static GtkTreeModel *
 
328
create_failed_model (void)
 
329
{
 
330
        GtkTreeModel *retval;
 
331
        GtkTreeIter iter;
 
332
 
 
333
        /* Creates a fake model to indicate there is no contents */
 
334
        retval = (GtkTreeModel *)gtk_list_store_new (EV_DOCUMENT_LINKS_COLUMN_NUM_COLUMNS,
 
335
                                                     G_TYPE_STRING,
 
336
                                                     G_TYPE_OBJECT,
 
337
                                                     G_TYPE_BOOLEAN,
 
338
                                                     G_TYPE_STRING);
 
339
 
 
340
        gtk_list_store_append (GTK_LIST_STORE (retval), &iter);
 
341
        gtk_list_store_set (GTK_LIST_STORE (retval), &iter,
 
342
                            EV_DOCUMENT_LINKS_COLUMN_MARKUP, _("No table of contents"),
 
343
                            EV_DOCUMENT_LINKS_COLUMN_EXPAND, FALSE,
 
344
                            EV_DOCUMENT_LINKS_COLUMN_LINK, NULL,
 
345
                            -1);
 
346
 
 
347
        return retval;
 
348
}
 
349
 
 
350
static void
 
351
gd_places_links_document_changed_cb (EvDocumentModel *model,
 
352
                                     GParamSpec      *pspec,
 
353
                                     GdPlacesLinks   *self)
 
354
{
 
355
        EvDocument *document = ev_document_model_get_document (model);
 
356
        GdPlacesLinksPrivate *priv = self->priv;
 
357
 
 
358
        if (!EV_IS_DOCUMENT_LINKS (document)) {
 
359
                return;
 
360
        }
 
361
 
 
362
        g_clear_object (&priv->document);
 
363
        priv->document = g_object_ref (document);
 
364
 
 
365
        if (priv->job != NULL) {
 
366
                ev_job_cancel (self->priv->job);
 
367
                g_clear_object (&priv->job);
 
368
        }
 
369
 
 
370
        if (!gd_places_page_supports_document (GD_PLACES_PAGE (self), document)) {
 
371
                GtkTreeModel *failed_model;
 
372
 
 
373
                failed_model = create_failed_model ();
 
374
                gtk_tree_view_set_model (GTK_TREE_VIEW (priv->tree_view), failed_model);
 
375
                g_object_unref (failed_model);
 
376
        } else {
 
377
                GtkTreeModel *loading_model;
 
378
 
 
379
                loading_model = create_loading_model ();
 
380
                gtk_tree_view_set_model (GTK_TREE_VIEW (priv->tree_view), loading_model);
 
381
                g_object_unref (loading_model);
 
382
 
 
383
                priv->job = ev_job_links_new (document);
 
384
                g_signal_connect (priv->job,
 
385
                                  "finished",
 
386
                                  G_CALLBACK (job_finished_cb),
 
387
                                  self);
 
388
 
 
389
                /* The priority doesn't matter for this job */
 
390
                ev_job_scheduler_push_job (priv->job, EV_JOB_PRIORITY_NONE);
 
391
        }
 
392
}
 
393
 
 
394
static gboolean
 
395
gd_places_links_supports_document (GdPlacesPage *page,
 
396
                                   EvDocument   *document)
 
397
{
 
398
        return (EV_IS_DOCUMENT_LINKS (document) &&
 
399
                ev_document_links_has_document_links (EV_DOCUMENT_LINKS (document)));
 
400
}
 
401
 
 
402
static void
 
403
gd_places_links_set_document_model (GdPlacesPage    *page,
 
404
                                    EvDocumentModel *model)
 
405
{
 
406
        GdPlacesLinks *self = GD_PLACES_LINKS (page);
 
407
        GdPlacesLinksPrivate *priv = self->priv;
 
408
 
 
409
        if (priv->document_model == model) {
 
410
                return;
 
411
        }
 
412
 
 
413
        if (priv->page_changed_id > 0) {
 
414
                g_signal_handler_disconnect (priv->document_model, priv->page_changed_id);
 
415
                priv->page_changed_id = 0;
 
416
        }
 
417
 
 
418
        if (priv->document_model != NULL) {
 
419
                g_signal_handlers_disconnect_by_func (priv->document_model,
 
420
                                                      gd_places_links_document_changed_cb,
 
421
                                                      page);
 
422
        }
 
423
 
 
424
        g_clear_object (&priv->document_model);
 
425
 
 
426
        priv->document_model = model;
 
427
 
 
428
        if (priv->document_model != NULL) {
 
429
                g_object_ref (priv->document_model);
 
430
                g_signal_connect (priv->document_model,
 
431
                                  "notify::document",
 
432
                                  G_CALLBACK (gd_places_links_document_changed_cb),
 
433
                                  page);
 
434
                gd_places_links_document_changed_cb (priv->document_model,
 
435
                                                     NULL,
 
436
                                                     self);
 
437
        }
 
438
}
 
439
 
 
440
static const char *
 
441
gd_places_links_get_name (GdPlacesPage *page)
 
442
{
 
443
        return GD_PLACES_LINKS (page)->priv->name;
 
444
}
 
445
 
 
446
static void
 
447
gd_places_links_dispose (GObject *object)
 
448
{
 
449
        GdPlacesLinks *self = GD_PLACES_LINKS (object);
 
450
 
 
451
        if (self->priv->link_activated_id > 0) {
 
452
                g_source_remove (self->priv->link_activated_id);
 
453
                self->priv->link_activated_id = 0;
 
454
        }
 
455
 
 
456
        if (self->priv->job != NULL) {
 
457
                ev_job_cancel (self->priv->job);
 
458
                g_clear_object (&self->priv->job);
 
459
        }
 
460
 
 
461
        if (self->priv->page_changed_id > 0) {
 
462
                g_signal_handler_disconnect (self->priv->document_model, self->priv->page_changed_id);
 
463
                self->priv->page_changed_id = 0;
 
464
        }
 
465
 
 
466
        g_clear_object (&self->priv->model);
 
467
        g_clear_object (&self->priv->document);
 
468
        g_clear_object (&self->priv->document_model);
 
469
 
 
470
        G_OBJECT_CLASS (gd_places_links_parent_class)->dispose (object);
 
471
}
 
472
 
 
473
static void
 
474
gd_places_links_set_property (GObject      *object,
 
475
                              guint         prop_id,
 
476
                              const GValue *value,
 
477
                              GParamSpec   *pspec)
 
478
{
 
479
 
 
480
        GdPlacesLinks *self = GD_PLACES_LINKS (object);
 
481
 
 
482
        switch (prop_id) {
 
483
        case PROP_DOCUMENT_MODEL:
 
484
                gd_places_links_set_document_model (GD_PLACES_PAGE (self), g_value_get_object (value));
 
485
                break;
 
486
        default:
 
487
                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 
488
                break;
 
489
        }
 
490
}
 
491
 
 
492
static void
 
493
gd_places_links_get_property (GObject    *object,
 
494
                              guint       prop_id,
 
495
                              GValue     *value,
 
496
                              GParamSpec *pspec)
 
497
{
 
498
        GdPlacesLinks *self = GD_PLACES_LINKS (object);
 
499
 
 
500
        switch (prop_id) {
 
501
        case PROP_NAME:
 
502
                g_value_set_string (value, self->priv->name);
 
503
                break;
 
504
        case PROP_DOCUMENT_MODEL:
 
505
                g_value_set_object (value, self->priv->document_model);
 
506
                break;
 
507
        default:
 
508
                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 
509
                break;
 
510
        }
 
511
}
 
512
 
 
513
static void
 
514
gd_places_links_init (GdPlacesLinks *self)
 
515
{
 
516
        self->priv = GD_PLACES_LINKS_GET_PRIVATE (self);
 
517
 
 
518
        self->priv->name = _("Contents");
 
519
 
 
520
        gd_places_links_construct (self);
 
521
}
 
522
 
 
523
static void
 
524
gd_places_links_page_iface_init (GdPlacesPageInterface *iface)
 
525
{
 
526
        iface->supports_document = gd_places_links_supports_document;
 
527
        iface->set_document_model = gd_places_links_set_document_model;
 
528
        iface->get_name = gd_places_links_get_name;
 
529
}
 
530
 
 
531
static void
 
532
gd_places_links_class_init (GdPlacesLinksClass *klass)
 
533
{
 
534
        GObjectClass *oclass = G_OBJECT_CLASS (klass);
 
535
 
 
536
        oclass->dispose = gd_places_links_dispose;
 
537
        oclass->set_property = gd_places_links_set_property;
 
538
        oclass->get_property = gd_places_links_get_property;
 
539
 
 
540
        signals[LINK_ACTIVATED] = g_signal_new ("link-activated",
 
541
                                                G_TYPE_FROM_CLASS (oclass),
 
542
                                                G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
 
543
                                                0,
 
544
                                                NULL, NULL,
 
545
                                                g_cclosure_marshal_VOID__OBJECT,
 
546
                                                G_TYPE_NONE, 1, G_TYPE_OBJECT);
 
547
 
 
548
        g_object_class_override_property (oclass, PROP_NAME, "name");
 
549
        g_object_class_override_property (oclass, PROP_DOCUMENT_MODEL, "document-model");
 
550
 
 
551
        g_type_class_add_private (oclass, sizeof (GdPlacesLinksPrivate));
 
552
}
 
553
 
 
554
GtkWidget *
 
555
gd_places_links_new (void)
 
556
{
 
557
        return g_object_new (GD_TYPE_PLACES_LINKS, NULL);
 
558
}