1
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3
* Copyright (C) 2009 Shaun McCance <shaunm@gnome.org>
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.
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.
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.
20
* Author: Shaun McCance <shaunm@gnome.org>
27
#include <glib/gi18n.h>
28
#include <glib-object.h>
32
#include <webkit/webkit.h>
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"
42
#define BOGUS_URI "file:///bogus/"
43
#define BOGUS_URI_LEN 14
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,
53
static void yelp_view_set_property (GObject *object,
58
static gboolean view_external_uri (YelpView *view,
60
static void view_install_uri (YelpView *view,
62
static void view_scrolled (GtkAdjustment *adjustment,
64
static void view_set_hadjustment (YelpView *view,
67
static void view_set_vadjustment (YelpView *view,
70
static void popup_open_link (GtkMenuItem *item,
72
static void popup_open_link_new (GtkMenuItem *item,
74
static void popup_copy_link (GtkMenuItem *item,
76
static void popup_save_image (GtkMenuItem *item,
78
static void popup_send_image (GtkMenuItem *item,
80
static void popup_copy_code (GtkMenuItem *item,
82
static void popup_save_code (GtkMenuItem *item,
84
static void view_populate_popup (YelpView *view,
87
static void view_script_alert (YelpView *view,
88
WebKitWebFrame *frame,
91
static gboolean view_navigation_requested (WebKitWebView *view,
92
WebKitWebFrame *frame,
93
WebKitNetworkRequest *request,
94
WebKitWebNavigationAction *action,
95
WebKitWebPolicyDecision *decision,
97
static void view_resource_request (WebKitWebView *view,
98
WebKitWebFrame *frame,
99
WebKitWebResource *resource,
100
WebKitNetworkRequest *request,
101
WebKitNetworkResponse *response,
103
static void view_document_loaded (WebKitWebView *view,
104
WebKitWebFrame *frame,
107
static void view_print (GtkAction *action,
109
static void view_history_action (GtkAction *action,
111
static void view_navigation_action (GtkAction *action,
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,
119
static void settings_set_fonts (YelpSettings *settings);
120
static void settings_show_text_cursor (YelpSettings *settings);
122
static void uri_resolved (YelpUri *uri,
124
static void document_callback (YelpDocument *document,
125
YelpDocumentSignal signal,
129
static const GtkActionEntry entries[] = {
130
{"YelpViewPrint", GTK_STOCK_PRINT,
134
G_CALLBACK (view_print) },
135
{"YelpViewGoBack", GTK_STOCK_GO_BACK,
139
G_CALLBACK (view_history_action) },
140
{"YelpViewGoForward", GTK_STOCK_GO_FORWARD,
144
G_CALLBACK (view_history_action) },
145
{"YelpViewGoPrevious", NULL,
146
N_("_Previous Page"),
149
G_CALLBACK (view_navigation_action) },
150
{"YelpViewGoNext", NULL,
152
"<Control>Page_Down",
154
G_CALLBACK (view_navigation_action) }
157
static gchar *nautilus_sendto = NULL;
176
static gint signals[LAST_SIGNAL] = { 0 };
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))
181
static WebKitWebSettings *websettings;
183
typedef struct _YelpActionEntry YelpActionEntry;
184
struct _YelpActionEntry {
186
YelpViewActionValidFunc func;
190
action_entry_free (YelpActionEntry *entry)
194
g_object_unref (entry->action);
198
typedef struct _YelpBackEntry YelpBackEntry;
199
struct _YelpBackEntry {
207
back_entry_free (YelpBackEntry *back)
211
g_object_unref (back->uri);
212
g_free (back->title);
217
typedef struct _YelpViewPrivate YelpViewPrivate;
218
struct _YelpViewPrivate {
220
YelpUri *resolve_uri;
223
YelpDocument *document;
224
GCancellable *cancellable;
225
GtkAdjustment *vadjustment;
226
GtkAdjustment *hadjustment;
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;
240
YelpViewState prevstate;
252
GtkActionGroup *action_group;
254
GSList *link_actions;
256
gint navigation_requested;
259
#define TARGET_TYPE_URI_LIST "text/uri-list"
265
yelp_view_init (YelpView *view)
268
YelpViewPrivate *priv = GET_PRIV (view);
270
g_object_set (view, "settings", websettings, NULL);
272
priv->cancellable = NULL;
274
priv->prevstate = priv->state = YELP_VIEW_STATE_BLANK;
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);
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),
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);
304
yelp_view_dispose (GObject *object)
306
YelpViewPrivate *priv = GET_PRIV (object);
308
view_clear_load (YELP_VIEW (object));
310
if (priv->vadjuster > 0) {
311
g_source_remove (priv->vadjuster);
315
if (priv->hadjuster > 0) {
316
g_source_remove (priv->hadjuster);
320
if (priv->action_group) {
321
g_object_unref (priv->action_group);
322
priv->action_group = NULL;
325
if (priv->document) {
326
g_object_unref (priv->document);
327
priv->document = NULL;
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);
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);
341
G_OBJECT_CLASS (yelp_view_parent_class)->dispose (object);
345
yelp_view_finalize (GObject *object)
347
YelpViewPrivate *priv = GET_PRIV (object);
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);
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);
360
g_free (priv->bogus_uri);
362
G_OBJECT_CLASS (yelp_view_parent_class)->finalize (object);
366
yelp_view_class_init (YelpViewClass *klass)
368
GObjectClass *object_class = G_OBJECT_CLASS (klass);
369
YelpSettings *settings = yelp_settings_get_default ();
371
nautilus_sendto = g_find_program_in_path ("nautilus-sendto");
373
websettings = webkit_web_settings_new ();
374
g_object_set (websettings, "enable-universal-access-from-file-uris", TRUE, NULL);
375
g_signal_connect (settings,
377
G_CALLBACK (settings_set_fonts),
379
settings_set_fonts (settings);
380
g_signal_connect (settings,
381
"notify::show-text-cursor",
382
G_CALLBACK (settings_show_text_cursor),
384
settings_show_text_cursor (settings);
386
klass->external_uri = view_external_uri;
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;
393
signals[NEW_VIEW_REQUESTED] =
394
g_signal_new ("new-view-requested",
395
G_TYPE_FROM_CLASS (klass),
398
g_cclosure_marshal_VOID__OBJECT,
399
G_TYPE_NONE, 1, YELP_TYPE_URI);
401
signals[EXTERNAL_URI] =
402
g_signal_new ("external-uri",
403
G_TYPE_FROM_CLASS (klass),
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);
411
g_signal_new ("loaded",
412
G_TYPE_FROM_CLASS (klass),
415
g_cclosure_marshal_VOID__VOID,
418
g_type_class_add_private (klass, sizeof (YelpViewPrivate));
420
g_object_class_install_property (object_class,
422
g_param_spec_object ("yelp-uri",
424
_("A YelpUri with the current location"),
426
G_PARAM_READWRITE | G_PARAM_STATIC_NAME |
427
G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
429
g_object_class_install_property (object_class,
431
g_param_spec_enum ("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));
439
g_object_class_install_property (object_class,
441
g_param_spec_string ("page-id",
443
N_("The ID of the root page of the page being viewed"),
446
G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
448
g_object_class_install_property (object_class,
450
g_param_spec_string ("root-title",
452
N_("The title of the root page of the page being viewed"),
455
G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
457
g_object_class_install_property (object_class,
459
g_param_spec_string ("page-title",
461
N_("The title of the page being viewed"),
464
G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
466
g_object_class_install_property (object_class,
468
g_param_spec_string ("page-desc",
469
N_("Page Description"),
470
N_("The description of the page being viewed"),
473
G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
475
g_object_class_install_property (object_class,
477
g_param_spec_string ("page-icon",
479
N_("The icon of the page being viewed"),
482
G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
486
yelp_view_get_property (GObject *object,
491
YelpViewPrivate *priv = GET_PRIV (object);
496
g_value_set_object (value, priv->uri);
499
g_value_set_string (value, priv->page_id);
501
case PROP_ROOT_TITLE:
502
g_value_set_string (value, priv->root_title);
504
case PROP_PAGE_TITLE:
505
g_value_set_string (value, priv->page_title);
508
g_value_set_string (value, priv->page_desc);
512
g_value_set_string (value, priv->page_icon);
514
g_value_set_string (value, "yelp-page-symbolic");
517
g_value_set_enum (value, priv->state);
520
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
526
yelp_view_set_property (GObject *object,
532
YelpViewPrivate *priv = GET_PRIV (object);
537
uri = g_value_get_object (value);
538
yelp_view_load_uri (YELP_VIEW (object), uri);
539
g_object_unref (uri);
542
priv->prevstate = priv->state;
543
priv->state = g_value_get_enum (value);
546
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
551
/******************************************************************************/
556
return (GtkWidget *) g_object_new (YELP_TYPE_VIEW, NULL);
560
yelp_view_load (YelpView *view,
563
YelpUri *yuri = yelp_uri_new (uri);
564
yelp_view_load_uri (view, yuri);
565
g_object_unref (yuri);
569
yelp_view_load_uri (YelpView *view,
572
YelpViewPrivate *priv = GET_PRIV (view);
574
g_object_set (view, "state", YELP_VIEW_STATE_LOADING, NULL);
576
gtk_action_set_sensitive (gtk_action_group_get_action (priv->action_group,
577
"YelpViewGoPrevious"),
579
gtk_action_set_sensitive (gtk_action_group_get_action (priv->action_group,
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;
589
g_object_unref (priv->resolve_uri);
591
priv->resolve_uri = g_object_ref (uri);
592
priv->uri_resolved = g_signal_connect (uri, "resolved",
593
G_CALLBACK (uri_resolved),
595
yelp_uri_resolve (uri);
598
uri_resolved (uri, view);
603
yelp_view_load_document (YelpView *view,
605
YelpDocument *document)
608
YelpViewPrivate *priv = GET_PRIV (view);
610
g_return_if_fail (yelp_uri_is_resolved (uri));
612
g_object_set (view, "state", YELP_VIEW_STATE_LOADING, NULL);
615
view_clear_load (view);
617
spec = g_object_class_find_property ((GObjectClass *) YELP_VIEW_GET_CLASS (view),
619
g_signal_emit_by_name (view, "notify::yelp-uri", spec);
620
g_object_ref (document);
622
g_object_unref (document);
623
priv->document = document;
625
view_load_page (view);
629
yelp_view_get_document (YelpView *view)
631
YelpViewPrivate *priv = GET_PRIV (view);
632
return priv->document;
636
yelp_view_get_action_group (YelpView *view)
638
YelpViewPrivate *priv = GET_PRIV (view);
639
return priv->action_group;
642
/******************************************************************************/
645
yelp_view_add_link_action (YelpView *view,
647
YelpViewActionValidFunc func,
650
YelpActionEntry *entry;
651
YelpViewPrivate *priv = GET_PRIV (view);
653
entry = g_new0 (YelpActionEntry, 1);
654
entry->action = g_object_ref (action);
658
priv->link_actions = g_slist_append (priv->link_actions, entry);
662
yelp_view_get_active_link_uri (YelpView *view)
664
YelpViewPrivate *priv = GET_PRIV (view);
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);
670
uri = yelp_uri_new_relative (priv->uri, priv->popup_link_uri);
676
yelp_view_get_active_link_text (YelpView *view)
678
YelpViewPrivate *priv = GET_PRIV (view);
679
return g_strdup (priv->popup_link_text);
682
/******************************************************************************/
685
view_external_uri (YelpView *view,
688
gchar *struri = yelp_uri_get_canonical_uri (uri);
689
g_app_info_launch_default_for_uri (struri, NULL, NULL);
694
typedef struct _YelpInstallInfo YelpInstallInfo;
695
struct _YelpInstallInfo {
701
yelp_install_info_free (YelpInstallInfo *info)
703
g_object_unref (info->view);
710
view_install_installed (GDBusConnection *connection,
712
YelpInstallInfo *info)
714
GError *error = NULL;
715
g_dbus_connection_call_finish (connection, res, &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.");
722
err = error->message;
725
GtkWidget *dialog = gtk_message_dialog_new (NULL, 0,
729
gtk_dialog_run ((GtkDialog *) dialog);
730
gtk_widget_destroy (dialog);
732
g_error_free (error);
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);
746
yelp_install_info_free (info);
750
view_install_uri (YelpView *view,
753
GDBusConnection *connection;
755
gboolean help = FALSE, ghelp = FALSE;
756
GVariantBuilder *strv;
757
YelpInstallInfo *info;
759
YelpViewPrivate *priv = GET_PRIV (view);
763
gchar *pkg, *confirm_search;
765
if (g_str_has_prefix (uri, "install-help:")) {
767
pkg = (gchar *) uri + 13;
769
else if (g_str_has_prefix (uri, "install-ghelp:")) {
771
pkg = (gchar *) uri + 14;
773
else if (g_str_has_prefix (uri, "install:")) {
774
pkg = (gchar *) uri + 8;
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);
784
info = g_new0 (YelpInstallInfo, 1);
785
info->view = g_object_ref (view);
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);
791
xid = gdk_x11_window_get_xid (gdkwin);
794
if (priv->state == YELP_VIEW_STATE_ERROR)
795
confirm_search = "hide-confirm-search";
800
const gchar * const *datadirs = g_get_system_data_dirs ();
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++) {
807
fname = g_build_filename (datadirs[datadirs_i], "gnome", "help",
808
pkg, "C", "index.page", NULL);
809
g_variant_builder_add (strv, "s", fname);
811
fname = g_build_filename (datadirs[datadirs_i], "gnome", "help",
812
pkg, "C", docbook, NULL);
813
g_variant_builder_add (strv, "s", fname);
817
fname = g_build_filename (datadirs[datadirs_i], "help", "C",
818
pkg, "index.page", NULL);
819
g_variant_builder_add (strv, "s", fname);
821
fname = g_build_filename (datadirs[datadirs_i], "help", "C",
822
pkg, "index.docbook", NULL);
823
g_variant_builder_add (strv, "s", fname);
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),
836
G_DBUS_CALL_FLAGS_NONE,
838
(GAsyncReadyCallback) view_install_installed,
840
g_variant_builder_unref (strv);
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]);
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),
857
G_DBUS_CALL_FLAGS_NONE,
859
(GAsyncReadyCallback) view_install_installed,
861
g_variant_builder_unref (strv);
864
g_object_unref (connection);
868
view_scrolled (GtkAdjustment *adjustment,
871
YelpViewPrivate *priv = GET_PRIV (view);
872
if (priv->back_cur == NULL || priv->back_cur->data == NULL)
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);
881
view_set_hadjustment (YelpView *view,
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);
890
if (priv->hadjustment)
891
priv->hadjuster = g_signal_connect (priv->hadjustment, "value-changed",
892
G_CALLBACK (view_scrolled), view);
896
view_set_vadjustment (YelpView *view,
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);
905
if (priv->vadjustment)
906
priv->vadjuster = g_signal_connect (priv->vadjustment, "value-changed",
907
G_CALLBACK (view_scrolled), view);
911
popup_open_link (GtkMenuItem *item,
914
YelpViewPrivate *priv = GET_PRIV (view);
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);
920
uri = yelp_uri_new_relative (priv->uri, priv->popup_link_uri);
922
yelp_view_load_uri (view, uri);
923
g_object_unref (uri);
925
g_free (priv->popup_link_uri);
926
priv->popup_link_uri = NULL;
928
g_free (priv->popup_link_text);
929
priv->popup_link_text = NULL;
933
popup_open_link_new (GtkMenuItem *item,
936
YelpViewPrivate *priv = GET_PRIV (view);
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);
942
uri = yelp_uri_new_relative (priv->uri, priv->popup_link_uri);
944
g_free (priv->popup_link_uri);
945
priv->popup_link_uri = NULL;
947
g_free (priv->popup_link_text);
948
priv->popup_link_text = NULL;
950
g_signal_emit (view, signals[NEW_VIEW_REQUESTED], 0, uri);
951
g_object_unref (uri);
955
popup_copy_link (GtkMenuItem *item,
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,
964
typedef struct _YelpSaveData YelpSaveData;
965
struct _YelpSaveData {
973
file_copied (GFile *file,
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,
983
"%s", error->message);
984
gtk_dialog_run (GTK_DIALOG (dialog));
985
gtk_widget_destroy (dialog);
987
g_object_unref (data->orig);
988
g_object_unref (data->dest);
989
g_object_unref (data->view);
990
g_object_unref (data->window);
994
popup_save_image (GtkMenuItem *item,
998
GtkWidget *dialog, *window;
1001
YelpViewPrivate *priv = GET_PRIV (view);
1003
for (window = gtk_widget_get_parent (GTK_WIDGET (view));
1004
window && !GTK_IS_WINDOW (window);
1005
window = gtk_widget_get_parent (window));
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;
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,
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);
1024
gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dialog),
1025
g_get_user_special_dir (G_USER_DIRECTORY_DESKTOP));
1027
res = gtk_dialog_run (GTK_DIALOG (dialog));
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,
1035
(GAsyncReadyCallback) file_copied,
1039
g_object_unref (data->orig);
1040
g_object_unref (data->view);
1041
g_object_unref (data->window);
1045
gtk_widget_destroy (dialog);
1049
popup_send_image (GtkMenuItem *item,
1054
GAppLaunchContext *context;
1055
GError *error = NULL;
1056
YelpViewPrivate *priv = GET_PRIV (view);
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)));
1061
app = g_app_info_create_from_commandline (command, NULL, 0, &error);
1063
g_app_info_launch (app, NULL, context, &error);
1064
g_object_unref (app);
1068
g_debug ("Could not launch nautilus-sendto: %s", error->message);
1069
g_error_free (error);
1072
g_object_unref (context);
1074
g_free (priv->popup_image_uri);
1075
priv->popup_image_uri = NULL;
1079
popup_copy_code (GtkMenuItem *item,
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);
1090
popup_save_code (GtkMenuItem *item,
1093
YelpViewPrivate *priv = GET_PRIV (view);
1094
GtkWidget *dialog, *window;
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;
1105
for (window = gtk_widget_get_parent (GTK_WIDGET (view));
1106
window && !GTK_IS_WINDOW (window);
1107
window = gtk_widget_get_parent (window));
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,
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);
1121
gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dialog),
1122
g_get_user_special_dir (G_USER_DIRECTORY_DOCUMENTS));
1124
res = gtk_dialog_run (GTK_DIALOG (dialog));
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,
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,
1138
"%s", error->message);
1139
gtk_dialog_run (GTK_DIALOG (dialog));
1140
gtk_widget_destroy (dialog);
1141
g_error_free (error);
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,
1151
"%s", error->message);
1152
gtk_dialog_run (GTK_DIALOG (dialog));
1153
gtk_widget_destroy (dialog);
1154
g_error_free (error);
1156
g_object_unref (datastream);
1158
g_object_unref (file);
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;
1166
gtk_widget_destroy (dialog);
1170
view_populate_popup (YelpView *view,
1174
WebKitHitTestResult *result;
1175
WebKitHitTestResultContext context;
1177
YelpViewPrivate *priv = GET_PRIV (view);
1180
WebKitDOMNode *node, *cur, *link_node = NULL, *code_node = NULL, *code_title_node = NULL;
1182
children = gtk_container_get_children (GTK_CONTAINER (menu));
1184
gtk_container_remove (GTK_CONTAINER (menu),
1185
GTK_WIDGET (children->data));
1186
children = children->next;
1188
g_list_free (children);
1190
event = gtk_get_current_event ();
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,
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,
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;
1224
if (context & WEBKIT_HIT_TEST_RESULT_CONTEXT_LINK) {
1226
g_object_get (result, "link-uri", &uri, NULL);
1227
g_free (priv->popup_link_uri);
1228
priv->popup_link_uri = uri;
1230
g_free (priv->popup_link_text);
1231
priv->popup_link_text = NULL;
1232
if (link_node != NULL) {
1233
WebKitDOMNode *child;
1238
child = (WebKitDOMNode *)
1239
webkit_dom_element_query_selector (WEBKIT_DOM_ELEMENT (link_node),
1240
"span.title", NULL);
1242
priv->popup_link_text = webkit_dom_node_get_text_content (child);
1244
if (priv->popup_link_text == NULL)
1245
priv->popup_link_text = webkit_dom_node_get_text_content (link_node);
1247
tmp = g_new0 (gchar, strlen(priv->popup_link_text) + 1);
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') {
1258
tmp[tmpi] = priv->popup_link_text[i];
1264
g_free (priv->popup_link_text);
1265
priv->popup_link_text = tmp;
1268
priv->popup_link_text = g_strdup (uri);
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.
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);
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);
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);
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);
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);
1314
for (cur = priv->link_actions; cur != NULL; cur = cur->next) {
1316
YelpActionEntry *entry = (YelpActionEntry *) cur->data;
1317
if (entry->func == NULL)
1320
add = (* entry->func) (view, entry->action,
1321
priv->popup_link_uri,
1324
item = gtk_action_create_menu_item (entry->action);
1325
gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1331
item = gtk_action_create_menu_item (gtk_action_group_get_action (priv->action_group,
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);
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.
1344
gboolean image = context & WEBKIT_HIT_TEST_RESULT_CONTEXT_IMAGE;
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);
1353
priv->popup_image_uri = uri;
1356
item = gtk_separator_menu_item_new ();
1357
gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1360
item = gtk_menu_item_new_with_mnemonic (_("_Save Image As..."));
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);
1367
if (nautilus_sendto) {
1369
item = gtk_menu_item_new_with_mnemonic (_("S_end Image To..."));
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);
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);
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);
1388
if (code_node != NULL) {
1389
item = gtk_separator_menu_item_new ();
1390
gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1392
priv->popup_code_node = code_node;
1393
priv->popup_code_title = code_title_node;
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);
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);
1406
g_object_unref (result);
1407
gdk_event_free (event);
1408
gtk_widget_show_all (GTK_WIDGET (menu));
1412
view_script_alert (YelpView *view,
1413
WebKitWebFrame *frame,
1417
printf ("\n\n===ALERT===\n%s\n\n", message);
1421
view_navigation_requested (WebKitWebView *view,
1422
WebKitWebFrame *frame,
1423
WebKitNetworkRequest *request,
1424
WebKitWebNavigationAction *action,
1425
WebKitWebPolicyDecision *decision,
1428
const gchar *requri = webkit_network_request_get_uri (request);
1429
YelpViewPrivate *priv = GET_PRIV (view);
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);
1439
else if (g_str_has_prefix (requri, BOGUS_URI)) {
1440
uri = yelp_uri_new_relative (priv->uri, requri + BOGUS_URI_LEN);
1443
uri = yelp_uri_new_relative (priv->uri, requri);
1445
webkit_web_policy_decision_ignore (decision);
1447
yelp_view_load_uri ((YelpView *) view, uri);
1448
g_object_unref (uri);
1454
view_resource_request (WebKitWebView *view,
1455
WebKitWebFrame *frame,
1456
WebKitWebResource *resource,
1457
WebKitNetworkRequest *request,
1458
WebKitNetworkResponse *response,
1461
YelpViewPrivate *priv = GET_PRIV (view);
1462
const gchar *requri = webkit_network_request_get_uri (request);
1466
if (!g_str_has_prefix (requri, BOGUS_URI))
1469
/* We get this signal for the page itself. Ignore. */
1470
if (g_str_equal (requri, priv->bogus_uri))
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);
1479
webkit_network_request_set_uri (request, "about:blank");
1484
view_document_loaded (WebKitWebView *view,
1485
WebKitWebFrame *frame,
1488
YelpViewPrivate *priv = GET_PRIV (view);
1489
gchar *search_terms;
1491
search_terms = yelp_uri_get_query (priv->uri, "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);
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);
1507
txt = g_strdup_printf (_("See all search results for “%s”"),
1509
webkit_dom_node_set_text_content (WEBKIT_DOM_NODE (link), txt, NULL);
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)),
1516
g_free (search_terms);
1521
view_print (GtkAction *action, YelpView *view)
1523
webkit_web_frame_print (webkit_web_view_get_main_frame (WEBKIT_WEB_VIEW (view)));
1527
view_history_action (GtkAction *action,
1531
YelpViewPrivate *priv = GET_PRIV (view);
1533
if (priv->back_cur == NULL)
1536
if (g_str_equal (gtk_action_get_name (action), "YelpViewGoBack"))
1537
newcur = priv->back_cur->next;
1539
newcur = priv->back_cur->prev;
1544
priv->back_cur = newcur;
1546
if (priv->back_cur->data == NULL)
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;
1556
view_navigation_action (GtkAction *action,
1559
YelpViewPrivate *priv = GET_PRIV (view);
1560
gchar *page_id, *new_id, *xref;
1563
page_id = yelp_uri_get_page_id (priv->uri);
1565
if (g_str_equal (gtk_action_get_name (action), "YelpViewGoPrevious"))
1566
new_id = yelp_document_get_prev_id (priv->document, page_id);
1568
new_id = yelp_document_get_next_id (priv->document, page_id);
1570
/* Just in case we screwed up somewhere */
1571
if (new_id == NULL) {
1572
gtk_action_set_sensitive (action, FALSE);
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);
1582
g_object_unref (new_uri);
1586
view_clear_load (YelpView *view)
1588
YelpViewPrivate *priv = GET_PRIV (view);
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;
1595
g_object_unref (priv->resolve_uri);
1597
priv->resolve_uri = NULL;
1600
g_object_unref (priv->uri);
1604
if (priv->cancellable) {
1605
g_cancellable_cancel (priv->cancellable);
1606
priv->cancellable = NULL;
1611
view_load_page (YelpView *view)
1613
YelpViewPrivate *priv = GET_PRIV (view);
1616
debug_print (DB_FUNCTION, "entering\n");
1618
g_return_if_fail (priv->cancellable == NULL);
1620
if (priv->document == NULL) {
1623
/* FIXME: and if priv->uri is NULL? */
1624
docuri = yelp_uri_get_document_uri (priv->uri);
1625
/* FIXME: CANT_READ isn't right */
1627
error = g_error_new (YELP_ERROR, YELP_ERROR_CANT_READ,
1628
_("Could not load a document for ‘%s’"),
1633
error = g_error_new (YELP_ERROR, YELP_ERROR_CANT_READ,
1634
_("Could not load a document"));
1636
view_show_error_page (view, error);
1637
g_error_free (error);
1641
page_id = yelp_uri_get_page_id (priv->uri);
1642
priv->cancellable = g_cancellable_new ();
1643
yelp_document_request_page (priv->document,
1646
(YelpDocumentCallback) document_callback,
1652
view_show_error_page (YelpView *view,
1655
YelpViewPrivate *priv = GET_PRIV (view);
1656
static const gchar *errorpage =
1658
"<style type='text/css'>"
1662
" background-color: %s;"
1664
"p { margin: 1em 0 0 0; }\n"
1667
" border-color: %s;"
1668
" border-top: solid 1px;"
1669
" border-bottom: solid 1px;"
1670
" background-color: %s;"
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;"
1679
"div.note div.contents {"
1682
"div.note div.title {"
1684
" margin-bottom: 0.2em;"
1685
" font-weight: bold;"
1688
"a { color: %s; text-decoration: none; }\n"
1691
"<div class='note'><div class='inner'>"
1692
"%s<div class='contents'>%s%s</div>"
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;
1700
gboolean doc404 = FALSE;
1701
const gchar *left = (gtk_widget_get_direction((GtkWidget *) view) == GTK_TEXT_DIR_RTL) ? "right" : "left";
1703
if (priv->uri && yelp_uri_get_document_type (priv->uri) == YELP_URI_DOCUMENT_TYPE_NOT_FOUND)
1705
if (error->domain == YELP_ERROR)
1706
switch (error->code) {
1707
case YELP_ERROR_NOT_FOUND:
1709
title = _("Document Not Found");
1711
title = _("Page Not Found");
1713
case YELP_ERROR_CANT_READ:
1714
title = _("Cannot Read");
1720
title = _("Unknown Error");
1721
title_m = g_markup_printf_escaped ("<div class='title'>%s</div>", title);
1723
content_beg = g_markup_printf_escaped ("<p>%s</p>", error->message);
1726
gchar *struri = yelp_uri_get_document_uri (priv->uri);
1728
gchar *pkg = NULL, *scheme = NULL;
1729
if (g_str_has_prefix (struri, "help:")) {
1733
else if (g_str_has_prefix (struri, "ghelp:")) {
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;
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 : "");
1755
g_object_set (view, "state", YELP_VIEW_STATE_ERROR, NULL);
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),
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),
1767
g_signal_emit_by_name (view, "notify::page-id", spec);
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),
1774
g_signal_emit_by_name (view, "notify::page-title", spec);
1776
g_free (priv->page_desc);
1777
priv->page_desc = NULL;
1779
priv->page_desc = yelp_uri_get_canonical_uri (priv->uri);
1780
spec = g_object_class_find_property ((GObjectClass *) YELP_VIEW_GET_CLASS (view),
1782
g_signal_emit_by_name (view, "notify::page-desc", spec);
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),
1788
g_signal_emit_by_name (view, "notify::page-icon", spec);
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),
1797
g_signal_handler_unblock (view, priv->navigation_requested);
1799
g_free (content_beg);
1800
if (content_end != NULL)
1801
g_free (content_end);
1807
settings_set_fonts (YelpSettings *settings)
1812
g_object_set (websettings,
1813
"default-encoding", "utf-8",
1814
"enable-private-browsing", TRUE,
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,
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,
1840
settings_show_text_cursor (YelpSettings *settings)
1842
g_object_set (websettings,
1843
"enable-caret-browsing",
1844
yelp_settings_get_show_text_cursor (settings),
1848
/******************************************************************************/
1851
uri_resolved (YelpUri *uri,
1854
YelpViewPrivate *priv = GET_PRIV (view);
1855
YelpDocument *document;
1856
YelpBackEntry *back;
1858
GSList *proxies, *cur;
1859
GError *error = NULL;
1863
if (yelp_uri_get_document_type (uri) != YELP_URI_DOCUMENT_TYPE_EXTERNAL) {
1865
view_clear_load (view);
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);
1880
g_signal_emit (view, signals[EXTERNAL_URI], 0, uri, &result);
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."),
1893
error = g_error_new (YELP_ERROR, YELP_ERROR_NOT_FOUND,
1894
_("The URI does not point to a valid page."));
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."),
1908
if (error == NULL) {
1909
document = yelp_document_get_for_uri (uri);
1911
g_object_unref (priv->document);
1912
priv->document = document;
1915
if (priv->document != NULL) {
1916
g_object_unref (priv->document);
1917
priv->document = NULL;
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);
1928
priv->back_list = g_list_prepend (priv->back_list, back);
1929
priv->back_cur = priv->back_list;
1931
priv->back_load = FALSE;
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;
1940
gtk_action_set_sensitive (action, TRUE);
1941
if (back->title && back->desc) {
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);
1949
else if (back->title)
1950
tooltip = g_markup_printf_escaped ("<span size='larger'>%s</span>",
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);
1957
for (cur = proxies; cur != NULL; cur = cur->next)
1958
gtk_widget_set_tooltip_text (GTK_WIDGET (cur->data), "");
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;
1968
gtk_action_set_sensitive (action, TRUE);
1969
if (back->title && back->desc) {
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);
1977
else if (back->title)
1978
tooltip = g_markup_printf_escaped ("<span size='larger'>%s</span>",
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);
1985
for (cur = proxies; cur != NULL; cur = cur->next)
1986
gtk_widget_set_tooltip_text (GTK_WIDGET (cur->data), "");
1989
spec = g_object_class_find_property ((GObjectClass *) YELP_VIEW_GET_CLASS (view),
1991
g_signal_emit_by_name (view, "notify::yelp-uri", spec);
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),
1999
g_signal_emit_by_name (view, "notify::page-id", spec);
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;
2010
spec = g_object_class_find_property ((GObjectClass *) YELP_VIEW_GET_CLASS (view),
2012
g_signal_emit_by_name (view, "notify::root-title", spec);
2014
spec = g_object_class_find_property ((GObjectClass *) YELP_VIEW_GET_CLASS (view),
2016
g_signal_emit_by_name (view, "notify::page-title", spec);
2018
spec = g_object_class_find_property ((GObjectClass *) YELP_VIEW_GET_CLASS (view),
2020
g_signal_emit_by_name (view, "notify::page-desc", spec);
2022
spec = g_object_class_find_property ((GObjectClass *) YELP_VIEW_GET_CLASS (view),
2024
g_signal_emit_by_name (view, "notify::page-icon", spec);
2027
view_load_page (view);
2029
view_show_error_page (view, error);
2030
g_error_free (error);
2035
document_callback (YelpDocument *document,
2036
YelpDocumentSignal signal,
2040
YelpViewPrivate *priv = GET_PRIV (view);
2042
debug_print (DB_FUNCTION, "entering\n");
2044
if (signal == YELP_DOCUMENT_SIGNAL_INFO) {
2045
gchar *prev_id, *next_id, *real_id;
2047
YelpBackEntry *back = NULL;
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)) {
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),
2060
g_signal_emit_by_name (view, "notify::page-id", spec);
2063
g_free (priv->root_title);
2064
g_free (priv->page_title);
2065
g_free (priv->page_desc);
2066
g_free (priv->page_icon);
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);
2074
back = priv->back_cur->data;
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);
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);
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);
2092
spec = g_object_class_find_property ((GObjectClass *) YELP_VIEW_GET_CLASS (view),
2094
g_signal_emit_by_name (view, "notify::root-title", spec);
2096
spec = g_object_class_find_property ((GObjectClass *) YELP_VIEW_GET_CLASS (view),
2098
g_signal_emit_by_name (view, "notify::page-title", spec);
2100
spec = g_object_class_find_property ((GObjectClass *) YELP_VIEW_GET_CLASS (view),
2102
g_signal_emit_by_name (view, "notify::page-desc", spec);
2104
spec = g_object_class_find_property ((GObjectClass *) YELP_VIEW_GET_CLASS (view),
2106
g_signal_emit_by_name (view, "notify::page-icon", spec);
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.
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)) {
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.
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;
2151
if (frag_id != NULL)
2152
priv->bogus_uri = g_strdup_printf ("%s%p#%s", BOGUS_URI, priv->uri, frag_id);
2154
priv->bogus_uri = g_strdup_printf ("%s%p", BOGUS_URI, priv->uri);
2156
g_signal_handler_block (view, priv->navigation_requested);
2157
webkit_web_view_load_string (WEBKIT_WEB_VIEW (view),
2162
g_signal_handler_unblock (view, priv->navigation_requested);
2163
g_object_set (view, "state", YELP_VIEW_STATE_LOADED, NULL);
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.
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.
2177
if (status == WEBKIT_LOAD_FINISHED ||
2178
status == WEBKIT_LOAD_FAILED)
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
2186
if (priv->vadjust > 0) {
2187
if (priv->vadjustment)
2188
gtk_adjustment_set_value (priv->vadjustment, priv->vadjust);
2191
if (priv->hadjust > 0) {
2192
if (priv->hadjustment)
2193
gtk_adjustment_set_value (priv->hadjustment, priv->hadjust);
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.
2203
if (priv->page_title == NULL) {
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),
2208
g_signal_emit_by_name (view, "notify::page-title", spec);
2214
yelp_document_finish_read (document, contents);
2215
g_signal_emit (view, signals[LOADED], 0);
2217
else if (signal == YELP_DOCUMENT_SIGNAL_ERROR) {
2218
view_show_error_page (view, error);