This code is not performance critical and the compiler will already inline whatever it thinks it might benefit it.
228 lines
7.1 KiB
Rust
228 lines
7.1 KiB
Rust
use chrono::prelude::*;
|
|
use failure::Error;
|
|
|
|
use gtk;
|
|
use gtk::prelude::*;
|
|
|
|
use hammond_data::dbqueries;
|
|
use hammond_data::EpisodeWidgetQuery;
|
|
use send_cell::SendCell;
|
|
|
|
use app::Action;
|
|
use utils::{self, lazy_load_full};
|
|
use widgets::EpisodeWidget;
|
|
|
|
use std::rc::Rc;
|
|
use std::sync::mpsc::Sender;
|
|
use std::sync::Mutex;
|
|
|
|
lazy_static! {
|
|
pub static ref EPISODES_VIEW_VALIGNMENT: Mutex<Option<SendCell<gtk::Adjustment>>> =
|
|
Mutex::new(None);
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
enum ListSplit {
|
|
Today,
|
|
Yday,
|
|
Week,
|
|
Month,
|
|
Rest,
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub struct HomeView {
|
|
pub container: gtk::Box,
|
|
scrolled_window: gtk::ScrolledWindow,
|
|
frame_parent: gtk::Box,
|
|
today_box: gtk::Box,
|
|
yday_box: gtk::Box,
|
|
week_box: gtk::Box,
|
|
month_box: gtk::Box,
|
|
rest_box: gtk::Box,
|
|
today_list: gtk::ListBox,
|
|
yday_list: gtk::ListBox,
|
|
week_list: gtk::ListBox,
|
|
month_list: gtk::ListBox,
|
|
rest_list: gtk::ListBox,
|
|
}
|
|
|
|
impl Default for HomeView {
|
|
fn default() -> Self {
|
|
let builder = gtk::Builder::new_from_resource("/org/gnome/hammond/gtk/episodes_view.ui");
|
|
let container: gtk::Box = builder.get_object("container").unwrap();
|
|
let scrolled_window: gtk::ScrolledWindow = builder.get_object("scrolled_window").unwrap();
|
|
let frame_parent: gtk::Box = builder.get_object("frame_parent").unwrap();
|
|
let today_box: gtk::Box = builder.get_object("today_box").unwrap();
|
|
let yday_box: gtk::Box = builder.get_object("yday_box").unwrap();
|
|
let week_box: gtk::Box = builder.get_object("week_box").unwrap();
|
|
let month_box: gtk::Box = builder.get_object("month_box").unwrap();
|
|
let rest_box: gtk::Box = builder.get_object("rest_box").unwrap();
|
|
let today_list: gtk::ListBox = builder.get_object("today_list").unwrap();
|
|
let yday_list: gtk::ListBox = builder.get_object("yday_list").unwrap();
|
|
let week_list: gtk::ListBox = builder.get_object("week_list").unwrap();
|
|
let month_list: gtk::ListBox = builder.get_object("month_list").unwrap();
|
|
let rest_list: gtk::ListBox = builder.get_object("rest_list").unwrap();
|
|
|
|
HomeView {
|
|
container,
|
|
scrolled_window,
|
|
frame_parent,
|
|
today_box,
|
|
yday_box,
|
|
week_box,
|
|
month_box,
|
|
rest_box,
|
|
today_list,
|
|
yday_list,
|
|
week_list,
|
|
month_list,
|
|
rest_list,
|
|
}
|
|
}
|
|
}
|
|
|
|
// TODO: REFACTOR ME
|
|
impl HomeView {
|
|
pub fn new(sender: Sender<Action>) -> Result<Rc<HomeView>, Error> {
|
|
use self::ListSplit::*;
|
|
|
|
let view = Rc::new(HomeView::default());
|
|
let ignore = utils::get_ignored_shows()?;
|
|
let episodes = dbqueries::get_episodes_widgets_filter_limit(&ignore, 100)?;
|
|
let now_utc = Utc::now();
|
|
|
|
let view_ = view.clone();
|
|
let func = move |ep: EpisodeWidgetQuery| {
|
|
let epoch = ep.epoch();
|
|
let widget = EpisodesViewWidget::new(ep, &sender);
|
|
|
|
match split(&now_utc, i64::from(epoch)) {
|
|
Today => add_to_box(&widget, &view_.today_list, &view_.today_box),
|
|
Yday => add_to_box(&widget, &view_.yday_list, &view_.yday_box),
|
|
Week => add_to_box(&widget, &view_.week_list, &view_.week_box),
|
|
Month => add_to_box(&widget, &view_.month_list, &view_.month_box),
|
|
Rest => add_to_box(&widget, &view_.rest_list, &view_.rest_box),
|
|
}
|
|
};
|
|
|
|
let view_ = view.clone();
|
|
let callback = move || {
|
|
view_
|
|
.set_vadjustment()
|
|
.map_err(|err| format!("{}", err))
|
|
.ok();
|
|
};
|
|
|
|
lazy_load_full(episodes, func, callback);
|
|
view.container.show_all();
|
|
Ok(view)
|
|
}
|
|
|
|
/// Set scrolled window vertical adjustment.
|
|
fn set_vadjustment(&self) -> Result<(), Error> {
|
|
let guard = EPISODES_VIEW_VALIGNMENT
|
|
.lock()
|
|
.map_err(|err| format_err!("Failed to lock widget align mutex: {}", err))?;
|
|
|
|
if let Some(ref sendcell) = *guard {
|
|
// Copy the vertical scrollbar adjustment from the old view into the new one.
|
|
sendcell
|
|
.try_get()
|
|
.map(|x| utils::smooth_scroll_to(&self.scrolled_window, &x));
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
/// Save the vertical scrollbar position.
|
|
pub fn save_alignment(&self) -> Result<(), Error> {
|
|
if let Ok(mut guard) = EPISODES_VIEW_VALIGNMENT.lock() {
|
|
let adj = self.scrolled_window
|
|
.get_vadjustment()
|
|
.ok_or_else(|| format_err!("Could not get the adjustment"))?;
|
|
*guard = Some(SendCell::new(adj));
|
|
info!("Saved episodes_view alignment.");
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
fn add_to_box(widget: &EpisodesViewWidget, listbox: >k::ListBox, box_: >k::Box) {
|
|
listbox.add(&widget.container);
|
|
box_.show();
|
|
}
|
|
|
|
fn split(now: &DateTime<Utc>, epoch: i64) -> ListSplit {
|
|
let ep = Utc.timestamp(epoch, 0);
|
|
|
|
if now.ordinal() == ep.ordinal() && now.year() == ep.year() {
|
|
ListSplit::Today
|
|
} else if now.ordinal() == ep.ordinal() + 1 && now.year() == ep.year() {
|
|
ListSplit::Yday
|
|
} else if now.iso_week().week() == ep.iso_week().week() && now.year() == ep.year() {
|
|
ListSplit::Week
|
|
} else if now.month() == ep.month() && now.year() == ep.year() {
|
|
ListSplit::Month
|
|
} else {
|
|
ListSplit::Rest
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
struct EpisodesViewWidget {
|
|
container: gtk::Box,
|
|
image: gtk::Image,
|
|
episode: gtk::Box,
|
|
}
|
|
|
|
impl Default for EpisodesViewWidget {
|
|
fn default() -> Self {
|
|
let builder =
|
|
gtk::Builder::new_from_resource("/org/gnome/hammond/gtk/episodes_view_widget.ui");
|
|
let container: gtk::Box = builder.get_object("container").unwrap();
|
|
let image: gtk::Image = builder.get_object("cover").unwrap();
|
|
let ep = EpisodeWidget::default();
|
|
container.pack_start(&ep.container, true, true, 6);
|
|
|
|
EpisodesViewWidget {
|
|
container,
|
|
image,
|
|
episode: ep.container,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl EpisodesViewWidget {
|
|
fn new(episode: EpisodeWidgetQuery, sender: &Sender<Action>) -> EpisodesViewWidget {
|
|
let builder =
|
|
gtk::Builder::new_from_resource("/org/gnome/hammond/gtk/episodes_view_widget.ui");
|
|
let container: gtk::Box = builder.get_object("container").unwrap();
|
|
let image: gtk::Image = builder.get_object("cover").unwrap();
|
|
let pid = episode.podcast_id();
|
|
let ep = EpisodeWidget::new(episode, sender);
|
|
|
|
let view = EpisodesViewWidget {
|
|
container,
|
|
image,
|
|
episode: ep.container,
|
|
};
|
|
|
|
view.init(pid);
|
|
view
|
|
}
|
|
|
|
fn init(&self, podcast_id: i32) {
|
|
self.set_cover(podcast_id)
|
|
.map_err(|err| error!("Failed to set a cover: {}", err))
|
|
.ok();
|
|
|
|
self.container.pack_start(&self.episode, true, true, 6);
|
|
}
|
|
|
|
fn set_cover(&self, podcast_id: i32) -> Result<(), Error> {
|
|
utils::set_image_from_path(&self.image, podcast_id, 64)
|
|
}
|
|
}
|