diff --git a/hammond-gtk/src/widgets/episode.rs b/hammond-gtk/src/widgets/episode.rs index 48ee722..dbf89de 100644 --- a/hammond-gtk/src/widgets/episode.rs +++ b/hammond-gtk/src/widgets/episode.rs @@ -1,11 +1,9 @@ use glib; use gtk; - -use chrono::prelude::*; use gtk::prelude::*; use failure::Error; -use humansize::{file_size_opts as size_opts, FileSize}; +use humansize::FileSize; use open; use take_mut; @@ -22,30 +20,10 @@ use std::path::Path; use std::sync::{Arc, Mutex}; use std::sync::mpsc::Sender; -lazy_static! { - pub static ref SIZE_OPTS: Arc = { - // Declare a custom humansize option struct - // See: https://docs.rs/humansize/1.0.2/humansize/file_size_opts/struct.FileSizeOpts.html - Arc::new(size_opts::FileSizeOpts { - divider: size_opts::Kilo::Binary, - units: size_opts::Kilo::Decimal, - decimal_places: 0, - decimal_zeroes: 0, - fixed_at: size_opts::FixedAt::No, - long_units: false, - space: true, - suffix: "", - allow_negative: false, - }) - }; - - static ref NOW: DateTime = Utc::now(); -} - #[derive(Debug, Clone)] pub struct EpisodeWidget { pub container: gtk::Box, - date: gtk::Label, + date: Arc>, title: Arc>, duration: Arc>, media: Arc>, @@ -73,6 +51,7 @@ impl Default for EpisodeWidget { let prog_separator: gtk::Label = builder.get_object("prog_separator").unwrap(); let title_machine = Arc::new(Mutex::new(TitleMachine::new(title, false))); + let date_machine = Arc::new(Mutex::new(DateMachine::new(date, 0))); let dur = DurationMachine::new(duration, separator1, None); let duration_machine = Arc::new(Mutex::new(dur)); let _media = MediaMachine::new( @@ -91,7 +70,7 @@ impl Default for EpisodeWidget { container, title: title_machine, duration: duration_machine, - date, + date: date_machine, media: media_machine, } } @@ -108,11 +87,13 @@ impl EpisodeWidget { WidgetExt::set_name(&self.container, &episode.rowid().to_string()); // Set the date label. - self.set_date(episode.epoch()); + if let Err(err) = self.set_date(episode.epoch()) { + error!("Failed to determine date state: {}", err); + } // Set the title label state. if let Err(err) = self.set_title(&episode) { - error!("Failed to set title state: {}", err); + error!("Failed to determine title state: {}", err); } // Set the duaration label. @@ -166,14 +147,12 @@ impl EpisodeWidget { } /// Set the date label depending on the current time. - fn set_date(&self, epoch: i32) { - let date = Utc.timestamp(i64::from(epoch), 0); - if NOW.year() == date.year() { - self.date.set_text(date.format("%e %b").to_string().trim()); - } else { - self.date - .set_text(date.format("%e %b %Y").to_string().trim()); - }; + fn set_date(&self, epoch: i32) -> Result<(), Error> { + let mut lock = self.date.lock().map_err(|err| format_err!("{}", err))?; + take_mut::take(lock.deref_mut(), |date| { + date.determine_state(i64::from(epoch)) + }); + Ok(()) } /// Set the duration label. diff --git a/hammond-gtk/src/widgets/episode_states.rs b/hammond-gtk/src/widgets/episode_states.rs index 3f20c13..4432d7a 100644 --- a/hammond-gtk/src/widgets/episode_states.rs +++ b/hammond-gtk/src/widgets/episode_states.rs @@ -8,13 +8,34 @@ use chrono; use glib; use gtk; + +use chrono::prelude::*; use gtk::prelude::*; -use humansize::FileSize; +use humansize::{file_size_opts as size_opts, FileSize}; use std::sync::{Arc, Mutex}; use manager::Progress as OtherProgress; -use widgets::episode::SIZE_OPTS; + +lazy_static! { + pub static ref SIZE_OPTS: Arc = { + // Declare a custom humansize option struct + // See: https://docs.rs/humansize/1.0.2/humansize/file_size_opts/struct.FileSizeOpts.html + Arc::new(size_opts::FileSizeOpts { + divider: size_opts::Kilo::Binary, + units: size_opts::Kilo::Decimal, + decimal_places: 0, + decimal_zeroes: 0, + fixed_at: size_opts::FixedAt::No, + long_units: false, + space: true, + suffix: "", + allow_negative: false, + }) + }; + + static ref NOW: DateTime = Utc::now(); +} #[derive(Debug, Clone)] pub struct UnInitialized; @@ -153,6 +174,96 @@ impl TitleMachine { } } +#[derive(Debug, Clone)] +pub struct Usual; +#[derive(Debug, Clone)] +pub struct YearShown; + +impl From for YearShown { + fn from(_: Usual) -> Self { + YearShown {} + } +} + +impl From for Usual { + fn from(_: YearShown) -> Self { + Usual {} + } +} + +#[derive(Debug, Clone)] +pub struct Date { + date: gtk::Label, + epoch: i64, + state: S, +} + +impl Date { + fn new(date: gtk::Label, epoch: i64) -> Self { + let ts = Utc.timestamp(i64::from(epoch), 0); + date.set_text(ts.format("%e %b").to_string().trim()); + + Date { + date, + epoch, + state: Usual {}, + } + } +} + +impl From> for Date { + fn from(f: Date) -> Self { + let ts = Utc.timestamp(f.epoch, 0); + f.date.set_text(ts.format("%e %b %Y").to_string().trim()); + + Date { + date: f.date, + epoch: f.epoch, + state: YearShown {}, + } + } +} + +impl From> for Date { + fn from(f: Date) -> Self { + let ts = Utc.timestamp(f.epoch, 0); + f.date.set_text(ts.format("%e %b").to_string().trim()); + + Date { + date: f.date, + epoch: f.epoch, + state: Usual {}, + } + } +} + +#[derive(Debug, Clone)] +pub enum DateMachine { + Usual(Date), + WithYear(Date), +} + +impl DateMachine { + pub fn new(label: gtk::Label, epoch: i64) -> Self { + let m = DateMachine::Usual(Date::::new(label, epoch)); + m.determine_state(epoch) + } + + pub fn determine_state(self, epoch: i64) -> Self { + use self::DateMachine::*; + + let ts = Utc.timestamp(epoch, 0); + let is_old = NOW.year() == ts.year(); + + match (self, is_old) { + (date @ Usual(_), false) => date, + (date @ WithYear(_), true) => date, + (Usual(val), true) => WithYear(val.into()), + (WithYear(val), false) => Usual(val.into()), + } + } +} + #[derive(Debug, Clone)] pub struct Duration { // TODO: make duration and separator diff types