using Adw; using Gtk; namespace PpubViewer { public class ViewerWindow : Adw.ApplicationWindow { private Adw.HeaderBar header_bar; private Box box; private Flap flap; private DocumentView.DocumentViewManager document_view; private Button back_button; private Button forward_button; private Button properties_button; private Button menu_button; private PopoverMenu menu; private ZoomSpinButton 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); box.append (flap); content = box; header_bar.show_end_title_buttons = true; default_width = 900; default_height = 700; 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); // header_bar.pack_end(menu); // menu_button = new Button.from_icon_name("open-menu-symbolic"); // menu_button.clicked.connect(() => menu.popup()); // header_bar.pack_end(menu_button); var zoom_squeezer = new Squeezer(); zoom_squeezer.transition_type = SqueezerTransitionType.CROSSFADE; zoom_spin = new ZoomSpinButton(false); var zoom_spin_small = new ZoomSpinButton(true); zoom_spin_small.adjustment = zoom_spin.adjustment; zoom_squeezer.add(zoom_spin); zoom_squeezer.add(zoom_spin_small); zoom_spin.zoom_level_changed.connect(change_zoom); header_bar.pack_end(zoom_squeezer); document_view = new DocumentView.DocumentViewManager(); document_view.link_clicked.connect(link_clicked); flap.content = document_view; 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 (); 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, document_view.get_scroll_position()); } load_document(file); } public void go_back() { current_page.scroll_position = document_view.get_scroll_position(); current_page = current_page.previous; load_document(current_page.file_name); } public void go_forward() { current_page.scroll_position = document_view.get_scroll_position(); 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; document_view.load_asset(publication, publication.get_asset(file)); Idle.add_once(() => { document_view.set_scroll_position(current_page.scroll_position); return false; }); } 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 $url."); 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 $uri."); dialog.body_use_markup = true; dialog.add_response("close", "Close"); dialog.present(); dialog.response.connect(() => dialog.close()); return; } var uris = new List(); uris.append(uri.to_string()); var dialog = new Adw.MessageDialog(this, "Open external link?", @"Do you want to open the external link $uri?"); 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 property_link_clicked(string file) { link_clicked(file); if(flap.folded) { flap.reveal_flap = false; } } private void change_zoom(int percent) { document_view.set_zoom_percentage(percent); } public override void size_allocate(int width, int height, int baseline) { base.size_allocate(width, height, baseline); document_view.reflow(); } } }