~ubuntu-branches/ubuntu/utopic/yelp/utopic-proposed

« back to all changes in this revision

Viewing changes to .pc/git_signal_handler.patch/libyelp/yelp-view.c

  • Committer: Package Import Robot
  • Author(s): Sebastien Bacher
  • Date: 2014-02-27 13:28:05 UTC
  • Revision ID: package-import@ubuntu.com-20140227132805-uc2nwl43x1n91mf7
Tags: 3.10.1-1ubuntu2
* debian/patches/git_signal_handler.patch:
  - Don't use g_source_remove for signal handlers (lp: #1276306)

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: 4 -*- */
 
2
/*
 
3
 * Copyright (C) 2009 Shaun McCance <shaunm@gnome.org>
 
4
 *
 
5
 * This program is free software; you can redistribute it and/or
 
6
 * modify it under the terms of the GNU General Public License as
 
7
 * published by the Free Software Foundation; either version 2 of the
 
8
 * License, or (at your option) any later version.
 
9
 *
 
10
 * This program is distributed in the hope that it will be useful,
 
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 
13
 * General Public License for more details.
 
14
 *
 
15
 * You should have received a copy of the GNU General Public
 
16
 * License along with this program; if not, write to the
 
17
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 
18
 * Boston, MA 02111-1307, USA.
 
19
 *
 
20
 * Author: Shaun McCance <shaunm@gnome.org>
 
21
 */
 
22
 
 
23
#ifdef HAVE_CONFIG_H
 
24
#include <config.h>
 
25
#endif
 
26
 
 
27
#include <glib/gi18n.h>
 
28
#include <glib-object.h>
 
29
#include <gio/gio.h>
 
30
#include <gtk/gtk.h>
 
31
#include <gdk/gdkx.h>
 
32
#include <webkit/webkit.h>
 
33
 
 
34
#include "yelp-debug.h"
 
35
#include "yelp-docbook-document.h"
 
36
#include "yelp-error.h"
 
37
#include "yelp-marshal.h"
 
38
#include "yelp-settings.h"
 
39
#include "yelp-types.h"
 
40
#include "yelp-view.h"
 
41
 
 
42
#define BOGUS_URI "file:///bogus/"
 
43
#define BOGUS_URI_LEN 14
 
44
 
 
45
static void        yelp_view_init                 (YelpView           *view);
 
46
static void        yelp_view_class_init           (YelpViewClass      *klass);
 
47
static void        yelp_view_dispose              (GObject            *object);
 
48
static void        yelp_view_finalize             (GObject            *object);
 
49
static void        yelp_view_get_property         (GObject            *object,
 
50
                                                   guint               prop_id,
 
51
                                                   GValue             *value,
 
52
                                                   GParamSpec         *pspec);
 
53
static void        yelp_view_set_property         (GObject            *object,
 
54
                                                   guint               prop_id,
 
55
                                                   const GValue       *value,
 
56
                                                   GParamSpec         *pspec);
 
57
 
 
58
static gboolean    view_external_uri              (YelpView           *view,
 
59
                                                   YelpUri            *uri);
 
60
static void        view_install_uri               (YelpView           *view,
 
61
                                                   const gchar        *uri);
 
62
static void        view_scrolled                  (GtkAdjustment      *adjustment,
 
63
                                                   YelpView           *view);
 
64
static void        view_set_hadjustment           (YelpView           *view,
 
65
                                                   GParamSpec         *pspec,
 
66
                                                   gpointer            data);
 
67
static void        view_set_vadjustment           (YelpView           *view,
 
68
                                                   GParamSpec         *pspec,
 
69
                                                   gpointer            data);
 
70
static void        popup_open_link                (GtkMenuItem        *item,
 
71
                                                   YelpView           *view);
 
72
static void        popup_open_link_new            (GtkMenuItem        *item,
 
73
                                                   YelpView           *view);
 
74
static void        popup_copy_link                (GtkMenuItem        *item,
 
75
                                                   YelpView           *view);
 
76
static void        popup_save_image               (GtkMenuItem        *item,
 
77
                                                   YelpView           *view);
 
78
static void        popup_send_image               (GtkMenuItem        *item,
 
79
                                                   YelpView           *view);
 
80
static void        popup_copy_code                (GtkMenuItem        *item,
 
81
                                                   YelpView           *view);
 
82
static void        popup_save_code                (GtkMenuItem        *item,
 
83
                                                   YelpView           *view);
 
84
static void        view_populate_popup            (YelpView           *view,
 
85
                                                   GtkMenu            *menu,
 
86
                                                   gpointer            data);
 
87
static void        view_script_alert              (YelpView           *view,
 
88
                                                   WebKitWebFrame     *frame,
 
89
                                                   gchar              *message,
 
90
                                                   gpointer            data);
 
91
static gboolean    view_navigation_requested      (WebKitWebView             *view,
 
92
                                                   WebKitWebFrame            *frame,
 
93
                                                   WebKitNetworkRequest      *request,
 
94
                                                   WebKitWebNavigationAction *action,
 
95
                                                   WebKitWebPolicyDecision   *decision,
 
96
                                                   gpointer                   user_data);
 
97
static void        view_resource_request          (WebKitWebView             *view,
 
98
                                                   WebKitWebFrame            *frame,
 
99
                                                   WebKitWebResource         *resource,
 
100
                                                   WebKitNetworkRequest      *request,
 
101
                                                   WebKitNetworkResponse     *response,
 
102
                                                   gpointer                   user_data);
 
103
static void        view_document_loaded           (WebKitWebView             *view,
 
104
                                                   WebKitWebFrame            *frame,
 
105
                                                   gpointer                   user_data);
 
106
 
 
107
static void        view_print                     (GtkAction          *action,
 
108
                                                   YelpView           *view);
 
109
static void        view_history_action            (GtkAction          *action,
 
110
                                                   YelpView           *view);
 
111
static void        view_navigation_action         (GtkAction          *action,
 
112
                                                   YelpView           *view);
 
113
 
 
114
static void        view_clear_load                (YelpView           *view);
 
115
static void        view_load_page                 (YelpView           *view);
 
116
static void        view_show_error_page           (YelpView           *view,
 
117
                                                   GError             *error);
 
118
 
 
119
static void        settings_set_fonts             (YelpSettings       *settings);
 
120
static void        settings_show_text_cursor      (YelpSettings       *settings);
 
121
 
 
122
static void        uri_resolved                   (YelpUri            *uri,
 
123
                                                   YelpView           *view);
 
124
static void        document_callback              (YelpDocument       *document,
 
125
                                                   YelpDocumentSignal  signal,
 
126
                                                   YelpView           *view,
 
127
                                                   GError             *error);
 
128
 
 
129
static const GtkActionEntry entries[] = {
 
130
    {"YelpViewPrint", GTK_STOCK_PRINT,
 
131
     N_("_Print..."),
 
132
     "<Control>P",
 
133
     NULL,
 
134
     G_CALLBACK (view_print) },
 
135
    {"YelpViewGoBack", GTK_STOCK_GO_BACK,
 
136
     N_("_Back"),
 
137
     "<Alt>Left",
 
138
     NULL,
 
139
     G_CALLBACK (view_history_action) },
 
140
    {"YelpViewGoForward", GTK_STOCK_GO_FORWARD,
 
141
     N_("_Forward"),
 
142
     "<Alt>Right",
 
143
     NULL,
 
144
     G_CALLBACK (view_history_action) },
 
145
    {"YelpViewGoPrevious", NULL,
 
146
     N_("_Previous Page"),
 
147
     "<Control>Page_Up",
 
148
     NULL,
 
149
     G_CALLBACK (view_navigation_action) },
 
150
    {"YelpViewGoNext", NULL,
 
151
     N_("_Next Page"),
 
152
     "<Control>Page_Down",
 
153
     NULL,
 
154
     G_CALLBACK (view_navigation_action) }
 
155
};
 
156
 
 
157
static gchar *nautilus_sendto = NULL;
 
158
 
 
159
enum {
 
160
    PROP_0,
 
161
    PROP_URI,
 
162
    PROP_STATE,
 
163
    PROP_PAGE_ID,
 
164
    PROP_ROOT_TITLE,
 
165
    PROP_PAGE_TITLE,
 
166
    PROP_PAGE_DESC,
 
167
    PROP_PAGE_ICON
 
168
};
 
169
 
 
170
enum {
 
171
    NEW_VIEW_REQUESTED,
 
172
    EXTERNAL_URI,
 
173
    LOADED,
 
174
    LAST_SIGNAL
 
175
};
 
176
static gint signals[LAST_SIGNAL] = { 0 };
 
177
 
 
178
G_DEFINE_TYPE (YelpView, yelp_view, WEBKIT_TYPE_WEB_VIEW);
 
179
#define GET_PRIV(object)(G_TYPE_INSTANCE_GET_PRIVATE ((object), YELP_TYPE_VIEW, YelpViewPrivate))
 
180
 
 
181
static WebKitWebSettings *websettings;
 
182
 
 
183
typedef struct _YelpActionEntry YelpActionEntry;
 
184
struct _YelpActionEntry {
 
185
    GtkAction               *action;
 
186
    YelpViewActionValidFunc  func;
 
187
    gpointer                 data;
 
188
};
 
189
static void
 
190
action_entry_free (YelpActionEntry *entry)
 
191
{
 
192
    if (entry == NULL)
 
193
        return;
 
194
    g_object_unref (entry->action);
 
195
    g_free (entry);
 
196
}
 
197
 
 
198
typedef struct _YelpBackEntry YelpBackEntry;
 
199
struct _YelpBackEntry {
 
200
    YelpUri *uri;
 
201
    gchar   *title;
 
202
    gchar   *desc;
 
203
    gdouble  hadj;
 
204
    gdouble  vadj;
 
205
};
 
206
static void
 
207
back_entry_free (YelpBackEntry *back)
 
208
{
 
209
    if (back == NULL)
 
210
        return;
 
211
    g_object_unref (back->uri);
 
212
    g_free (back->title);
 
213
    g_free (back->desc);
 
214
    g_free (back);
 
215
}
 
216
 
 
217
typedef struct _YelpViewPrivate YelpViewPrivate;
 
218
struct _YelpViewPrivate {
 
219
    YelpUri       *uri;
 
220
    YelpUri       *resolve_uri;
 
221
    gulong         uri_resolved;
 
222
    gchar         *bogus_uri;
 
223
    YelpDocument  *document;
 
224
    GCancellable  *cancellable;
 
225
    GtkAdjustment *vadjustment;
 
226
    GtkAdjustment *hadjustment;
 
227
    gdouble        vadjust;
 
228
    gdouble        hadjust;
 
229
    gulong         vadjuster;
 
230
    gulong         hadjuster;
 
231
 
 
232
    gchar         *popup_link_uri;
 
233
    gchar         *popup_link_text;
 
234
    gchar         *popup_image_uri;
 
235
    WebKitDOMNode *popup_code_node;
 
236
    WebKitDOMNode *popup_code_title;
 
237
    gchar         *popup_code_text;
 
238
 
 
239
    YelpViewState  state;
 
240
    YelpViewState  prevstate;
 
241
 
 
242
    gchar         *page_id;
 
243
    gchar         *root_title;
 
244
    gchar         *page_title;
 
245
    gchar         *page_desc;
 
246
    gchar         *page_icon;
 
247
 
 
248
    GList          *back_list;
 
249
    GList          *back_cur;
 
250
    gboolean        back_load;
 
251
 
 
252
    GtkActionGroup *action_group;
 
253
 
 
254
    GSList         *link_actions;
 
255
 
 
256
    gint            navigation_requested;
 
257
};
 
258
 
 
259
#define TARGET_TYPE_URI_LIST     "text/uri-list"
 
260
enum {
 
261
    TARGET_URI_LIST
 
262
};
 
263
 
 
264
static void
 
265
yelp_view_init (YelpView *view)
 
266
{
 
267
    GtkAction *action;
 
268
    YelpViewPrivate *priv = GET_PRIV (view);
 
269
 
 
270
    g_object_set (view, "settings", websettings, NULL);
 
271
 
 
272
    priv->cancellable = NULL;
 
273
 
 
274
    priv->prevstate = priv->state = YELP_VIEW_STATE_BLANK;
 
275
 
 
276
    priv->navigation_requested =
 
277
        g_signal_connect (view, "navigation-policy-decision-requested",
 
278
                          G_CALLBACK (view_navigation_requested), NULL);
 
279
    g_signal_connect (view, "resource-request-starting",
 
280
                      G_CALLBACK (view_resource_request), NULL);
 
281
    g_signal_connect (view, "document-load-finished",
 
282
                      G_CALLBACK (view_document_loaded), NULL);
 
283
    g_signal_connect (view, "notify::hadjustment",
 
284
                      G_CALLBACK (view_set_hadjustment), NULL);
 
285
    g_signal_connect (view, "notify::vadjustment",
 
286
                      G_CALLBACK (view_set_vadjustment), NULL);
 
287
    g_signal_connect (view, "populate-popup",
 
288
                      G_CALLBACK (view_populate_popup), NULL);
 
289
    g_signal_connect (view, "script-alert",
 
290
                      G_CALLBACK (view_script_alert), NULL);
 
291
 
 
292
    priv->action_group = gtk_action_group_new ("YelpView");
 
293
    gtk_action_group_set_translation_domain (priv->action_group, GETTEXT_PACKAGE);
 
294
    gtk_action_group_add_actions (priv->action_group,
 
295
                                  entries, G_N_ELEMENTS (entries),
 
296
                                  view);
 
297
    action = gtk_action_group_get_action (priv->action_group, "YelpViewGoBack");
 
298
    gtk_action_set_sensitive (action, FALSE);
 
299
    action = gtk_action_group_get_action (priv->action_group, "YelpViewGoForward");
 
300
    gtk_action_set_sensitive (action, FALSE);
 
301
}
 
