Move import
This commit is contained in:
parent
c6ce888cc7
commit
ac6ac42860
@ -326,6 +326,7 @@ Tobias Bernard
|
|||||||
<property name="can_focus">True</property>
|
<property name="can_focus">True</property>
|
||||||
<property name="receives_default">True</property>
|
<property name="receives_default">True</property>
|
||||||
<property name="text" translatable="yes">Import Shows</property>
|
<property name="text" translatable="yes">Import Shows</property>
|
||||||
|
<property name="action-name">app.import</property>
|
||||||
</object>
|
</object>
|
||||||
<packing>
|
<packing>
|
||||||
<property name="expand">False</property>
|
<property name="expand">False</property>
|
||||||
|
|||||||
@ -2,6 +2,20 @@
|
|||||||
<interface>
|
<interface>
|
||||||
<!-- interface-requires gtk+ 3.0 -->
|
<!-- interface-requires gtk+ 3.0 -->
|
||||||
<menu id="app-menu">
|
<menu id="app-menu">
|
||||||
|
<section>
|
||||||
|
<item>
|
||||||
|
<attribute name="label" translatable="yes">_Check for new episodes</attribute>
|
||||||
|
<attribute name="action">app.refresh</attribute>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<attribute name="label" translatable="yes">_Import Shows</attribute>
|
||||||
|
<attribute name="action">app.import</attribute>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<attribute name="label" translatable="yes">_Export Shows</attribute>
|
||||||
|
<attribute name="action">app.export</attribute>
|
||||||
|
</item>
|
||||||
|
</section>
|
||||||
<section>
|
<section>
|
||||||
<item>
|
<item>
|
||||||
<attribute name="label" translatable="yes">_Preferences</attribute>
|
<attribute name="label" translatable="yes">_Preferences</attribute>
|
||||||
|
|||||||
@ -7,6 +7,7 @@ use gtk::prelude::*;
|
|||||||
use gtk::SettingsExt as GtkSettingsExt;
|
use gtk::SettingsExt as GtkSettingsExt;
|
||||||
|
|
||||||
use hammond_data::Podcast;
|
use hammond_data::Podcast;
|
||||||
|
use hammond_data::{opml};
|
||||||
|
|
||||||
use appnotif::{InAppNotification, UndoState};
|
use appnotif::{InAppNotification, UndoState};
|
||||||
use headerbar::Header;
|
use headerbar::Header;
|
||||||
@ -19,6 +20,8 @@ use std::rc::Rc;
|
|||||||
use std::sync::mpsc::{channel, Receiver, Sender};
|
use std::sync::mpsc::{channel, Receiver, Sender};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use rayon;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum Action {
|
pub enum Action {
|
||||||
RefreshAllViews,
|
RefreshAllViews,
|
||||||
@ -67,9 +70,21 @@ impl App {
|
|||||||
let window = gtk::Window::new(gtk::WindowType::Toplevel);
|
let window = gtk::Window::new(gtk::WindowType::Toplevel);
|
||||||
window.set_title("Hammond");
|
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
|
// Ideally a lot more than actions would happen in startup & window
|
||||||
// creation would be in activate
|
// 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);
|
let about = SimpleAction::new("about", None);
|
||||||
// Should investigate use of active_window here
|
// Should investigate use of active_window here
|
||||||
about.connect_activate(clone!(window => move |_, _| about_dialog(&window)));
|
about.connect_activate(clone!(window => move |_, _| about_dialog(&window)));
|
||||||
@ -80,14 +95,6 @@ impl App {
|
|||||||
app.add_action(&quit);
|
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
|
// Create a content instance
|
||||||
let content =
|
let content =
|
||||||
Rc::new(Content::new(sender.clone()).expect("Content Initialization failed."));
|
Rc::new(Content::new(sender.clone()).expect("Content Initialization failed."));
|
||||||
@ -274,3 +281,63 @@ fn about_dialog(window: >k::Window) {
|
|||||||
|
|
||||||
dialog.show();
|
dialog.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn on_import_clicked(window: >k::Window, sender: &Sender<Action>) {
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|||||||
@ -4,16 +4,15 @@ use gtk::prelude::*;
|
|||||||
|
|
||||||
use failure::Error;
|
use failure::Error;
|
||||||
use failure::ResultExt;
|
use failure::ResultExt;
|
||||||
use rayon;
|
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
use hammond_data::{dbqueries, opml, Source};
|
use hammond_data::{dbqueries, Source};
|
||||||
|
|
||||||
use std::sync::mpsc::Sender;
|
use std::sync::mpsc::Sender;
|
||||||
|
|
||||||
use app::Action;
|
use app::Action;
|
||||||
use stacks::Content;
|
use stacks::Content;
|
||||||
use utils::{self, itunes_to_rss, refresh};
|
use utils::{itunes_to_rss, refresh};
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
// TODO: split this into smaller
|
// TODO: split this into smaller
|
||||||
@ -23,7 +22,6 @@ pub struct Header {
|
|||||||
switch: gtk::StackSwitcher,
|
switch: gtk::StackSwitcher,
|
||||||
back: gtk::Button,
|
back: gtk::Button,
|
||||||
show_title: gtk::Label,
|
show_title: gtk::Label,
|
||||||
import: gtk::ModelButton,
|
|
||||||
export: gtk::ModelButton,
|
export: gtk::ModelButton,
|
||||||
update_button: gtk::ModelButton,
|
update_button: gtk::ModelButton,
|
||||||
update_box: gtk::Box,
|
update_box: gtk::Box,
|
||||||
@ -40,7 +38,6 @@ impl Default for Header {
|
|||||||
let switch = builder.get_object("switch").unwrap();
|
let switch = builder.get_object("switch").unwrap();
|
||||||
let back = builder.get_object("back").unwrap();
|
let back = builder.get_object("back").unwrap();
|
||||||
let show_title = builder.get_object("show_title").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 export = builder.get_object("export").unwrap();
|
||||||
let update_button = builder.get_object("update_button").unwrap();
|
let update_button = builder.get_object("update_button").unwrap();
|
||||||
let update_box = builder.get_object("update_notification").unwrap();
|
let update_box = builder.get_object("update_notification").unwrap();
|
||||||
@ -53,7 +50,6 @@ impl Default for Header {
|
|||||||
switch,
|
switch,
|
||||||
back,
|
back,
|
||||||
show_title,
|
show_title,
|
||||||
import,
|
|
||||||
export,
|
export,
|
||||||
update_button,
|
update_button,
|
||||||
update_box,
|
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.
|
// Add the Headerbar to the window.
|
||||||
window.set_titlebar(&self.container);
|
window.set_titlebar(&self.container);
|
||||||
|
|
||||||
@ -222,63 +214,3 @@ fn on_url_change(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_import_clicked(window: >k::Window, sender: &Sender<Action>) {
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user