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