1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * Copyright (C) 2009 Shaun McCance <shaunm@gnome.org>
4 * Copyright (C) 2014 Igalia S.L.
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License as
8 * published by the Free Software Foundation; either version 2 of the
9 * License, or (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
16 * You should have received a copy of the GNU General Public
17 * License along with this program; if not, see <http://www.gnu.org/licenses/>.
19 * Author: Shaun McCance <shaunm@gnome.org>
26 #include <glib/gi18n.h>
27 #include <glib-object.h>
30 #ifdef GDK_WINDOWING_X11
34 #include <webkit2/webkit2.h>
36 #include "yelp-debug.h"
37 #include "yelp-docbook-document.h"
38 #include "yelp-error.h"
39 #include "yelp-marshal.h"
40 #include "yelp-settings.h"
41 #include "yelp-types.h"
42 #include "yelp-view.h"
43 #include "yelp-uri-builder.h"
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
,
51 static void yelp_view_set_property (GObject
*object
,
55 static void yelp_view_resolve_uri (YelpView
*view
,
58 static gboolean
view_external_uri (YelpView
*view
,
60 static void view_install_uri (YelpView
*view
,
62 static void popup_open_link (GtkAction
*action
,
64 static void popup_open_link_new (GtkAction
*action
,
66 static void popup_copy_link (GtkAction
*action
,
68 static void popup_save_image (GtkAction
*action
,
70 static void popup_send_image (GtkAction
*action
,
72 static void popup_copy_code (GtkAction
*action
,
74 static void popup_save_code (GtkAction
*action
,
76 static void popup_copy_clipboard (GtkAction
*action
,
78 static gboolean
view_populate_context_menu (YelpView
*view
,
79 WebKitContextMenu
*context_menu
,
81 WebKitHitTestResult
*hit_test_result
,
83 static gboolean
view_script_dialog (YelpView
*view
,
84 WebKitScriptDialog
*dialog
,
86 static gboolean
view_policy_decision_requested (YelpView
*view
,
87 WebKitPolicyDecision
*decision
,
88 WebKitPolicyDecisionType type
,
90 static void view_print_action (GAction
*action
,
93 static void view_history_action (GAction
*action
,
96 static void view_history_changed (YelpView
*view
);
97 static void view_navigation_action (GAction
*action
,
101 static void view_clear_load (YelpView
*view
);
102 static void view_load_page (YelpView
*view
);
103 static void view_show_error_page (YelpView
*view
,
106 static void settings_set_fonts (YelpSettings
*settings
,
108 static void settings_show_text_cursor (YelpSettings
*settings
);
110 static void uri_resolved (YelpUri
*uri
,
112 static void yelp_view_register_custom_schemes (void);
113 static void view_load_failed (WebKitWebView
*web_view
,
114 WebKitLoadEvent load_event
,
118 static void view_load_status_changed (WebKitWebView
*view
,
119 WebKitLoadEvent load_event
,
121 static void yelp_view_register_extensions (void);
123 static gchar
*nautilus_sendto
= NULL
;
142 static gint signals
[LAST_SIGNAL
] = { 0 };
144 G_DEFINE_TYPE (YelpView
, yelp_view
, WEBKIT_TYPE_WEB_VIEW
)
145 #define GET_PRIV(object)(G_TYPE_INSTANCE_GET_PRIVATE ((object), YELP_TYPE_VIEW, YelpViewPrivate))
147 static WebKitSettings
*
148 yelp_view_get_global_settings (void)
150 static WebKitSettings
*websettings
= NULL
;
153 websettings
= webkit_settings_new_with_settings ("default-charset", "utf-8", NULL
);
158 typedef struct _YelpActionEntry YelpActionEntry
;
159 struct _YelpActionEntry
{
161 YelpViewActionValidFunc func
;
165 action_entry_free (YelpActionEntry
*entry
)
169 g_object_unref (entry
->action
);
173 typedef struct _RequestAsyncData RequestAsyncData
;
174 struct _RequestAsyncData
{
175 WebKitURISchemeRequest
*request
;
176 GFile
*resource_file
;
180 static RequestAsyncData
*
181 request_async_data_new (WebKitURISchemeRequest
*request
, gchar
*page_id
)
183 RequestAsyncData
*data
;
185 data
= g_slice_new0 (RequestAsyncData
);
186 data
->request
= g_object_ref (request
);
187 data
->page_id
= g_strdup (page_id
);
192 request_async_data_free (RequestAsyncData
*data
)
194 g_object_unref (data
->request
);
195 g_clear_pointer (&data
->page_id
, g_free
);
196 g_slice_free (RequestAsyncData
, data
);
199 typedef struct _YelpViewPrivate YelpViewPrivate
;
200 struct _YelpViewPrivate
{
202 YelpUri
*resolve_uri
;
204 YelpDocument
*document
;
205 GCancellable
*cancellable
;
206 gulong fonts_changed
;
208 gchar
*popup_link_uri
;
209 gchar
*popup_link_text
;
210 gchar
*popup_image_uri
;
211 gchar
*popup_code_text
;
212 gchar
*popup_code_title
;
215 YelpViewState prevstate
;
223 GSimpleAction
*print_action
;
224 GSimpleAction
*back_action
;
225 GSimpleAction
*forward_action
;
226 GSimpleAction
*prev_action
;
227 GSimpleAction
*next_action
;
229 GtkActionGroup
*popup_actions
;
231 GSList
*link_actions
;
233 gboolean resolve_uri_on_policy_decision
;
234 gboolean load_page_after_resolved
;
237 #define TARGET_TYPE_URI_LIST "text/uri-list"
243 yelp_view_init (YelpView
*view
)
245 static const GtkActionEntry popup_action_entries
[] = {
248 N_("C_opy Code Block"), NULL
, NULL
,
249 G_CALLBACK (popup_copy_code
)
253 N_("_Copy Link Location"), NULL
, NULL
,
254 G_CALLBACK (popup_copy_link
)
258 N_("_Open Link"), NULL
, NULL
,
259 G_CALLBACK (popup_open_link
)
263 N_("Open Link in New _Window"), NULL
, NULL
,
264 G_CALLBACK (popup_open_link_new
)
269 G_CALLBACK (popup_open_link
)
272 "InstallPackages", NULL
,
273 N_("_Install Packages"), NULL
, NULL
,
274 G_CALLBACK (popup_open_link
)
278 N_("Save Code _Block Asā¦"), NULL
, NULL
,
279 G_CALLBACK (popup_save_code
)
284 G_CALLBACK (popup_save_image
)
289 G_CALLBACK (popup_send_image
)
293 N_("_Copy Text"), NULL
, NULL
,
294 G_CALLBACK (popup_copy_clipboard
)
298 YelpViewPrivate
*priv
= GET_PRIV (view
);
300 priv
->cancellable
= NULL
;
302 priv
->prevstate
= priv
->state
= YELP_VIEW_STATE_BLANK
;
304 priv
->resolve_uri_on_policy_decision
= TRUE
;
305 g_signal_connect (view
, "decide-policy",
306 G_CALLBACK (view_policy_decision_requested
), NULL
);
307 g_signal_connect (view
, "load-changed",
308 G_CALLBACK (view_load_status_changed
), NULL
);
309 g_signal_connect (view
, "load-failed",
310 G_CALLBACK (view_load_failed
), NULL
);
311 g_signal_connect (view
, "context-menu",
312 G_CALLBACK (view_populate_context_menu
), NULL
);
313 g_signal_connect (view
, "script-dialog",
314 G_CALLBACK (view_script_dialog
), NULL
);
316 priv
->popup_actions
= gtk_action_group_new ("PopupActions");
317 gtk_action_group_add_actions (priv
->popup_actions
, popup_action_entries
,
318 G_N_ELEMENTS (popup_action_entries
), view
);
320 priv
->print_action
= g_simple_action_new ("yelp-view-print", NULL
);
321 g_signal_connect (priv
->print_action
,
323 G_CALLBACK (view_print_action
),
326 priv
->back_action
= g_simple_action_new ("yelp-view-go-back", NULL
);
327 g_simple_action_set_enabled (priv
->back_action
, FALSE
);
328 g_signal_connect (priv
->back_action
,
330 G_CALLBACK (view_history_action
),
333 priv
->forward_action
= g_simple_action_new ("yelp-view-go-forward", NULL
);
334 g_simple_action_set_enabled (priv
->forward_action
, FALSE
);
335 g_signal_connect (priv
->forward_action
,
337 G_CALLBACK (view_history_action
),
340 priv
->prev_action
= g_simple_action_new ("yelp-view-go-previous", NULL
);
341 g_simple_action_set_enabled (priv
->prev_action
, FALSE
);
342 g_signal_connect (priv
->prev_action
,
344 G_CALLBACK (view_navigation_action
),
347 priv
->next_action
= g_simple_action_new ("yelp-view-go-next", NULL
);
348 g_simple_action_set_enabled (priv
->next_action
, FALSE
);
349 g_signal_connect (priv
->next_action
,
351 G_CALLBACK (view_navigation_action
),
356 yelp_view_constructed (GObject
*object
)
358 YelpView
*view
= YELP_VIEW (object
);
359 YelpViewPrivate
*priv
= GET_PRIV (view
);
360 YelpSettings
*settings
= yelp_settings_get_default ();
362 G_OBJECT_CLASS (yelp_view_parent_class
)->constructed (object
);
364 g_signal_connect_swapped (webkit_web_view_get_back_forward_list (WEBKIT_WEB_VIEW (view
)),
366 G_CALLBACK (view_history_changed
),
369 priv
->fonts_changed
= g_signal_connect (settings
,
371 G_CALLBACK (settings_set_fonts
),
373 settings_set_fonts (settings
, view
);
377 yelp_view_dispose (GObject
*object
)
379 YelpViewPrivate
*priv
= GET_PRIV (object
);
381 view_clear_load (YELP_VIEW (object
));
383 if (priv
->fonts_changed
> 0) {
384 g_signal_handler_disconnect (yelp_settings_get_default (),
385 priv
->fonts_changed
);
386 priv
->fonts_changed
= 0;
389 if (priv
->print_action
) {
390 g_object_unref (priv
->print_action
);
391 priv
->print_action
= NULL
;
394 if (priv
->back_action
) {
395 g_object_unref (priv
->back_action
);
396 priv
->back_action
= NULL
;
399 if (priv
->forward_action
) {
400 g_object_unref (priv
->forward_action
);
401 priv
->forward_action
= NULL
;
404 if (priv
->prev_action
) {
405 g_object_unref (priv
->prev_action
);
406 priv
->prev_action
= NULL
;
409 if (priv
->next_action
) {
410 g_object_unref (priv
->next_action
);
411 priv
->next_action
= NULL
;
414 if (priv
->document
) {
415 g_object_unref (priv
->document
);
416 priv
->document
= NULL
;
419 while (priv
->link_actions
) {
420 action_entry_free (priv
->link_actions
->data
);
421 priv
->link_actions
= g_slist_delete_link (priv
->link_actions
, priv
->link_actions
);
424 G_OBJECT_CLASS (yelp_view_parent_class
)->dispose (object
);
428 yelp_view_finalize (GObject
*object
)
430 YelpViewPrivate
*priv
= GET_PRIV (object
);
432 g_free (priv
->popup_link_uri
);
433 g_free (priv
->popup_link_text
);
434 g_free (priv
->popup_image_uri
);
435 g_free (priv
->popup_code_text
);
436 g_free (priv
->popup_code_title
);
438 g_free (priv
->page_id
);
439 g_free (priv
->root_title
);
440 g_free (priv
->page_title
);
441 g_free (priv
->page_desc
);
442 g_free (priv
->page_icon
);
444 g_object_unref (priv
->popup_actions
);
446 G_OBJECT_CLASS (yelp_view_parent_class
)->finalize (object
);
450 yelp_view_class_init (YelpViewClass
*klass
)
452 GObjectClass
*object_class
= G_OBJECT_CLASS (klass
);
453 YelpSettings
*settings
= yelp_settings_get_default ();
455 nautilus_sendto
= g_find_program_in_path ("nautilus-sendto");
457 g_signal_connect (settings
,
458 "notify::show-text-cursor",
459 G_CALLBACK (settings_show_text_cursor
),
461 settings_show_text_cursor (settings
);
463 yelp_view_register_extensions ();
464 yelp_view_register_custom_schemes ();
466 klass
->external_uri
= view_external_uri
;
468 object_class
->constructed
= yelp_view_constructed
;
469 object_class
->dispose
= yelp_view_dispose
;
470 object_class
->finalize
= yelp_view_finalize
;
471 object_class
->get_property
= yelp_view_get_property
;
472 object_class
->set_property
= yelp_view_set_property
;
474 signals
[NEW_VIEW_REQUESTED
] =
475 g_signal_new ("new-view-requested",
476 G_TYPE_FROM_CLASS (klass
),
479 g_cclosure_marshal_VOID__OBJECT
,
480 G_TYPE_NONE
, 1, YELP_TYPE_URI
);
482 signals
[EXTERNAL_URI
] =
483 g_signal_new ("external-uri",
484 G_TYPE_FROM_CLASS (klass
),
486 G_STRUCT_OFFSET (YelpViewClass
, external_uri
),
487 g_signal_accumulator_true_handled
, NULL
,
488 yelp_marshal_BOOLEAN__OBJECT
,
489 G_TYPE_BOOLEAN
, 1, YELP_TYPE_URI
);
492 g_signal_new ("loaded",
493 G_TYPE_FROM_CLASS (klass
),
496 g_cclosure_marshal_VOID__VOID
,
499 g_type_class_add_private (klass
, sizeof (YelpViewPrivate
));
501 g_object_class_install_property (object_class
,
503 g_param_spec_object ("yelp-uri",
505 "A YelpUri with the current location",
507 G_PARAM_READWRITE
| G_PARAM_STATIC_NAME
|
508 G_PARAM_STATIC_NICK
| G_PARAM_STATIC_BLURB
));
510 g_object_class_install_property (object_class
,
512 g_param_spec_enum ("state",
514 "The loading state of the view",
515 YELP_TYPE_VIEW_STATE
,
516 YELP_VIEW_STATE_BLANK
,
517 G_PARAM_READWRITE
| G_PARAM_STATIC_NAME
|
518 G_PARAM_STATIC_NICK
| G_PARAM_STATIC_BLURB
));
520 g_object_class_install_property (object_class
,
522 g_param_spec_string ("page-id",
524 "The ID of the root page of the page being viewed",
527 G_PARAM_STATIC_NICK
| G_PARAM_STATIC_BLURB
));
529 g_object_class_install_property (object_class
,
531 g_param_spec_string ("root-title",
533 "The title of the root page of the page being viewed",
536 G_PARAM_STATIC_NICK
| G_PARAM_STATIC_BLURB
));
538 g_object_class_install_property (object_class
,
540 g_param_spec_string ("page-title",
542 "The title of the page being viewed",
545 G_PARAM_STATIC_NICK
| G_PARAM_STATIC_BLURB
));
547 g_object_class_install_property (object_class
,
549 g_param_spec_string ("page-desc",
551 "The description of the page being viewed",
554 G_PARAM_STATIC_NICK
| G_PARAM_STATIC_BLURB
));
556 g_object_class_install_property (object_class
,
558 g_param_spec_string ("page-icon",
560 "The icon of the page being viewed",
563 G_PARAM_STATIC_NICK
| G_PARAM_STATIC_BLURB
));
567 yelp_view_get_property (GObject
*object
,
572 YelpViewPrivate
*priv
= GET_PRIV (object
);
577 g_value_set_object (value
, priv
->uri
);
580 g_value_set_string (value
, priv
->page_id
);
582 case PROP_ROOT_TITLE
:
583 g_value_set_string (value
, priv
->root_title
);
585 case PROP_PAGE_TITLE
:
586 g_value_set_string (value
, priv
->page_title
);
589 g_value_set_string (value
, priv
->page_desc
);
593 g_value_set_string (value
, priv
->page_icon
);
595 g_value_set_string (value
, "yelp-page-symbolic");
598 g_value_set_enum (value
, priv
->state
);
601 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, prop_id
, pspec
);
607 yelp_view_set_property (GObject
*object
,
613 YelpViewPrivate
*priv
= GET_PRIV (object
);
618 uri
= g_value_get_object (value
);
619 yelp_view_load_uri (YELP_VIEW (object
), uri
);
620 g_object_unref (uri
);
623 priv
->prevstate
= priv
->state
;
624 priv
->state
= g_value_get_enum (value
);
627 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, prop_id
, pspec
);
632 /******************************************************************************/
637 return GTK_WIDGET (g_object_new (YELP_TYPE_VIEW
,
638 "settings", yelp_view_get_global_settings (), NULL
));
642 yelp_view_load (YelpView
*view
,
645 YelpUri
*yuri
= yelp_uri_new (uri
);
646 yelp_view_load_uri (view
, yuri
);
647 g_object_unref (yuri
);
651 yelp_view_load_uri (YelpView
*view
,
654 YelpViewPrivate
*priv
= GET_PRIV (view
);
656 g_object_set (view
, "state", YELP_VIEW_STATE_LOADING
, NULL
);
658 g_simple_action_set_enabled (priv
->prev_action
, FALSE
);
659 g_simple_action_set_enabled (priv
->next_action
, FALSE
);
661 priv
->load_page_after_resolved
= TRUE
;
662 yelp_view_resolve_uri (view
, uri
);
666 yelp_view_load_document (YelpView
*view
,
668 YelpDocument
*document
)
671 YelpViewPrivate
*priv
= GET_PRIV (view
);
673 g_return_if_fail (yelp_uri_is_resolved (uri
));
675 g_object_set (view
, "state", YELP_VIEW_STATE_LOADING
, NULL
);
678 view_clear_load (view
);
680 spec
= g_object_class_find_property ((GObjectClass
*) YELP_VIEW_GET_CLASS (view
),
682 g_signal_emit_by_name (view
, "notify::yelp-uri", spec
);
683 g_object_ref (document
);
685 g_object_unref (document
);
686 priv
->document
= document
;
688 view_load_page (view
);
692 yelp_view_get_document (YelpView
*view
)
694 YelpViewPrivate
*priv
= GET_PRIV (view
);
695 return priv
->document
;
699 yelp_view_register_actions (YelpView
*view
,
702 YelpViewPrivate
*priv
= GET_PRIV (view
);
703 g_action_map_add_action (map
, G_ACTION (priv
->print_action
));
704 g_action_map_add_action (map
, G_ACTION (priv
->back_action
));
705 g_action_map_add_action (map
, G_ACTION (priv
->forward_action
));
706 g_action_map_add_action (map
, G_ACTION (priv
->prev_action
));
707 g_action_map_add_action (map
, G_ACTION (priv
->next_action
));
710 /******************************************************************************/
713 yelp_view_resolve_uri (YelpView
*view
,
716 YelpViewPrivate
*priv
= GET_PRIV (view
);
718 if (yelp_uri_is_resolved (uri
)) {
719 uri_resolved (uri
, view
);
723 if (priv
->resolve_uri
!= NULL
) {
724 if (priv
->uri_resolved
!= 0) {
725 g_signal_handler_disconnect (priv
->resolve_uri
, priv
->uri_resolved
);
726 priv
->uri_resolved
= 0;
728 g_object_unref (priv
->resolve_uri
);
730 priv
->resolve_uri
= g_object_ref (uri
);
731 priv
->uri_resolved
= g_signal_connect (uri
, "resolved",
732 G_CALLBACK (uri_resolved
),
734 yelp_uri_resolve (uri
);
738 document_callback (YelpDocument
*document
,
739 YelpDocumentSignal signal
,
740 RequestAsyncData
*data
,
743 const gchar
*contents
;
745 GInputStream
*stream
;
748 if (signal
== YELP_DOCUMENT_SIGNAL_INFO
)
751 if (signal
== YELP_DOCUMENT_SIGNAL_ERROR
) {
752 webkit_uri_scheme_request_finish_error (data
->request
, error
);
756 mime_type
= yelp_document_get_mime_type (document
, data
->page_id
);
758 contents
= yelp_document_read_contents (document
, data
->page_id
);
760 content_length
= strlen (contents
);
762 stream
= g_memory_input_stream_new_from_data (g_strdup (contents
), content_length
, g_free
);
763 yelp_document_finish_read (document
, contents
);
765 webkit_uri_scheme_request_finish (data
->request
,
770 g_object_unref (stream
);
774 help_cb_uri_resolved (YelpUri
*uri
,
775 WebKitURISchemeRequest
*request
)
777 YelpDocument
*document
;
779 if ((document
= yelp_document_get_for_uri (uri
))) {
780 RequestAsyncData
*data
;
783 page_id
= yelp_uri_get_page_id (uri
);
784 data
= request_async_data_new (request
, page_id
);
787 yelp_document_request_page (document
,
790 (YelpDocumentCallback
) document_callback
,
792 (GDestroyNotify
) request_async_data_free
);
793 g_object_unref (document
);
796 YelpUriDocumentType doctype
;
800 doctype
= yelp_uri_get_document_type (uri
);
801 if (doctype
== YELP_URI_DOCUMENT_TYPE_NOT_FOUND
) {
802 struri
= yelp_uri_get_canonical_uri (uri
);
804 error
= g_error_new (YELP_ERROR
, YELP_ERROR_NOT_FOUND
,
805 _("The URI ā%sā does not point to a valid page."),
810 error
= g_error_new (YELP_ERROR
, YELP_ERROR_NOT_FOUND
,
811 _("The URI does not point to a valid page."));
813 } else if (doctype
== YELP_URI_DOCUMENT_TYPE_ERROR
) {
814 struri
= yelp_uri_get_canonical_uri (uri
);
815 error
= g_error_new (YELP_ERROR
, YELP_ERROR_PROCESSING
,
816 _("The URI ā%sā could not be parsed."),
820 error
= g_error_new (YELP_ERROR
, YELP_ERROR_UNKNOWN
,
821 _("Unknown Error."));
824 webkit_uri_scheme_request_finish_error (request
, error
);
825 g_error_free (error
);
830 help_uri_scheme_request_cb (WebKitURISchemeRequest
*request
,
836 uri_str
= build_yelp_uri (webkit_uri_scheme_request_get_uri (request
));
838 uri
= yelp_uri_new (uri_str
);
841 g_signal_connect (uri
, "resolved", G_CALLBACK (help_cb_uri_resolved
), request
);
842 yelp_uri_resolve (uri
);
844 g_object_unref (uri
);
847 static const gchar
*help_schemes
[] = { "help", "ghelp", "gnome-help", "help-list", "info", "man", NULL
};
850 yelp_view_register_custom_schemes (void)
852 WebKitWebContext
*context
= webkit_web_context_get_default ();
853 WebKitSecurityManager
*sec_manager
= webkit_web_context_get_security_manager (context
);
855 gchar
*network_scheme
;
857 for (i
= 0; help_schemes
[i
] != NULL
; i
++) {
858 network_scheme
= build_network_scheme (help_schemes
[i
]);
860 webkit_web_context_register_uri_scheme (context
, network_scheme
,
861 (WebKitURISchemeRequestCallback
) help_uri_scheme_request_cb
,
864 webkit_security_manager_register_uri_scheme_as_local (sec_manager
, network_scheme
);
866 g_free (network_scheme
);
870 /******************************************************************************/
873 yelp_view_add_link_action (YelpView
*view
,
875 YelpViewActionValidFunc func
,
878 YelpActionEntry
*entry
;
879 YelpViewPrivate
*priv
= GET_PRIV (view
);
881 entry
= g_new0 (YelpActionEntry
, 1);
882 entry
->action
= g_object_ref (action
);
886 priv
->link_actions
= g_slist_append (priv
->link_actions
, entry
);
890 yelp_view_get_active_link_uri (YelpView
*view
)
892 YelpViewPrivate
*priv
= GET_PRIV (view
);
895 uri
= yelp_uri_new_relative (priv
->uri
, priv
->popup_link_uri
);
901 yelp_view_get_active_link_text (YelpView
*view
)
903 YelpViewPrivate
*priv
= GET_PRIV (view
);
904 return g_strdup (priv
->popup_link_text
);
907 /******************************************************************************/
910 yelp_view_register_extensions (void)
912 WebKitWebContext
*context
= webkit_web_context_get_default ();
914 webkit_web_context_set_web_extensions_directory (context
, YELP_WEB_EXTENSIONS_DIR
);
917 /******************************************************************************/
920 view_external_uri (YelpView
*view
,
923 gchar
*struri
= yelp_uri_get_canonical_uri (uri
);
925 GAppInfo
*app_info
= NULL
;
927 uri_scheme
= g_uri_parse_scheme (struri
);
928 if (uri_scheme
&& *uri_scheme
)
929 app_info
= g_app_info_get_default_for_uri_scheme (uri_scheme
);
934 if (!strstr (g_app_info_get_executable (app_info
), "yelp"))
939 l
.next
= l
.prev
= NULL
;
940 g_app_info_launch_uris (app_info
, &l
, NULL
, NULL
);
943 g_object_unref (app_info
);
949 typedef struct _YelpInstallInfo YelpInstallInfo
;
950 struct _YelpInstallInfo
{
956 yelp_install_info_free (YelpInstallInfo
*info
)
958 g_object_unref (info
->view
);
965 view_install_installed (GDBusConnection
*connection
,
967 YelpInstallInfo
*info
)
969 GError
*error
= NULL
;
970 g_dbus_connection_call_finish (connection
, res
, &error
);
972 const gchar
*err
= NULL
;
973 if (error
->domain
== G_DBUS_ERROR
) {
974 if (error
->code
== G_DBUS_ERROR_SERVICE_UNKNOWN
)
975 err
= _("You do not have PackageKit. Package install links require PackageKit.");
977 err
= error
->message
;
980 GtkWidget
*dialog
= gtk_message_dialog_new (NULL
, 0,
984 gtk_dialog_run ((GtkDialog
*) dialog
);
985 gtk_widget_destroy (dialog
);
987 g_error_free (error
);
989 else if (info
->uri
) {
990 gchar
*struri
, *docuri
;
991 YelpViewPrivate
*priv
= GET_PRIV (info
->view
);
992 docuri
= yelp_uri_get_document_uri (priv
->uri
);
993 if (g_str_equal (docuri
, info
->uri
)) {
994 struri
= yelp_uri_get_canonical_uri (priv
->uri
);
995 yelp_view_load (info
->view
, struri
);
1001 yelp_install_info_free (info
);
1005 view_install_uri (YelpView
*view
,
1008 GDBusConnection
*connection
;
1009 GError
*error
= NULL
;
1010 gboolean help
= FALSE
, ghelp
= FALSE
;
1011 GVariantBuilder
*strv
;
1012 YelpInstallInfo
*info
;
1014 YelpViewPrivate
*priv
= GET_PRIV (view
);
1018 const gchar
*pkg
= NULL
, *confirm_search
;
1020 if (g_str_has_prefix (uri
, "install-help:")) {
1022 pkg
= (gchar
*) uri
+ 13;
1024 else if (g_str_has_prefix (uri
, "install-ghelp:")) {
1026 pkg
= (gchar
*) uri
+ 14;
1028 else if (g_str_has_prefix (uri
, "install:")) {
1029 pkg
= (gchar
*) uri
+ 8;
1032 connection
= g_bus_get_sync (G_BUS_TYPE_SESSION
, NULL
, &error
);
1033 if (connection
== NULL
) {
1034 g_warning ("Unable to connect to dbus: %s", error
->message
);
1035 g_error_free (error
);
1039 info
= g_new0 (YelpInstallInfo
, 1);
1040 info
->view
= g_object_ref (view
);
1042 gtkwin
= gtk_widget_get_toplevel (GTK_WIDGET (view
));
1043 if (gtkwin
!= NULL
&& gtk_widget_is_toplevel (gtkwin
)) {
1044 gdkwin
= gtk_widget_get_window (gtkwin
);
1045 #ifdef GDK_WINDOWING_X11
1046 if (gdkwin
!= NULL
&& GDK_IS_X11_WINDOW (gdkwin
))
1047 xid
= gdk_x11_window_get_xid (gdkwin
);
1051 if (priv
->state
== YELP_VIEW_STATE_ERROR
)
1052 confirm_search
= "hide-confirm-search";
1054 confirm_search
= "";
1056 if (help
|| ghelp
) {
1057 const gchar
* const *datadirs
= g_get_system_data_dirs ();
1059 gchar
*docbook
, *fname
;
1060 docbook
= g_strconcat (pkg
, ".xml", NULL
);
1061 strv
= g_variant_builder_new (G_VARIANT_TYPE ("as"));
1062 for (datadirs_i
= 0; datadirs
[datadirs_i
] != NULL
; datadirs_i
++) {
1064 fname
= g_build_filename (datadirs
[datadirs_i
], "gnome", "help",
1065 pkg
, "C", "index.page", NULL
);
1066 g_variant_builder_add (strv
, "s", fname
);
1068 fname
= g_build_filename (datadirs
[datadirs_i
], "gnome", "help",
1069 pkg
, "C", docbook
, NULL
);
1070 g_variant_builder_add (strv
, "s", fname
);
1074 fname
= g_build_filename (datadirs
[datadirs_i
], "help", "C",
1075 pkg
, "index.page", NULL
);
1076 g_variant_builder_add (strv
, "s", fname
);
1078 fname
= g_build_filename (datadirs
[datadirs_i
], "help", "C",
1079 pkg
, "index.docbook", NULL
);
1080 g_variant_builder_add (strv
, "s", fname
);
1085 info
->uri
= g_strconcat (ghelp
? "ghelp:" : "help:", pkg
, NULL
);
1086 g_dbus_connection_call (connection
,
1087 "org.freedesktop.PackageKit",
1088 "/org/freedesktop/PackageKit",
1089 "org.freedesktop.PackageKit.Modify",
1090 "InstallProvideFiles",
1091 g_variant_new ("(uass)", xid
, strv
, confirm_search
),
1093 G_DBUS_CALL_FLAGS_NONE
,
1095 (GAsyncReadyCallback
) view_install_installed
,
1097 g_variant_builder_unref (strv
);
1102 strv
= g_variant_builder_new (G_VARIANT_TYPE ("as"));
1103 pkgs
= g_strsplit (pkg
, ",", 0);
1104 for (i
= 0; pkgs
[i
]; i
++)
1105 g_variant_builder_add (strv
, "s", pkgs
[i
]);
1107 g_dbus_connection_call (connection
,
1108 "org.freedesktop.PackageKit",
1109 "/org/freedesktop/PackageKit",
1110 "org.freedesktop.PackageKit.Modify",
1111 "InstallPackageNames",
1112 g_variant_new ("(uass)", xid
, strv
, confirm_search
),
1114 G_DBUS_CALL_FLAGS_NONE
,
1116 (GAsyncReadyCallback
) view_install_installed
,
1118 g_variant_builder_unref (strv
);
1121 g_object_unref (connection
);
1125 popup_open_link (GtkAction
*action
,
1128 YelpViewPrivate
*priv
= GET_PRIV (view
);
1131 uri
= yelp_uri_new_relative (priv
->uri
, priv
->popup_link_uri
);
1133 yelp_view_load_uri (view
, uri
);
1134 g_object_unref (uri
);
1136 g_free (priv
->popup_link_uri
);
1137 priv
->popup_link_uri
= NULL
;
1139 g_free (priv
->popup_link_text
);
1140 priv
->popup_link_text
= NULL
;
1144 popup_open_link_new (GtkAction
*action
,
1147 YelpViewPrivate
*priv
= GET_PRIV (view
);
1150 uri
= yelp_uri_new_relative (priv
->uri
, priv
->popup_link_uri
);
1152 g_free (priv
->popup_link_uri
);
1153 priv
->popup_link_uri
= NULL
;
1155 g_free (priv
->popup_link_text
);
1156 priv
->popup_link_text
= NULL
;
1158 g_signal_emit (view
, signals
[NEW_VIEW_REQUESTED
], 0, uri
);
1159 g_object_unref (uri
);
1163 popup_copy_link (GtkAction
*action
,
1166 YelpViewPrivate
*priv
= GET_PRIV (view
);
1167 gtk_clipboard_set_text (gtk_widget_get_clipboard (GTK_WIDGET (view
), GDK_SELECTION_CLIPBOARD
),
1168 priv
->popup_link_uri
,
1172 typedef struct _YelpSaveData YelpSaveData
;
1173 struct _YelpSaveData
{
1181 file_copied (GFile
*file
,
1185 GError
*error
= NULL
;
1186 if (!g_file_copy_finish (file
, res
, &error
)) {
1187 GtkWidget
*dialog
= gtk_message_dialog_new (gtk_widget_get_visible (GTK_WIDGET (data
->window
)) ? data
->window
: NULL
,
1188 GTK_DIALOG_DESTROY_WITH_PARENT
,
1191 "%s", error
->message
);
1192 gtk_dialog_run (GTK_DIALOG (dialog
));
1193 gtk_widget_destroy (dialog
);
1195 g_object_unref (data
->orig
);
1196 g_object_unref (data
->dest
);
1197 g_object_unref (data
->view
);
1198 g_object_unref (data
->window
);
1202 popup_save_image (GtkAction
*action
,
1206 GtkWidget
*dialog
, *window
;
1209 YelpViewPrivate
*priv
= GET_PRIV (view
);
1211 for (window
= gtk_widget_get_parent (GTK_WIDGET (view
));
1212 window
&& !GTK_IS_WINDOW (window
);
1213 window
= gtk_widget_get_parent (window
));
1215 data
= g_new0 (YelpSaveData
, 1);
1216 data
->orig
= g_file_new_for_uri (priv
->popup_image_uri
);
1217 data
->view
= g_object_ref (view
);
1218 data
->window
= g_object_ref (GTK_WINDOW (window
));
1219 g_free (priv
->popup_image_uri
);
1220 priv
->popup_image_uri
= NULL
;
1222 dialog
= gtk_file_chooser_dialog_new (_("Save Image"),
1223 GTK_WINDOW (window
),
1224 GTK_FILE_CHOOSER_ACTION_SAVE
,
1225 GTK_STOCK_CANCEL
, GTK_RESPONSE_CANCEL
,
1226 GTK_STOCK_SAVE
, GTK_RESPONSE_OK
,
1228 gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (dialog
), TRUE
);
1229 basename
= g_file_get_basename (data
->orig
);
1230 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (dialog
), basename
);
1232 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dialog
),
1233 g_get_user_special_dir (G_USER_DIRECTORY_DESKTOP
));
1235 res
= gtk_dialog_run (GTK_DIALOG (dialog
));
1237 if (res
== GTK_RESPONSE_OK
) {
1238 data
->dest
= gtk_file_chooser_get_file (GTK_FILE_CHOOSER (dialog
));
1239 g_file_copy_async (data
->orig
, data
->dest
,
1240 G_FILE_COPY_OVERWRITE
,
1243 (GAsyncReadyCallback
) file_copied
,
1247 g_object_unref (data
->orig
);
1248 g_object_unref (data
->view
);
1249 g_object_unref (data
->window
);
1253 gtk_widget_destroy (dialog
);
1257 popup_send_image (GtkAction
*action
,
1262 GAppLaunchContext
*context
;
1263 GError
*error
= NULL
;
1264 YelpViewPrivate
*priv
= GET_PRIV (view
);
1266 command
= g_strdup_printf ("%s %s", nautilus_sendto
, priv
->popup_image_uri
);
1267 context
= (GAppLaunchContext
*) gdk_display_get_app_launch_context (gtk_widget_get_display (GTK_WIDGET (view
)));
1269 app
= g_app_info_create_from_commandline (command
, NULL
, 0, &error
);
1271 g_app_info_launch (app
, NULL
, context
, &error
);
1272 g_object_unref (app
);
1276 g_debug ("Could not launch nautilus-sendto: %s", error
->message
);
1277 g_error_free (error
);
1280 g_object_unref (context
);
1282 g_free (priv
->popup_image_uri
);
1283 priv
->popup_image_uri
= NULL
;
1287 popup_copy_code (GtkAction
*action
,
1290 YelpViewPrivate
*priv
= GET_PRIV (view
);
1291 GtkClipboard
*clipboard
;
1293 if (!priv
->popup_code_text
)
1296 clipboard
= gtk_clipboard_get (GDK_SELECTION_CLIPBOARD
);
1297 gtk_clipboard_set_text (clipboard
, priv
->popup_code_text
, -1);
1301 popup_save_code (GtkAction
*action
,
1304 YelpViewPrivate
*priv
= GET_PRIV (view
);
1305 GtkWidget
*dialog
, *window
;
1308 if (!priv
->popup_code_text
)
1311 if (!g_str_has_suffix (priv
->popup_code_text
, "\n")) {
1312 gchar
*tmp
= g_strconcat (priv
->popup_code_text
, "\n", NULL
);
1313 g_free (priv
->popup_code_text
);
1314 priv
->popup_code_text
= tmp
;
1317 for (window
= gtk_widget_get_parent (GTK_WIDGET (view
));
1318 window
&& !GTK_IS_WINDOW (window
);
1319 window
= gtk_widget_get_parent (window
));
1321 dialog
= gtk_file_chooser_dialog_new (_("Save Code"),
1322 GTK_WINDOW (window
),
1323 GTK_FILE_CHOOSER_ACTION_SAVE
,
1324 GTK_STOCK_CANCEL
, GTK_RESPONSE_CANCEL
,
1325 GTK_STOCK_SAVE
, GTK_RESPONSE_OK
,
1327 gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (dialog
), TRUE
);
1329 if (priv
->popup_code_title
)
1330 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (dialog
), priv
->popup_code_title
);
1332 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dialog
),
1333 g_get_user_special_dir (G_USER_DIRECTORY_DOCUMENTS
));
1335 res
= gtk_dialog_run (GTK_DIALOG (dialog
));
1337 if (res
== GTK_RESPONSE_OK
) {
1338 GError
*error
= NULL
;
1339 GFile
*file
= gtk_file_chooser_get_file (GTK_FILE_CHOOSER (dialog
));
1340 GFileOutputStream
*stream
= g_file_replace (file
, NULL
, FALSE
,
1344 if (stream
== NULL
) {
1345 GtkWidget
*dlg
= gtk_message_dialog_new (gtk_widget_get_visible (window
) ? GTK_WINDOW (window
) : NULL
,
1346 GTK_DIALOG_DESTROY_WITH_PARENT
,
1349 "%s", error
->message
);
1350 gtk_dialog_run (GTK_DIALOG (dlg
));
1351 gtk_widget_destroy (dlg
);
1352 g_error_free (error
);
1355 /* FIXME: we should do this async */
1356 GDataOutputStream
*datastream
= g_data_output_stream_new (G_OUTPUT_STREAM (stream
));
1357 if (!g_data_output_stream_put_string (datastream
, priv
->popup_code_text
, NULL
, &error
)) {
1358 GtkWidget
*dlg
= gtk_message_dialog_new (gtk_widget_get_visible (window
) ? GTK_WINDOW (window
) : NULL
,
1359 GTK_DIALOG_DESTROY_WITH_PARENT
,
1362 "%s", error
->message
);
1363 gtk_dialog_run (GTK_DIALOG (dlg
));
1364 gtk_widget_destroy (dlg
);
1365 g_error_free (error
);
1367 g_object_unref (datastream
);
1369 g_object_unref (file
);
1372 gtk_widget_destroy (dialog
);
1376 popup_copy_clipboard (GtkAction
*action
,
1379 webkit_web_view_execute_editing_command (WEBKIT_WEB_VIEW (view
), WEBKIT_EDITING_COMMAND_COPY
);
1383 view_populate_context_menu (YelpView
*view
,
1384 WebKitContextMenu
*context_menu
,
1386 WebKitHitTestResult
*hit_test_result
,
1389 YelpViewPrivate
*priv
= GET_PRIV (view
);
1390 WebKitContextMenuItem
*item
;
1392 GVariant
*dom_info_variant
;
1393 GVariantDict dom_info_dict
;
1395 webkit_context_menu_remove_all (context_menu
);
1397 /* We extract the info about the dom tree that we build in the web-extension.*/
1398 dom_info_variant
= webkit_context_menu_get_user_data (context_menu
);
1399 if (dom_info_variant
)
1400 g_variant_dict_init (&dom_info_dict
, dom_info_variant
);
1402 if (webkit_hit_test_result_context_is_link (hit_test_result
)) {
1403 const gchar
*uri
= webkit_hit_test_result_get_link_uri (hit_test_result
);
1404 g_free (priv
->popup_link_uri
);
1406 priv
->popup_link_uri
= build_yelp_uri (uri
);
1408 g_clear_pointer (&priv
->popup_link_text
, g_free
);
1409 if (dom_info_variant
)
1410 g_variant_dict_lookup (&dom_info_dict
, "link-title", "s",
1411 &(priv
->popup_link_text
));
1413 if (!priv
->popup_link_text
)
1414 priv
->popup_link_text
= g_strdup (uri
);
1416 if (g_str_has_prefix (priv
->popup_link_uri
, "mailto:")) {
1417 gchar
*label
= g_strdup_printf (_("Send email to %s"),
1418 priv
->popup_link_uri
+ 7);
1419 action
= gtk_action_group_get_action (priv
->popup_actions
,
1421 gtk_action_set_label (action
, label
);
1422 item
= webkit_context_menu_item_new (action
);
1423 webkit_context_menu_append (context_menu
, item
);
1426 else if (g_str_has_prefix (priv
->popup_link_uri
, "install:")) {
1427 action
= gtk_action_group_get_action (priv
->popup_actions
,
1429 item
= webkit_context_menu_item_new (action
);
1430 webkit_context_menu_append (context_menu
, item
);
1435 action
= gtk_action_group_get_action (priv
->popup_actions
,
1437 item
= webkit_context_menu_item_new (action
);
1438 webkit_context_menu_append (context_menu
, item
);
1440 if (g_str_has_prefix (priv
->popup_link_uri
, "http://") ||
1441 g_str_has_prefix (priv
->popup_link_uri
, "https://")) {
1442 action
= gtk_action_group_get_action (priv
->popup_actions
,
1444 item
= webkit_context_menu_item_new (action
);
1445 webkit_context_menu_append (context_menu
, item
);
1448 action
= gtk_action_group_get_action (priv
->popup_actions
,
1450 item
= webkit_context_menu_item_new (action
);
1451 webkit_context_menu_append (context_menu
, item
);
1454 for (l
= priv
->link_actions
; l
!= NULL
; l
= l
->next
) {
1456 YelpActionEntry
*entry
= (YelpActionEntry
*) l
->data
;
1457 if (entry
->func
== NULL
)
1460 add
= (* entry
->func
) (view
, entry
->action
,
1461 priv
->popup_link_uri
,
1464 item
= webkit_context_menu_item_new (entry
->action
);
1465 webkit_context_menu_append (context_menu
, item
);
1472 item
= gtk_action_create_menu_item (gtk_action_group_get_action (priv
->action_group
,
1474 gtk_menu_shell_append (GTK_MENU_SHELL (menu
), item
);
1475 item
= gtk_action_create_menu_item (gtk_action_group_get_action (priv
->action_group
,
1476 "YelpViewGoForward"));
1477 gtk_menu_shell_append (GTK_MENU_SHELL (menu
), item
);
1481 if (webkit_hit_test_result_context_is_image (hit_test_result
) ||
1482 webkit_hit_test_result_context_is_media (hit_test_result
)) {
1483 /* This doesn't currently work for video with automatic controls,
1484 * because WebKit puts the hit test on the div with the controls.
1486 gboolean image
= webkit_hit_test_result_context_is_image (hit_test_result
);
1487 const gchar
*uri
= image
? webkit_hit_test_result_get_image_uri (hit_test_result
) :
1488 webkit_hit_test_result_get_media_uri (hit_test_result
);
1490 g_free (priv
->popup_image_uri
);
1492 yelp_uri
= build_yelp_uri (uri
);
1494 if (g_str_has_prefix (yelp_uri
, "help:") ||
1495 g_str_has_prefix (yelp_uri
, "ghelp:") ||
1496 g_str_has_prefix (yelp_uri
, "gnome-help:")) {
1497 gchar
*image_uri
= strstr (yelp_uri
, "/");
1500 image_uri
[0] = '\0';
1504 if (image_uri
&& image_uri
[0] != '\0')
1505 priv
->popup_image_uri
= yelp_uri_locate_file_uri (priv
->uri
, image_uri
);
1507 priv
->popup_image_uri
= NULL
;
1510 priv
->popup_image_uri
= yelp_uri
;
1515 item
= webkit_context_menu_item_new_separator ();
1516 webkit_context_menu_append (context_menu
, item
);
1518 action
= gtk_action_group_get_action (priv
->popup_actions
,
1521 gtk_action_set_label (action
, image
? _("_Save Image Asā¦") :
1522 _("_Save Video Asā¦"));
1524 item
= webkit_context_menu_item_new (action
);
1525 webkit_context_menu_append (context_menu
, item
);
1527 if (nautilus_sendto
) {
1528 action
= gtk_action_group_get_action (priv
->popup_actions
,
1530 gtk_action_set_label (action
, image
? _("S_end Image Toā¦") :
1531 _("S_end Video Toā¦"));
1533 item
= webkit_context_menu_item_new (action
);
1534 webkit_context_menu_append (context_menu
, item
);
1538 if (webkit_hit_test_result_context_is_selection (hit_test_result
)) {
1539 item
= webkit_context_menu_item_new_separator ();
1540 webkit_context_menu_append (context_menu
, item
);
1542 action
= gtk_action_group_get_action (priv
->popup_actions
,
1544 item
= webkit_context_menu_item_new (action
);
1545 webkit_context_menu_append (context_menu
, item
);
1548 g_clear_pointer (&priv
->popup_code_title
, g_free
);
1549 if (dom_info_variant
)
1550 g_variant_dict_lookup (&dom_info_dict
, "code-title",
1551 "s", &(priv
->popup_code_title
));
1553 g_clear_pointer (&priv
->popup_code_text
, g_free
);
1554 if (dom_info_variant
)
1555 g_variant_dict_lookup (&dom_info_dict
, "code-text",
1556 "s", &(priv
->popup_code_text
));
1558 if (priv
->popup_code_text
) {
1559 item
= webkit_context_menu_item_new_separator ();
1560 webkit_context_menu_append (context_menu
, item
);
1562 action
= gtk_action_group_get_action (priv
->popup_actions
,
1564 item
= webkit_context_menu_item_new (action
);
1565 webkit_context_menu_append (context_menu
, item
);
1567 action
= gtk_action_group_get_action (priv
->popup_actions
,
1569 item
= webkit_context_menu_item_new (action
);
1570 webkit_context_menu_append (context_menu
, item
);
1577 view_script_dialog (YelpView
*view
,
1578 WebKitScriptDialog
*dialog
,
1581 WebKitScriptDialogType type
= webkit_script_dialog_get_dialog_type (dialog
);
1583 if (type
!= WEBKIT_SCRIPT_DIALOG_ALERT
)
1586 printf ("\n\n===ALERT===\n%s\n\n", webkit_script_dialog_get_message (dialog
));
1591 view_policy_decision_requested (YelpView
*view
,
1592 WebKitPolicyDecision
*decision
,
1593 WebKitPolicyDecisionType type
,
1596 YelpViewPrivate
*priv
= GET_PRIV (view
);
1597 WebKitNavigationAction
*action
;
1598 WebKitURIRequest
*request
;
1602 if (type
!= WEBKIT_POLICY_DECISION_TYPE_NAVIGATION_ACTION
)
1605 if (!priv
->resolve_uri_on_policy_decision
) {
1606 priv
->resolve_uri_on_policy_decision
= TRUE
;
1610 action
= webkit_navigation_policy_decision_get_navigation_action (WEBKIT_NAVIGATION_POLICY_DECISION (decision
));
1611 request
= webkit_navigation_action_get_request (action
);
1612 fixed_uri
= build_yelp_uri (webkit_uri_request_get_uri (request
));
1613 if (webkit_navigation_action_get_navigation_type (action
) == WEBKIT_NAVIGATION_TYPE_BACK_FORWARD
) {
1614 uri
= yelp_uri_new (fixed_uri
);
1615 priv
->load_page_after_resolved
= FALSE
;
1616 yelp_view_resolve_uri (view
, uri
);
1617 g_object_unref (uri
);
1623 webkit_policy_decision_ignore (decision
);
1625 uri
= yelp_uri_new_relative (priv
->uri
, fixed_uri
);
1626 yelp_view_load_uri ((YelpView
*) view
, uri
);
1627 g_object_unref (uri
);
1634 view_load_status_changed (WebKitWebView
*view
,
1635 WebKitLoadEvent load_event
,
1638 YelpViewPrivate
*priv
= GET_PRIV (view
);
1640 if (priv
->state
== YELP_VIEW_STATE_ERROR
)
1643 switch (load_event
) {
1644 case WEBKIT_LOAD_COMMITTED
: {
1648 real_id
= yelp_document_get_page_id (priv
->document
, priv
->page_id
);
1649 if (priv
->page_id
&& real_id
&& g_str_equal (real_id
, priv
->page_id
)) {
1653 g_free (priv
->page_id
);
1654 priv
->page_id
= real_id
;
1655 spec
= g_object_class_find_property ((GObjectClass
*) YELP_VIEW_GET_CLASS (view
),
1657 g_signal_emit_by_name (view
, "notify::page-id", spec
);
1660 g_free (priv
->root_title
);
1661 g_free (priv
->page_title
);
1662 g_free (priv
->page_desc
);
1663 g_free (priv
->page_icon
);
1665 priv
->root_title
= yelp_document_get_root_title (priv
->document
, priv
->page_id
);
1666 priv
->page_title
= yelp_document_get_page_title (priv
->document
, priv
->page_id
);
1667 priv
->page_desc
= yelp_document_get_page_desc (priv
->document
, priv
->page_id
);
1668 priv
->page_icon
= yelp_document_get_page_icon (priv
->document
, priv
->page_id
);
1670 spec
= g_object_class_find_property ((GObjectClass
*) YELP_VIEW_GET_CLASS (view
),
1672 g_signal_emit_by_name (view
, "notify::root-title", spec
);
1674 spec
= g_object_class_find_property ((GObjectClass
*) YELP_VIEW_GET_CLASS (view
),
1676 g_signal_emit_by_name (view
, "notify::page-title", spec
);
1678 spec
= g_object_class_find_property ((GObjectClass
*) YELP_VIEW_GET_CLASS (view
),
1680 g_signal_emit_by_name (view
, "notify::page-desc", spec
);
1682 spec
= g_object_class_find_property ((GObjectClass
*) YELP_VIEW_GET_CLASS (view
),
1684 g_signal_emit_by_name (view
, "notify::page-icon", spec
);
1687 case WEBKIT_LOAD_FINISHED
:
1688 g_object_set (view
, "state", YELP_VIEW_STATE_LOADED
, NULL
);
1690 /* If the document didn't give us a page title, get it from WebKit.
1691 * We let the main loop run through so that WebKit gets the title
1692 * set so that we can send notify::page-title before loaded. It
1693 * simplifies things if YelpView consumers can assume the title
1694 * is set before loaded is triggered.
1696 if (priv
->page_title
== NULL
) {
1698 priv
->page_title
= g_strdup (webkit_web_view_get_title (view
));
1699 spec
= g_object_class_find_property ((GObjectClass
*) YELP_VIEW_GET_CLASS (view
),
1701 g_signal_emit_by_name (view
, "notify::page-title", spec
);
1704 g_signal_emit (view
, signals
[LOADED
], 0);
1707 case WEBKIT_LOAD_STARTED
:
1708 case WEBKIT_LOAD_REDIRECTED
:
1715 view_load_failed (WebKitWebView
*view
,
1716 WebKitLoadEvent load_event
,
1721 view_show_error_page (YELP_VIEW (view
), error
);
1725 view_print_action (GAction
*action
, GVariant
*parameter
, YelpView
*view
)
1728 WebKitPrintOperation
*print_operation
;
1729 GtkPrintSettings
*settings
;
1730 YelpViewPrivate
*priv
= GET_PRIV (view
);
1732 window
= gtk_widget_get_toplevel (GTK_WIDGET (view
));
1734 print_operation
= webkit_print_operation_new (WEBKIT_WEB_VIEW (view
));
1736 settings
= gtk_print_settings_new ();
1737 gtk_print_settings_set (settings
,
1738 GTK_PRINT_SETTINGS_OUTPUT_BASENAME
,
1740 webkit_print_operation_set_print_settings (print_operation
, settings
);
1742 webkit_print_operation_run_dialog (print_operation
, GTK_WINDOW (window
));
1743 g_object_unref (print_operation
);
1744 g_object_unref (settings
);
1748 view_history_action (GAction
*action
,
1749 GVariant
*parameter
,
1752 if (g_str_equal (g_action_get_name (action
), "yelp-view-go-back"))
1753 webkit_web_view_go_back (WEBKIT_WEB_VIEW (view
));
1755 webkit_web_view_go_forward (WEBKIT_WEB_VIEW (view
));
1759 view_history_changed (YelpView
*view
)
1761 YelpViewPrivate
*priv
= GET_PRIV (view
);
1762 WebKitWebView
*web_view
= WEBKIT_WEB_VIEW (view
);
1764 g_simple_action_set_enabled (priv
->back_action
, webkit_web_view_can_go_back (web_view
));
1765 g_simple_action_set_enabled (priv
->forward_action
, webkit_web_view_can_go_forward (web_view
));
1769 view_navigation_action (GAction
*action
,
1770 GVariant
*parameter
,
1773 YelpViewPrivate
*priv
= GET_PRIV (view
);
1774 gchar
*page_id
, *new_id
, *xref
;
1777 page_id
= yelp_uri_get_page_id (priv
->uri
);
1779 if (g_str_equal (g_action_get_name (action
), "yelp-view-go-previous"))
1780 new_id
= yelp_document_get_prev_id (priv
->document
, page_id
);
1782 new_id
= yelp_document_get_next_id (priv
->document
, page_id
);
1784 /* Just in case we screwed up somewhere */
1785 if (new_id
== NULL
) {
1786 g_simple_action_set_enabled (G_SIMPLE_ACTION (action
), FALSE
);
1790 xref
= g_strconcat ("xref:", new_id
, NULL
);
1791 new_uri
= yelp_uri_new_relative (priv
->uri
, xref
);
1792 yelp_view_load_uri (view
, new_uri
);
1796 g_object_unref (new_uri
);
1800 view_clear_load (YelpView
*view
)
1802 YelpViewPrivate
*priv
= GET_PRIV (view
);
1804 if (priv
->resolve_uri
!= NULL
) {
1805 if (priv
->uri_resolved
!= 0) {
1806 g_signal_handler_disconnect (priv
->resolve_uri
, priv
->uri_resolved
);
1807 priv
->uri_resolved
= 0;
1809 g_object_unref (priv
->resolve_uri
);
1811 priv
->resolve_uri
= NULL
;
1814 g_object_unref (priv
->uri
);
1818 if (priv
->cancellable
) {
1819 g_cancellable_cancel (priv
->cancellable
);
1820 priv
->cancellable
= NULL
;
1825 fix_docbook_uri (YelpUri
*docbook_uri
, YelpDocument
* document
)
1828 gchar
*retval
, *canonical
;
1830 canonical
= yelp_uri_get_canonical_uri (docbook_uri
);
1831 soup_uri
= soup_uri_new (canonical
);
1834 /* We don't have actual page and frag IDs for DocBook. We just map IDs
1835 of block elements. The result is that we get xref:someid#someid.
1836 If someid is really the page ID, we just drop the frag reference.
1837 Otherwise, normal page views scroll past the link trail.
1839 if (soup_uri
->fragment
&& YELP_IS_DOCBOOK_DOCUMENT (document
)) {
1840 gchar
*page_id
= yelp_uri_get_page_id (docbook_uri
);
1841 gchar
*real_id
= yelp_document_get_page_id (document
, page_id
);
1843 if (g_str_equal (real_id
, soup_uri
->fragment
))
1844 soup_uri_set_fragment (soup_uri
, NULL
);
1850 retval
= soup_uri_to_string (soup_uri
, FALSE
);
1851 soup_uri_free (soup_uri
);
1857 view_load_page (YelpView
*view
)
1859 YelpViewPrivate
*priv
= GET_PRIV (view
);
1860 gchar
*uri_str
, *tmp_uri
;
1862 g_return_if_fail (priv
->cancellable
== NULL
);
1864 if (priv
->document
== NULL
) {
1867 /* FIXME: and if priv->uri is NULL? */
1868 docuri
= yelp_uri_get_document_uri (priv
->uri
);
1869 /* FIXME: CANT_READ isn't right */
1871 error
= g_error_new (YELP_ERROR
, YELP_ERROR_CANT_READ
,
1872 _("Could not load a document for ā%sā"),
1877 error
= g_error_new (YELP_ERROR
, YELP_ERROR_CANT_READ
,
1878 _("Could not load a document"));
1880 view_show_error_page (view
, error
);
1881 g_error_free (error
);
1885 uri_str
= yelp_uri_get_canonical_uri (priv
->uri
);
1887 if (YELP_IS_DOCBOOK_DOCUMENT (priv
->document
)){
1889 uri_str
= fix_docbook_uri (priv
->uri
, priv
->document
);
1894 uri_str
= build_network_uri (uri_str
);
1897 priv
->resolve_uri_on_policy_decision
= FALSE
;
1898 webkit_web_view_load_uri (WEBKIT_WEB_VIEW (view
), uri_str
);
1903 #define FORMAT_ERRORPAGE \
1905 "<style type='text/css'>" \
1909 " background-color: %s;" \
1911 "p { margin: 1em 0 0 0; }\n" \
1914 " border-color: %s;" \
1915 " border-top: solid 1px;" \
1916 " border-bottom: solid 1px;" \
1917 " background-color: %s;" \
1919 "div.note div.inner {" \
1920 " margin: 0; padding: 0;" \
1921 " background-image: url(%s);" \
1922 " background-position: %s top;" \
1923 " background-repeat: no-repeat;" \
1924 " min-height: %ipx;" \
1926 "div.note div.contents {" \
1927 " margin-%s: %ipx;" \
1929 "div.note div.title {" \
1930 " margin-%s: %ipx;" \
1931 " margin-bottom: 0.2em;" \
1932 " font-weight: bold;" \
1935 "a { color: %s; text-decoration: none; }\n" \
1938 "<div class='note'><div class='inner'>" \
1939 "%s<div class='contents'>%s%s</div>" \
1944 view_show_error_page (YelpView
*view
,
1947 YelpViewPrivate
*priv
= GET_PRIV (view
);
1948 YelpSettings
*settings
= yelp_settings_get_default ();
1949 gchar
*page
, *title
= NULL
, *title_m
, *content_beg
, *content_end
;
1950 gchar
*textcolor
, *bgcolor
, *noteborder
, *notebg
, *titlecolor
, *noteicon
, *linkcolor
;
1953 gboolean doc404
= FALSE
;
1954 const gchar
*left
= (gtk_widget_get_direction ((GtkWidget
*) view
) == GTK_TEXT_DIR_RTL
) ? "right" : "left";
1956 if (priv
->uri
&& yelp_uri_get_document_type (priv
->uri
) == YELP_URI_DOCUMENT_TYPE_NOT_FOUND
)
1958 if (error
->domain
== YELP_ERROR
)
1959 switch (error
->code
) {
1960 case YELP_ERROR_NOT_FOUND
:
1962 title
= _("Document Not Found");
1964 title
= _("Page Not Found");
1966 case YELP_ERROR_CANT_READ
:
1967 title
= _("Cannot Read");
1973 title
= _("Unknown Error");
1974 title_m
= g_markup_printf_escaped ("<div class='title'>%s</div>", title
);
1976 content_beg
= g_markup_printf_escaped ("<p>%s</p>", error
->message
);
1979 gchar
*struri
= yelp_uri_get_document_uri (priv
->uri
);
1981 const gchar
*pkg
= NULL
, *scheme
= NULL
;
1982 if (g_str_has_prefix (struri
, "help:")) {
1986 else if (g_str_has_prefix (struri
, "ghelp:")) {
1991 content_end
= g_markup_printf_escaped ("<p><a href='install-%s:%s'>%s</a></p>",
1993 _("Search for packages containing this document."));
1997 textcolor
= yelp_settings_get_color (settings
, YELP_SETTINGS_COLOR_TEXT
);
1998 bgcolor
= yelp_settings_get_color (settings
, YELP_SETTINGS_COLOR_BASE
);
1999 noteborder
= yelp_settings_get_color (settings
, YELP_SETTINGS_COLOR_RED_BORDER
);
2000 notebg
= yelp_settings_get_color (settings
, YELP_SETTINGS_COLOR_YELLOW_BASE
);
2001 titlecolor
= yelp_settings_get_color (settings
, YELP_SETTINGS_COLOR_TEXT_LIGHT
);
2002 linkcolor
= yelp_settings_get_color (settings
, YELP_SETTINGS_COLOR_LINK
);
2003 noteicon
= yelp_settings_get_icon (settings
, YELP_SETTINGS_ICON_WARNING
);
2004 iconsize
= yelp_settings_get_icon_size (settings
) + 6;
2006 page
= g_strdup_printf (FORMAT_ERRORPAGE
,
2007 textcolor
, bgcolor
, noteborder
, notebg
, noteicon
,
2008 left
, iconsize
, left
, iconsize
, left
, iconsize
,
2009 titlecolor
, linkcolor
, title_m
, content_beg
,
2010 (content_end
!= NULL
) ? content_end
: "");
2012 g_object_set (view
, "state", YELP_VIEW_STATE_ERROR
, NULL
);
2015 g_free (priv
->root_title
);
2016 priv
->root_title
= g_strdup (title
);
2017 spec
= g_object_class_find_property ((GObjectClass
*) YELP_VIEW_GET_CLASS (view
),
2019 g_signal_emit_by_name (view
, "notify::root-title", spec
);
2020 g_free (priv
->page_id
);
2021 priv
->page_id
= g_strdup ("index");
2022 spec
= g_object_class_find_property ((GObjectClass
*) YELP_VIEW_GET_CLASS (view
),
2024 g_signal_emit_by_name (view
, "notify::page-id", spec
);
2027 g_free (priv
->page_title
);
2028 priv
->page_title
= g_strdup (title
);
2029 spec
= g_object_class_find_property ((GObjectClass
*) YELP_VIEW_GET_CLASS (view
),
2031 g_signal_emit_by_name (view
, "notify::page-title", spec
);
2033 g_free (priv
->page_desc
);
2034 priv
->page_desc
= NULL
;
2036 priv
->page_desc
= yelp_uri_get_canonical_uri (priv
->uri
);
2037 spec
= g_object_class_find_property ((GObjectClass
*) YELP_VIEW_GET_CLASS (view
),
2039 g_signal_emit_by_name (view
, "notify::page-desc", spec
);
2041 g_free (priv
->page_icon
);
2042 priv
->page_icon
= g_strdup ("dialog-warning");
2043 spec
= g_object_class_find_property ((GObjectClass
*) YELP_VIEW_GET_CLASS (view
),
2045 g_signal_emit_by_name (view
, "notify::page-icon", spec
);
2047 g_signal_emit (view
, signals
[LOADED
], 0);
2049 priv
->resolve_uri_on_policy_decision
= FALSE
;
2050 webkit_web_view_load_html (WEBKIT_WEB_VIEW (view
),
2054 g_free (content_beg
);
2055 if (content_end
!= NULL
)
2056 g_free (content_end
);
2060 settings_set_fonts (YelpSettings
*settings
,
2067 view
= (YelpView
*) user_data
;
2069 family
= yelp_settings_get_font_family (settings
,
2070 YELP_SETTINGS_FONT_VARIABLE
);
2071 size
= yelp_settings_get_font_size (settings
,
2072 YELP_SETTINGS_FONT_VARIABLE
);
2073 g_object_set (webkit_web_view_get_settings (WEBKIT_WEB_VIEW (view
)),
2074 "default-font-family", family
,
2075 "sans-serif-font-family", family
,
2076 "default-font-size", webkit_settings_font_size_to_pixels (size
),
2080 family
= yelp_settings_get_font_family (settings
,
2081 YELP_SETTINGS_FONT_FIXED
);
2082 size
= yelp_settings_get_font_size (settings
,
2083 YELP_SETTINGS_FONT_FIXED
);
2084 g_object_set (webkit_web_view_get_settings (WEBKIT_WEB_VIEW (view
)),
2085 "monospace-font-family", family
,
2086 "default-monospace-font-size", webkit_settings_font_size_to_pixels (size
),
2092 settings_show_text_cursor (YelpSettings
*settings
)
2094 webkit_settings_set_enable_caret_browsing (yelp_view_get_global_settings (),
2095 yelp_settings_get_show_text_cursor (settings
));
2098 /******************************************************************************/
2101 uri_resolved (YelpUri
*uri
,
2104 YelpViewPrivate
*priv
= GET_PRIV (view
);
2105 YelpUriDocumentType doctype
;
2106 YelpDocument
*document
;
2107 GError
*error
= NULL
;
2111 doctype
= yelp_uri_get_document_type (uri
);
2113 if (doctype
!= YELP_URI_DOCUMENT_TYPE_EXTERNAL
) {
2115 view_clear_load (view
);
2119 if (doctype
== YELP_URI_DOCUMENT_TYPE_EXTERNAL
) {
2120 g_object_set (view
, "state", priv
->prevstate
, NULL
);
2121 struri
= yelp_uri_get_canonical_uri (uri
);
2122 if (g_str_has_prefix (struri
, "install:") ||
2123 g_str_has_prefix (struri
, "install-ghelp:") ||
2124 g_str_has_prefix (struri
, "install-help:")) {
2125 view_install_uri (view
, struri
);
2129 g_signal_emit (view
, signals
[EXTERNAL_URI
], 0, uri
, &result
);
2134 else if (doctype
== YELP_URI_DOCUMENT_TYPE_NOT_FOUND
) {
2135 struri
= yelp_uri_get_canonical_uri (uri
);
2136 if (struri
!= NULL
) {
2137 error
= g_error_new (YELP_ERROR
, YELP_ERROR_NOT_FOUND
,
2138 _("The URI ā%sā does not point to a valid page."),
2143 error
= g_error_new (YELP_ERROR
, YELP_ERROR_NOT_FOUND
,
2144 _("The URI does not point to a valid page."));
2147 else if (doctype
== YELP_URI_DOCUMENT_TYPE_ERROR
) {
2148 struri
= yelp_uri_get_canonical_uri (uri
);
2149 error
= g_error_new (YELP_ERROR
, YELP_ERROR_PROCESSING
,
2150 _("The URI ā%sā could not be parsed."),
2155 spec
= g_object_class_find_property ((GObjectClass
*) YELP_VIEW_GET_CLASS (view
),
2157 g_signal_emit_by_name (view
, "notify::yelp-uri", spec
);
2159 g_free (priv
->page_id
);
2160 priv
->page_id
= NULL
;
2161 if (priv
->uri
!= NULL
)
2162 priv
->page_id
= yelp_uri_get_page_id (priv
->uri
);
2163 spec
= g_object_class_find_property ((GObjectClass
*) YELP_VIEW_GET_CLASS (view
),
2165 g_signal_emit_by_name (view
, "notify::page-id", spec
);
2167 g_free (priv
->root_title
);
2168 g_free (priv
->page_title
);
2169 g_free (priv
->page_desc
);
2170 g_free (priv
->page_icon
);
2171 priv
->root_title
= NULL
;
2172 priv
->page_title
= NULL
;
2173 priv
->page_desc
= NULL
;
2174 priv
->page_icon
= NULL
;
2176 spec
= g_object_class_find_property ((GObjectClass
*) YELP_VIEW_GET_CLASS (view
),
2178 g_signal_emit_by_name (view
, "notify::root-title", spec
);
2180 spec
= g_object_class_find_property ((GObjectClass
*) YELP_VIEW_GET_CLASS (view
),
2182 g_signal_emit_by_name (view
, "notify::page-title", spec
);
2184 spec
= g_object_class_find_property ((GObjectClass
*) YELP_VIEW_GET_CLASS (view
),
2186 g_signal_emit_by_name (view
, "notify::page-desc", spec
);
2188 spec
= g_object_class_find_property ((GObjectClass
*) YELP_VIEW_GET_CLASS (view
),
2190 g_signal_emit_by_name (view
, "notify::page-icon", spec
);
2192 if (error
== NULL
) {
2193 document
= yelp_document_get_for_uri (uri
);
2195 g_object_unref (priv
->document
);
2196 priv
->document
= document
;
2198 if (priv
->load_page_after_resolved
)
2199 view_load_page (view
);
2201 if (priv
->document
!= NULL
) {
2202 g_object_unref (priv
->document
);
2203 priv
->document
= NULL
;
2205 view_show_error_page (view
, error
);
2206 g_error_free (error
);