1
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8; -*-
3
* Copyright (C) 2004 Red Hat, Inc.
4
* Copyright (C) 2013 Red Hat, Inc.
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)
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.
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.
27
#include <glib/gi18n.h>
30
#include <evince-document.h>
31
#include <evince-view.h>
35
#include "gd-places-links.h"
36
#include "gd-places-page.h"
38
struct _GdPlacesLinksPrivate {
41
guint page_changed_id;
42
guint link_activated_id;
47
EvDocumentModel *document_model;
62
static guint signals[N_SIGNALS];
64
static void gd_places_links_page_iface_init (GdPlacesPageInterface *iface);
66
G_DEFINE_TYPE_EXTENDED (GdPlacesLinks,
70
G_IMPLEMENT_INTERFACE (GD_TYPE_PLACES_PAGE,
71
gd_places_links_page_iface_init))
74
#define GD_PLACES_LINKS_GET_PRIVATE(object) \
75
(G_TYPE_INSTANCE_GET_PRIVATE ((object), GD_TYPE_PLACES_LINKS, GdPlacesLinksPrivate))
78
emit_link_activated (GdPlacesLinks *self)
80
GtkTreeSelection *selection;
84
selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self->priv->tree_view));
85
if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
87
EvDocumentModel *document_model;
89
gtk_tree_model_get (model, &iter,
90
EV_DOCUMENT_LINKS_COLUMN_LINK, &link,
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);
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);
108
g_object_unref (document_model);
110
g_object_unref (link);
113
self->priv->link_activated_id = 0;
119
schedule_emit_link_activated (GdPlacesLinks *self)
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);
129
update_page_cb_foreach (GtkTreeModel *model,
137
EvDocumentLinks *document_links;
139
gtk_tree_model_get (model, iter,
140
EV_DOCUMENT_LINKS_COLUMN_LINK, &link,
146
document_links = EV_DOCUMENT_LINKS (self->priv->document);
148
dest_page = ev_document_links_get_link_page (document_links, link);
149
g_object_unref (link);
151
current_page = ev_document_model_get_page (self->priv->document_model);
153
if (dest_page == current_page) {
154
gtk_tree_view_expand_to_path (GTK_TREE_VIEW (self->priv->tree_view),
156
gtk_tree_view_set_cursor (GTK_TREE_VIEW (self->priv->tree_view),
166
gd_places_links_set_current_page (GdPlacesLinks *self,
169
GtkTreeSelection *selection;
173
selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self->priv->tree_view));
175
if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
178
gtk_tree_model_get (model, &iter,
179
EV_DOCUMENT_LINKS_COLUMN_LINK, &link,
183
EvDocumentLinks *document_links = EV_DOCUMENT_LINKS (self->priv->document);
185
dest_page = ev_document_links_get_link_page (document_links, link);
186
g_object_unref (link);
188
if (dest_page == current_page) {
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
199
gtk_tree_model_foreach (model,
200
(GtkTreeModelForeachFunc)update_page_cb_foreach,
205
update_page_cb (GdPlacesLinks *self,
209
gd_places_links_set_current_page (self, new_page);
213
job_finished_cb (EvJobLinks *job,
216
GdPlacesLinksPrivate *priv = self->priv;
217
GtkTreeSelection *selection;
219
g_clear_object (&priv->model);
220
priv->model = g_object_ref (job->model);
222
gtk_tree_view_set_model (GTK_TREE_VIEW (priv->tree_view), job->model);
224
g_clear_object (&priv->job);
226
selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->tree_view));
227
gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE);
229
gtk_tree_view_expand_all (GTK_TREE_VIEW (priv->tree_view));
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),
238
gd_places_links_set_current_page (self,
239
ev_document_model_get_page (priv->document_model));
242
static GtkTreeModel *
243
create_loading_model (void)
245
GtkTreeModel *retval;
248
/* Creates a fake model to indicate that we're loading */
249
retval = (GtkTreeModel *)gtk_list_store_new (EV_DOCUMENT_LINKS_COLUMN_NUM_COLUMNS,
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,
266
gd_places_links_construct (GdPlacesLinks *self)
268
GdPlacesLinksPrivate *priv;
270
GtkTreeViewColumn *column;
271
GtkCellRenderer *renderer;
272
GtkTreeSelection *selection;
276
swindow = gtk_scrolled_window_new (NULL, NULL);
278
gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (swindow),
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);
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);
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);
295
gtk_box_pack_start (GTK_BOX (self), swindow, TRUE, TRUE, 0);
296
gtk_widget_show_all (GTK_WIDGET (self));
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);
302
renderer = (GtkCellRenderer *)
303
g_object_new (GTK_TYPE_CELL_RENDERER_TEXT,
304
"ellipsize", PANGO_ELLIPSIZE_END,
305
"weight", PANGO_WEIGHT_BOLD,
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,
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,
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,
327
static GtkTreeModel *
328
create_failed_model (void)
330
GtkTreeModel *retval;
333
/* Creates a fake model to indicate there is no contents */
334
retval = (GtkTreeModel *)gtk_list_store_new (EV_DOCUMENT_LINKS_COLUMN_NUM_COLUMNS,
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,
351
gd_places_links_document_changed_cb (EvDocumentModel *model,
355
EvDocument *document = ev_document_model_get_document (model);
356
GdPlacesLinksPrivate *priv = self->priv;
358
if (!EV_IS_DOCUMENT_LINKS (document)) {
362
g_clear_object (&priv->document);
363
priv->document = g_object_ref (document);
365
if (priv->job != NULL) {
366
ev_job_cancel (self->priv->job);
367
g_clear_object (&priv->job);
370
if (!gd_places_page_supports_document (GD_PLACES_PAGE (self), document)) {
371
GtkTreeModel *failed_model;
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);
377
GtkTreeModel *loading_model;
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);
383
priv->job = ev_job_links_new (document);
384
g_signal_connect (priv->job,
386
G_CALLBACK (job_finished_cb),
389
/* The priority doesn't matter for this job */
390
ev_job_scheduler_push_job (priv->job, EV_JOB_PRIORITY_NONE);
395
gd_places_links_supports_document (GdPlacesPage *page,
396
EvDocument *document)
398
return (EV_IS_DOCUMENT_LINKS (document) &&
399
ev_document_links_has_document_links (EV_DOCUMENT_LINKS (document)));
403
gd_places_links_set_document_model (GdPlacesPage *page,
404
EvDocumentModel *model)
406
GdPlacesLinks *self = GD_PLACES_LINKS (page);
407
GdPlacesLinksPrivate *priv = self->priv;
409
if (priv->document_model == model) {
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;
418
if (priv->document_model != NULL) {
419
g_signal_handlers_disconnect_by_func (priv->document_model,
420
gd_places_links_document_changed_cb,
424
g_clear_object (&priv->document_model);
426
priv->document_model = model;
428
if (priv->document_model != NULL) {
429
g_object_ref (priv->document_model);
430
g_signal_connect (priv->document_model,
432
G_CALLBACK (gd_places_links_document_changed_cb),
434
gd_places_links_document_changed_cb (priv->document_model,
441
gd_places_links_get_name (GdPlacesPage *page)
443
return GD_PLACES_LINKS (page)->priv->name;
447
gd_places_links_dispose (GObject *object)
449
GdPlacesLinks *self = GD_PLACES_LINKS (object);
451
if (self->priv->link_activated_id > 0) {
452
g_source_remove (self->priv->link_activated_id);
453
self->priv->link_activated_id = 0;
456
if (self->priv->job != NULL) {
457
ev_job_cancel (self->priv->job);
458
g_clear_object (&self->priv->job);
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;
466
g_clear_object (&self->priv->model);
467
g_clear_object (&self->priv->document);
468
g_clear_object (&self->priv->document_model);
470
G_OBJECT_CLASS (gd_places_links_parent_class)->dispose (object);
474
gd_places_links_set_property (GObject *object,
480
GdPlacesLinks *self = GD_PLACES_LINKS (object);
483
case PROP_DOCUMENT_MODEL:
484
gd_places_links_set_document_model (GD_PLACES_PAGE (self), g_value_get_object (value));
487
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
493
gd_places_links_get_property (GObject *object,
498
GdPlacesLinks *self = GD_PLACES_LINKS (object);
502
g_value_set_string (value, self->priv->name);
504
case PROP_DOCUMENT_MODEL:
505
g_value_set_object (value, self->priv->document_model);
508
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
514
gd_places_links_init (GdPlacesLinks *self)
516
self->priv = GD_PLACES_LINKS_GET_PRIVATE (self);
518
self->priv->name = _("Contents");
520
gd_places_links_construct (self);
524
gd_places_links_page_iface_init (GdPlacesPageInterface *iface)
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;
532
gd_places_links_class_init (GdPlacesLinksClass *klass)
534
GObjectClass *oclass = G_OBJECT_CLASS (klass);
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;
540
signals[LINK_ACTIVATED] = g_signal_new ("link-activated",
541
G_TYPE_FROM_CLASS (oclass),
542
G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
545
g_cclosure_marshal_VOID__OBJECT,
546
G_TYPE_NONE, 1, G_TYPE_OBJECT);
548
g_object_class_override_property (oclass, PROP_NAME, "name");
549
g_object_class_override_property (oclass, PROP_DOCUMENT_MODEL, "document-model");
551
g_type_class_add_private (oclass, sizeof (GdPlacesLinksPrivate));
555
gd_places_links_new (void)
557
return g_object_new (GD_TYPE_PLACES_LINKS, NULL);