302
 
 
303
static void
 
304
yelp_view_dispose (GObject *object)
 
305
{
 
306
    YelpViewPrivate *priv = GET_PRIV (object);
 
307
 
 
308
    view_clear_load (YELP_VIEW (object));
 
309
 
 
310
    if (priv->vadjuster > 0) {
 
311
        g_source_remove (priv->vadjuster);
 
312
        priv->vadjuster = 0;
 
313
    }
 
314
 
 
315
    if (priv->hadjuster > 0) {
 
316
        g_source_remove (priv->hadjuster);
 
317
        priv->hadjuster = 0;
 
318
    }
 
319
 
 
320
    if (priv->action_group) {
 
321
        g_object_unref (priv->action_group);
 
322
        priv->action_group = NULL;
 
323
    }
 
324
 
 
325
    if (priv->document) {
 
326
        g_object_unref (priv->document);
 
327
        priv->document = NULL;
 
328
    }
 
329
 
 
330
    while (priv->link_actions) {
 
331
        action_entry_free (priv->link_actions->data);
 
332
        priv->link_actions = g_slist_delete_link (priv->link_actions, priv->link_actions);
 
333
    }
 
334
 
 
335
    priv->back_cur = NULL;
 
336
    while (priv->back_list) {
 
337
        back_entry_free ((YelpBackEntry *) priv->back_list->data);
 
338
        priv->back_list = g_list_delete_link (priv->back_list, priv->back_list);
 
339
    }
 
340
 
 
341
    G_OBJECT_CLASS (yelp_view_parent_class)->dispose (object);
 
342
}
 
343
 
 
344
static void
 
345
yelp_view_finalize (GObject *object)
 
346
{
 
347
    YelpViewPrivate *priv = GET_PRIV (object);
 
348
 
 
349
    g_free (priv->popup_link_uri);
 
350
    g_free (priv->popup_link_text);
 
351
    g_free (priv->popup_image_uri);
 
352
    g_free (priv->popup_code_text);
 
353
 
 
354
    g_free (priv->page_id);
 
355
    g_free (priv->root_title);
 
356
    g_free (priv->page_title);
 
357
    g_free (priv->page_desc);
 
358
    g_free (priv->page_icon);
 
359
 
 
360
    g_free (priv->bogus_uri);
 
361
 
 
362
    G_OBJECT_CLASS (yelp_view_parent_class)->finalize (object);
 
363
}
 
364
 
 
365
static void
 
366
yelp_view_class_init (YelpViewClass *klass)
 
