2020-10-31 23:07:25 +01:00
|
|
|
use super::*;
|
|
|
|
|
use crate::backend::*;
|
|
|
|
|
use crate::database::*;
|
|
|
|
|
use crate::widgets::*;
|
2020-11-02 00:52:04 +01:00
|
|
|
use gettextrs::gettext;
|
2020-10-31 23:07:25 +01:00
|
|
|
use glib::clone;
|
|
|
|
|
use gtk::prelude::*;
|
|
|
|
|
use gtk_macros::get_widget;
|
|
|
|
|
use std::cell::RefCell;
|
|
|
|
|
use std::rc::Rc;
|
|
|
|
|
|
|
|
|
|
pub struct TracksEditor {
|
2020-11-03 17:53:13 +01:00
|
|
|
window: libhandy::Window,
|
2020-10-31 23:07:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl TracksEditor {
|
|
|
|
|
pub fn new<F: Fn() -> () + 'static, P: IsA<gtk::Window>>(
|
|
|
|
|
backend: Rc<Backend>,
|
|
|
|
|
parent: &P,
|
|
|
|
|
callback: F,
|
|
|
|
|
) -> Self {
|
2020-11-01 12:49:10 +01:00
|
|
|
let builder = gtk::Builder::from_resource("/de/johrpan/musicus/ui/tracks_editor.ui");
|
2020-10-31 23:07:25 +01:00
|
|
|
|
2020-11-03 17:53:13 +01:00
|
|
|
get_widget!(builder, libhandy::Window, window);
|
2020-10-31 23:07:25 +01:00
|
|
|
get_widget!(builder, gtk::Button, cancel_button);
|
|
|
|
|
get_widget!(builder, gtk::Button, save_button);
|
|
|
|
|
get_widget!(builder, gtk::Button, recording_button);
|
|
|
|
|
get_widget!(builder, gtk::Stack, recording_stack);
|
|
|
|
|
get_widget!(builder, gtk::Label, work_label);
|
|
|
|
|
get_widget!(builder, gtk::Label, performers_label);
|
|
|
|
|
get_widget!(builder, gtk::ScrolledWindow, scroll);
|
|
|
|
|
get_widget!(builder, gtk::Button, add_track_button);
|
|
|
|
|
get_widget!(builder, gtk::Button, edit_track_button);
|
|
|
|
|
get_widget!(builder, gtk::Button, remove_track_button);
|
|
|
|
|
get_widget!(builder, gtk::Button, move_track_up_button);
|
|
|
|
|
get_widget!(builder, gtk::Button, move_track_down_button);
|
|
|
|
|
|
|
|
|
|
window.set_transient_for(Some(parent));
|
|
|
|
|
|
|
|
|
|
cancel_button.connect_clicked(clone!(@strong window => move |_| {
|
|
|
|
|
window.close();
|
|
|
|
|
}));
|
|
|
|
|
|
|
|
|
|
let recording = Rc::new(RefCell::new(None::<RecordingDescription>));
|
2020-11-01 11:04:31 +01:00
|
|
|
let tracks = Rc::new(RefCell::new(Vec::<TrackDescription>::new()));
|
2020-10-31 23:07:25 +01:00
|
|
|
|
|
|
|
|
let track_list = List::new(
|
2020-11-01 11:04:31 +01:00
|
|
|
clone!(@strong recording => move |track: &TrackDescription| {
|
2020-10-31 23:07:25 +01:00
|
|
|
let mut title_parts = Vec::<String>::new();
|
|
|
|
|
for part in &track.work_parts {
|
|
|
|
|
if let Some(recording) = &*recording.borrow() {
|
|
|
|
|
title_parts.push(recording.work.parts[*part].title.clone());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let title = if title_parts.is_empty() {
|
2020-11-02 00:52:04 +01:00
|
|
|
gettext("Unknown")
|
2020-10-31 23:07:25 +01:00
|
|
|
} else {
|
|
|
|
|
title_parts.join(", ")
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let title_label = gtk::Label::new(Some(&title));
|
|
|
|
|
title_label.set_ellipsize(pango::EllipsizeMode::End);
|
|
|
|
|
title_label.set_halign(gtk::Align::Start);
|
|
|
|
|
|
|
|
|
|
let file_name_label = gtk::Label::new(Some(&track.file_name));
|
|
|
|
|
file_name_label.set_ellipsize(pango::EllipsizeMode::End);
|
|
|
|
|
file_name_label.set_opacity(0.5);
|
|
|
|
|
file_name_label.set_halign(gtk::Align::Start);
|
|
|
|
|
|
|
|
|
|
let vbox = gtk::Box::new(gtk::Orientation::Vertical, 0);
|
|
|
|
|
vbox.add(&title_label);
|
|
|
|
|
vbox.add(&file_name_label);
|
|
|
|
|
|
|
|
|
|
vbox.upcast()
|
|
|
|
|
}),
|
|
|
|
|
|_| true,
|
2020-11-02 00:52:04 +01:00
|
|
|
&gettext("Add some tracks."),
|
2020-10-31 23:07:25 +01:00
|
|
|
);
|
|
|
|
|
|
|
|
|
|
let autofill_parts = Rc::new(clone!(@strong recording, @strong tracks, @strong track_list => move || {
|
|
|
|
|
if let Some(recording) = &*recording.borrow() {
|
|
|
|
|
let mut tracks = tracks.borrow_mut();
|
|
|
|
|
for (index, _) in recording.work.parts.iter().enumerate() {
|
|
|
|
|
if let Some(mut track) = tracks.get_mut(index) {
|
|
|
|
|
track.work_parts = vec!(index);
|
|
|
|
|
} else {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
track_list.show_items(tracks.clone());
|
|
|
|
|
}
|
|
|
|
|
}));
|
|
|
|
|
|
|
|
|
|
recording_button.connect_clicked(clone!(
|
|
|
|
|
@strong backend,
|
|
|
|
|
@strong window,
|
|
|
|
|
@strong save_button,
|
|
|
|
|
@strong work_label,
|
|
|
|
|
@strong performers_label,
|
|
|
|
|
@strong recording_stack,
|
|
|
|
|
@strong recording,
|
|
|
|
|
@strong autofill_parts => move |_| {
|
|
|
|
|
RecordingSelector::new(
|
|
|
|
|
backend.clone(),
|
|
|
|
|
&window,
|
|
|
|
|
clone!(
|
|
|
|
|
@strong save_button,
|
|
|
|
|
@strong work_label,
|
|
|
|
|
@strong performers_label,
|
|
|
|
|
@strong recording_stack,
|
|
|
|
|
@strong recording,
|
|
|
|
|
@strong autofill_parts => move |r| {
|
|
|
|
|
work_label.set_text(&r.work.get_title());
|
|
|
|
|
performers_label.set_text(&r.get_performers());
|
|
|
|
|
recording_stack.set_visible_child_name("selected");
|
|
|
|
|
recording.replace(Some(r));
|
|
|
|
|
save_button.set_sensitive(true);
|
|
|
|
|
autofill_parts();
|
|
|
|
|
}
|
|
|
|
|
)).show();
|
|
|
|
|
}
|
|
|
|
|
));
|
|
|
|
|
|
2020-11-01 11:04:31 +01:00
|
|
|
let callback = Rc::new(callback);
|
|
|
|
|
save_button.connect_clicked(clone!(@strong window, @strong backend, @strong recording, @strong tracks, @strong callback => move |_| {
|
|
|
|
|
let context = glib::MainContext::default();
|
|
|
|
|
let window = window.clone();
|
|
|
|
|
let backend = backend.clone();
|
|
|
|
|
let recording = recording.clone();
|
|
|
|
|
let tracks = tracks.clone();
|
|
|
|
|
let callback = callback.clone();
|
|
|
|
|
context.spawn_local(async move {
|
|
|
|
|
backend.add_tracks(recording.borrow().as_ref().unwrap().id, tracks.borrow().clone()).await.unwrap();
|
|
|
|
|
callback();
|
|
|
|
|
window.close();
|
|
|
|
|
});
|
|
|
|
|
|
2020-10-31 23:07:25 +01:00
|
|
|
}));
|
|
|
|
|
|
|
|
|
|
add_track_button.connect_clicked(clone!(@strong window, @strong tracks, @strong track_list, @strong autofill_parts => move |_| {
|
|
|
|
|
let music_library_path = backend.get_music_library_path().unwrap();
|
|
|
|
|
|
2020-11-02 00:52:04 +01:00
|
|
|
let dialog = gtk::FileChooserNative::new(Some(&gettext("Select audio files")), Some(&window), gtk::FileChooserAction::Open, None, None);
|
2020-10-31 23:07:25 +01:00
|
|
|
dialog.set_select_multiple(true);
|
|
|
|
|
dialog.set_current_folder(&music_library_path);
|
|
|
|
|
|
|
|
|
|
if let gtk::ResponseType::Accept = dialog.run() {
|
|
|
|
|
let mut index = match track_list.get_selected_index() {
|
|
|
|
|
Some(index) => index + 1,
|
|
|
|
|
None => tracks.borrow().len(),
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
let mut tracks = tracks.borrow_mut();
|
|
|
|
|
for file_name in dialog.get_filenames() {
|
|
|
|
|
let file_name = file_name.strip_prefix(&music_library_path).unwrap();
|
2020-11-01 11:04:31 +01:00
|
|
|
tracks.insert(index, TrackDescription {
|
2020-10-31 23:07:25 +01:00
|
|
|
work_parts: Vec::new(),
|
|
|
|
|
file_name: String::from(file_name.to_str().unwrap()),
|
|
|
|
|
});
|
|
|
|
|
index += 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
track_list.show_items(tracks.borrow().clone());
|
|
|
|
|
autofill_parts();
|
|
|
|
|
track_list.select_index(index);
|
|
|
|
|
}
|
|
|
|
|
}));
|
|
|
|
|
|
|
|
|
|
remove_track_button.connect_clicked(
|
|
|
|
|
clone!(@strong tracks, @strong track_list => move |_| {
|
|
|
|
|
match track_list.get_selected_index() {
|
|
|
|
|
Some(index) => {
|
|
|
|
|
tracks.borrow_mut().remove(index);
|
|
|
|
|
track_list.show_items(tracks.borrow().clone());
|
|
|
|
|
track_list.select_index(index);
|
|
|
|
|
}
|
|
|
|
|
None => (),
|
|
|
|
|
}
|
|
|
|
|
}),
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
move_track_up_button.connect_clicked(
|
|
|
|
|
clone!(@strong tracks, @strong track_list => move |_| {
|
|
|
|
|
match track_list.get_selected_index() {
|
|
|
|
|
Some(index) => {
|
|
|
|
|
if index > 0 {
|
|
|
|
|
tracks.borrow_mut().swap(index - 1, index);
|
|
|
|
|
track_list.show_items(tracks.borrow().clone());
|
|
|
|
|
track_list.select_index(index - 1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
None => (),
|
|
|
|
|
}
|
|
|
|
|
}),
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
move_track_down_button.connect_clicked(
|
|
|
|
|
clone!(@strong tracks, @strong track_list => move |_| {
|
|
|
|
|
match track_list.get_selected_index() {
|
|
|
|
|
Some(index) => {
|
|
|
|
|
if index < tracks.borrow().len() - 1 {
|
|
|
|
|
tracks.borrow_mut().swap(index, index + 1);
|
|
|
|
|
track_list.show_items(tracks.borrow().clone());
|
|
|
|
|
track_list.select_index(index + 1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
None => (),
|
|
|
|
|
}
|
|
|
|
|
}),
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
edit_track_button.connect_clicked(clone!(@strong window, @strong tracks, @strong track_list, @strong recording => move |_| {
|
|
|
|
|
if let Some(index) = track_list.get_selected_index() {
|
|
|
|
|
if let Some(recording) = &*recording.borrow() {
|
|
|
|
|
TrackEditor::new(&window, tracks.borrow()[index].clone(), recording.work.clone(), clone!(@strong tracks, @strong track_list => move |track| {
|
|
|
|
|
let mut tracks = tracks.borrow_mut();
|
|
|
|
|
tracks[index] = track;
|
|
|
|
|
track_list.show_items(tracks.clone());
|
|
|
|
|
track_list.select_index(index);
|
|
|
|
|
})).show();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}));
|
|
|
|
|
|
|
|
|
|
scroll.add(&track_list.widget);
|
|
|
|
|
|
|
|
|
|
Self { window }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn show(&self) {
|
|
|
|
|
self.window.show();
|
|
|
|
|
}
|
|
|
|
|
}
|