1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * Copyright (C) 2009 Shaun McCance <shaunm@gnome.org>
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License as
7 * published by the Free Software Foundation; either version 2 of the
8 * License, or (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License for more details.
15 * You should have received a copy of the GNU General Public
16 * License along with this program; if not, write to the
17 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 * Boston, MA 02111-1307, USA.
20 * Author: Shaun McCance <shaunm@gnome.org>
27 #include <glib/gi18n.h>
28 #include <glib-object.h>
31 #include <webkit/webkit.h>
33 #include "yelp-debug.h"
34 #include "yelp-docbook-document.h"
35 #include "yelp-error.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
,
51 static void yelp_view_set_property (GObject
*object
,
56 static void view_scrolled (GtkAdjustment
*adjustment
,
58 static void view_set_hadjustment (YelpView
*view
,
61 static void view_set_vadjustment (YelpView
*view
,
64 static void popup_open_link (GtkMenuItem
*item
,
66 static void popup_open_link_new (GtkMenuItem
*item
,
68 static void popup_save_image (GtkMenuItem
*item
,
70 static void popup_send_image (GtkMenuItem
*item
,
72 static void popup_copy_code (GtkMenuItem
*item
,
74 static void popup_save_code (GtkMenuItem
*item
,
76 static void view_populate_popup (YelpView
*view
,
79 static void view_script_alert (YelpView
*view
,
80 WebKitWebFrame
*frame
,
83 static gboolean
view_navigation_requested (WebKitWebView
*view
,
84 WebKitWebFrame
*frame
,
85 WebKitNetworkRequest
*request
,
86 WebKitWebNavigationAction
*action
,
87 WebKitWebPolicyDecision
*decision
,
89 static void view_resource_request (WebKitWebView
*view
,
90 WebKitWebFrame
*frame
,
91 WebKitWebResource
*resource
,
92 WebKitNetworkRequest
*request
,
93 WebKitNetworkResponse
*response
,
96 static void view_print (GtkAction
*action
,
98 static void view_history_action (GtkAction
*action
,
100 static void view_navigation_action (GtkAction
*action
,
103 static void view_clear_load (YelpView
*view
);
104 static void view_load_page (YelpView
*view
);
105 static void view_show_error_page (YelpView
*view
,
108 static void settings_set_fonts (YelpSettings
*settings
);
109 static void settings_show_text_cursor (YelpSettings
*settings
);
111 static void uri_resolved (YelpUri
*uri
,
113 static void document_callback (YelpDocument
*document
,
114 YelpDocumentSignal signal
,
118 static const GtkActionEntry entries
[] = {
119 {"YelpViewPrint", GTK_STOCK_PRINT
,
123 G_CALLBACK (view_print
) },
124 {"YelpViewGoBack", GTK_STOCK_GO_BACK
,
128 G_CALLBACK (view_history_action
) },
129 {"YelpViewGoForward", GTK_STOCK_GO_FORWARD
,
133 G_CALLBACK (view_history_action
) },
134 {"YelpViewGoPrevious", NULL
,
135 N_("_Previous Page"),
138 G_CALLBACK (view_navigation_action
) },
139 {"YelpViewGoNext", NULL
,
141 "<Control>Page_Down",
143 G_CALLBACK (view_navigation_action
) }
146 static gchar
*nautilus_sendto
= NULL
;
165 static gint signals
[LAST_SIGNAL
] = { 0 };
167 G_DEFINE_TYPE (YelpView
, yelp_view
, WEBKIT_TYPE_WEB_VIEW
);
168 #define GET_PRIV(object)(G_TYPE_INSTANCE_GET_PRIVATE ((object), YELP_TYPE_VIEW, YelpViewPrivate))
170 static WebKitWebSettings
*websettings
;
172 typedef struct _YelpActionEntry YelpActionEntry
;
173 struct _YelpActionEntry
{
175 YelpViewActionValidFunc func
;
178 action_entry_free (YelpActionEntry
*entry
)
182 g_object_unref (entry
->action
);
186 typedef struct _YelpBackEntry YelpBackEntry
;
187 struct _YelpBackEntry
{
195 back_entry_free (YelpBackEntry
*back
)
199 g_object_unref (back
->uri
);
200 g_free (back
->title
);
205 typedef struct _YelpViewPrivate YelpViewPrivate
;
206 struct _YelpViewPrivate
{
210 YelpDocument
*document
;
211 GCancellable
*cancellable
;
212 GtkAdjustment
*vadjustment
;
213 GtkAdjustment
*hadjustment
;
219 gchar
*popup_link_uri
;
220 gchar
*popup_link_text
;
221 gchar
*popup_image_uri
;
222 WebKitDOMNode
*popup_code_node
;
223 WebKitDOMNode
*popup_code_title
;
224 gchar
*popup_code_text
;
238 GtkActionGroup
*action_group
;
240 GSList
*link_actions
;
242 gint navigation_requested
;
245 #define TARGET_TYPE_URI_LIST "text/uri-list"
251 yelp_view_init (YelpView
*view
)
253 YelpViewPrivate
*priv
= GET_PRIV (view
);
255 g_object_set (view
, "settings", websettings
, NULL
);
257 priv
->cancellable
= NULL
;
259 priv
->state
= YELP_VIEW_STATE_BLANK
;
261 priv
->navigation_requested
=
262 g_signal_connect (view
, "navigation-policy-decision-requested",
263 G_CALLBACK (view_navigation_requested
), NULL
);
264 g_signal_connect (view
, "resource-request-starting",
265 G_CALLBACK (view_resource_request
), NULL
);
266 g_signal_connect (view
, "notify::hadjustment",
267 G_CALLBACK (view_set_hadjustment
), NULL
);
268 g_signal_connect (view
, "notify::vadjustment",
269 G_CALLBACK (view_set_vadjustment
), NULL
);
270 g_signal_connect (view
, "populate-popup",
271 G_CALLBACK (view_populate_popup
), NULL
);
272 g_signal_connect (view
, "script-alert",
273 G_CALLBACK (view_script_alert
), NULL
);
275 priv
->action_group
= gtk_action_group_new ("YelpView");
276 gtk_action_group_set_translation_domain (priv
->action_group
, GETTEXT_PACKAGE
);
277 gtk_action_group_add_actions (priv
->action_group
,
278 entries
, G_N_ELEMENTS (entries
),
283 yelp_view_dispose (GObject
*object
)
285 YelpViewPrivate
*priv
= GET_PRIV (object
);
288 g_object_unref (priv
->uri
);
292 if (priv
->vadjuster
> 0) {
293 g_source_remove (priv
->vadjuster
);
297 if (priv
->hadjuster
> 0) {
298 g_source_remove (priv
->hadjuster
);
302 if (priv
->cancellable
) {
303 g_cancellable_cancel (priv
->cancellable
);
304 g_object_unref (priv
->cancellable
);
305 priv
->cancellable
= NULL
;
308 if (priv
->action_group
) {
309 g_object_unref (priv
->action_group
);
310 priv
->action_group
= NULL
;
313 if (priv
->document
) {
314 g_object_unref (priv
->document
);
315 priv
->document
= NULL
;
318 while (priv
->link_actions
) {
319 action_entry_free (priv
->link_actions
->data
);
320 priv
->link_actions
= g_slist_delete_link (priv
->link_actions
, priv
->link_actions
);
323 priv
->back_cur
= NULL
;
324 while (priv
->back_list
) {
325 back_entry_free ((YelpBackEntry
*) priv
->back_list
->data
);
326 priv
->back_list
= g_list_delete_link (priv
->back_list
, priv
->back_list
);
329 G_OBJECT_CLASS (yelp_view_parent_class
)->dispose (object
);
333 yelp_view_finalize (GObject
*object
)
335 YelpViewPrivate
*priv
= GET_PRIV (object
);
337 g_free (priv
->popup_link_uri
);
338 g_free (priv
->popup_link_text
);
339 g_free (priv
->popup_image_uri
);
340 g_free (priv
->popup_code_text
);
342 g_free (priv
->page_id
);
343 g_free (priv
->root_title
);
344 g_free (priv
->page_title
);
345 g_free (priv
->page_desc
);
346 g_free (priv
->page_icon
);
348 g_free (priv
->bogus_uri
);
350 G_OBJECT_CLASS (yelp_view_parent_class
)->finalize (object
);
354 yelp_view_class_init (YelpViewClass
*klass
)
356 GObjectClass
*object_class
= G_OBJECT_CLASS (klass
);
357 YelpSettings
*settings
= yelp_settings_get_default ();
359 nautilus_sendto
= g_find_program_in_path ("nautilus-sendto");
361 websettings
= webkit_web_settings_new ();
362 g_object_set (websettings
, "enable-universal-access-from-file-uris", TRUE
, NULL
);
363 g_signal_connect (settings
,
365 G_CALLBACK (settings_set_fonts
),
367 settings_set_fonts (settings
);
368 g_signal_connect (settings
,
369 "notify::show-text-cursor",
370 G_CALLBACK (settings_show_text_cursor
),
372 settings_show_text_cursor (settings
);
374 object_class
->dispose
= yelp_view_dispose
;
375 object_class
->finalize
= yelp_view_finalize
;
376 object_class
->get_property
= yelp_view_get_property
;
377 object_class
->set_property
= yelp_view_set_property
;
379 signals
[NEW_VIEW_REQUESTED
] =
380 g_signal_new ("new-view-requested",
381 G_TYPE_FROM_CLASS (klass
),
384 g_cclosure_marshal_VOID__OBJECT
,
385 G_TYPE_NONE
, 1, YELP_TYPE_URI
);
387 signals
[EXTERNAL_URI
] =
388 g_signal_new ("external-uri",
389 G_TYPE_FROM_CLASS (klass
),
392 g_cclosure_marshal_VOID__OBJECT
,
393 G_TYPE_NONE
, 1, YELP_TYPE_URI
);
396 g_signal_new ("loaded",
397 G_TYPE_FROM_CLASS (klass
),
400 g_cclosure_marshal_VOID__VOID
,
403 g_type_class_add_private (klass
, sizeof (YelpViewPrivate
));
405 g_object_class_install_property (object_class
,
407 g_param_spec_object ("yelp-uri",
409 _("A YelpUri with the current location"),
411 G_PARAM_READWRITE
| G_PARAM_STATIC_NAME
|
412 G_PARAM_STATIC_NICK
| G_PARAM_STATIC_BLURB
));
414 g_object_class_install_property (object_class
,
416 g_param_spec_enum ("state",
418 N_("The loading state of the view"),
419 YELP_TYPE_VIEW_STATE
,
420 YELP_VIEW_STATE_BLANK
,
421 G_PARAM_READWRITE
| G_PARAM_STATIC_NAME
|
422 G_PARAM_STATIC_NICK
| G_PARAM_STATIC_BLURB
));
424 g_object_class_install_property (object_class
,
426 g_param_spec_string ("page-id",
428 N_("The ID of the root page of the page being viewed"),
431 G_PARAM_STATIC_NICK
| G_PARAM_STATIC_BLURB
));
433 g_object_class_install_property (object_class
,
435 g_param_spec_string ("root-title",
437 N_("The title of the root page of the page being viewed"),
440 G_PARAM_STATIC_NICK
| G_PARAM_STATIC_BLURB
));
442 g_object_class_install_property (object_class
,
444 g_param_spec_string ("page-title",
446 N_("The title of the page being viewed"),
449 G_PARAM_STATIC_NICK
| G_PARAM_STATIC_BLURB
));
451 g_object_class_install_property (object_class
,
453 g_param_spec_string ("page-desc",
454 N_("Page Description"),
455 N_("The description of the page being viewed"),
458 G_PARAM_STATIC_NICK
| G_PARAM_STATIC_BLURB
));
460 g_object_class_install_property (object_class
,
462 g_param_spec_string ("page-icon",
464 N_("The icon of the page being viewed"),
467 G_PARAM_STATIC_NICK
| G_PARAM_STATIC_BLURB
));
471 yelp_view_get_property (GObject
*object
,
476 YelpViewPrivate
*priv
= GET_PRIV (object
);
481 g_value_set_object (value
, priv
->uri
);
484 g_value_set_string (value
, priv
->page_id
);
486 case PROP_ROOT_TITLE
:
487 g_value_set_string (value
, priv
->root_title
);
489 case PROP_PAGE_TITLE
:
490 g_value_set_string (value
, priv
->page_title
);
493 g_value_set_string (value
, priv
->page_desc
);
497 g_value_set_string (value
, priv
->page_icon
);
499 g_value_set_string (value
, "help-contents");
502 g_value_set_enum (value
, priv
->state
);
505 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, prop_id
, pspec
);
511 yelp_view_set_property (GObject
*object
,
517 YelpViewPrivate
*priv
= GET_PRIV (object
);
522 uri
= g_value_get_object (value
);
523 yelp_view_load_uri (YELP_VIEW (object
), uri
);
524 g_object_unref (uri
);
527 priv
->state
= g_value_get_enum (value
);
530 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, prop_id
, pspec
);
535 /******************************************************************************/
540 return (GtkWidget
*) g_object_new (YELP_TYPE_VIEW
, NULL
);
544 yelp_view_load (YelpView
*view
,
547 YelpUri
*yuri
= yelp_uri_new (uri
);
548 yelp_view_load_uri (view
, yuri
);
549 g_object_unref (yuri
);
553 yelp_view_load_uri (YelpView
*view
,
556 YelpViewPrivate
*priv
= GET_PRIV (view
);
559 view_clear_load (view
);
560 g_object_set (view
, "state", YELP_VIEW_STATE_LOADING
, NULL
);
562 g_free (priv
->page_id
);
563 g_free (priv
->root_title
);
564 g_free (priv
->page_title
);
565 g_free (priv
->page_desc
);
566 g_free (priv
->page_icon
);
567 priv
->page_id
= NULL
;
568 priv
->root_title
= NULL
;
569 priv
->page_title
= NULL
;
570 priv
->page_desc
= NULL
;
571 priv
->page_icon
= NULL
;
573 spec
= g_object_class_find_property ((GObjectClass
*) YELP_VIEW_GET_CLASS (view
),
575 g_signal_emit_by_name (view
, "notify::page-id", spec
);
577 spec
= g_object_class_find_property ((GObjectClass
*) YELP_VIEW_GET_CLASS (view
),
579 g_signal_emit_by_name (view
, "notify::root-title", spec
);
581 spec
= g_object_class_find_property ((GObjectClass
*) YELP_VIEW_GET_CLASS (view
),
583 g_signal_emit_by_name (view
, "notify::page-title", spec
);
585 spec
= g_object_class_find_property ((GObjectClass
*) YELP_VIEW_GET_CLASS (view
),
587 g_signal_emit_by_name (view
, "notify::page-desc", spec
);
589 spec
= g_object_class_find_property ((GObjectClass
*) YELP_VIEW_GET_CLASS (view
),
591 g_signal_emit_by_name (view
, "notify::page-icon", spec
);
593 gtk_action_set_sensitive (gtk_action_group_get_action (priv
->action_group
,
594 "YelpViewGoPrevious"),
596 gtk_action_set_sensitive (gtk_action_group_get_action (priv
->action_group
,
600 priv
->uri
= g_object_ref (uri
);
601 if (!yelp_uri_is_resolved (uri
)) {
602 priv
->uri_resolved
= g_signal_connect (uri
, "resolved",
603 G_CALLBACK (uri_resolved
),
605 yelp_uri_resolve (uri
);
608 uri_resolved (uri
, view
);
613 yelp_view_load_document (YelpView
*view
,
615 YelpDocument
*document
)
617 YelpViewPrivate
*priv
= GET_PRIV (view
);
619 g_return_if_fail (yelp_uri_is_resolved (uri
));
621 view_clear_load (view
);
622 g_object_set (view
, "state", YELP_VIEW_STATE_LOADING
, NULL
);
624 priv
->uri
= g_object_ref (uri
);
625 g_object_ref (document
);
627 g_object_unref (document
);
628 priv
->document
= document
;
630 view_load_page (view
);
634 yelp_view_get_document (YelpView
*view
)
636 YelpViewPrivate
*priv
= GET_PRIV (view
);
637 return priv
->document
;
641 yelp_view_get_action_group (YelpView
*view
)
643 YelpViewPrivate
*priv
= GET_PRIV (view
);
644 return priv
->action_group
;
647 /******************************************************************************/
650 yelp_view_add_link_action (YelpView
*view
,
652 YelpViewActionValidFunc func
,
655 YelpActionEntry
*entry
;
656 YelpViewPrivate
*priv
= GET_PRIV (view
);
658 entry
= g_new0 (YelpActionEntry
, 1);
659 entry
->action
= g_object_ref (action
);
663 priv
->link_actions
= g_slist_append (priv
->link_actions
, entry
);
667 yelp_view_get_active_link_uri (YelpView
*view
)
669 YelpViewPrivate
*priv
= GET_PRIV (view
);
672 if (g_str_has_prefix (priv
->popup_link_uri
, BOGUS_URI
))
673 uri
= yelp_uri_new_relative (priv
->uri
, priv
->popup_link_uri
+ BOGUS_URI_LEN
);
675 uri
= yelp_uri_new_relative (priv
->uri
, priv
->popup_link_uri
);
681 yelp_view_get_active_link_text (YelpView
*view
)
683 YelpViewPrivate
*priv
= GET_PRIV (view
);
684 return g_strdup (priv
->popup_link_text
);
687 /******************************************************************************/
690 view_scrolled (GtkAdjustment
*adjustment
,
693 YelpViewPrivate
*priv
= GET_PRIV (view
);
694 if (priv
->back_cur
== NULL
|| priv
->back_cur
->data
== NULL
)
696 if (adjustment
== priv
->vadjustment
)
697 ((YelpBackEntry
*) priv
->back_cur
->data
)->vadj
= gtk_adjustment_get_value (adjustment
);
698 else if (adjustment
= priv
->hadjustment
)
699 ((YelpBackEntry
*) priv
->back_cur
->data
)->hadj
= gtk_adjustment_get_value (adjustment
);
703 view_set_hadjustment (YelpView
*view
,
707 YelpViewPrivate
*priv
= GET_PRIV (view
);
708 priv
->hadjustment
= gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (view
));
709 if (priv
->hadjuster
> 0)
710 g_source_remove (priv
->hadjuster
);
712 if (priv
->hadjustment
)
713 priv
->hadjuster
= g_signal_connect (priv
->hadjustment
, "value-changed",
714 G_CALLBACK (view_scrolled
), view
);
718 view_set_vadjustment (YelpView
*view
,
722 YelpViewPrivate
*priv
= GET_PRIV (view
);
723 priv
->vadjustment
= gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (view
));
724 if (priv
->vadjuster
> 0)
725 g_source_remove (priv
->vadjuster
);
727 if (priv
->vadjustment
)
728 priv
->vadjuster
= g_signal_connect (priv
->vadjustment
, "value-changed",
729 G_CALLBACK (view_scrolled
), view
);
733 popup_open_link (GtkMenuItem
*item
,
736 YelpViewPrivate
*priv
= GET_PRIV (view
);
739 if (g_str_has_prefix (priv
->popup_link_uri
, BOGUS_URI
))
740 uri
= yelp_uri_new_relative (priv
->uri
, priv
->popup_link_uri
+ BOGUS_URI_LEN
);
742 uri
= yelp_uri_new_relative (priv
->uri
, priv
->popup_link_uri
);
744 yelp_view_load_uri (view
, uri
);
746 g_free (priv
->popup_link_uri
);
747 priv
->popup_link_uri
= NULL
;
749 g_free (priv
->popup_link_text
);
750 priv
->popup_link_text
= NULL
;
754 popup_open_link_new (GtkMenuItem
*item
,
757 YelpViewPrivate
*priv
= GET_PRIV (view
);
760 if (g_str_has_prefix (priv
->popup_link_uri
, BOGUS_URI
))
761 uri
= yelp_uri_new_relative (priv
->uri
, priv
->popup_link_uri
+ BOGUS_URI_LEN
);
763 uri
= yelp_uri_new_relative (priv
->uri
, priv
->popup_link_uri
);
765 g_free (priv
->popup_link_uri
);
766 priv
->popup_link_uri
= NULL
;
768 g_free (priv
->popup_link_text
);
769 priv
->popup_link_text
= NULL
;
771 g_signal_emit (view
, signals
[NEW_VIEW_REQUESTED
], 0, uri
);
772 g_object_unref (uri
);
775 typedef struct _YelpSaveData YelpSaveData
;
776 struct _YelpSaveData
{
784 file_copied (GFile
*file
,
788 GError
*error
= NULL
;
789 if (!g_file_copy_finish (file
, res
, &error
)) {
790 GtkWidget
*dialog
= gtk_message_dialog_new (gtk_widget_get_visible (GTK_WIDGET (data
->window
)) ? data
->window
: NULL
,
791 GTK_DIALOG_DESTROY_WITH_PARENT
,
794 "%s", error
->message
);
795 gtk_dialog_run (GTK_DIALOG (dialog
));
796 gtk_widget_destroy (dialog
);
798 g_object_unref (data
->orig
);
799 g_object_unref (data
->dest
);
800 g_object_unref (data
->view
);
801 g_object_unref (data
->window
);
805 popup_save_image (GtkMenuItem
*item
,
809 GtkWidget
*dialog
, *window
;
812 YelpViewPrivate
*priv
= GET_PRIV (view
);
814 for (window
= gtk_widget_get_parent (GTK_WIDGET (view
));
815 window
&& !GTK_IS_WINDOW (window
);
816 window
= gtk_widget_get_parent (window
));
818 data
= g_new0 (YelpSaveData
, 1);
819 data
->orig
= g_file_new_for_uri (priv
->popup_image_uri
);
820 data
->view
= g_object_ref (view
);
821 data
->window
= g_object_ref (window
);
822 g_free (priv
->popup_image_uri
);
823 priv
->popup_image_uri
= NULL
;
825 dialog
= gtk_file_chooser_dialog_new (_("Save Image"),
827 GTK_FILE_CHOOSER_ACTION_SAVE
,
828 GTK_STOCK_CANCEL
, GTK_RESPONSE_CANCEL
,
829 GTK_STOCK_SAVE
, GTK_RESPONSE_OK
,
831 gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (dialog
), TRUE
);
832 basename
= g_file_get_basename (data
->orig
);
833 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (dialog
), basename
);
835 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dialog
),
836 g_get_user_special_dir (G_USER_DIRECTORY_DESKTOP
));
838 res
= gtk_dialog_run (GTK_DIALOG (dialog
));
840 if (res
== GTK_RESPONSE_OK
) {
841 data
->dest
= gtk_file_chooser_get_file (GTK_FILE_CHOOSER (dialog
));
842 g_file_copy_async (data
->orig
, data
->dest
,
843 G_FILE_COPY_OVERWRITE
,
846 (GAsyncReadyCallback
) file_copied
,
850 g_object_unref (data
->orig
);
851 g_object_unref (data
->view
);
852 g_object_unref (data
->window
);
856 gtk_widget_destroy (dialog
);
860 popup_send_image (GtkMenuItem
*item
,
865 GAppLaunchContext
*context
;
866 GError
*error
= NULL
;
867 YelpViewPrivate
*priv
= GET_PRIV (view
);
869 command
= g_strdup_printf ("%s %s", nautilus_sendto
, priv
->popup_image_uri
);
870 context
= (GAppLaunchContext
*) gdk_app_launch_context_new ();
872 app
= g_app_info_create_from_commandline (command
, NULL
, 0, &error
);
874 g_app_info_launch (app
, NULL
, context
, &error
);
875 g_object_unref (app
);
879 g_debug ("Could not launch nautilus-sendto: %s", error
->message
);
880 g_error_free (error
);
883 g_object_unref (context
);
885 g_free (priv
->popup_image_uri
);
886 priv
->popup_image_uri
= NULL
;
890 popup_copy_code (GtkMenuItem
*item
,
893 YelpViewPrivate
*priv
= GET_PRIV (view
);
894 GtkClipboard
*clipboard
= gtk_clipboard_get (GDK_SELECTION_CLIPBOARD
);
895 gchar
*content
= webkit_dom_node_get_text_content (priv
->popup_code_node
);
896 gtk_clipboard_set_text (clipboard
, content
, -1);
901 popup_save_code (GtkMenuItem
*item
,
904 YelpViewPrivate
*priv
= GET_PRIV (view
);
905 GtkWidget
*dialog
, *window
;
908 g_free (priv
->popup_code_text
);
909 priv
->popup_code_text
= webkit_dom_node_get_text_content (priv
->popup_code_node
);
910 if (!g_str_has_suffix (priv
->popup_code_text
, "\n")) {
911 gchar
*tmp
= g_strconcat (priv
->popup_code_text
, "\n", NULL
);
912 g_free (priv
->popup_code_text
);
913 priv
->popup_code_text
= tmp
;
916 for (window
= gtk_widget_get_parent (GTK_WIDGET (view
));
917 window
&& !GTK_IS_WINDOW (window
);
918 window
= gtk_widget_get_parent (window
));
920 dialog
= gtk_file_chooser_dialog_new (_("Save Code"),
922 GTK_FILE_CHOOSER_ACTION_SAVE
,
923 GTK_STOCK_CANCEL
, GTK_RESPONSE_CANCEL
,
924 GTK_STOCK_SAVE
, GTK_RESPONSE_OK
,
926 gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (dialog
), TRUE
);
927 if (priv
->popup_code_title
) {
928 gchar
*filename
= webkit_dom_node_get_text_content (priv
->popup_code_title
);
929 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (dialog
), filename
);
932 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dialog
),
933 g_get_user_special_dir (G_USER_DIRECTORY_DOCUMENTS
));
935 res
= gtk_dialog_run (GTK_DIALOG (dialog
));
937 if (res
== GTK_RESPONSE_OK
) {
938 GError
*error
= NULL
;
939 GFile
*file
= gtk_file_chooser_get_file (GTK_FILE_CHOOSER (dialog
));
940 GFileOutputStream
*stream
= g_file_replace (file
, NULL
, FALSE
,
944 if (stream
== NULL
) {
945 GtkWidget
*dialog
= gtk_message_dialog_new (gtk_widget_get_visible (window
) ? GTK_WINDOW (window
) : NULL
,
946 GTK_DIALOG_DESTROY_WITH_PARENT
,
949 "%s", error
->message
);
950 gtk_dialog_run (GTK_DIALOG (dialog
));
951 gtk_widget_destroy (dialog
);
952 g_error_free (error
);
955 /* FIXME: we should do this async */
956 GDataOutputStream
*datastream
= g_data_output_stream_new (G_OUTPUT_STREAM (stream
));
957 if (!g_data_output_stream_put_string (datastream
, priv
->popup_code_text
, NULL
, &error
)) {
958 GtkWidget
*dialog
= gtk_message_dialog_new (gtk_widget_get_visible (window
) ? GTK_WINDOW (window
) : NULL
,
959 GTK_DIALOG_DESTROY_WITH_PARENT
,
962 "%s", error
->message
);
963 gtk_dialog_run (GTK_DIALOG (dialog
));
964 gtk_widget_destroy (dialog
);
965 g_error_free (error
);
967 g_object_unref (datastream
);
969 g_object_unref (file
);
972 priv
->popup_code_node
= NULL
;
973 priv
->popup_code_title
= NULL
;
974 g_free (priv
->popup_code_text
);
975 priv
->popup_code_text
= NULL
;
977 gtk_widget_destroy (dialog
);
981 view_populate_popup (YelpView
*view
,
985 WebKitHitTestResult
*result
;
986 WebKitHitTestResultContext context
;
988 YelpViewPrivate
*priv
= GET_PRIV (view
);
991 WebKitDOMNode
*node
, *cur
, *link_node
= NULL
, *code_node
= NULL
, *code_title_node
= NULL
;
993 children
= gtk_container_get_children (GTK_CONTAINER (menu
));
995 gtk_container_remove (GTK_CONTAINER (menu
),
996 GTK_WIDGET (children
->data
));
997 children
= children
->next
;
999 g_list_free (children
);
1001 event
= gtk_get_current_event ();
1003 result
= webkit_web_view_get_hit_test_result (WEBKIT_WEB_VIEW (view
), (GdkEventButton
*) event
);
1004 g_object_get (result
,
1005 "context", &context
,
1006 "inner-node", &node
,
1008 for (cur
= node
; cur
!= NULL
; cur
= webkit_dom_node_get_parent_node (cur
)) {
1009 if (WEBKIT_DOM_IS_ELEMENT (cur
) &&
1010 webkit_dom_element_webkit_matches_selector ((WebKitDOMElement
*) cur
,
1014 if (WEBKIT_DOM_IS_ELEMENT (cur
) &&
1015 webkit_dom_element_webkit_matches_selector ((WebKitDOMElement
*) cur
,
1016 "div.code", NULL
)) {
1017 WebKitDOMNode
*title
;
1018 code_node
= (WebKitDOMNode
*)
1019 webkit_dom_element_query_selector ((WebKitDOMElement
*) cur
,
1020 "pre.contents", NULL
);
1021 title
= webkit_dom_node_get_parent_node (cur
);
1022 if (title
!= NULL
&& WEBKIT_DOM_IS_ELEMENT (title
) &&
1023 webkit_dom_element_webkit_matches_selector ((WebKitDOMElement
*) title
,
1024 "div.contents", NULL
)) {
1025 title
= webkit_dom_node_get_previous_sibling (title
);
1026 if (title
!= NULL
&& WEBKIT_DOM_IS_ELEMENT (title
) &&
1027 webkit_dom_element_webkit_matches_selector ((WebKitDOMElement
*) title
,
1028 "div.title", NULL
)) {
1029 code_title_node
= title
;
1035 if (context
& WEBKIT_HIT_TEST_RESULT_CONTEXT_LINK
) {
1037 g_object_get (result
, "link-uri", &uri
, NULL
);
1038 g_free (priv
->popup_link_uri
);
1039 priv
->popup_link_uri
= uri
;
1041 g_free (priv
->popup_link_text
);
1042 priv
->popup_link_text
= NULL
;
1043 if (link_node
!= NULL
) {
1044 WebKitDOMNode
*child
;
1049 child
= (WebKitDOMNode
*)
1050 webkit_dom_element_query_selector (WEBKIT_DOM_ELEMENT (link_node
),
1051 "div.linkdiv div.title", NULL
);
1053 priv
->popup_link_text
= webkit_dom_node_get_text_content (child
);
1055 if (priv
->popup_link_text
== NULL
)
1056 priv
->popup_link_text
= webkit_dom_node_get_text_content (link_node
);
1058 tmp
= g_new0 (gchar
, strlen(priv
->popup_link_text
) + 1);
1060 for (i
= 0, tmpi
= 0; priv
->popup_link_text
[i
] != '\0'; i
++) {
1061 if (priv
->popup_link_text
[i
] == ' ' || priv
->popup_link_text
[i
] == '\n') {
1069 tmp
[tmpi
] = priv
->popup_link_text
[i
];
1075 g_free (priv
->popup_link_text
);
1076 priv
->popup_link_text
= tmp
;
1079 priv
->popup_link_text
= g_strdup (uri
);
1082 if (g_str_has_prefix (priv
->popup_link_uri
, "mailto:")) {
1083 /* Not using a mnemonic because underscores are common in email
1084 * addresses, and we'd have to escape them. There doesn't seem
1085 * to be a quick GTK+ function for this. In practice, there will
1086 * probably only be one menu item for mailto link popups anyway,
1087 * so the mnemonic's not that big of a deal.
1089 gchar
*label
= g_strdup_printf (_("Send email to %s"),
1090 priv
->popup_link_uri
+ 7);
1091 item
= gtk_menu_item_new_with_label (label
);
1092 g_signal_connect (item
, "activate",
1093 G_CALLBACK (popup_open_link
), view
);
1094 gtk_menu_shell_append (GTK_MENU_SHELL (menu
), item
);
1099 item
= gtk_menu_item_new_with_mnemonic (_("_Open Link"));
1100 g_signal_connect (item
, "activate",
1101 G_CALLBACK (popup_open_link
), view
);
1102 gtk_menu_shell_append (GTK_MENU_SHELL (menu
), item
);
1104 item
= gtk_menu_item_new_with_mnemonic (_("Open Link in New _Window"));
1105 g_signal_connect (item
, "activate",
1106 G_CALLBACK (popup_open_link_new
), view
);
1107 gtk_menu_shell_append (GTK_MENU_SHELL (menu
), item
);
1109 for (cur
= priv
->link_actions
; cur
!= NULL
; cur
= cur
->next
) {
1111 YelpActionEntry
*entry
= (YelpActionEntry
*) cur
->data
;
1112 if (entry
->func
== NULL
)
1115 add
= (* entry
->func
) (view
, entry
->action
,
1116 priv
->popup_link_uri
,
1119 item
= gtk_action_create_menu_item (entry
->action
);
1120 gtk_menu_shell_append (GTK_MENU_SHELL (menu
), item
);
1126 item
= gtk_action_create_menu_item (gtk_action_group_get_action (priv
->action_group
,
1128 gtk_menu_shell_append (GTK_MENU_SHELL (menu
), item
);
1129 item
= gtk_action_create_menu_item (gtk_action_group_get_action (priv
->action_group
,
1130 "YelpViewGoForward"));
1131 gtk_menu_shell_append (GTK_MENU_SHELL (menu
), item
);
1134 if ((context
& WEBKIT_HIT_TEST_RESULT_CONTEXT_IMAGE
) ||
1135 (context
& WEBKIT_HIT_TEST_RESULT_CONTEXT_MEDIA
)) {
1136 /* This doesn't currently work for video with automatic controls,
1137 * because WebKit puts the hit test on the div with the controls.
1139 gboolean image
= context
& WEBKIT_HIT_TEST_RESULT_CONTEXT_IMAGE
;
1141 g_object_get (result
, image
? "image-uri" : "media-uri", &uri
, NULL
);
1142 g_free (priv
->popup_image_uri
);
1143 if (g_str_has_prefix (uri
, BOGUS_URI
)) {
1144 priv
->popup_image_uri
= yelp_uri_locate_file_uri (priv
->uri
, uri
+ BOGUS_URI_LEN
);
1148 priv
->popup_image_uri
= uri
;
1151 item
= gtk_separator_menu_item_new ();
1152 gtk_menu_shell_append (GTK_MENU_SHELL (menu
), item
);
1155 item
= gtk_menu_item_new_with_mnemonic (_("_Save Image As..."));
1157 item
= gtk_menu_item_new_with_mnemonic (_("_Save Video As..."));
1158 g_signal_connect (item
, "activate",
1159 G_CALLBACK (popup_save_image
), view
);
1160 gtk_menu_shell_append (GTK_MENU_SHELL (menu
), item
);
1162 if (nautilus_sendto
) {
1164 item
= gtk_menu_item_new_with_mnemonic (_("S_end Image To..."));
1166 item
= gtk_menu_item_new_with_mnemonic (_("S_end Video To..."));
1167 g_signal_connect (item
, "activate",
1168 G_CALLBACK (popup_send_image
), view
);
1169 gtk_menu_shell_append (GTK_MENU_SHELL (menu
), item
);
1173 if (context
& WEBKIT_HIT_TEST_RESULT_CONTEXT_SELECTION
) {
1174 item
= gtk_separator_menu_item_new ();
1175 gtk_menu_shell_append (GTK_MENU_SHELL (menu
), item
);
1177 item
= gtk_menu_item_new_with_mnemonic (_("_Copy Text"));
1178 g_signal_connect_swapped (item
, "activate",
1179 G_CALLBACK (webkit_web_view_copy_clipboard
), view
);
1180 gtk_menu_shell_append (GTK_MENU_SHELL (menu
), item
);
1183 if (code_node
!= NULL
) {
1184 item
= gtk_separator_menu_item_new ();
1185 gtk_menu_shell_append (GTK_MENU_SHELL (menu
), item
);
1187 priv
->popup_code_node
= code_node
;
1188 priv
->popup_code_title
= code_title_node
;
1190 item
= gtk_menu_item_new_with_mnemonic (_("C_opy Code Block"));
1191 g_signal_connect (item
, "activate",
1192 G_CALLBACK (popup_copy_code
), view
);
1193 gtk_menu_shell_append (GTK_MENU_SHELL (menu
), item
);
1195 item
= gtk_menu_item_new_with_mnemonic (_("Save Code _Block As..."));
1196 g_signal_connect (item
, "activate",
1197 G_CALLBACK (popup_save_code
), view
);
1198 gtk_menu_shell_append (GTK_MENU_SHELL (menu
), item
);
1201 g_object_unref (result
);
1202 gdk_event_free (event
);
1203 gtk_widget_show_all (GTK_WIDGET (menu
));
1207 view_script_alert (YelpView
*view
,
1208 WebKitWebFrame
*frame
,
1212 printf ("\n\n===ALERT===\n%s\n\n", message
);
1216 view_navigation_requested (WebKitWebView
*view
,
1217 WebKitWebFrame
*frame
,
1218 WebKitNetworkRequest
*request
,
1219 WebKitWebNavigationAction
*action
,
1220 WebKitWebPolicyDecision
*decision
,
1223 const gchar
*requri
= webkit_network_request_get_uri (request
);
1224 YelpViewPrivate
*priv
= GET_PRIV (view
);
1227 if (g_str_has_prefix (requri
, BOGUS_URI
))
1228 uri
= yelp_uri_new_relative (priv
->uri
, requri
+ BOGUS_URI_LEN
);
1230 uri
= yelp_uri_new_relative (priv
->uri
, requri
);
1232 webkit_web_policy_decision_ignore (decision
);
1234 yelp_view_load_uri ((YelpView
*) view
, uri
);
1240 view_resource_request (WebKitWebView
*view
,
1241 WebKitWebFrame
*frame
,
1242 WebKitWebResource
*resource
,
1243 WebKitNetworkRequest
*request
,
1244 WebKitNetworkResponse
*response
,
1247 YelpViewPrivate
*priv
= GET_PRIV (view
);
1248 const gchar
*requri
= webkit_network_request_get_uri (request
);
1252 if (!g_str_has_prefix (requri
, BOGUS_URI
))
1255 /* We get this signal for the page itself. Ignore. */
1256 if (g_str_equal (requri
, priv
->bogus_uri
))
1259 newpath
= yelp_uri_locate_file_uri (priv
->uri
, requri
+ BOGUS_URI_LEN
);
1260 if (newpath
!= NULL
) {
1261 webkit_network_request_set_uri (request
, newpath
);
1265 webkit_network_request_set_uri (request
, "about:blank");
1270 view_print (GtkAction
*action
, YelpView
*view
)
1272 webkit_web_frame_print (webkit_web_view_get_main_frame (WEBKIT_WEB_VIEW (view
)));
1276 view_history_action (GtkAction
*action
,
1280 YelpViewPrivate
*priv
= GET_PRIV (view
);
1282 if (priv
->back_cur
== NULL
)
1285 if (g_str_equal (gtk_action_get_name (action
), "YelpViewGoBack"))
1286 newcur
= priv
->back_cur
->next
;
1288 newcur
= priv
->back_cur
->prev
;
1293 priv
->back_cur
= newcur
;
1295 if (priv
->back_cur
->data
== NULL
)
1298 priv
->back_load
= TRUE
;
1299 yelp_view_load_uri (view
, ((YelpBackEntry
*) priv
->back_cur
->data
)->uri
);
1300 priv
->vadjust
= ((YelpBackEntry
*) priv
->back_cur
->data
)->vadj
;
1301 priv
->hadjust
= ((YelpBackEntry
*) priv
->back_cur
->data
)->hadj
;
1305 view_navigation_action (GtkAction
*action
,
1308 YelpViewPrivate
*priv
= GET_PRIV (view
);
1309 gchar
*page_id
, *new_id
, *xref
;
1312 page_id
= yelp_uri_get_page_id (priv
->uri
);
1314 if (g_str_equal (gtk_action_get_name (action
), "YelpViewGoPrevious"))
1315 new_id
= yelp_document_get_prev_id (priv
->document
, page_id
);
1317 new_id
= yelp_document_get_next_id (priv
->document
, page_id
);
1319 /* Just in case we screwed up somewhere */
1320 if (new_id
== NULL
) {
1321 gtk_action_set_sensitive (action
, FALSE
);
1325 xref
= g_strconcat ("xref:", new_id
, NULL
);
1326 new_uri
= yelp_uri_new_relative (priv
->uri
, xref
);
1327 yelp_view_load_uri (view
, new_uri
);
1331 g_object_unref (new_uri
);
1335 view_clear_load (YelpView
*view
)
1337 YelpViewPrivate
*priv
= GET_PRIV (view
);
1340 if (priv
->uri_resolved
!= 0) {
1341 g_signal_handler_disconnect (priv
->uri
, priv
->uri_resolved
);
1342 priv
->uri_resolved
= 0;
1344 g_object_unref (priv
->uri
);
1348 if (priv
->cancellable
) {
1349 g_cancellable_cancel (priv
->cancellable
);
1350 priv
->cancellable
= NULL
;
1355 view_load_page (YelpView
*view
)
1357 YelpViewPrivate
*priv
= GET_PRIV (view
);
1360 debug_print (DB_FUNCTION
, "entering\n");
1362 g_return_if_fail (priv
->cancellable
== NULL
);
1364 if (priv
->document
== NULL
) {
1367 /* FIXME: and if priv->uri is NULL? */
1368 docuri
= yelp_uri_get_document_uri (priv
->uri
);
1369 /* FIXME: CANT_READ isn't right */
1371 error
= g_error_new (YELP_ERROR
, YELP_ERROR_CANT_READ
,
1372 _("Could not load a document for ‘%s’"),
1377 error
= g_error_new (YELP_ERROR
, YELP_ERROR_CANT_READ
,
1378 _("Could not load a document"));
1380 view_show_error_page (view
, error
);
1384 page_id
= yelp_uri_get_page_id (priv
->uri
);
1385 priv
->cancellable
= g_cancellable_new ();
1386 yelp_document_request_page (priv
->document
,
1389 (YelpDocumentCallback
) document_callback
,
1395 view_show_error_page (YelpView
*view
,
1398 YelpViewPrivate
*priv
= GET_PRIV (view
);
1399 static const gchar
*errorpage
=
1401 "<style type='text/css'>"
1405 " background-color: %s;"
1409 " border-color: %s;"
1410 " border-top: solid 1px;"
1411 " border-bottom: solid 1px;"
1412 " background-color: %s;"
1414 "div.note div.inner {"
1415 " margin: 0; padding: 0;"
1416 " background-image: url(%s);"
1417 " background-position: %s top;"
1418 " background-repeat: no-repeat;"
1419 " min-height: %ipx;"
1421 "div.note div.contents {"
1424 "div.note div.title {"
1426 " margin-bottom: 0.2em;"
1427 " font-weight: bold;"
1432 "<div class='note'><div class='inner'>"
1433 "<div class='title'>%s</div>"
1434 "<div class='contents'>%s</div>"
1437 YelpSettings
*settings
= yelp_settings_get_default ();
1438 gchar
*page
, *title
= NULL
;
1439 gchar
*textcolor
, *bgcolor
, *noteborder
, *notebg
, *titlecolor
, *noteicon
;
1441 const gchar
*left
= (gtk_widget_get_direction((GtkWidget
*) view
) == GTK_TEXT_DIR_RTL
) ? "right" : "left";
1442 if (error
->domain
== YELP_ERROR
)
1443 switch (error
->code
) {
1444 case YELP_ERROR_NOT_FOUND
:
1445 title
= _("Not Found");
1447 case YELP_ERROR_CANT_READ
:
1448 title
= _("Cannot Read");
1454 title
= _("Unknown Error");
1455 textcolor
= yelp_settings_get_color (settings
, YELP_SETTINGS_COLOR_TEXT
);
1456 bgcolor
= yelp_settings_get_color (settings
, YELP_SETTINGS_COLOR_BASE
);
1457 noteborder
= yelp_settings_get_color (settings
, YELP_SETTINGS_COLOR_RED_BORDER
);
1458 notebg
= yelp_settings_get_color (settings
, YELP_SETTINGS_COLOR_YELLOW_BASE
);
1459 titlecolor
= yelp_settings_get_color (settings
, YELP_SETTINGS_COLOR_TEXT_LIGHT
);
1460 noteicon
= yelp_settings_get_icon (settings
, YELP_SETTINGS_ICON_WARNING
);
1461 iconsize
= yelp_settings_get_icon_size (settings
) + 6;
1462 page
= g_strdup_printf (errorpage
,
1463 textcolor
, bgcolor
, noteborder
, notebg
, noteicon
,
1464 left
, iconsize
, left
, iconsize
, left
, iconsize
,
1465 titlecolor
, title
, error
->message
);
1466 g_object_set (view
, "state", YELP_VIEW_STATE_ERROR
, NULL
);
1467 g_signal_handler_block (view
, priv
->navigation_requested
);
1468 webkit_web_view_load_string (WEBKIT_WEB_VIEW (view
),
1473 g_signal_handler_unblock (view
, priv
->navigation_requested
);
1474 g_error_free (error
);
1480 settings_set_fonts (YelpSettings
*settings
)
1485 g_object_set (websettings
,
1486 "default-encoding", "utf-8",
1487 "enable-private-browsing", TRUE
,
1490 family
= yelp_settings_get_font_family (settings
,
1491 YELP_SETTINGS_FONT_VARIABLE
);
1492 size
= yelp_settings_get_font_size (settings
,
1493 YELP_SETTINGS_FONT_VARIABLE
);
1494 g_object_set (websettings
,
1495 "default-font-family", family
,
1496 "sans-serif-font-family", family
,
1497 "default-font-size", size
,
1501 family
= yelp_settings_get_font_family (settings
,
1502 YELP_SETTINGS_FONT_FIXED
);
1503 size
= yelp_settings_get_font_size (settings
,
1504 YELP_SETTINGS_FONT_FIXED
);
1505 g_object_set (websettings
,
1506 "monospace-font-family", family
,
1507 "default-monospace-font-size", size
,
1513 settings_show_text_cursor (YelpSettings
*settings
)
1515 g_object_set (websettings
,
1516 "enable-caret-browsing",
1517 yelp_settings_get_show_text_cursor (settings
),
1521 /******************************************************************************/
1524 uri_resolved (YelpUri
*uri
,
1527 YelpViewPrivate
*priv
= GET_PRIV (view
);
1528 YelpDocument
*document
;
1529 YelpBackEntry
*back
;
1531 GSList
*proxies
, *cur
;
1536 debug_print (DB_FUNCTION
, "entering\n");
1538 switch (yelp_uri_get_document_type (uri
)) {
1539 case YELP_URI_DOCUMENT_TYPE_EXTERNAL
:
1540 g_signal_emit (view
, signals
[EXTERNAL_URI
], 0, uri
);
1542 case YELP_URI_DOCUMENT_TYPE_NOT_FOUND
:
1543 struri
= yelp_uri_get_canonical_uri (uri
);
1544 if (struri
!= NULL
) {
1545 error
= g_error_new (YELP_ERROR
, YELP_ERROR_NOT_FOUND
,
1546 _("The URI ‘%s’ does not point to a valid page."),
1551 error
= g_error_new (YELP_ERROR
, YELP_ERROR_NOT_FOUND
,
1552 _("The URI does not point to a valid page."),
1555 view_show_error_page (view
, error
);
1557 case YELP_URI_DOCUMENT_TYPE_ERROR
:
1558 struri
= yelp_uri_get_canonical_uri (uri
);
1559 error
= g_error_new (YELP_ERROR
, YELP_ERROR_PROCESSING
,
1560 _("The URI ‘%s’ could not be parsed."),
1563 view_show_error_page (view
, error
);
1569 document
= yelp_document_get_for_uri (uri
);
1571 g_object_unref (priv
->document
);
1572 priv
->document
= document
;
1574 if (!priv
->back_load
) {
1575 back
= g_new0 (YelpBackEntry
, 1);
1576 back
->uri
= g_object_ref (uri
);
1577 while (priv
->back_list
!= priv
->back_cur
) {
1578 back_entry_free ((YelpBackEntry
*) priv
->back_list
->data
);
1579 priv
->back_list
= g_list_delete_link (priv
->back_list
, priv
->back_list
);
1581 priv
->back_list
= g_list_prepend (priv
->back_list
, back
);
1582 priv
->back_cur
= priv
->back_list
;
1584 priv
->back_load
= FALSE
;
1586 action
= gtk_action_group_get_action (priv
->action_group
, "YelpViewGoBack");
1587 gtk_action_set_sensitive (action
, FALSE
);
1588 proxies
= gtk_action_get_proxies (action
);
1589 if (priv
->back_cur
->next
&& priv
->back_cur
->next
->data
) {
1590 gchar
*tooltip
= "";
1591 back
= priv
->back_cur
->next
->data
;
1593 gtk_action_set_sensitive (action
, TRUE
);
1594 if (back
->title
&& back
->desc
) {
1596 color
= yelp_settings_get_color (yelp_settings_get_default (),
1597 YELP_SETTINGS_COLOR_TEXT_LIGHT
);
1598 tooltip
= g_markup_printf_escaped ("<span size='larger'>%s</span>\n<span color='%s'>%s</span>",
1599 back
->title
, color
, back
->desc
);
1602 else if (back
->title
)
1603 tooltip
= g_markup_printf_escaped ("<span size='larger'>%s</span>",
1605 /* Can't seem to use markup on GtkAction tooltip */
1606 for (cur
= proxies
; cur
!= NULL
; cur
= cur
->next
)
1607 gtk_widget_set_tooltip_markup (GTK_WIDGET (cur
->data
), tooltip
);
1610 for (cur
= proxies
; cur
!= NULL
; cur
= cur
->next
)
1611 gtk_widget_set_tooltip_text (GTK_WIDGET (cur
->data
), "");
1614 action
= gtk_action_group_get_action (priv
->action_group
, "YelpViewGoForward");
1615 gtk_action_set_sensitive (action
, FALSE
);
1616 proxies
= gtk_action_get_proxies (action
);
1617 if (priv
->back_cur
->prev
&& priv
->back_cur
->prev
->data
) {
1618 gchar
*tooltip
= "";
1619 back
= priv
->back_cur
->prev
->data
;
1621 gtk_action_set_sensitive (action
, TRUE
);
1622 if (back
->title
&& back
->desc
) {
1624 color
= yelp_settings_get_color (yelp_settings_get_default (),
1625 YELP_SETTINGS_COLOR_TEXT_LIGHT
);
1626 tooltip
= g_markup_printf_escaped ("<span size='larger'>%s</span>\n<span color='%s'>%s</span>",
1627 back
->title
, color
, back
->desc
);
1630 else if (back
->title
)
1631 tooltip
= g_markup_printf_escaped ("<span size='larger'>%s</span>",
1633 /* Can't seem to use markup on GtkAction tooltip */
1634 for (cur
= proxies
; cur
!= NULL
; cur
= cur
->next
)
1635 gtk_widget_set_tooltip_markup (GTK_WIDGET (cur
->data
), tooltip
);
1638 for (cur
= proxies
; cur
!= NULL
; cur
= cur
->next
)
1639 gtk_widget_set_tooltip_text (GTK_WIDGET (cur
->data
), "");
1642 spec
= g_object_class_find_property ((GObjectClass
*) YELP_VIEW_GET_CLASS (view
),
1644 g_signal_emit_by_name (view
, "notify::yelp-uri", spec
);
1646 g_free (priv
->page_id
);
1647 priv
->page_id
= yelp_uri_get_page_id (priv
->uri
);
1648 spec
= g_object_class_find_property ((GObjectClass
*) YELP_VIEW_GET_CLASS (view
),
1650 g_signal_emit_by_name (view
, "notify::page-id", spec
);
1652 view_load_page (view
);
1656 document_callback (YelpDocument
*document
,
1657 YelpDocumentSignal signal
,
1661 YelpViewPrivate
*priv
= GET_PRIV (view
);
1663 debug_print (DB_FUNCTION
, "entering\n");
1665 if (signal
== YELP_DOCUMENT_SIGNAL_INFO
) {
1666 gchar
*prev_id
, *next_id
, *real_id
;
1668 YelpBackEntry
*back
= NULL
;
1671 real_id
= yelp_document_get_page_id (document
, priv
->page_id
);
1672 if (priv
->page_id
&& real_id
&& g_str_equal (real_id
, priv
->page_id
)) {
1677 g_free (priv
->page_id
);
1678 priv
->page_id
= real_id
;
1679 spec
= g_object_class_find_property ((GObjectClass
*) YELP_VIEW_GET_CLASS (view
),
1681 g_signal_emit_by_name (view
, "notify::page-id", spec
);
1684 g_free (priv
->root_title
);
1685 g_free (priv
->page_title
);
1686 g_free (priv
->page_desc
);
1687 g_free (priv
->page_icon
);
1689 priv
->root_title
= yelp_document_get_root_title (document
, priv
->page_id
);
1690 priv
->page_title
= yelp_document_get_page_title (document
, priv
->page_id
);
1691 priv
->page_desc
= yelp_document_get_page_desc (document
, priv
->page_id
);
1692 priv
->page_icon
= yelp_document_get_page_icon (document
, priv
->page_id
);
1695 back
= priv
->back_cur
->data
;
1697 g_free (back
->title
);
1698 back
->title
= g_strdup (priv
->page_title
);
1699 g_free (back
->desc
);
1700 back
->desc
= g_strdup (priv
->page_desc
);
1703 prev_id
= yelp_document_get_prev_id (document
, priv
->page_id
);
1704 action
= gtk_action_group_get_action (priv
->action_group
, "YelpViewGoPrevious");
1705 gtk_action_set_sensitive (action
, prev_id
!= NULL
);
1708 next_id
= yelp_document_get_next_id (document
, priv
->page_id
);
1709 action
= gtk_action_group_get_action (priv
->action_group
, "YelpViewGoNext");
1710 gtk_action_set_sensitive (action
, next_id
!= NULL
);
1713 spec
= g_object_class_find_property ((GObjectClass
*) YELP_VIEW_GET_CLASS (view
),
1715 g_signal_emit_by_name (view
, "notify::root-title", spec
);
1717 spec
= g_object_class_find_property ((GObjectClass
*) YELP_VIEW_GET_CLASS (view
),
1719 g_signal_emit_by_name (view
, "notify::page-title", spec
);
1721 spec
= g_object_class_find_property ((GObjectClass
*) YELP_VIEW_GET_CLASS (view
),
1723 g_signal_emit_by_name (view
, "notify::page-desc", spec
);
1725 spec
= g_object_class_find_property ((GObjectClass
*) YELP_VIEW_GET_CLASS (view
),
1727 g_signal_emit_by_name (view
, "notify::page-icon", spec
);
1729 else if (signal
== YELP_DOCUMENT_SIGNAL_CONTENTS
) {
1730 YelpUriDocumentType doctype
;
1731 const gchar
*contents
;
1732 gchar
*mime_type
, *page_id
, *frag_id
, *full_uri
;
1733 page_id
= yelp_uri_get_page_id (priv
->uri
);
1734 debug_print (DB_ARG
, " document.uri.page_id=\"%s\"\n", page_id
);
1735 mime_type
= yelp_document_get_mime_type (document
, page_id
);
1736 contents
= yelp_document_read_contents (document
, page_id
);
1737 frag_id
= yelp_uri_get_frag_id (priv
->uri
);
1738 g_free (priv
->bogus_uri
);
1739 /* We don't have actual page and frag IDs for DocBook. We just map IDs
1740 of block elements. The result is that we get xref:someid#someid.
1741 If someid is really the page ID, we just drop the frag reference.
1742 Otherwise, normal page views scroll past the link trail.
1744 if (frag_id
!= NULL
) {
1745 if (YELP_IS_DOCBOOK_DOCUMENT (document
)) {
1746 gchar
*real_id
= yelp_document_get_page_id (document
, page_id
);
1747 if (g_str_equal (real_id
, frag_id
)) {
1754 /* We have to give WebKit a URI in a scheme it understands, otherwise we
1755 won't get the resource-request-starting signal. So we can't use the
1756 canonical URI, because it might be something like ghelp. We also have
1757 to give it something unique, because WebKit ignores our load_string
1758 call if the URI isn't different. We could try to construct something
1759 based on actual file locations, but in fact it doesn't matter. So
1760 we just make a bogus URI that's easy to process later.
1762 doctype
= yelp_uri_get_document_type (priv
->uri
);
1763 full_uri
= yelp_uri_get_canonical_uri (priv
->uri
);
1764 if (g_str_has_prefix (full_uri
, "file:/") &&
1765 (doctype
== YELP_URI_DOCUMENT_TYPE_TEXT
||
1766 doctype
== YELP_URI_DOCUMENT_TYPE_HTML
||
1767 doctype
== YELP_URI_DOCUMENT_TYPE_XHTML
)) {
1768 priv
->bogus_uri
= full_uri
;
1772 if (frag_id
!= NULL
)
1773 priv
->bogus_uri
= g_strdup_printf ("%s%p#%s", BOGUS_URI
, priv
->uri
, frag_id
);
1775 priv
->bogus_uri
= g_strdup_printf ("%s%p", BOGUS_URI
, priv
->uri
);
1777 g_signal_handler_block (view
, priv
->navigation_requested
);
1778 webkit_web_view_load_string (WEBKIT_WEB_VIEW (view
),
1783 g_signal_handler_unblock (view
, priv
->navigation_requested
);
1784 g_object_set (view
, "state", YELP_VIEW_STATE_LOADED
, NULL
);
1786 /* If we need to set the GtkAdjustment or trigger the page title
1787 * from what WebKit thinks it is (see comment below), we need to
1788 * let the main loop run through.
1790 if (priv
->vadjust
> 0 || priv
->hadjust
> 0 || priv
->page_title
== NULL
)
1791 while (g_main_context_pending (NULL
))
1792 g_main_context_iteration (NULL
, FALSE
);
1794 /* Setting adjustments only work after the page is loaded. These
1795 * are set by view_history_action, and they're reset to 0 after
1798 if (priv
->vadjust
> 0) {
1799 if (priv
->vadjustment
)
1800 gtk_adjustment_set_value (priv
->vadjustment
, priv
->vadjust
);
1803 if (priv
->hadjust
> 0) {
1804 if (priv
->hadjustment
)
1805 gtk_adjustment_set_value (priv
->hadjustment
, priv
->hadjust
);
1809 /* If the document didn't give us a page title, get it from WebKit.
1810 * We let the main loop run through so that WebKit gets the title
1811 * set so that we can send notify::page-title before loaded. It
1812 * simplifies things if YelpView consumers can assume the title
1813 * is set before loaded is triggered.
1815 if (priv
->page_title
== NULL
) {
1817 priv
->page_title
= g_strdup (webkit_web_view_get_title (WEBKIT_WEB_VIEW (view
)));
1818 spec
= g_object_class_find_property ((GObjectClass
*) YELP_VIEW_GET_CLASS (view
),
1820 g_signal_emit_by_name (view
, "notify::page-title", spec
);
1826 yelp_document_finish_read (document
, contents
);
1827 g_signal_emit (view
, signals
[LOADED
], 0);
1829 else if (signal
== YELP_DOCUMENT_SIGNAL_ERROR
) {
1830 view_show_error_page (view
, error
);