headerbar.rs 6.52 KB
Newer Older
Zander Brown's avatar
Zander Brown committed
1
use gio::MenuModel;
2
use glib;
3 4
use gtk;
use gtk::prelude::*;
5 6 7

use failure::Error;
use failure::ResultExt;
8
use url::Url;
9

10
use crossbeam_channel::Sender;
Zander Brown's avatar
Zander Brown committed
11
use hammond_data::{dbqueries, Source};
12

13
use app::Action;
14
use stacks::Content;
Zander Brown's avatar
Zander Brown committed
15
use utils::{itunes_to_rss, refresh};
16

17
#[derive(Debug, Clone)]
18
// TODO: split this into smaller
19
pub struct Header {
20
    container: gtk::HeaderBar,
21
    add_toggle: gtk::MenuButton,
22
    switch: gtk::StackSwitcher,
23
    back: gtk::Button,
24
    show_title: gtk::Label,
25 26 27
    update_box: gtk::Box,
    update_label: gtk::Label,
    update_spinner: gtk::Spinner,
Zander Brown's avatar
Zander Brown committed
28
    menu_button: gtk::MenuButton,
Zander Brown's avatar
Zander Brown committed
29
    app_menu: MenuModel,
30 31
}

32 33
impl Default for Header {
    fn default() -> Header {
34
        let builder = gtk::Builder::new_from_resource("/org/gnome/Hammond/gtk/headerbar.ui");
35

36 37 38 39 40 41 42 43
        let header = builder.get_object("headerbar").unwrap();
        let add_toggle = builder.get_object("add_toggle").unwrap();
        let switch = builder.get_object("switch").unwrap();
        let back = builder.get_object("back").unwrap();
        let show_title = builder.get_object("show_title").unwrap();
        let update_box = builder.get_object("update_notification").unwrap();
        let update_label = builder.get_object("update_label").unwrap();
        let update_spinner = builder.get_object("update_spinner").unwrap();
Zander Brown's avatar
Zander Brown committed
44
        let menu_button = builder.get_object("menu_button").unwrap();
45
        let menus = gtk::Builder::new_from_resource("/org/gnome/Hammond/gtk/menus.ui");
Zander Brown's avatar
Zander Brown committed
46
        let app_menu = menus.get_object("menu").unwrap();
47

48
        Header {
49 50
            container: header,
            add_toggle,
51
            switch,
52
            back,
53
            show_title,
54 55 56
            update_box,
            update_label,
            update_spinner,
Zander Brown's avatar
Zander Brown committed
57
            menu_button,
58
            app_menu,
59 60 61 62
        }
    }
}

63
// TODO: Refactor components into smaller state machines
64
impl Header {
Zander Brown's avatar
Zander Brown committed
65 66 67 68 69
    pub fn new(
        content: &Content,
        window: &gtk::ApplicationWindow,
        sender: &Sender<Action>,
    ) -> Header {
70
        let h = Header::default();
71
        h.init(content, window, &sender);
72
        h
73 74
    }

Zander Brown's avatar
Zander Brown committed
75 76 77 78 79 80
    pub fn init(
        &self,
        content: &Content,
        window: &gtk::ApplicationWindow,
        sender: &Sender<Action>,
    ) {
81
        let builder = gtk::Builder::new_from_resource("/org/gnome/Hammond/gtk/headerbar.ui");
82

83
        let add_popover: gtk::Popover = builder.get_object("add_popover").unwrap();
84 85
        let new_url: gtk::Entry = builder.get_object("new_url").unwrap();
        let add_button: gtk::Button = builder.get_object("add_button").unwrap();
86
        let result_label: gtk::Label = builder.get_object("result_label").unwrap();
87
        self.switch.set_stack(&content.get_stack());
88

89
        new_url.connect_changed(clone!(add_button => move |url| {
90 91 92
            on_url_change(url, &result_label, &add_button)
                .map_err(|err| error!("Error: {}", err))
                .ok();
93
        }));
94

95
        add_button.connect_clicked(clone!(add_popover, new_url, sender => move |_| {
96 97 98
            on_add_bttn_clicked(&new_url, sender.clone())
                .map_err(|err| error!("Error: {}", err))
                .ok();
99 100
            add_popover.hide();
        }));
101

102 103
        self.add_toggle.set_popover(&add_popover);

104 105 106
        // Add the Headerbar to the window.
        window.set_titlebar(&self.container);

Jordan Petridis's avatar
Jordan Petridis committed
107 108
        let switch = &self.switch;
        let add_toggle = &self.add_toggle;
109
        let show_title = &self.show_title;
110
        self.back.connect_clicked(
111 112 113 114 115
            clone!(switch, add_toggle, show_title, sender => move |back| {
                switch.show();
                add_toggle.show();
                back.hide();
                show_title.hide();
116 117 118
                sender.send(Action::ShowShowsAnimated)
                    .map_err(|err| error!("Action Sender: {}", err))
                    .ok();
119
            }),
120
        );
121

Zander Brown's avatar
Zander Brown committed
122
        self.menu_button.set_menu_model(Some(&self.app_menu));
123 124
    }

125
    pub fn switch_to_back(&self, title: &str) {
Jordan Petridis's avatar
Jordan Petridis committed
126 127
        self.switch.hide();
        self.add_toggle.hide();
128
        self.back.show();
129
        self.set_show_title(title);
130
        self.show_title.show();
131 132
    }

Jordan Petridis's avatar
Jordan Petridis committed
133 134 135
    pub fn switch_to_normal(&self) {
        self.switch.show();
        self.add_toggle.show();
136
        self.back.hide();
137
        self.show_title.hide();
138
    }
Jordan Petridis's avatar
Jordan Petridis committed
139

140 141 142
    pub fn set_show_title(&self, title: &str) {
        self.show_title.set_text(title)
    }
143 144 145 146 147 148 149 150 151 152 153 154 155 156