367
{
 
368
    GObjectClass *object_class = G_OBJECT_CLASS (klass);
 
369
    YelpSettings *settings = yelp_settings_get_default ();
 
370
 
 
371
    nautilus_sendto = g_find_program_in_path ("nautilus-sendto");
 
372
 
 
373
    websettings = webkit_web_settings_new ();
 
374
    g_object_set (websettings, "enable-universal-access-from-file-uris", TRUE, NULL);
 
375
    g_signal_connect (settings,
 
376
                      "fonts-changed",
 
377
                      G_CALLBACK (settings_set_fonts),
 
378
                      NULL);
 
379
    settings_set_fonts (settings);
 
380
    g_signal_connect (settings,
 
381
                      "notify::show-text-cursor",
 
382
                      G_CALLBACK (settings_show_text_cursor),
 
383
                      NULL);
 
384
    settings_show_text_cursor (settings);
 
385
 
 
386
    klass->external_uri = view_external_uri;
 
387
 
 
388
    object_class->dispose = yelp_view_dispose;
 
389
    object_class->finalize = yelp_view_finalize;
 
390
    object_class->get_property = yelp_view_get_property;
 
391
    object_class->set_property = yelp_view_set_property;
 
392
 
 
393
    signals[NEW_VIEW_REQUESTED] =
 
394
        g_signal_new ("new-view-requested",
 
395
                      G_TYPE_FROM_CLASS (klass),
 
396
                      G_SIGNAL_RUN_LAST,
 
397
                      0, NULL, NULL,
 
398
                      g_cclosure_marshal_VOID__OBJECT,
 
399
                      G_TYPE_NONE, 1, YELP_TYPE_URI);
 
400
 
 
401
    signals[EXTERNAL_URI] =
 
402
        g_signal_new ("external-uri",
 
403
                      G_TYPE_FROM_CLASS (klass),
 
404
                      G_SIGNAL_RUN_LAST,
 
405
                      G_STRUCT_OFFSET (YelpViewClass, external_uri),
 
406
                      g_signal_accumulator_true_handled, NULL,
 
407
                      yelp_marshal_BOOLEAN__OBJECT,
 
408
                      G_TYPE_BOOLEAN, 1, YELP_TYPE_URI);
 
409
 
 
410
    signals[LOADED] =
 
411
        g_signal_new ("loaded",
 
412
                      G_TYPE_FROM_CLASS (klass),
 
413
                      G_SIGNAL_RUN_LAST,
 
414
                      0, NULL, NULL,
 
415
                      g_cclosure_marshal_VOID__VOID,
 
416
                      G_TYPE_NONE, 0);
 
417
 
 
418
    g_type_class_add_private (klass, sizeof (YelpViewPrivate));
 
419
 
 
420
    g_object_class_install_property (object_class,
 
421
                                     PROP_URI,
 
422
                                     g_param_spec_object ("yelp-uri",
 
423
                                                          _("Yelp URI"),
 
424
                                                          _("A YelpUri with the current location"),
 
425
                                                          YELP_TYPE_URI,
 
426
                                                          G_PARAM_READWRITE | G_PARAM_STATIC_NAME |
 
427
                                                          G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
 
428
 
 
429
    g_object_class_install_property (object_class,
 
430
                                     PROP_STATE,
 
431
                                     g_param_spec_enum ("state",
 
432
                                                        N_("Loading State"),
 
433
                                                        N_("The loading state of the view"),
 
434
                                                        YELP_TYPE_VIEW_STATE,
 
435
                                                        YELP_VIEW_STATE_BLANK,
 
436
                                                        G_PARAM_READWRITE | G_PARAM_STATIC_NAME |
 
437
                                                        G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
 
438
 
 
439
    g_object_class_install_property (object_class,
 
440
                                     PROP_PAGE_ID,
 
441
                                     g_param_spec_string ("page-id",
 
442
                                                          N_("Page ID"),
 
443
                                                          N_("The ID of the root page of the page being viewed"),
 
444
                                                          NULL,
 
445
                                                          G_PARAM_READABLE |
 
446
                                                          G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
 
447
 
 
448
    g_object_class_install_property (object_class,
 
449
                                     PROP_ROOT_TITLE,
 
450
                                     g_param_spec_string ("root-title",
 
451
                                                          N_("Root Title"),
 
452
                                                          N_("The title of the root page of the page being viewed"),
 
453
                                                          NULL,
 
454
                                                          G_PARAM_READABLE |
 
455
                                                          G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
 
456
 
 
457
    g_object_class_install_property (object_class,
 
458
                                     PROP_PAGE_TITLE,
 
459
                                     g_param_spec_string ("page-title",
 
460
                                                          N_("Page Title"),
 
461
                                                          N_("The title of the page being viewed"),
 
462
                                                          NULL,
 
463
                                                          G_PARAM_READABLE |
 
464
                                                          G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
 
465
 
 
466
    g_object_class_install_property (object_class,
 
467
                                     PROP_PAGE_DESC,
 
468
                                     g_param_spec_string ("page-desc",
 
469
                                                          N_("Page Description"),
 
470
                                                          N_("The description of the page being viewed"),
 
471
                                                          NULL,
 
472
                                                          G_PARAM_READABLE |
 
473
                                                          G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
 
474
 
 
475
    g_object_class_install_property (object_class,
 
476
                                     PROP_PAGE_ICON,
 
477
                                     g_param_spec_string ("page-icon",
 
478
                                                          N_("Page Icon"),
 
479
                                                          N_("The icon of the page being viewed"),
 
480
                                                          NULL,
 
481
                                                          G_PARAM_READABLE |
 
482
                                                          G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
 
483
}
 
484
 
 
485
static void
 
486
yelp_view_get_property (GObject    *object,
 
487
                        guint       prop_id,
 
488
                        GValue     *value,
 
489
                        GParamSpec *pspec)
 
490
{
 
491
    YelpViewPrivate *priv = GET_PRIV (object);
 
492
 
 
493
    switch (prop_id)
 
494
        {
 
495
        case PROP_URI:
 
496
            g_value_set_object (value, priv->uri);
 
497
            break;
 
498
        case PROP_PAGE_ID:
 
499
            g_value_set_string (value, priv->page_id);
 
500
            break;
 
501
        case PROP_ROOT_TITLE:
 
502
            g_value_set_string (value, priv->root_title);
 
503
            break;
 
504
        case PROP_PAGE_TITLE:
 
505
            g_value_set_string (value, priv->page_title);
 
506
            break;
 
507
        case PROP_PAGE_DESC:
 
508
            g_value_set_string (value, priv->page_desc);
 
509
            break;
 
510
        case PROP_PAGE_ICON:
 
511
            if (priv->page_icon)
 
512
                g_value_set_string (value, priv->page_icon);
 
513
            else
 
514
                g_value_set_string (value, "yelp-page-symbolic");
 
515
            break;
 
516
        case PROP_STATE:
 
517
            g_value_set_enum (value, priv->state);
 
518
            break;
 
519
        default:
 
520
            G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 
521
            break;
 
522
    }
 
523
}
 
524
 
 
525
static void
 
526
yelp_view_set_property (GObject      *object,
 
527
                        guint         prop_id,
 
528
                        const GValue *value,
 
529
                        GParamSpec   *pspec)
 
530
{
 
531
    YelpUri *uri;
 
532
    YelpViewPrivate *priv = GET_PRIV (object);
 
533
 
 
534
    switch (prop_id)
 
535
        {
 
536
        case PROP_URI:
 
537
            uri = g_value_get_object (value);
 
538
            yelp_view_load_uri (YELP_VIEW (object), uri);
 
539
            g_object_unref (uri);
 
540
            break;
 
541
        case PROP_STATE:
 
542
            priv->prevstate = priv->state;
 
543
            priv->state = g_value_get_enum (value);
 
544
            break;
 
545
        default:
 
546
            G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 
547
            break;
 
548
    }
 
549
}
 
550
 
 
551
/******************************************************************************/
 
552
 
 
553
GtkWidget *
 
554
yelp_view_new (void)
 
555
{
 
556
    return (GtkWidget *) g_object_new (YELP_TYPE_VIEW, NULL);
 
557
}
 
558
 
 
559
void
 
560
yelp_view_load (YelpView    *view,
 
561
                const gchar *uri)
 
562
{
 
563
    YelpUri *yuri = yelp_uri_new (uri);
 
564
    yelp_view_load_uri (view, yuri);
 
565
    g_object_unref (yuri);
 
566
}
 
567
 
 
568
void
 
569
yelp_view_load_uri (YelpView *view,
 
570
                    YelpUri  *uri)
 
571
{
 
572
    YelpViewPrivate *priv = GET_PRIV (view);
 
573
 
 
574
    g_object_set (view, "state", YELP_VIEW_STATE_LOADING, NULL);
 
575
 
 
576
    gtk_action_set_sensitive (gtk_action_group_get_action (priv->action_group,
 
577
                                                           "YelpViewGoPrevious"),
 
578
                              FALSE);
 
579
    gtk_action_set_sensitive (gtk_action_group_get_action (priv->action_group,
 
580
                                                           "YelpViewGoNext"),
 
581
                              FALSE);
 
582
 
 
583
    if (!yelp_uri_is_resolved (uri)) {
 
584
        if (priv->resolve_uri != NULL) {
 
585
            if (priv->uri_resolved != 0) {
 
586
                g_signal_handler_disconnect (priv->resolve_uri, priv->uri_resolved);
 
587
                priv->uri_resolved = 0;
 
588
            }
 
589
            g_object_unref (priv->resolve_uri);
 
590
        }
 
591
        priv->resolve_uri = g_object_ref (uri);
 
592
        priv->uri_resolved = g_signal_connect (uri, "resolved",
 
593
                                               G_CALLBACK (uri_resolved),
 
594
                                               view);
 
595
        yelp_uri_resolve (uri);
 
596
    }
 
597
    else {
 
598
        uri_resolved (uri, view);
 
599
    }
 
600
}
 
601
 
 
602
void
 
603
yelp_view_load_document (YelpView     *view,
 
604
                         YelpUri      *uri,
 
605
                         YelpDocument *document)
 
606
{
 
607
    GParamSpec *spec;
 
608
    YelpViewPrivate *priv = GET_PRIV (view);
 
609
 
 
610
    g_return_if_fail (yelp_uri_is_resolved (uri));
 
611
 
 
612
    g_object_set (view, "state", YELP_VIEW_STATE_LOADING, NULL);
 
613
 
 
614
    g_object_ref (uri);
 
615
    view_clear_load (view);
 
616
    priv->uri = uri;
 
617
    spec = g_object_class_find_property ((GObjectClass *) YELP_VIEW_GET_CLASS (view),
 
618
                                         "yelp-uri");
 
619
    g_signal_emit_by_name (view, "notify::yelp-uri", spec);
 
620
    g_object_ref (document);
 
621
    if (priv->document)
 
622
        g_object_unref (document);
 
623
    priv->document = document;
 
624
 
 
625
    view_load_page (view);
 
626
}
 
627
 
 
628
YelpDocument *
 
629
yelp_view_get_document (YelpView *view)
 
630
{
 
631
    YelpViewPrivate *priv = GET_PRIV (view);
 
632
    return priv->document;
 
633
}
 
634
 
 
635
GtkActionGroup *
 
636
yelp_view_get_action_group (YelpView *view)
 
637
{
 
638
    YelpViewPrivate *priv = GET_PRIV (view);
 
639
    return priv->action_group;
 
640
}
 
641
 
 
642
/******************************************************************************/
 
643
 
 
644
void
 
645
yelp_view_add_link_action (YelpView                *view,
 
646
                           GtkAction               *action,
 
647
                           YelpViewActionValidFunc  func,
 
648
                           gpointer                 data)
 
649
{
 
650
    YelpActionEntry *entry;
 
651
    YelpViewPrivate *priv = GET_PRIV (view);
 
652
 
 
653
    entry = g_new0 (YelpActionEntry, 1);
 
654
    entry->action = g_object_ref (action);
 
655
    entry->func = func;
 
656
    entry->data = data;
 
657
 
 
658
    priv->link_actions = g_slist_append (priv->link_actions, entry);
 
659
}
 
660
 
 
661
YelpUri *
 
662
yelp_view_get_active_link_uri (YelpView *view)
 
663
{
 
664
    YelpViewPrivate *priv = GET_PRIV (view);
 
665
    YelpUri *uri;
 
666
 
 
667
    if (g_str_has_prefix (priv->popup_link_uri, BOGUS_URI))
 
668
        uri = yelp_uri_new_relative (priv->uri, priv->popup_link_uri + BOGUS_URI_LEN);
 
669
    else
 
670
        uri = yelp_uri_new_relative (priv->uri, priv->popup_link_uri);
 
671
 
 
672
    return uri;
 
673
}
 
674
 
 
675
gchar *
 
676
yelp_view_get_active_link_text (YelpView *view)
 
677
{
 
678
    YelpViewPrivate *priv = GET_PRIV (view);
 
679
    return g_strdup (priv->popup_link_text);
 
680
}
 
681
 
 
682
/******************************************************************************/
 
683
 
 
684
static gboolean
 
685
view_external_uri (YelpView *view,
 
686
                   YelpUri  *uri)
 
687
{
 
688
    gchar *struri = yelp_uri_get_canonical_uri (uri);
 
689
    g_app_info_launch_default_for_uri (struri, NULL, NULL);
 
690
    g_free (struri);
 
691
    return TRUE;
 
692
}
 
693
 
 
694
typedef struct _YelpInstallInfo YelpInstallInfo;
 
695
struct _YelpInstallInfo {
 
696
    YelpView *view;
 
697
    gchar *uri;
 
698
};
 
699
 
 
700
static void
 
701
yelp_install_info_free (YelpInstallInfo *info)
 
702
{
 
703
    g_object_unref (info->view);
 
704
    if (info->uri)
 
705
        g_free (info->uri);
 
706
    g_free (info);
 
707
}
 
708
 
 
709
static void
 
710
view_install_installed (GDBusConnection *connection,
 
711
                        GAsyncResult    *res,
 
712
                        YelpInstallInfo *info)
 
713
{
 
714
    GError *error = NULL;
 
715
    g_dbus_connection_call_finish (connection, res, &error);
 
716
    if (error) {
 
717
        const gchar *err = NULL;
 
718
        if (error->domain == G_DBUS_ERROR) {
 
719
            if (error->code == G_DBUS_ERROR_SERVICE_UNKNOWN)
 
720
                err = _("You do not have PackageKit. Package install links require PackageKit.");
 
721
            else
 
722
                err = error->message;
 
723
        }
 
724
        if (err != NULL) {
 
725
            GtkWidget *dialog = gtk_message_dialog_new (NULL, 0,
 
726
                                                        GTK_MESSAGE_ERROR,
 
727
                                                        GTK_BUTTONS_CLOSE,
 
728
                                                        "%s", err);
 
729
            gtk_dialog_run ((GtkDialog *) dialog);
 
730
            gtk_widget_destroy (dialog);
 
731
        }
 
732
        g_error_free (error);
 
733
    }
 
734
    else if (info->uri) {
 
735
        gchar *struri, *docuri;
 
736
        YelpViewPrivate *priv = GET_PRIV (info->view);
 
737
        docuri = yelp_uri_get_document_uri (priv->uri);
 
738
        if (g_str_equal (docuri, info->uri)) {
 
739
            struri = yelp_uri_get_canonical_uri (priv->uri);
 
740
            yelp_view_load (info->view, struri);
 
741
            g_free (struri);
 
742
        }
 
743
        g_free (docuri);
 
744
    }
 
745
 
 
746
    yelp_install_info_free (info);
 
747
}
 
748
 
 
749
static void
 
750
view_install_uri (YelpView    *view,
 
751
                  const gchar *uri)
 
752
{
 
753
    GDBusConnection *connection;
 
754
    GError *error;
 
755
    gboolean help = FALSE, ghelp = FALSE;
 
756
    GVariantBuilder *strv;
 
757
    YelpInstallInfo *info;
 
758
    guint32 xid = 0;
 
759
    YelpViewPrivate *priv = GET_PRIV (view);
 
760
    GtkWidget *gtkwin;
 
761
    GdkWindow *gdkwin;
 
762
    /* do not free */
 
763
    gchar *pkg, *confirm_search;
 
764
 
 
765
    if (g_str_has_prefix (uri, "install-help:")) {
 
766
        help = TRUE;
 
767
        pkg = (gchar *) uri + 13;
 
768
    }
 
769
    else if (g_str_has_prefix (uri, "install-ghelp:")) {
 
770
        ghelp = TRUE;
 
771
        pkg = (gchar *) uri + 14;
 
772
    }
 
773
    else if (g_str_has_prefix (uri, "install:")) {
 
774
        pkg = (gchar *) uri + 8;
 
775
    }
 
776
 
 
777
    connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
 
778
    if (connection == NULL) {
 
779
        g_warning ("Unable to connect to dbus: %s", error->message);
 
780
        g_error_free (error);
 
781
        return;
 
782
    }
 
783
 
 
784
    info = g_new0 (YelpInstallInfo, 1);
 
785
    info->view = g_object_ref (view);
 
786
 
 
787
    gtkwin = gtk_widget_get_toplevel (GTK_WIDGET (view));
 
788
    if (gtkwin != NULL && gtk_widget_is_toplevel (gtkwin)) {
 
789
        gdkwin = gtk_widget_get_window (gtkwin);
 
790
        if (gdkwin != NULL)
 
791
            xid = gdk_x11_window_get_xid (gdkwin);
 
792
    }
 
793
 
 
794
    if (priv->state == YELP_VIEW_STATE_ERROR)
 
795
        confirm_search = "hide-confirm-search";
 
796
    else
 
797
        confirm_search = "";
 
798
 
 
799
    if (help || ghelp) {
 
800
        const gchar * const *datadirs = g_get_system_data_dirs ();
 
801
        gint datadirs_i;
 
802
        gchar *docbook, *fname;
 
803
        docbook = g_strconcat (pkg, ".xml", NULL);
 
804
        strv = g_variant_builder_new (G_VARIANT_TYPE ("as"));
 
805
        for (datadirs_i = 0; datadirs[datadirs_i] != NULL; datadirs_i++) {
 
806
            if (ghelp) {
 
807
                fname = g_build_filename (datadirs[datadirs_i], "gnome", "help",
 
808
                                          pkg, "C", "index.page", NULL);
 
809
                g_variant_builder_add (strv, "s", fname);
 
810
                g_free (fname);
 
811
                fname = g_build_filename (datadirs[datadirs_i], "gnome", "help",
 
812
                                          pkg, "C", docbook, NULL);
 
813
                g_variant_builder_add (strv, "s", fname);
 
814
                g_free (fname);
 
815
            }
 
816
            else {
 
817
                fname = g_build_filename (datadirs[datadirs_i], "help", "C",
 
818
                                          pkg, "index.page", NULL);
 
819
                g_variant_builder_add (strv, "s", fname);
 
820
                g_free (fname);
 
821
                fname = g_build_filename (datadirs[datadirs_i], "help", "C",
 
822
                                          pkg, "index.docbook", NULL);
 
823
                g_variant_builder_add (strv, "s", fname);
 
824
                g_free (fname);
 
825
            }
 
826
        }
 
827
        g_free (docbook);
 
828
        info->uri = g_strconcat (ghelp ? "ghelp:" : "help:", pkg, NULL);
 
829
        g_dbus_connection_call (connection,
 
830
                                "org.freedesktop.PackageKit",
 
831
                                "/org/freedesktop/PackageKit",
 
832
                                "org.freedesktop.PackageKit.Modify",
 
833
                                "InstallProvideFiles",
 
834
                                g_variant_new ("(uass)", xid, strv, confirm_search),
 
835
                                NULL,
 
836
                                G_DBUS_CALL_FLAGS_NONE,
 
837
                                G_MAXINT, NULL,
 
838
                                (GAsyncReadyCallback) view_install_installed,
 
839
                                info);
 
840
        g_variant_builder_unref (strv);
 
841
    }
 
842
    else {
 
843
        gchar **pkgs;
 
844
        gint i;
 
845
        strv = g_variant_builder_new (G_VARIANT_TYPE ("as"));
 
846
        pkgs = g_strsplit (pkg, ",", 0);
 
847
        for (i = 0; pkgs[i]; i++)
 
848
            g_variant_builder_add (strv, "s", pkgs[i]);
 
849
        g_strfreev (pkgs);
 
850
        g_dbus_connection_call (connection,
 
851
                                "org.freedesktop.PackageKit",
 
852
                                "/org/freedesktop/PackageKit",
 
853
                                "org.freedesktop.PackageKit.Modify",
 
854
                                "InstallPackageNames",
 
855
                                g_variant_new ("(uass)", xid, strv, confirm_search),
 
856
                                NULL,
 
857
                                G_DBUS_CALL_FLAGS_NONE,
 
858
                                G_MAXINT, NULL,
 
859
                                (GAsyncReadyCallback) view_install_installed,
 
860
                                info);
 
861
        g_variant_builder_unref (strv);
 
862
    }
 
863
 
 
864
    g_object_unref (connection);
 
865
}
 
866
 
 
867
static void
 
868
view_scrolled (GtkAdjustment *adjustment,
 
869
               YelpView      *view)
 
870
{
 
871
    YelpViewPrivate *priv = GET_PRIV (view);
 
872
    if (priv->back_cur == NULL || priv->back_cur->data == NULL)
 
873
        return;
 
874
    if (adjustment == priv->vadjustment)
 
875
        ((YelpBackEntry *) priv->back_cur->data)->vadj = gtk_adjustment_get_value (adjustment);
 
876
    else if (adjustment = priv->hadjustment)
 
877
        ((YelpBackEntry *) priv->back_cur->data)->hadj = gtk_adjustment_get_value (adjustment);
 
878
}
 
879
 
 
880
static void
 
881
view_set_hadjustment (YelpView      *view,
 
882
                      GParamSpec    *pspec,
 
883
                      gpointer       data)
 
884
{
 
885
    YelpViewPrivate *priv = GET_PRIV (view);
 
886
    priv->hadjustment = gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (view));
 
887
    if (priv->hadjuster > 0)
 
888
        g_source_remove (priv->hadjuster);
 
889
    priv->hadjuster = 0;
 
890
    if (priv->hadjustment)
 
891
        priv->hadjuster = g_signal_connect (priv->hadjustment, "value-changed",
 
892
                                            G_CALLBACK (view_scrolled), view);
 
893
}
 
894
 
 
895
static void
 
896
view_set_vadjustment (YelpView      *view,
 
897
                      GParamSpec    *pspec,
 
898
                      gpointer       data)
 
899
{
 
900
    YelpViewPrivate *priv = GET_PRIV (view);
 
901
    priv->vadjustment = gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (view));
 
902
    if (priv->vadjuster > 0)
 
903
        g_source_remove (priv->vadjuster);
 
904
    priv->vadjuster = 0;
 
905
    if (priv->vadjustment)
 
906
        priv->vadjuster = g_signal_connect (priv->vadjustment, "value-changed",
 
907
                                            G_CALLBACK (view_scrolled), view);
 
908
}
 
909
 
 
910
static void
 
911
popup_open_link (GtkMenuItem *item,
 
912
                 YelpView    *view)
 
913
{
 
914
    YelpViewPrivate *priv = GET_PRIV (view);
 
915
    YelpUri *uri;
 
916
 
 
917
    if (g_str_has_prefix (priv->popup_link_uri, BOGUS_URI))
 
918
        uri = yelp_uri_new_relative (priv->uri, priv->popup_link_uri + BOGUS_URI_LEN);
 
919
    else
 
920
        uri = yelp_uri_new_relative (priv->uri, priv->popup_link_uri);
 
921
 
 
922
    yelp_view_load_uri (view, uri);
 
923
    g_object_unref (uri);
 
924
 
 
925
    g_free (priv->popup_link_uri);
 
926
    priv->popup_link_uri = NULL;
 
927
 
 
928
    g_free (priv->popup_link_text);
 
929
    priv->popup_link_text = NULL;
 
930
}
 
931
 
 
932
static void
 
933
popup_open_link_new (GtkMenuItem *item,
 
934
                     YelpView    *view)
 
935
{
 
936
    YelpViewPrivate *priv = GET_PRIV (view);
 
937
    YelpUri *uri;
 
938
 
 
939
    if (g_str_has_prefix (priv->popup_link_uri, BOGUS_URI))
 
940
        uri = yelp_uri_new_relative (priv->uri, priv->popup_link_uri + BOGUS_URI_LEN);
 
941
    else
 
942
        uri = yelp_uri_new_relative (priv->uri, priv->popup_link_uri);
 
943
 
 
944
    g_free (priv->popup_link_uri);
 
945
    priv->popup_link_uri = NULL;
 
946
 
 
947
    g_free (priv->popup_link_text);
 
948
    priv->popup_link_text = NULL;
 
949
 
 
950
    g_signal_emit (view, signals[NEW_VIEW_REQUESTED], 0, uri);
 
951
    g_object_unref (uri);
 
952
}
 
953
 
 
954
static void
 
955
popup_copy_link (GtkMenuItem *item,
 
956
                 YelpView    *view)
 
957
{
 
958
    YelpViewPrivate *priv = GET_PRIV (view);
 
959
    gtk_clipboard_set_text (gtk_widget_get_clipboard (GTK_WIDGET (view), GDK_SELECTION_CLIPBOARD),
 
960
                            priv->popup_link_uri,
 
961
                            -1);
 
962
}
 
963
 
 
964
typedef struct _YelpSaveData YelpSaveData;
 
965
struct _YelpSaveData {
 
966
    GFile     *orig;
 
967
    GFile     *dest;
 
968
    YelpView  *view;
 
969
    GtkWindow *window;
 
970
};
 
971
 
 
972
static void
 
973
file_copied (GFile        *file,
 
974
             GAsyncResult *res,
 
975
             YelpSaveData *data)
 
976
{
 
977
    GError *error = NULL;
 
978
    if (!g_file_copy_finish (file, res, &error)) {
 
979
        GtkWidget *dialog = gtk_message_dialog_new (gtk_widget_get_visible (GTK_WIDGET (data->window)) ? data->window : NULL,
 
980
                                                    GTK_DIALOG_DESTROY_WITH_PARENT,
 
981
                                                    GTK_MESSAGE_ERROR,
 
982
                                                    GTK_BUTTONS_OK,
 
983
                                                    "%s", error->message);
 
984
        gtk_dialog_run (GTK_DIALOG (dialog));
 
985
        gtk_widget_destroy (dialog);
 
986
    }
 
987
    g_object_unref (data->orig);
 
988
    g_object_unref (data->dest);
 
989
    g_object_unref (data->view);
 
990
    g_object_unref (data->window);
 
991
}
 
992
 
 
993
static void
 
994
popup_save_image (GtkMenuItem *item,
 
995
                  YelpView    *view)
 
996
{
 
997
    YelpSaveData *data;
 
998
    GtkWidget *dialog, *window;
 
999
    gchar *basename;
 
1000
    gint res;
 
1001
    YelpViewPrivate *priv = GET_PRIV (view);
 
1002
 
 
1003
    for (window = gtk_widget_get_parent (GTK_WIDGET (view));
 
1004
         window && !GTK_IS_WINDOW (window);
 
1005
         window = gtk_widget_get_parent (window));
 
1006
 
 
1007
    data = g_new0 (YelpSaveData, 1);
 
1008
    data->orig = g_file_new_for_uri (priv->popup_image_uri);
 
1009
    data->view = g_object_ref (view);
 
1010
    data->window = g_object_ref (window);
 
1011
    g_free (priv->popup_image_uri);
 
1012
    priv->popup_image_uri = NULL;
 
1013
 
 
1014
    dialog = gtk_file_chooser_dialog_new (_("Save Image"),
 
1015
                                          GTK_WINDOW (window),
 
1016
                                          GTK_FILE_CHOOSER_ACTION_SAVE,
 
1017
                                          GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
 
1018
                                          GTK_STOCK_SAVE, GTK_RESPONSE_OK,
 
1019
                                          NULL);
 
1020
    gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (dialog), TRUE);
 
1021
    basename = g_file_get_basename (data->orig);
 
1022
    gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (dialog), basename);
 
1023
    g_free (basename);
 
1024
    gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dialog),
 
1025
                                         g_get_user_special_dir (G_USER_DIRECTORY_DESKTOP));
 
1026
 
 
1027
    res = gtk_dialog_run (GTK_DIALOG (dialog));
 
1028
 
 
1029
    if (res == GTK_RESPONSE_OK) {
 
1030
        data->dest = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (dialog));
 
1031
        g_file_copy_async (data->orig, data->dest,
 
1032
                           G_FILE_COPY_OVERWRITE,
 
1033
                           G_PRIORITY_DEFAULT,
 
1034
                           NULL, NULL, NULL,
 
1035
                           (GAsyncReadyCallback) file_copied,
 
1036
                           data);
 
1037
    }
 
1038
    else {
 
1039
        g_object_unref (data->orig);
 
1040
        g_object_unref (data->view);
 
1041
        g_object_unref (data->window);
 
1042
        g_free (data);
 
1043
    }
 
1044
 
 
1045
    gtk_widget_destroy (dialog);
 
1046
}
 
1047
 
 
1048
static void
 
1049
popup_send_image (GtkMenuItem *item,
 
1050
                  YelpView    *view)
 
1051
{
 
1052
    gchar *command;
 
1053
    GAppInfo *app;
 
1054
    GAppLaunchContext *context;
 
1055
    GError *error = NULL;
 
1056
    YelpViewPrivate *priv = GET_PRIV (view);
 
1057
 
 
1058
    command = g_strdup_printf ("%s %s", nautilus_sendto, priv->popup_image_uri);
 
1059
    context = (GAppLaunchContext *) gdk_display_get_app_launch_context (gtk_widget_get_display (GTK_WIDGET (item)));
 
1060
 
 
1061
    app = g_app_info_create_from_commandline (command, NULL, 0, &error);
 
1062
    if (app) {
 
1063
        g_app_info_launch (app, NULL, context, &error);
 
1064
        g_object_unref (app);
 
1065
    }
 
1066
 
 
1067
    if (error) {
 
1068
        g_debug ("Could not launch nautilus-sendto: %s", error->message);
 
1069
        g_error_free (error);
 
1070
    }
 
1071
 
 
1072
    g_object_unref (context);
 
1073
    g_free (command);
 
1074
    g_free (priv->popup_image_uri);
 
1075
    priv->popup_image_uri = NULL;
 
1076
}
 
1077
 
 
1078
static void
 
1079
popup_copy_code (GtkMenuItem *item,
 
1080
                 YelpView    *view)
 
1081
{
 
1082
    YelpViewPrivate *priv = GET_PRIV (view);
 
1083
    GtkClipboard *clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
 
1084
    gchar *content = webkit_dom_node_get_text_content (priv->popup_code_node);
 
1085
    gtk_clipboard_set_text (clipboard, content, -1);
 
1086
    g_free (content);
 
1087
}
 
1088
 
 
1089
static void
 
1090
popup_save_code (GtkMenuItem *item,
 
1091
                 YelpView    *view)
 
1092
{
 
1093
    YelpViewPrivate *priv = GET_PRIV (view);
 
1094
    GtkWidget *dialog, *window;
 
1095
    gint res;
 
1096
 
 
1097
    g_free (priv->popup_code_text);
 
1098
    priv->popup_code_text = webkit_dom_node_get_text_content (priv->popup_code_node);
 
1099
    if (!g_str_has_suffix (priv->popup_code_text, "\n")) {
 
1100
        gchar *tmp = g_strconcat (priv->popup_code_text, "\n", NULL);
 
1101
        g_free (priv->popup_code_text);
 
1102
        priv->popup_code_text = tmp;
 
1103
    }
 
1104
 
 
1105
    for (window = gtk_widget_get_parent (GTK_WIDGET (view));
 
1106
         window && !GTK_IS_WINDOW (window);
 
1107
         window = gtk_widget_get_parent (window));
 
1108
 
 
1109
    dialog = gtk_file_chooser_dialog_new (_("Save Code"),
 
1110
                                          GTK_WINDOW (window),
 
1111
                                          GTK_FILE_CHOOSER_ACTION_SAVE,
 
1112
                                          GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
 
1113
                                          GTK_STOCK_SAVE, GTK_RESPONSE_OK,
 
1114
                                          NULL);
 
1115
    gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (dialog), TRUE);
 
1116
    if (priv->popup_code_title) {
 
1117
        gchar *filename = webkit_dom_node_get_text_content (priv->popup_code_title);
 
1118
        gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (dialog), filename);
 
1119
        g_free (filename);
 
1120
    }
 
1121
    gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dialog),
 
1122
                                         g_get_user_special_dir (G_USER_DIRECTORY_DOCUMENTS));
 
1123
 
 
1124
    res = gtk_dialog_run (GTK_DIALOG (dialog));
 
1125
 
 
1126
    if (res == GTK_RESPONSE_OK) {
 
1127
        GError *error = NULL;
 
1128
        GFile *file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (dialog));
 
1129
        GFileOutputStream *stream = g_file_replace (file, NULL, FALSE,
 
1130
                                                    G_FILE_CREATE_NONE,
 
1131
                                                    NULL,
 
1132
                                                    &error);
 
1133
        if (stream == NULL) {
 
1134
            GtkWidget *dialog = gtk_message_dialog_new (gtk_widget_get_visible (window) ? GTK_WINDOW (window) : NULL,
 
1135
                                                        GTK_DIALOG_DESTROY_WITH_PARENT,
 
1136
                                                        GTK_MESSAGE_ERROR,
 
1137
                                                        GTK_BUTTONS_OK,
 
1138
                                                        "%s", error->message);
 
1139
            gtk_dialog_run (GTK_DIALOG (dialog));
 
1140
            gtk_widget_destroy (dialog);
 
1141
            g_error_free (error);
 
1142
        }
 
1143
        else {
 
1144
            /* FIXME: we should do this async */
 
1145
            GDataOutputStream *datastream = g_data_output_stream_new (G_OUTPUT_STREAM (stream));
 
1146
            if (!g_data_output_stream_put_string (datastream, priv->popup_code_text, NULL, &error)) {
 
1147
                GtkWidget *dialog = gtk_message_dialog_new (gtk_widget_get_visible (window) ? GTK_WINDOW (window) : NULL,
 
1148
                                                            GTK_DIALOG_DESTROY_WITH_PARENT,
 
1149
                                                            GTK_MESSAGE_ERROR,
 
1150
                                                            GTK_BUTTONS_OK,
 
1151
                                                            "%s", error->message);
 
1152
                gtk_dialog_run (GTK_DIALOG (dialog));
 
1153
                gtk_widget_destroy (dialog);
 
1154
                g_error_free (error);
 
1155
            }
 
1156
            g_object_unref (datastream);
 
1157
        }
 
1158
        g_object_unref (file);
 
1159
    }
 
1160
 
 
1161
    priv->popup_code_node = NULL;
 
1162
    priv->popup_code_title = NULL;
 
1163
    g_free (priv->popup_code_text);
 
1164
    priv->popup_code_text = NULL;
 
1165
 
 
1166
    gtk_widget_destroy (dialog);
 
1167
}
 
1168
 
 
1169
static void
 
1170
view_populate_popup (YelpView *view,
 
1171
                     GtkMenu  *menu,
 
1172
                     gpointer  data)
 
1173
{
 
1174
    WebKitHitTestResult *result;
 
1175
    WebKitHitTestResultContext context;
 
1176
    GdkEvent *event;
 
1177
    YelpViewPrivate *priv = GET_PRIV (view);
 
1178
    GList *children;
 
1179
    GtkWidget *item;
 
1180
    WebKitDOMNode *node, *cur, *link_node = NULL, *code_node = NULL, *code_title_node = NULL;
 
1181
 
 
1182
    children = gtk_container_get_children (GTK_CONTAINER (menu));
 
1183
    while (children) {
 
1184
        gtk_container_remove (GTK_CONTAINER (menu),
 
1185
                              GTK_WIDGET (children->data));
 
1186
        children = children->next;
 
1187
    }
 
1188
    g_list_free (children);
 
1189
 
 
1190
    event = gtk_get_current_event ();
 
1191
 
 
1192
    result = webkit_web_view_get_hit_test_result (WEBKIT_WEB_VIEW (view), (GdkEventButton *) event);
 
1193
    g_object_get (result,
 
1194
                  "context", &context,
 
1195
                  "inner-node", &node,
 
1196
                  NULL);
 
1197
    for (cur = node; cur != NULL; cur = webkit_dom_node_get_parent_node (cur)) {
 
1198
        if (WEBKIT_DOM_IS_ELEMENT (cur) &&
 
1199
            webkit_dom_element_webkit_matches_selector ((WebKitDOMElement *) cur,
 
1200
                                                        "a", NULL))
 
1201
            link_node = cur;
 
1202
 
 
1203
        if (WEBKIT_DOM_IS_ELEMENT (cur) &&
 
1204
            webkit_dom_element_webkit_matches_selector ((WebKitDOMElement *) cur,
 
1205
                                                        "div.code", NULL)) {
 
1206
            WebKitDOMNode *title;
 
1207
            code_node = (WebKitDOMNode *)
 
1208
                webkit_dom_element_query_selector ((WebKitDOMElement *) cur,
 
1209
                                                   "pre.contents", NULL);
 
1210
            title = webkit_dom_node_get_parent_node (cur);
 
1211
            if (title != NULL && WEBKIT_DOM_IS_ELEMENT (title) &&
 
1212
                webkit_dom_element_webkit_matches_selector ((WebKitDOMElement *) title,
 
1213
                                                            "div.contents", NULL)) {
 
1214
                title = webkit_dom_node_get_previous_sibling (title);
 
1215
                if (title != NULL && WEBKIT_DOM_IS_ELEMENT (title) &&
 
1216
                    webkit_dom_element_webkit_matches_selector ((WebKitDOMElement *) title,
 
1217
                                                                "div.title", NULL)) {
 
1218
                    code_title_node = title;
 
1219
                }
 
1220
            }
 
1221
        }
 
1222
    }
 
1223
 
 
1224
    if (context & WEBKIT_HIT_TEST_RESULT_CONTEXT_LINK) {
 
1225
        gchar *uri;
 
1226
        g_object_get (result, "link-uri", &uri, NULL);
 
1227
        g_free (priv->popup_link_uri);
 
1228
        priv->popup_link_uri = uri;
 
1229
 
 
1230
        g_free (priv->popup_link_text);
 
1231
        priv->popup_link_text = NULL;
 
1232
        if (link_node != NULL) {
 
1233
            WebKitDOMNode *child;
 
1234
            gchar *tmp;
 
1235
            gint i, tmpi;
 
1236
            gboolean ws;
 
1237
 
 
1238
            child = (WebKitDOMNode *)
 
1239
                webkit_dom_element_query_selector (WEBKIT_DOM_ELEMENT (link_node),
 
1240
                                                   "span.title", NULL);
 
1241
            if (child != NULL)
 
1242
                priv->popup_link_text = webkit_dom_node_get_text_content (child);
 
1243
 
 
1244
            if (priv->popup_link_text == NULL)
 
1245
                priv->popup_link_text = webkit_dom_node_get_text_content (link_node);
 
1246
 
 
1247
            tmp = g_new0 (gchar, strlen(priv->popup_link_text) + 1);
 
1248
            ws = FALSE;
 
1249
            for (i = 0, tmpi = 0; priv->popup_link_text[i] != '\0'; i++) {
 
1250
                if (priv->popup_link_text[i] == ' ' || priv->popup_link_text[i] == '\n') {
 
1251
                    if (!ws) {
 
1252
                        tmp[tmpi] = ' ';
 
1253
                        tmpi++;
 
1254
                        ws = TRUE;
 
1255
                    }
 
1256
                }
 
1257
                else {
 
1258
                    tmp[tmpi] = priv->popup_link_text[i];
 
1259
                    tmpi++;
 
1260
                    ws = FALSE;
 
1261
                }
 
1262
            }
 
1263
            tmp[tmpi] = '\0';
 
1264
            g_free (priv->popup_link_text);
 
1265
            priv->popup_link_text = tmp;
 
1266
        }
 
1267
        else {
 
1268
            priv->popup_link_text = g_strdup (uri);
 
1269
        }
 
1270
 
 
1271
        if (g_str_has_prefix (priv->popup_link_uri, "mailto:")) {
 
1272
            gchar *label = g_strdup_printf (_("Send email to %s"),
 
1273
                                            priv->popup_link_uri + 7);
 
1274
            /* Not using a mnemonic because underscores are common in email
 
1275
             * addresses, and we'd have to escape them. There doesn't seem
 
1276
             * to be a quick GTK+ function for this. In practice, there will
 
1277
             * probably only be one menu item for mailto link popups anyway,
 
1278
             * so the mnemonic's not that big of a deal.
 
1279
             */
 
1280
            item = gtk_menu_item_new_with_label (label);
 
1281
            g_signal_connect (item, "activate",
 
1282
                              G_CALLBACK (popup_open_link), view);
 
1283
            gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
 
1284
            g_free (label);
 
1285
        }
 
1286
        else if (g_str_has_prefix (priv->popup_link_uri, "install:")) {
 
1287
            item = gtk_menu_item_new_with_mnemonic (_("_Install Packages"));
 
1288
            g_signal_connect (item, "activate",
 
1289
                              G_CALLBACK (popup_open_link), view);
 
1290
            gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
 
1291
        }
 
1292
        else {
 
1293
            GSList *cur;
 
1294
 
 
1295
            item = gtk_menu_item_new_with_mnemonic (_("_Open Link"));
 
1296
            g_signal_connect (item, "activate",
 
1297
                              G_CALLBACK (popup_open_link), view);
 
1298
            gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
 
1299
 
 
1300
            if (g_str_has_prefix (priv->popup_link_uri, "http://") ||
 
1301
                g_str_has_prefix (priv->popup_link_uri, "https://")) {
 
1302
                item = gtk_menu_item_new_with_mnemonic (_("_Copy Link Location"));
 
1303
                g_signal_connect (item, "activate",
 
1304
                                  G_CALLBACK (popup_copy_link), view);
 
1305
                gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
 
1306
            }
 
1307
            else {
 
1308
                item = gtk_menu_item_new_with_mnemonic (_("Open Link in New _Window"));
 
1309
                g_signal_connect (item, "activate",
 
1310
                                  G_CALLBACK (popup_open_link_new), view);
 
1311
                gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
 
1312
            }
 
1313
 
 
1314
            for (cur = priv->link_actions; cur != NULL; cur = cur->next) {
 
1315
                gboolean add;
 
1316
                YelpActionEntry *entry = (YelpActionEntry *) cur->data;
 
1317
                if (entry->func == NULL)
 
1318
                    add = TRUE;
 
1319
                else
 
1320
                    add = (* entry->func) (view, entry->action,
 
1321
                                           priv->popup_link_uri,
 
1322
                                           entry->data);
 
1323
                if (add) {
 
1324
                    item = gtk_action_create_menu_item (entry->action);
 
1325
                    gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
 
1326
                }
 
1327
            }
 
1328
        }
 
1329
    }
 
1330
    else {
 
1331
        item = gtk_action_create_menu_item (gtk_action_group_get_action (priv->action_group,
 
1332
                                                                         "YelpViewGoBack"));
 
1333
        gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
 
1334
        item = gtk_action_create_menu_item (gtk_action_group_get_action (priv->action_group,
 
1335
                                                                         "YelpViewGoForward"));
 
1336
        gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
 
1337
    }
 
1338
 
 
1339
    if ((context & WEBKIT_HIT_TEST_RESULT_CONTEXT_IMAGE) ||
 
1340
        (context & WEBKIT_HIT_TEST_RESULT_CONTEXT_MEDIA)) {
 
1341
        /* This doesn't currently work for video with automatic controls,
 
1342
         * because WebKit puts the hit test on the div with the controls.
 
1343
         */
 
1344
        gboolean image = context & WEBKIT_HIT_TEST_RESULT_CONTEXT_IMAGE;
 
1345
        gchar *uri;
 
1346
        g_object_get (result, image ? "image-uri" : "media-uri", &uri, NULL);
 
1347
        g_free (priv->popup_image_uri);
 
1348
        if (g_str_has_prefix (uri, BOGUS_URI)) {
 
1349
            priv->popup_image_uri = yelp_uri_locate_file_uri (priv->uri, uri + BOGUS_URI_LEN);
 
1350
            g_free (uri);
 
1351
        }
 
1352
        else {
 
1353
            priv->popup_image_uri = uri;
 
1354
        }
 
1355
 
 
1356
        item = gtk_separator_menu_item_new ();
 
1357
        gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
 
1358
 
 
1359
        if (image)
 
1360
            item = gtk_menu_item_new_with_mnemonic (_("_Save Image As..."));
 
1361
        else
 
1362
            item = gtk_menu_item_new_with_mnemonic (_("_Save Video As..."));
 
1363
        g_signal_connect (item, "activate",
 
1364
                          G_CALLBACK (popup_save_image), view);
 
1365
        gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
 
1366
 
 
1367
        if (nautilus_sendto) {
 
1368
            if (image)
 
1369
                item = gtk_menu_item_new_with_mnemonic (_("S_end Image To..."));
 
1370
            else
 
1371
                item = gtk_menu_item_new_with_mnemonic (_("S_end Video To..."));
 
1372
            g_signal_connect (item, "activate",
 
1373
                              G_CALLBACK (popup_send_image), view);
 
1374
            gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
 
1375
        }
 
1376
    }
 
1377
 
 
1378
    if (context & WEBKIT_HIT_TEST_RESULT_CONTEXT_SELECTION) {
 
1379
        item = gtk_separator_menu_item_new ();
 
1380
        gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
 
1381
 
 
1382
        item = gtk_menu_item_new_with_mnemonic (_("_Copy Text"));
 
1383
        g_signal_connect_swapped (item, "activate",
 
1384
                                  G_CALLBACK (webkit_web_view_copy_clipboard), view);
 
1385
        gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
 
1386
    }
 
1387
 
 
1388
    if (code_node != NULL) {
 
1389
        item = gtk_separator_menu_item_new ();
 
1390
        gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
 
1391
 
 
1392
        priv->popup_code_node = code_node;
 
1393
        priv->popup_code_title = code_title_node;
 
1394
 
 
1395
        item = gtk_menu_item_new_with_mnemonic (_("C_opy Code Block"));
 
1396
        g_signal_connect (item, "activate",
 
1397
                          G_CALLBACK (popup_copy_code), view);
 
1398
        gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
 
1399
 
 
1400
        item = gtk_menu_item_new_with_mnemonic (_("Save Code _Block As..."));
 
1401
        g_signal_connect (item, "activate",
 
1402
                          G_CALLBACK (popup_save_code), view);
 
1403
        gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
 
1404
    }
 
1405
 
 
1406
    g_object_unref (result);
 
1407
    gdk_event_free (event);
 
1408
    gtk_widget_show_all (GTK_WIDGET (menu));
 
1409
}
 
1410
 
 
1411
static void
 
1412
view_script_alert (YelpView        *view,
 
1413
                   WebKitWebFrame  *frame,
 
1414
                   gchar           *message,
 
1415
                   gpointer         data)
 
1416
{
 
1417
    printf ("\n\n===ALERT===\n%s\n\n", message);
 
1418
}
 
1419
 
 
1420
static gboolean
 
1421
view_navigation_requested (WebKitWebView             *view,
 
1422
                           WebKitWebFrame            *frame,
 
1423
                           WebKitNetworkRequest      *request,
 
1424
                           WebKitWebNavigationAction *action,
 
1425
                           WebKitWebPolicyDecision   *decision,
 
1426
                           gpointer                   user_data)
 
1427
{
 
1428
    const gchar *requri = webkit_network_request_get_uri (request);
 
1429
    YelpViewPrivate *priv = GET_PRIV (view);
 
1430
    YelpUri *uri;
 
1431
 
 
1432
    if (priv->bogus_uri &&
 
1433
        g_str_has_prefix (requri, priv->bogus_uri) &&
 
1434
        requri[strlen(priv->bogus_uri)] == '#') {
 
1435
        gchar *tmp = g_strconcat("xref:", requri + strlen(priv->bogus_uri), NULL);
 
1436
        uri = yelp_uri_new_relative (priv->uri, tmp);
 
1437
        g_free (tmp);
 
1438
    }
 
1439
    else if (g_str_has_prefix (requri, BOGUS_URI)) {
 
1440
        uri = yelp_uri_new_relative (priv->uri, requri + BOGUS_URI_LEN);
 
1441
    }
 
1442
    else
 
1443
        uri = yelp_uri_new_relative (priv->uri, requri);
 
1444
 
 
1445
    webkit_web_policy_decision_ignore (decision);
 
1446
 
 
1447
    yelp_view_load_uri ((YelpView *) view, uri);
 
1448
    g_object_unref (uri);
 
1449
 
 
1450
    return TRUE;
 
1451
}
 
1452
 
 
1453
static void
 
1454
view_resource_request (WebKitWebView         *view,
 
1455
                       WebKitWebFrame        *frame,
 
1456
                       WebKitWebResource     *resource,
 
1457
                       WebKitNetworkRequest  *request,
 
1458
                       WebKitNetworkResponse *response,
 
1459
                       gpointer               user_data)
 
1460
{
 
1461
    YelpViewPrivate *priv = GET_PRIV (view);
 
1462
    const gchar *requri = webkit_network_request_get_uri (request);
 
1463
    gchar last;
 
1464
    gchar *newpath;
 
1465
 
 
1466
    if (!g_str_has_prefix (requri, BOGUS_URI))
 
1467
        return;
 
1468
 
 
1469
    /* We get this signal for the page itself.  Ignore. */
 
1470
    if (g_str_equal (requri, priv->bogus_uri))
 
1471
        return;
 
1472
 
 
1473
    newpath = yelp_uri_locate_file_uri (priv->uri, requri + BOGUS_URI_LEN);
 
1474
    if (newpath != NULL) {
 
1475
        webkit_network_request_set_uri (request, newpath);
 
1476
        g_free (newpath);
 
1477
    }
 
1478
    else {
 
1479
        webkit_network_request_set_uri (request, "about:blank");
 
1480
    }
 
1481
}
 
1482
 
 
1483
static void
 
1484
view_document_loaded (WebKitWebView   *view,
 
1485
                      WebKitWebFrame  *frame,
 
1486
                      gpointer         user_data)
 
1487
{
 
1488
    YelpViewPrivate *priv = GET_PRIV (view);
 
1489
    gchar *search_terms;
 
1490
 
 
1491
    search_terms = yelp_uri_get_query (priv->uri, "terms");
 
1492
 
 
1493
    if (search_terms) {
 
1494
        WebKitDOMDocument *doc;
 
1495
        WebKitDOMElement *body, *link;
 
1496
        doc = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
 
1497
        body = webkit_dom_document_query_selector (doc, "div.body", NULL);
 
1498
        if (body) {
 
1499
            gchar *tmp, *uri, *txt;
 
1500
            link = webkit_dom_document_create_element (doc, "a", NULL);
 
1501
            webkit_dom_element_set_attribute (link, "class", "fullsearch", NULL);
 
1502
            tmp = g_uri_escape_string (search_terms, NULL, FALSE);
 
1503
            uri = g_strconcat ("xref:search=", tmp, NULL);
 
1504
            webkit_dom_element_set_attribute (link, "href", uri, NULL);
 
1505
            g_free (tmp);
 
1506
            g_free (uri);
 
1507
            txt = g_strdup_printf (_("See all search results for “%s”"),
 
1508
                                   search_terms);
 
1509
            webkit_dom_node_set_text_content (WEBKIT_DOM_NODE (link), txt, NULL);
 
1510
            g_free (txt);
 
1511
            webkit_dom_node_insert_before (WEBKIT_DOM_NODE (body),
 
1512
                                           WEBKIT_DOM_NODE (link),
 
1513
                                           webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (body)),
 
1514
                                           NULL);
 
1515
        }
 
1516
        g_free (search_terms);
 
1517
    }
 
1518
}
 
1519
 
 
1520
static void
 
1521
view_print (GtkAction *action, YelpView  *view)
 
1522
{
 
1523
    webkit_web_frame_print (webkit_web_view_get_main_frame (WEBKIT_WEB_VIEW (view)));
 
1524
}
 
1525
 
 
1526
static void
 
1527
view_history_action (GtkAction *action,
 
1528
                     YelpView  *view)
 
1529
{
 
1530
    GList *newcur;
 
1531
    YelpViewPrivate *priv = GET_PRIV (view);
 
1532
 
 
1533
    if (priv->back_cur == NULL)
 
1534
        return;
 
1535
 
 
1536
    if (g_str_equal (gtk_action_get_name (action), "YelpViewGoBack"))
 
1537
        newcur = priv->back_cur->next;
 
1538
    else
 
1539
        newcur = priv->back_cur->prev;
 
1540
 
 
1541
    if (newcur == NULL)
 
1542
        return;
 
1543
 
 
1544
    priv->back_cur = newcur;
 
1545
 
 
1546
    if (priv->back_cur->data == NULL)
 
1547
        return;
 
1548
 
 
1549
    priv->back_load = TRUE;
 
1550
    yelp_view_load_uri (view, ((YelpBackEntry *) priv->back_cur->data)->uri);
 
1551
    priv->vadjust = ((YelpBackEntry *) priv->back_cur->data)->vadj;
 
1552
    priv->hadjust = ((YelpBackEntry *) priv->back_cur->data)->hadj;
 
1553
}
 
1554
 
 
1555
static void
 
1556
view_navigation_action (GtkAction *action,
 
1557
                        YelpView  *view)
 
1558
{
 
1559
    YelpViewPrivate *priv = GET_PRIV (view);
 
1560
    gchar *page_id, *new_id, *xref;
 
1561
    YelpUri *new_uri;
 
1562
 
 
1563
    page_id = yelp_uri_get_page_id (priv->uri);
 
1564
 
 
1565
    if (g_str_equal (gtk_action_get_name (action), "YelpViewGoPrevious"))
 
1566
        new_id = yelp_document_get_prev_id (priv->document, page_id);
 
1567
    else
 
1568
        new_id = yelp_document_get_next_id (priv->document, page_id);
 
1569
 
 
1570
    /* Just in case we screwed up somewhere */
 
1571
    if (new_id == NULL) {
 
1572
        gtk_action_set_sensitive (action, FALSE);
 
1573
        return;
 
1574
    }
 
1575
 
 
1576
    xref = g_strconcat ("xref:", new_id, NULL);
 
1577
    new_uri = yelp_uri_new_relative (priv->uri, xref);
 
1578
    yelp_view_load_uri (view, new_uri);
 
1579
 
 
1580
    g_free (xref);
 
1581
    g_free (new_id);
 
1582
    g_object_unref (new_uri);
 
1583
}
 
1584
 
 
1585
static void
 
1586
view_clear_load (YelpView *view)
 
1587
{
 
1588
    YelpViewPrivate *priv = GET_PRIV (view);
 
1589
 
 
1590
    if (priv->resolve_uri != NULL) {
 
1591
        if (priv->uri_resolved != 0) {
 
1592
            g_signal_handler_disconnect (priv->resolve_uri, priv->uri_resolved);
 
1593
            priv->uri_resolved = 0;
 
1594
        }
 
1595
        g_object_unref (priv->resolve_uri);
 
1596
    }
 
1597
    priv->resolve_uri = NULL;
 
1598
 
 
1599
    if (priv->uri) {
 
1600
        g_object_unref (priv->uri);
 
1601
        priv->uri = NULL;
 
1602
    }
 
1603
 
 
1604
    if (priv->cancellable) {
 
1605
        g_cancellable_cancel (priv->cancellable);
 
1606
        priv->cancellable = NULL;
 
1607
    }
 
1608
}
 
1609
 
 
1610
static void
 
1611
view_load_page (YelpView *view)
 
1612
{
 
1613
    YelpViewPrivate *priv = GET_PRIV (view);
 
1614
    gchar *page_id;
 
1615
 
 
1616
    debug_print (DB_FUNCTION, "entering\n");
 
1617
 
 
1618
    g_return_if_fail (priv->cancellable == NULL);
 
1619
 
 
1620
    if (priv->document == NULL) {
 
1621
        GError *error;
 
1622
        gchar *docuri;
 
1623
        /* FIXME: and if priv->uri is NULL? */
 
1624
        docuri = yelp_uri_get_document_uri (priv->uri);
 
1625
        /* FIXME: CANT_READ isn't right */
 
1626
        if (docuri) {
 
1627
            error = g_error_new (YELP_ERROR, YELP_ERROR_CANT_READ,
 
1628
                                 _("Could not load a document for ‘%s’"),
 
1629
                                 docuri);
 
1630
            g_free (docuri);
 
1631
        }
 
1632
        else {
 
1633
            error = g_error_new (YELP_ERROR, YELP_ERROR_CANT_READ,
 
1634
                                 _("Could not load a document"));
 
1635
        }
 
1636
        view_show_error_page (view, error);
 
1637
        g_error_free (error);
 
1638
        return;
 
1639
    }
 
1640
 
 
1641
    page_id = yelp_uri_get_page_id (priv->uri);
 
1642
    priv->cancellable = g_cancellable_new ();
 
1643
    yelp_document_request_page (priv->document,
 
1644
                                page_id,
 
1645
                                priv->cancellable,
 
1646
                                (YelpDocumentCallback) document_callback,
 
1647
                                view);
 
1648
    g_free (page_id);
 
1649
}
 
1650
 
 
1651
static void
 
1652
view_show_error_page (YelpView *view,
 
1653
                      GError   *error)
 
1654
{
 
1655
    YelpViewPrivate *priv = GET_PRIV (view);
 
1656
    static const gchar *errorpage =
 
1657
        "<html><head>"
 
1658
        "<style type='text/css'>"
 
1659
        "body {"
 
1660
        " margin: 1em;"
 
1661
        " color: %s;"
 
1662
        " background-color: %s;"
 
1663
        " }\n"
 
1664
        "p { margin: 1em 0 0 0; }\n"
 
1665
        "div.note {"
 
1666
        " padding: 6px;"
 
1667
        " border-color: %s;"
 
1668
        " border-top: solid 1px;"
 
1669
        " border-bottom: solid 1px;"
 
1670
        " background-color: %s;"
 
1671
        " }\n"
 
1672
        "div.note div.inner {"
 
1673
        " margin: 0; padding: 0;"
 
1674
        " background-image: url(%s);"
 
1675
        " background-position: %s top;"
 
1676
        " background-repeat: no-repeat;"
 
1677
        " min-height: %ipx;"
 
1678
        " }\n"
 
1679
        "div.note div.contents {"
 
1680
        " margin-%s: %ipx;"
 
1681
        " }\n"
 
1682
        "div.note div.title {"
 
1683
        " margin-%s: %ipx;"
 
1684
        " margin-bottom: 0.2em;"
 
1685
        " font-weight: bold;"
 
1686
        " color: %s;"
 
1687
        " }\n"
 
1688
        "a { color: %s; text-decoration: none; }\n"
 
1689
        "</style>"
 
1690
        "</head><body>"
 
1691
        "<div class='note'><div class='inner'>"
 
1692
        "%s<div class='contents'>%s%s</div>"
 
1693
        "</div></div>"
 
1694
        "</body></html>";
 
1695
    YelpSettings *settings = yelp_settings_get_default ();
 
1696
    gchar *page, *title = NULL, *link = NULL, *title_m, *content_beg, *content_end;
 
1697
    gchar *textcolor, *bgcolor, *noteborder, *notebg, *titlecolor, *noteicon, *linkcolor;
 
1698
    gint iconsize;
 
1699
    GParamSpec *spec;
 
1700
    gboolean doc404 = FALSE;
 
1701
    const gchar *left = (gtk_widget_get_direction((GtkWidget *) view) == GTK_TEXT_DIR_RTL) ? "right" : "left";
 
1702
 
 
1703
    if (priv->uri && yelp_uri_get_document_type (priv->uri) == YELP_URI_DOCUMENT_TYPE_NOT_FOUND)
 
1704
        doc404 = TRUE;
 
1705
    if (error->domain == YELP_ERROR)
 
1706
        switch (error->code) {
 
1707
        case YELP_ERROR_NOT_FOUND:
 
1708
            if (doc404)
 
1709
                title = _("Document Not Found");
 
1710
            else
 
1711
                title = _("Page Not Found");
 
1712
            break;
 
1713
        case YELP_ERROR_CANT_READ:
 
1714
            title = _("Cannot Read");
 
1715
            break;
 
1716
        default:
 
1717
            break;
 
1718
        }
 
1719
    if (title == NULL)
 
1720
        title = _("Unknown Error");
 
1721
    title_m = g_markup_printf_escaped ("<div class='title'>%s</div>", title);
 
1722
 
 
1723
    content_beg = g_markup_printf_escaped ("<p>%s</p>", error->message);
 
1724
    content_end = NULL;
 
1725
    if (doc404) {
 
1726
        gchar *struri = yelp_uri_get_document_uri (priv->uri);
 
1727
        /* do not free */
 
1728
        gchar *pkg = NULL, *scheme = NULL;
 
1729
        if (g_str_has_prefix (struri, "help:")) {
 
1730
            scheme = "help";
 
1731
            pkg = struri + 5;
 
1732
        }
 
1733
        else if (g_str_has_prefix (struri, "ghelp:")) {
 
1734
            scheme = "ghelp";
 
1735
            pkg = struri + 6;
 
1736
        }
 
1737
        g_free (struri);
 
1738
    }
 
1739
 
 
1740
    textcolor = yelp_settings_get_color (settings, YELP_SETTINGS_COLOR_TEXT);
 
1741
    bgcolor = yelp_settings_get_color (settings, YELP_SETTINGS_COLOR_BASE);
 
1742
    noteborder = yelp_settings_get_color (settings, YELP_SETTINGS_COLOR_RED_BORDER);
 
1743
    notebg = yelp_settings_get_color (settings, YELP_SETTINGS_COLOR_YELLOW_BASE);
 
1744
    titlecolor = yelp_settings_get_color (settings, YELP_SETTINGS_COLOR_TEXT_LIGHT);
 
1745
    linkcolor = yelp_settings_get_color (settings, YELP_SETTINGS_COLOR_LINK);
 
1746
    noteicon = yelp_settings_get_icon (settings, YELP_SETTINGS_ICON_WARNING);
 
1747
    iconsize = yelp_settings_get_icon_size (settings) + 6;
 
1748
 
 
1749
    page = g_strdup_printf (errorpage,
 
1750
                            textcolor, bgcolor, noteborder, notebg, noteicon,
 
1751
                            left, iconsize, left, iconsize, left, iconsize,
 
1752
                            titlecolor, linkcolor, title_m, content_beg,
 
1753
                            (content_end != NULL) ? content_end : "");
 
1754
 
 
1755
    g_object_set (view, "state", YELP_VIEW_STATE_ERROR, NULL);
 
1756
 
 
1757
    if (doc404) {
 
1758
        g_free (priv->root_title);
 
1759
        priv->root_title = g_strdup (title);
 
1760
        spec = g_object_class_find_property ((GObjectClass *) YELP_VIEW_GET_CLASS (view),
 
1761
                                             "root-title");
 
1762
        g_signal_emit_by_name (view, "notify::root-title", spec);
 
1763
        g_free (priv->page_id);
 
1764
        priv->page_id = g_strdup ("index");
 
1765
        spec = g_object_class_find_property ((GObjectClass *) YELP_VIEW_GET_CLASS (view),
 
1766
                                             "page-id");
 
1767
        g_signal_emit_by_name (view, "notify::page-id", spec);
 
1768
    }
 
1769
 
 
1770
    g_free (priv->page_title);
 
1771
    priv->page_title = g_strdup (title);
 
1772
    spec = g_object_class_find_property ((GObjectClass *) YELP_VIEW_GET_CLASS (view),
 
1773
                                         "page-title");
 
1774
    g_signal_emit_by_name (view, "notify::page-title", spec);
 
1775
 
 
1776
    g_free (priv->page_desc);
 
1777
    priv->page_desc = NULL;
 
1778
    if (priv->uri)
 
1779
        priv->page_desc = yelp_uri_get_canonical_uri (priv->uri);
 
1780
    spec = g_object_class_find_property ((GObjectClass *) YELP_VIEW_GET_CLASS (view),
 
1781
                                         "page-desc");
 
1782
    g_signal_emit_by_name (view, "notify::page-desc", spec);
 
1783
 
 
1784
    g_free (priv->page_icon);
 
1785
    priv->page_icon = g_strdup ("dialog-warning");
 
1786
    spec = g_object_class_find_property ((GObjectClass *) YELP_VIEW_GET_CLASS (view),
 
1787
                                         "page-icon");
 
1788
    g_signal_emit_by_name (view, "notify::page-icon", spec);
 
1789
 
 
1790
    g_signal_emit (view, signals[LOADED], 0);
 
1791
    g_signal_handler_block (view, priv->navigation_requested);
 
1792
    webkit_web_view_load_string (WEBKIT_WEB_VIEW (view),
 
1793
                                 page,
 
1794
                                 "text/html",
 
1795
                                 "UTF-8",
 
1796
                                 "file:///error/");
 
1797
    g_signal_handler_unblock (view, priv->navigation_requested);
 
1798
    g_free (title_m);
 
1799
    g_free (content_beg);
 
1800
    if (content_end != NULL)
 
1801
        g_free (content_end);
 
1802
    g_free (page);
 
1803
}
 
1804
 
 
1805
 
 
1806
static void
 
1807
settings_set_fonts (YelpSettings *settings)
 
1808
{
 
1809
    gchar *family;
 
1810
    gint size;
 
1811
 
 
1812
    g_object_set (websettings,
 
1813
                  "default-encoding", "utf-8",
 
1814
                  "enable-private-browsing", TRUE,
 
1815
                  NULL);
 
1816
 
 
1817
    family = yelp_settings_get_font_family (settings,
 
1818
                                            YELP_SETTINGS_FONT_VARIABLE);
 
1819
    size = yelp_settings_get_font_size (settings,
 
1820
                                        YELP_SETTINGS_FONT_VARIABLE);
 
1821
    g_object_set (websettings,
 
1822
                  "default-font-family", family,
 
1823
                  "sans-serif-font-family", family,
 
1824
                  "default-font-size", size,
 
1825
                  NULL);
 
1826
    g_free (family);
 
1827
 
 
1828
    family = yelp_settings_get_font_family (settings,
 
1829
                                            YELP_SETTINGS_FONT_FIXED);
 
1830
    size = yelp_settings_get_font_size (settings,
 
1831
                                        YELP_SETTINGS_FONT_FIXED);
 
1832
    g_object_set (websettings,
 
1833
                  "monospace-font-family", family,
 
1834
                  "default-monospace-font-size", size,
 
1835
                  NULL);
 
1836
    g_free (family);
 
1837
}
 
1838
 
 
1839
static void
 
1840
settings_show_text_cursor (YelpSettings *settings)
 
1841
{
 
1842
    g_object_set (websettings,
 
1843
                  "enable-caret-browsing",
 
1844
                  yelp_settings_get_show_text_cursor (settings),
 
1845
                  NULL);
 
1846
}
 
1847
 
 
1848
/******************************************************************************/
 
1849
 
 
1850
static void
 
1851
uri_resolved (YelpUri  *uri,
 
1852
              YelpView *view)
 
1853
{
 
1854
    YelpViewPrivate *priv = GET_PRIV (view);
 
1855
    YelpDocument *document;
 
1856
    YelpBackEntry *back;
 
1857
    GtkAction *action;
 
1858
    GSList *proxies, *cur;
 
1859
    GError *error = NULL;
 
1860
    gchar *struri;
 
1861
    GParamSpec *spec;
 
1862
 
 
1863
    if (yelp_uri_get_document_type (uri) != YELP_URI_DOCUMENT_TYPE_EXTERNAL) {
 
1864
        g_object_ref (uri);
 
1865
        view_clear_load (view);
 
1866
        priv->uri = uri;
 
1867
    }
 
1868
 
 
1869
    switch (yelp_uri_get_document_type (uri)) {
 
1870
    case YELP_URI_DOCUMENT_TYPE_EXTERNAL:
 
1871
        g_object_set (view, "state", priv->prevstate, NULL);
 
1872
        struri = yelp_uri_get_canonical_uri (uri);
 
1873
        if (g_str_has_prefix (struri, "install:") ||
 
1874
            g_str_has_prefix (struri, "install-ghelp:") ||
 
1875
            g_str_has_prefix (struri, "install-help:")) {
 
1876
            view_install_uri (view, struri);
 
1877
        }
 
1878
        else {
 
1879
            gboolean result;
 
1880
            g_signal_emit (view, signals[EXTERNAL_URI], 0, uri, &result);
 
1881
        }
 
1882
        g_free (struri);
 
1883
        return;
 
1884
    case YELP_URI_DOCUMENT_TYPE_NOT_FOUND:
 
1885
        struri = yelp_uri_get_canonical_uri (uri);
 
1886
        if (struri != NULL) {
 
1887
            error = g_error_new (YELP_ERROR, YELP_ERROR_NOT_FOUND,
 
1888
                                 _("The URI ‘%s’ does not point to a valid page."),
 
1889
                                 struri);
 
1890
            g_free (struri);
 
1891
        }
 
1892
        else {
 
1893
            error = g_error_new (YELP_ERROR, YELP_ERROR_NOT_FOUND,
 
1894
                                 _("The URI does not point to a valid page."));
 
1895
        }
 
1896
        break;
 
1897
    case YELP_URI_DOCUMENT_TYPE_ERROR:
 
1898
        struri = yelp_uri_get_canonical_uri (uri);
 
1899
        error = g_error_new (YELP_ERROR, YELP_ERROR_PROCESSING,
 
1900
                             _("The URI ‘%s’ could not be parsed."),
 
1901
                             struri);
 
1902
        g_free (struri);
 
1903
        break;
 
1904
    default:
 
1905
        break;
 
1906
    }
 
1907
 
 
1908
    if (error == NULL) {
 
1909
        document  = yelp_document_get_for_uri (uri);
 
1910
        if (priv->document)
 
1911
            g_object_unref (priv->document);
 
1912
        priv->document = document;
 
1913
    }
 
1914
    else {
 
1915
        if (priv->document != NULL) {
 
1916
            g_object_unref (priv->document);
 
1917
            priv->document = NULL;
 
1918
        }
 
1919
    }
 
1920
 
 
1921
    if (!priv->back_load) {
 
1922
        back = g_new0 (YelpBackEntry, 1);
 
1923
        back->uri = g_object_ref (uri);
 
1924
        while (priv->back_list != priv->back_cur) {
 
1925
            back_entry_free ((YelpBackEntry *) priv->back_list->data);
 
1926
            priv->back_list = g_list_delete_link (priv->back_list, priv->back_list);
 
1927
        }
 
1928
        priv->back_list = g_list_prepend (priv->back_list, back);
 
1929
        priv->back_cur = priv->back_list;
 
1930
    }
 
1931
    priv->back_load = FALSE;
 
1932
 
 
1933
    action = gtk_action_group_get_action (priv->action_group, "YelpViewGoBack");
 
1934
    gtk_action_set_sensitive (action, FALSE);
 
1935
    proxies = gtk_action_get_proxies (action);
 
1936
    if (priv->back_cur->next && priv->back_cur->next->data) {
 
1937
        gchar *tooltip = "";
 
1938
        back = priv->back_cur->next->data;
 
1939
 
 
1940
        gtk_action_set_sensitive (action, TRUE);
 
1941
        if (back->title && back->desc) {
 
1942
            gchar *color;
 
1943
            color = yelp_settings_get_color (yelp_settings_get_default (),
 
1944
                                             YELP_SETTINGS_COLOR_TEXT_LIGHT);
 
1945
            tooltip = g_markup_printf_escaped ("<span size='larger'>%s</span>\n<span color='%s'>%s</span>",
 
1946
                                               back->title, color, back->desc);
 
1947
            g_free (color);
 
1948
        }
 
1949
        else if (back->title)
 
1950
            tooltip = g_markup_printf_escaped ("<span size='larger'>%s</span>",
 
1951
                                               back->title);
 
1952
        /* Can't seem to use markup on GtkAction tooltip */
 
1953
        for (cur = proxies; cur != NULL; cur = cur->next)
 
1954
            gtk_widget_set_tooltip_markup (GTK_WIDGET (cur->data), tooltip);
 
1955
    }
 
1956
    else {
 
1957
        for (cur = proxies; cur != NULL; cur = cur->next)
 
1958
            gtk_widget_set_tooltip_text (GTK_WIDGET (cur->data), "");
 
1959
    }
 
1960
 
 
1961
    action = gtk_action_group_get_action (priv->action_group, "YelpViewGoForward");
 
1962
    gtk_action_set_sensitive (action, FALSE);
 
1963
    proxies = gtk_action_get_proxies (action);
 
1964
    if (priv->back_cur->prev && priv->back_cur->prev->data) {
 
1965
        gchar *tooltip = "";
 
1966
        back = priv->back_cur->prev->data;
 
1967
 
 
1968
        gtk_action_set_sensitive (action, TRUE);
 
1969
        if (back->title && back->desc) {
 
1970
            gchar *color;
 
1971
            color = yelp_settings_get_color (yelp_settings_get_default (),
 
1972
                                             YELP_SETTINGS_COLOR_TEXT_LIGHT);
 
1973
            tooltip = g_markup_printf_escaped ("<span size='larger'>%s</span>\n<span color='%s'>%s</span>",
 
1974
                                               back->title, color, back->desc);
 
1975
            g_free (color);
 
1976
        }
 
1977
        else if (back->title)
 
1978
            tooltip = g_markup_printf_escaped ("<span size='larger'>%s</span>",
 
1979
                                               back->title);
 
1980
        /* Can't seem to use markup on GtkAction tooltip */
 
1981
        for (cur = proxies; cur != NULL; cur = cur->next)
 
1982
            gtk_widget_set_tooltip_markup (GTK_WIDGET (cur->data), tooltip);
 
1983
    }
 
1984
    else {
 
1985
        for (cur = proxies; cur != NULL; cur = cur->next)
 
1986
            gtk_widget_set_tooltip_text (GTK_WIDGET (cur->data), "");
 
1987
    }
 
1988
 
 
1989
    spec = g_object_class_find_property ((GObjectClass *) YELP_VIEW_GET_CLASS (view),
 
1990
                                         "yelp-uri");
 
1991
    g_signal_emit_by_name (view, "notify::yelp-uri", spec);
 
1992
 
 
1993
    g_free (priv->page_id);
 
1994
    priv->page_id = NULL;
 
1995
    if (priv->uri != NULL)
 
1996
        priv->page_id = yelp_uri_get_page_id (priv->uri);
 
1997
    spec = g_object_class_find_property ((GObjectClass *) YELP_VIEW_GET_CLASS (view),
 
1998
                                         "page-id");
 
1999
    g_signal_emit_by_name (view, "notify::page-id", spec);
 
2000
 
 
2001
    g_free (priv->root_title);
 
2002
    g_free (priv->page_title);
 
2003
    g_free (priv->page_desc);
 
2004
    g_free (priv->page_icon);
 
2005
    priv->root_title = NULL;
 
2006
    priv->page_title = NULL;
 
2007
    priv->page_desc = NULL;
 
2008
    priv->page_icon = NULL;
 
2009
 
 
2010
    spec = g_object_class_find_property ((GObjectClass *) YELP_VIEW_GET_CLASS (view),
 
2011
                                         "root-title");
 
2012
    g_signal_emit_by_name (view, "notify::root-title", spec);
 
2013
 
 
2014
    spec = g_object_class_find_property ((GObjectClass *) YELP_VIEW_GET_CLASS (view),
 
2015
                                         "page-title");
 
2016
    g_signal_emit_by_name (view, "notify::page-title", spec);
 
2017
 
 
2018
    spec = g_object_class_find_property ((GObjectClass *) YELP_VIEW_GET_CLASS (view),
 
2019
                                         "page-desc");
 
2020
    g_signal_emit_by_name (view, "notify::page-desc", spec);
 
2021
 
 
2022
    spec = g_object_class_find_property ((GObjectClass *) YELP_VIEW_GET_CLASS (view),
 
2023
                                         "page-icon");
 
2024
    g_signal_emit_by_name (view, "notify::page-icon", spec);
 
2025
 
 
2026
    if (error == NULL)
 
2027
        view_load_page (view);
 
2028
    else {
 
2029
        view_show_error_page (view, error);
 
2030
        g_error_free (error);
 
2031
    }
 
2032
}
 
2033
 
 
2034
static void
 
2035
document_callback (YelpDocument       *document,
 
2036
                   YelpDocumentSignal  signal,
 
2037
                   YelpView           *view,
 
2038
                   GError             *error)
 
2039
{
 
2040
    YelpViewPrivate *priv = GET_PRIV (view);
 
2041
 
 
2042
    debug_print (DB_FUNCTION, "entering\n");
 
2043
 
 
2044
    if (signal == YELP_DOCUMENT_SIGNAL_INFO) {
 
2045
        gchar *prev_id, *next_id, *real_id;
 
2046
        GtkAction *action;
 
2047
        YelpBackEntry *back = NULL;
 
2048
        GParamSpec *spec;
 
2049
 
 
2050
        real_id = yelp_document_get_page_id (document, priv->page_id);
 
2051
        if (priv->page_id && real_id && g_str_equal (real_id, priv->page_id)) {
 
2052
            g_free (real_id);
 
2053
        }
 
2054
        else {
 
2055
            GParamSpec *spec;
 
2056
            g_free (priv->page_id);
 
2057
            priv->page_id = real_id;
 
2058
            spec = g_object_class_find_property ((GObjectClass *) YELP_VIEW_GET_CLASS (view),
 
2059
                                                 "page-id");
 
2060
            g_signal_emit_by_name (view, "notify::page-id", spec);
 
2061
        }
 
2062
 
 
2063
        g_free (priv->root_title);
 
2064
        g_free (priv->page_title);
 
2065
        g_free (priv->page_desc);
 
2066
        g_free (priv->page_icon);
 
2067
 
 
2068
        priv->root_title = yelp_document_get_root_title (document, priv->page_id);
 
2069
        priv->page_title = yelp_document_get_page_title (document, priv->page_id);
 
2070
        priv->page_desc = yelp_document_get_page_desc (document, priv->page_id);
 
2071
        priv->page_icon = yelp_document_get_page_icon (document, priv->page_id);
 
2072
 
 
2073
        if (priv->back_cur)
 
2074
            back = priv->back_cur->data;
 
2075
        if (back) {
 
2076
            g_free (back->title);
 
2077
            back->title = g_strdup (priv->page_title);
 
2078
            g_free (back->desc);
 
2079
            back->desc = g_strdup (priv->page_desc);
 
2080
        }
 
2081
 
 
2082
        prev_id = yelp_document_get_prev_id (document, priv->page_id);
 
2083
        action = gtk_action_group_get_action (priv->action_group, "YelpViewGoPrevious");
 
2084
        gtk_action_set_sensitive (action, prev_id != NULL);
 
2085
        g_free (prev_id);
 
2086
 
 
2087
        next_id = yelp_document_get_next_id (document, priv->page_id);
 
2088
        action = gtk_action_group_get_action (priv->action_group, "YelpViewGoNext");
 
2089
        gtk_action_set_sensitive (action, next_id != NULL);
 
2090
        g_free (next_id);
 
2091
 
 
2092
        spec = g_object_class_find_property ((GObjectClass *) YELP_VIEW_GET_CLASS (view),
 
2093
                                             "root-title");
 
2094
        g_signal_emit_by_name (view, "notify::root-title", spec);
 
2095
 
 
2096
        spec = g_object_class_find_property ((GObjectClass *) YELP_VIEW_GET_CLASS (view),
 
2097
                                             "page-title");
 
2098
        g_signal_emit_by_name (view, "notify::page-title", spec);
 
2099
 
 
2100
        spec = g_object_class_find_property ((GObjectClass *) YELP_VIEW_GET_CLASS (view),
 
2101
                                             "page-desc");
 
2102
        g_signal_emit_by_name (view, "notify::page-desc", spec);
 
2103
 
 
2104
        spec = g_object_class_find_property ((GObjectClass *) YELP_VIEW_GET_CLASS (view),
 
2105
                                             "page-icon");
 
2106
        g_signal_emit_by_name (view, "notify::page-icon", spec);
 
2107
    }
 
2108
    else if (signal == YELP_DOCUMENT_SIGNAL_CONTENTS) {
 
2109
        YelpUriDocumentType doctype;
 
2110
        const gchar *contents;
 
2111
        gchar *mime_type, *page_id, *frag_id, *full_uri;
 
2112
        page_id = yelp_uri_get_page_id (priv->uri);
 
2113
        debug_print (DB_ARG, "    document.uri.page_id=\"%s\"\n", page_id);
 
2114
        mime_type = yelp_document_get_mime_type (document, page_id);
 
2115
        contents = yelp_document_read_contents (document, page_id);
 
2116
        frag_id = yelp_uri_get_frag_id (priv->uri);
 
2117
        g_free (priv->bogus_uri);
 
2118
        /* We don't have actual page and frag IDs for DocBook. We just map IDs
 
2119
           of block elements.  The result is that we get xref:someid#someid.
 
2120
           If someid is really the page ID, we just drop the frag reference.
 
2121
           Otherwise, normal page views scroll past the link trail.
 
2122
         */
 
2123
        if (frag_id != NULL) {
 
2124
            if (YELP_IS_DOCBOOK_DOCUMENT (document)) {
 
2125
                gchar *real_id = yelp_document_get_page_id (document, page_id);
 
2126
                if (g_str_equal (real_id, frag_id)) {
 
2127
                    g_free (frag_id);
 
2128
                    frag_id = NULL;
 
2129
                }
 
2130
                g_free (real_id);
 
2131
            }
 
2132
        }
 
2133
        /* We have to give WebKit a URI in a scheme it understands, otherwise we
 
2134
           won't get the resource-request-starting signal.  So we can't use the
 
2135
           canonical URI, because it might be something like ghelp.  We also have
 
2136
           to give it something unique, because WebKit ignores our load_string
 
2137
           call if the URI isn't different.  We could try to construct something
 
2138
           based on actual file locations, but in fact it doesn't matter.  So
 
2139
           we just make a bogus URI that's easy to process later.
 
2140
         */
 
2141
        doctype = yelp_uri_get_document_type (priv->uri);
 
2142
        full_uri = yelp_uri_get_canonical_uri (priv->uri);
 
2143
        if (g_str_has_prefix (full_uri, "file:/") &&
 
2144
            (doctype == YELP_URI_DOCUMENT_TYPE_TEXT ||
 
2145
             doctype == YELP_URI_DOCUMENT_TYPE_HTML ||
 
2146
             doctype == YELP_URI_DOCUMENT_TYPE_XHTML )) {
 
2147
            priv->bogus_uri = full_uri;
 
2148
        }
 
2149
        else {
 
2150
            g_free (full_uri);
 
2151
            if (frag_id != NULL)
 
2152
                priv->bogus_uri = g_strdup_printf ("%s%p#%s", BOGUS_URI, priv->uri, frag_id);
 
2153
            else
 
2154
                priv->bogus_uri = g_strdup_printf ("%s%p", BOGUS_URI, priv->uri);
 
2155
        }
 
2156
        g_signal_handler_block (view, priv->navigation_requested);
 
2157
        webkit_web_view_load_string (WEBKIT_WEB_VIEW (view),
 
2158
                                     contents,
 
2159
                                     mime_type,
 
2160
                                     "UTF-8",
 
2161
                                     priv->bogus_uri);
 
2162
        g_signal_handler_unblock (view, priv->navigation_requested);
 
2163
        g_object_set (view, "state", YELP_VIEW_STATE_LOADED, NULL);
 
2164
 
 
2165
        /* If we need to set the GtkAdjustment or trigger the page title
 
2166
         * from what WebKit thinks it is (see comment below), we need to
 
2167
         * let the main loop run through.
 
2168
         */
 
2169
        if (priv->vadjust > 0 || priv->hadjust > 0 ||  priv->page_title == NULL)
 
2170
            while (g_main_context_pending (NULL)) {
 
2171
                WebKitLoadStatus status;
 
2172
                status = webkit_web_view_get_load_status (WEBKIT_WEB_VIEW (view));
 
2173
                g_main_context_iteration (NULL, FALSE);
 
2174
                /* Sometimes some runaway JavaScript causes there to always
 
2175
                 * be pending sources. Break out if the document is loaded.
 
2176
                 */
 
2177
                if (status == WEBKIT_LOAD_FINISHED ||
 
2178
                    status == WEBKIT_LOAD_FAILED)
 
2179
                    break;
 
2180
            }
 
2181
 
 
2182
        /* Setting adjustments only work after the page is loaded. These
 
2183
         * are set by view_history_action, and they're reset to 0 after
 
2184
         * each load here.
 
2185
         */
 
2186
        if (priv->vadjust > 0) {
 
2187
            if (priv->vadjustment)
 
2188
                gtk_adjustment_set_value (priv->vadjustment, priv->vadjust);
 
2189
            priv->vadjust = 0;
 
2190
        }
 
2191
        if (priv->hadjust > 0) {
 
2192
            if (priv->hadjustment)
 
2193
                gtk_adjustment_set_value (priv->hadjustment, priv->hadjust);
 
2194
            priv->hadjust = 0;
 
2195
        }
 
2196
 
 
2197
        /* If the document didn't give us a page title, get it from WebKit.
 
2198
         * We let the main loop run through so that WebKit gets the title
 
2199
         * set so that we can send notify::page-title before loaded. It
 
2200
         * simplifies things if YelpView consumers can assume the title
 
2201
         * is set before loaded is triggered.
 
2202
         */
 
2203
        if (priv->page_title == NULL) {
 
2204
            GParamSpec *spec;
 
2205
            priv->page_title = g_strdup (webkit_web_view_get_title (WEBKIT_WEB_VIEW (view)));
 
2206
            spec = g_object_class_find_property ((GObjectClass *) YELP_VIEW_GET_CLASS (view),
 
2207
                                                 "page-title");
 
2208
            g_signal_emit_by_name (view, "notify::page-title", spec);
 
2209
        }
 
2210
 
 
2211
        g_free (frag_id);
 
2212
        g_free (page_id);
 
2213
        g_free (mime_type);
 
2214
        yelp_document_finish_read (document, contents);
 
2215
        g_signal_emit (view, signals[LOADED], 0);
 
2216
    }
 
2217
    else if (signal == YELP_DOCUMENT_SIGNAL_ERROR) {
 
2218
        view_show_error_page (view, error);
 
2219
    }
 
2220
}