|
@@ -9,13 +9,26 @@ namespace PpubViewer {
|
|
|
private Adw.HeaderBar header_bar;
|
|
|
private Box box;
|
|
|
private Flap flap;
|
|
|
+ private ClampScrollable clamp;
|
|
|
private GtkCommonMark.MarkdownView markdown_view;
|
|
|
+ private ScrolledWindow scrolled_window;
|
|
|
+
|
|
|
+ private Button back_button;
|
|
|
+ private Button forward_button;
|
|
|
+ private Button properties_button;
|
|
|
+
|
|
|
+ private Button menu_button;
|
|
|
+ private PopoverMenu menu;
|
|
|
+ private SpinButton zoom_spin;
|
|
|
|
|
|
private Ppub.Publication publication;
|
|
|
+ private NavigationHistoryItem current_page = null;
|
|
|
+ private WindowTitle window_title;
|
|
|
|
|
|
public ViewerWindow(Adw.Application app) {
|
|
|
application = app;
|
|
|
header_bar = new Adw.HeaderBar ();
|
|
|
+ window_title = new WindowTitle("PPUB Viewer", "");
|
|
|
flap = new Flap();
|
|
|
box = new Box (Orientation.VERTICAL, 0);
|
|
|
box.append (header_bar);
|
|
@@ -24,25 +37,120 @@ namespace PpubViewer {
|
|
|
header_bar.show_end_title_buttons = true;
|
|
|
default_width = 600;
|
|
|
default_height = 400;
|
|
|
+ header_bar.title_widget = window_title;
|
|
|
+ flap.separator = new Separator(Orientation.VERTICAL);
|
|
|
+
|
|
|
+ properties_button = new Button.from_icon_name("sidebar-show-symbolic");
|
|
|
+ header_bar.pack_start(properties_button);
|
|
|
+
|
|
|
+ header_bar.pack_start(new Separator(Orientation.VERTICAL));
|
|
|
+
|
|
|
+ back_button = new Button.from_icon_name("go-previous-symbolic");
|
|
|
+ back_button.sensitive = false;
|
|
|
+ back_button.clicked.connect(go_back);
|
|
|
+ header_bar.pack_start(back_button);
|
|
|
+ forward_button = new Button.from_icon_name("go-next-symbolic");
|
|
|
+ forward_button.sensitive = false;
|
|
|
+ forward_button.clicked.connect(go_forward);
|
|
|
+ header_bar.pack_start(forward_button);
|
|
|
+
|
|
|
+ menu = new PopoverMenu.from_model(null);
|
|
|
+ menu_button = new Button.from_icon_name("open-menu-symbolic");
|
|
|
+ menu_button.clicked.connect(() => menu.popdown());
|
|
|
+ header_bar.pack_end(menu_button);
|
|
|
+ var zoom_adjustment = new Adjustment(100, 80, 300, 10, 50, 0);
|
|
|
+ zoom_adjustment.value_changed.connect(change_zoom);
|
|
|
+ zoom_spin = new SpinButton(zoom_adjustment, 1, 0);
|
|
|
+ header_bar.pack_end(zoom_spin);
|
|
|
+
|
|
|
|
|
|
- var scrolled_window = new ScrolledWindow();
|
|
|
+ scrolled_window = new ScrolledWindow();
|
|
|
scrolled_window.set_policy (PolicyType.NEVER, PolicyType.AUTOMATIC);
|
|
|
scrolled_window.vexpand = true;
|
|
|
- var clamp = new ClampScrollable ();
|
|
|
- markdown_view = new GtkCommonMark.MarkdownView ();
|
|
|
+ clamp = new ClampScrollable ();
|
|
|
clamp.maximum_size = 800;
|
|
|
+ scrolled_window.child = clamp;
|
|
|
+
|
|
|
+ var status_page = new StatusPage();
|
|
|
+ status_page.vexpand = true;
|
|
|
+ status_page.title = "PPUB Viewer";
|
|
|
+ status_page.description = "Open a PPUB to view it here";
|
|
|
+ status_page.icon_name = "text-x-generic-symbolic";
|
|
|
+ flap.content = status_page;
|
|
|
+
|
|
|
+ markdown_view = new GtkCommonMark.MarkdownView ();
|
|
|
markdown_view.set_wrap_mode (WrapMode.WORD_CHAR);
|
|
|
markdown_view.widget_embedded.connect(widget_embedded);
|
|
|
+ markdown_view.link_activated.connect(link_clicked);
|
|
|
clamp.child = markdown_view;
|
|
|
- scrolled_window.child = clamp;
|
|
|
- flap.content = scrolled_window;
|
|
|
+
|
|
|
+ Gtk.Settings.get_default().notify["gtk-application-prefer-dark-theme"].connect(() => configure_tags());
|
|
|
+
|
|
|
+ properties_button.clicked.connect(() => {
|
|
|
+ flap.reveal_flap = !flap.reveal_flap;
|
|
|
+ });
|
|
|
|
|
|
}
|
|
|
|
|
|
public void load_ppub(File file) throws Error {
|
|
|
publication = new Ppub.Publication(file.get_path());
|
|
|
var asset = publication.get_default_asset ();
|
|
|
- markdown_view.load_from_stream (publication.read_asset (asset.name));
|
|
|
+
|
|
|
+ window_title.title = publication.metadata.title ?? "Untitled PPUB";
|
|
|
+ window_title.subtitle = publication.metadata.author_name ?? "";
|
|
|
+ title = window_title.title + " - PPUB Viewer";
|
|
|
+
|
|
|
+ var scroller = new ScrolledWindow();
|
|
|
+ scroller.add_css_class("background");
|
|
|
+ scroller.hscrollbar_policy = PolicyType.NEVER;
|
|
|
+ var pane = new PropertiesPane(publication.metadata, false);
|
|
|
+ scroller.child = pane;
|
|
|
+ pane.link_clicked.connect(property_link_clicked);
|
|
|
+
|
|
|
+ flap.flap = scroller;
|
|
|
+ navigate(asset.name);
|
|
|
+ }
|
|
|
+
|
|
|
+ public void navigate(string file) {
|
|
|
+
|
|
|
+ if(current_page == null) {
|
|
|
+ current_page = new NavigationHistoryItem();
|
|
|
+ current_page.file_name = file;
|
|
|
+ current_page.scroll_position = 0;
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ current_page = current_page.add_next(file, scrolled_window.vadjustment.get_value());
|
|
|
+ }
|
|
|
+
|
|
|
+ load_document(file);
|
|
|
+ }
|
|
|
+
|
|
|
+ public void go_back() {
|
|
|
+ current_page.scroll_position = scrolled_window.vadjustment.get_value();
|
|
|
+ current_page = current_page.previous;
|
|
|
+ load_document(current_page.file_name);
|
|
|
+ }
|
|
|
+
|
|
|
+ public void go_forward() {
|
|
|
+ current_page.scroll_position = scrolled_window.vadjustment.get_value();
|
|
|
+ current_page = current_page.next;
|
|
|
+ load_document(current_page.file_name);
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ public void load_document(string file) {
|
|
|
+
|
|
|
+ back_button.sensitive = current_page.previous != null;
|
|
|
+ forward_button.sensitive = current_page.next != null;
|
|
|
+
|
|
|
+ flap.content = scrolled_window;
|
|
|
+ markdown_view.buffer.set_text("", 0);
|
|
|
+ markdown_view.load_from_stream (publication.read_asset (file));
|
|
|
+ Idle.add_once(() => {
|
|
|
+ configure_tags();
|
|
|
+ scrolled_window.vadjustment.set_value(current_page.scroll_position);
|
|
|
+ return false;
|
|
|
+ });
|
|
|
}
|
|
|
|
|
|
private void widget_embedded(GtkCommonMark.MarkdownViewEmbeddedWidgetHost widget, string file, string title) {
|
|
@@ -57,7 +165,73 @@ namespace PpubViewer {
|
|
|
widget.append (image);
|
|
|
}
|
|
|
|
|
|
+ private void link_clicked(string url) {
|
|
|
+
|
|
|
+ var asset = publication.get_asset(Uri.unescape_string(url));
|
|
|
+ if(asset != null) {
|
|
|
+ navigate(asset.name);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ Uri uri;
|
|
|
+
|
|
|
+ try{
|
|
|
+ uri = Uri.parse(url, UriFlags.PARSE_RELAXED);
|
|
|
+ }
|
|
|
+ catch {
|
|
|
+ var dialog = new Adw.MessageDialog(this, "Unrecognised link", @"Could not determine how to handle the link <i>$url</i>.");
|
|
|
+ dialog.body_use_markup = true;
|
|
|
+ dialog.add_response("close", "Close");
|
|
|
+ dialog.present();
|
|
|
+ dialog.response.connect(() => dialog.close());
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ var app = AppInfo.get_default_for_uri_scheme(uri.get_scheme());
|
|
|
+ if(app == null) {
|
|
|
+ var dialog = new Adw.MessageDialog(this, "No application", @"Could not find an application to handle the URI <i>$uri</i>.");
|
|
|
+ dialog.body_use_markup = true;
|
|
|
+ dialog.add_response("close", "Close");
|
|
|
+ dialog.present();
|
|
|
+ dialog.response.connect(() => dialog.close());
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ var uris = new List<string>();
|
|
|
+ uris.append(uri.to_string());
|
|
|
+
|
|
|
+ var dialog = new Adw.MessageDialog(this, "Open external link?", @"Do you want to open the external link <i>$uri</i>?");
|
|
|
+ dialog.body_use_markup = true;
|
|
|
+ dialog.add_response("cancel", @"Cancel");
|
|
|
+ dialog.add_response("open", @"Open in $(app.get_display_name())");
|
|
|
+ dialog.present();
|
|
|
+
|
|
|
+ dialog.response.connect(id => {
|
|
|
+ if(id == "open") {
|
|
|
+ app.launch_uris(uris, null);
|
|
|
+ }
|
|
|
+ dialog.close();
|
|
|
+ });
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ private void configure_tags() {
|
|
|
+ var link = new LinkButton("");
|
|
|
+ markdown_view.tag_manager.update_link_colour(link.get_color());
|
|
|
+ }
|
|
|
+
|
|
|
+ private void property_link_clicked(string file) {
|
|
|
+ link_clicked(file);
|
|
|
+ if(flap.folded) {
|
|
|
+ flap.reveal_flap = false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private void change_zoom() {
|
|
|
+ var scale = zoom_spin.adjustment.value / 100;
|
|
|
+ markdown_view.tag_manager.font_scale = (float)scale;
|
|
|
|
|
|
+ }
|
|
|
|
|
|
}
|
|
|
|