    pub fn show_update_notification(&self) {
        self.update_spinner.start();
        self.update_box.show();
        self.update_spinner.show();
        self.update_label.show();
    }

    pub fn hide_update_notification(&self) {
        self.update_spinner.stop();
        self.update_box.hide();
        self.update_spinner.hide();
        self.update_label.hide();
    }
Zander Brown's avatar
Zander Brown committed
157 158 159 160

    pub fn open_menu(&self) {
        self.menu_button.clicked();
    }
161 162
}

163
// FIXME: THIS ALSO SUCKS!
164
fn on_add_bttn_clicked(entry: &gtk::Entry, sender: Sender<Action>) -> Result<(), Error> {
165
    let url = entry.get_text().unwrap_or_default();
166 167 168 169 170 171 172 173 174
    let url = if url.contains("itunes.com") || url.contains("apple.com") {
        info!("Detected itunes url.");
        let foo = itunes_to_rss(&url)?;
        info!("Resolved to {}", foo);
        foo
    } else {
        url.to_owned()
    };

175 176 177
    let source = Source::from_url(&url).context("Failed to convert url to a Source entry.")?;
    entry.set_text("");

Jordan Petridis's avatar
Jordan Petridis committed
178
    gtk::idle_add(move || {
179 180 181
        refresh(Some(vec![source.clone()]), sender.clone());
        glib::Continue(false)
    });
182
    Ok(())
183
}
184

185
// FIXME: THIS SUCKS!
186 187 188 189 190 191 192 193
fn on_url_change(
    entry: &gtk::Entry,
    result: &gtk::Label,
    add_button: &gtk::Button,
) -> Result<(), Error> {
    let uri = entry
        .get_text()
        .ok_or_else(|| format_err!("GtkEntry blew up somehow."))?;
194 195 196
    debug!("Url: {}", uri);

    let url = Url::parse(&uri);
197
    // TODO: refactor to avoid duplication
198
    match url {
199
        Ok(u) => {
200
            if !dbqueries::source_exists(u.as_str())? {
201 202 203 204 205 206 207 208
                add_button.set_sensitive(true);
                result.hide();
                result.set_label("");
            } else {
                add_button.set_sensitive(false);
                result.set_label("Show already exists.");
                result.show();
            }
209
            Ok(())
210 211 212
        }
        Err(err) => {
            add_button.set_sensitive(false);
213 214 215 216 217 218 219
            if !uri.is_empty() {
                result.set_label("Invalid url.");
                result.show();
                error!("Error: {}", err);
            } else {
                result.hide();
            }
220
            Ok(())
221 222 223
        }
    }
}