diff --git a/hammond-gtk/resources/gtk/headerbar.ui b/hammond-gtk/resources/gtk/headerbar.ui index 8033dd0..f9f33a6 100644 --- a/hammond-gtk/resources/gtk/headerbar.ui +++ b/hammond-gtk/resources/gtk/headerbar.ui @@ -326,6 +326,7 @@ Tobias Bernard True True Import Shows + app.import False diff --git a/hammond-gtk/resources/gtk/menus.ui b/hammond-gtk/resources/gtk/menus.ui index 1155b65..eebf8f8 100644 --- a/hammond-gtk/resources/gtk/menus.ui +++ b/hammond-gtk/resources/gtk/menus.ui @@ -2,6 +2,20 @@ +
+ + _Check for new episodes + app.refresh + + + _Import Shows + app.import + + + _Export Shows + app.export + +
_Preferences diff --git a/hammond-gtk/src/app.rs b/hammond-gtk/src/app.rs index 76dce3a..f0ab33d 100644 --- a/hammond-gtk/src/app.rs +++ b/hammond-gtk/src/app.rs @@ -7,6 +7,7 @@ use gtk::prelude::*; use gtk::SettingsExt as GtkSettingsExt; use hammond_data::Podcast; +use hammond_data::{opml}; use appnotif::{InAppNotification, UndoState}; use headerbar::Header; @@ -19,6 +20,8 @@ use std::rc::Rc; use std::sync::mpsc::{channel, Receiver, Sender}; use std::sync::Arc; +use rayon; + #[derive(Debug, Clone)] pub enum Action { RefreshAllViews, @@ -67,9 +70,21 @@ impl App { let window = gtk::Window::new(gtk::WindowType::Toplevel); window.set_title("Hammond"); + let (sender, receiver) = channel(); + + window.connect_delete_event(clone!(application, settings, window => move |_, _| { + WindowGeometry::from_window(&window).write(&settings); + application.quit(); + Inhibit(false) + })); + // Ideally a lot more than actions would happen in startup & window // creation would be in activate - application.connect_startup(clone!(window => move |app| { + application.connect_startup(clone!(window, sender => move |app| { + let import = SimpleAction::new("import", None); + import.connect_activate(clone!(window, sender => move |_, _| on_import_clicked(&window, &sender))); + app.add_action(&import); + let about = SimpleAction::new("about", None); // Should investigate use of active_window here about.connect_activate(clone!(window => move |_, _| about_dialog(&window))); @@ -80,14 +95,6 @@ impl App { app.add_action(&quit); })); - window.connect_delete_event(clone!(application, settings, window => move |_, _| { - WindowGeometry::from_window(&window).write(&settings); - application.quit(); - Inhibit(false) - })); - - let (sender, receiver) = channel(); - // Create a content instance let content = Rc::new(Content::new(sender.clone()).expect("Content Initialization failed.")); @@ -274,3 +281,63 @@ fn about_dialog(window: >k::Window) { dialog.show(); } + +fn on_import_clicked(window: >k::Window, sender: &Sender) { + use glib::translate::ToGlib; + use gtk::{FileChooserAction, FileChooserDialog, FileFilter, ResponseType}; + + // let dialog = FileChooserDialog::new(title, Some(&window), FileChooserAction::Open); + // TODO: It might be better to use a FileChooserNative widget. + // Create the FileChooser Dialog + let dialog = FileChooserDialog::with_buttons( + Some("Select the file from which to you want to Import Shows."), + Some(window), + FileChooserAction::Open, + &[ + ("_Cancel", ResponseType::Cancel), + ("_Open", ResponseType::Accept), + ], + ); + + // Do not show hidden(.thing) files + dialog.set_show_hidden(false); + + // Set a filter to show only xml files + let filter = FileFilter::new(); + FileFilterExt::set_name(&filter, Some("OPML file")); + filter.add_mime_type("application/xml"); + filter.add_mime_type("text/xml"); + dialog.add_filter(&filter); + + dialog.connect_response(clone!(sender => move |dialog, resp| { + debug!("Dialong Response {}", resp); + if resp == ResponseType::Accept.to_glib() { + // TODO: Show an in-app notifictaion if the file can not be accessed + if let Some(filename) = dialog.get_filename() { + debug!("File selected: {:?}", filename); + + rayon::spawn(clone!(sender => move || { + // Parse the file and import the feeds + if let Ok(sources) = opml::import_from_file(filename) { + // Refresh the succesfully parsed feeds to index them + utils::refresh(Some(sources), sender) + } else { + let text = String::from("Failed to parse the Imported file"); + sender.send(Action::ErrorNotification(text)) + .map_err(|err| error!("Action Sender: {}", err)) + .ok(); + } + })) + } else { + let text = String::from("Selected File could not be accessed."); + sender.send(Action::ErrorNotification(text)) + .map_err(|err| error!("Action Sender: {}", err)) + .ok(); + } + } + + dialog.destroy(); + })); + + dialog.run(); +} diff --git a/hammond-gtk/src/headerbar.rs b/hammond-gtk/src/headerbar.rs index 4cdaf52..7050de0 100644 --- a/hammond-gtk/src/headerbar.rs +++ b/hammond-gtk/src/headerbar.rs @@ -4,16 +4,15 @@ use gtk::prelude::*; use failure::Error; use failure::ResultExt; -use rayon; use url::Url; -use hammond_data::{dbqueries, opml, Source}; +use hammond_data::{dbqueries, Source}; use std::sync::mpsc::Sender; use app::Action; use stacks::Content; -use utils::{self, itunes_to_rss, refresh}; +use utils::{itunes_to_rss, refresh}; #[derive(Debug, Clone)] // TODO: split this into smaller @@ -23,7 +22,6 @@ pub struct Header { switch: gtk::StackSwitcher, back: gtk::Button, show_title: gtk::Label, - import: gtk::ModelButton, export: gtk::ModelButton, update_button: gtk::ModelButton, update_box: gtk::Box, @@ -40,7 +38,6 @@ impl Default for Header { let switch = builder.get_object("switch").unwrap(); let back = builder.get_object("back").unwrap(); let show_title = builder.get_object("show_title").unwrap(); - let import = builder.get_object("import").unwrap(); let export = builder.get_object("export").unwrap(); let update_button = builder.get_object("update_button").unwrap(); let update_box = builder.get_object("update_notification").unwrap(); @@ -53,7 +50,6 @@ impl Default for Header { switch, back, show_title, - import, export, update_button, update_box, @@ -104,10 +100,6 @@ impl Header { })); })); - self.import.connect_clicked( - clone!(window, sender => move |_| on_import_clicked(&window, &sender)), - ); - // Add the Headerbar to the window. window.set_titlebar(&self.container); @@ -222,63 +214,3 @@ fn on_url_change( } } } - -fn on_import_clicked(window: >k::Window, sender: &Sender) { - use glib::translate::ToGlib; - use gtk::{FileChooserAction, FileChooserDialog, FileFilter, ResponseType}; - - // let dialog = FileChooserDialog::new(title, Some(&window), FileChooserAction::Open); - // TODO: It might be better to use a FileChooserNative widget. - // Create the FileChooser Dialog - let dialog = FileChooserDialog::with_buttons( - Some("Select the file from which to you want to Import Shows."), - Some(window), - FileChooserAction::Open, - &[ - ("_Cancel", ResponseType::Cancel), - ("_Open", ResponseType::Accept), - ], - ); - - // Do not show hidden(.thing) files - dialog.set_show_hidden(false); - - // Set a filter to show only xml files - let filter = FileFilter::new(); - FileFilterExt::set_name(&filter, Some("OPML file")); - filter.add_mime_type("application/xml"); - filter.add_mime_type("text/xml"); - dialog.add_filter(&filter); - - dialog.connect_response(clone!(sender => move |dialog, resp| { - debug!("Dialong Response {}", resp); - if resp == ResponseType::Accept.to_glib() { - // TODO: Show an in-app notifictaion if the file can not be accessed - if let Some(filename) = dialog.get_filename() { - debug!("File selected: {:?}", filename); - - rayon::spawn(clone!(sender => move || { - // Parse the file and import the feeds - if let Ok(sources) = opml::import_from_file(filename) { - // Refresh the succesfully parsed feeds to index them - utils::refresh(Some(sources), sender) - } else { - let text = String::from("Failed to parse the Imported file"); - sender.send(Action::ErrorNotification(text)) - .map_err(|err| error!("Action Sender: {}", err)) - .ok(); - } - })) - } else { - let text = String::from("Selected File could not be accessed."); - sender.send(Action::ErrorNotification(text)) - .map_err(|err| error!("Action Sender: {}", err)) - .ok(); - } - } - - dialog.destroy(); - })); - - dialog.run(); -}