flaccy 1.5.0

Lossless music player for Linux
use crate::app::AppCore;
use gtk::gdk;
use gtk::gio;
use gtk::glib;
use gtk::prelude::*;
use std::rc::Rc;

pub fn track_menu(core: &Rc<AppCore>, rel_path: &str, loved: bool) -> gio::Menu {
    let menu = gio::Menu::new();
    let queue_section = gio::Menu::new();
    queue_section.append_item(&item("Play Next", "win.track-play-next", rel_path));
    queue_section.append_item(&item("Add to Queue", "win.track-queue", rel_path));
    menu.append_section(None, &queue_section);

    let station_section = gio::Menu::new();
    station_section.append_item(&item("Start Station", "win.track-station", rel_path));
    menu.append_section(None, &station_section);

    let love_section = gio::Menu::new();
    let love_label = if loved { "Unlove" } else { "Love on Last.fm" };
    love_section.append_item(&item(love_label, "win.track-love", rel_path));
    menu.append_section(None, &love_section);

    let playlist_menu = gio::Menu::new();
    for playlist in core.db.fetch_playlists() {
        let entry = gio::MenuItem::new(Some(&playlist.name), None);
        entry.set_action_and_target_value(
            Some("win.playlist-add"),
            Some(&(playlist.id, rel_path.to_string()).to_variant()),
        );
        playlist_menu.append_item(&entry);
    }
    playlist_menu.append_item(&item("New Playlist…", "win.playlist-new-with-track", rel_path));
    menu.append_submenu(Some("Add to Playlist"), &playlist_menu);

    let share_section = gio::Menu::new();
    share_section.append_item(&item("Copy song.link", "win.track-songlink", rel_path));
    menu.append_section(None, &share_section);
    menu
}

pub fn album_menu(key: &str) -> gio::Menu {
    let menu = gio::Menu::new();
    let play_section = gio::Menu::new();
    play_section.append_item(&item("Play", "win.play-album", key));
    play_section.append_item(&item("Shuffle", "win.shuffle-album", key));
    menu.append_section(None, &play_section);
    let queue_section = gio::Menu::new();
    queue_section.append_item(&item("Play Next", "win.album-play-next", key));
    queue_section.append_item(&item("Add to Queue", "win.album-queue", key));
    menu.append_section(None, &queue_section);
    let extra_section = gio::Menu::new();
    extra_section.append_item(&item("Start Station", "win.album-station", key));
    extra_section.append_item(&item("Copy song.link", "win.album-songlink", key));
    extra_section.append_item(&item("Enrich Metadata", "win.album-enrich", key));
    menu.append_section(None, &extra_section);
    menu
}

pub fn artist_menu(artist: &str) -> gio::Menu {
    let menu = gio::Menu::new();
    let play_section = gio::Menu::new();
    play_section.append_item(&item("Play", "win.artist-play", artist));
    play_section.append_item(&item("Shuffle", "win.artist-shuffle", artist));
    menu.append_section(None, &play_section);
    let station_section = gio::Menu::new();
    station_section.append_item(&item("Start Station", "win.artist-station", artist));
    menu.append_section(None, &station_section);
    menu
}

fn item(label: &str, action: &str, target: &str) -> gio::MenuItem {
    let entry = gio::MenuItem::new(Some(label), None);
    entry.set_action_and_target_value(Some(action), Some(&target.to_variant()));
    entry
}

pub fn popup_menu_at(parent: &impl IsA<gtk::Widget>, menu: &gio::Menu, x: f64, y: f64) {
    let popover = gtk::PopoverMenu::from_model(Some(menu));
    popover.set_parent(parent);
    popover.set_has_arrow(false);
    popover.set_pointing_to(Some(&gdk::Rectangle::new(x as i32, y as i32, 1, 1)));
    popover.connect_closed(|popover| {
        let popover = popover.clone();
        glib::idle_add_local_once(move || popover.unparent());
    });
    popover.popup();
}

pub fn attach_track_context_menu(
    widget: &impl IsA<gtk::Widget>,
    core: &Rc<AppCore>,
    rel_path: String,
) {
    let gesture = gtk::GestureClick::builder()
        .button(gdk::BUTTON_SECONDARY)
        .build();
    let core = Rc::clone(core);
    let target = widget.clone().upcast::<gtk::Widget>();
    gesture.connect_pressed(move |_, _, x, y| {
        let loved = core
            .library
            .borrow()
            .track_by_rel_path(&rel_path)
            .map(|t| t.loved)
            .unwrap_or(false);
        let menu = track_menu(&core, &rel_path, loved);
        popup_menu_at(&target, &menu, x, y);
    });
    widget.add_controller(gesture);
}

pub fn attach_album_context_menu(widget: &impl IsA<gtk::Widget>, key: String) {
    let gesture = gtk::GestureClick::builder()
        .button(gdk::BUTTON_SECONDARY)
        .build();
    let target = widget.clone().upcast::<gtk::Widget>();
    gesture.connect_pressed(move |_, _, x, y| {
        let menu = album_menu(&key);
        popup_menu_at(&target, &menu, x, y);
    });
    widget.add_controller(gesture);
}