2024-05-31 13:39:27 +02:00
|
|
|
use crate::{
|
2024-06-01 13:49:13 +02:00
|
|
|
db::{
|
|
|
|
|
self,
|
2024-06-05 19:03:04 +02:00
|
|
|
models::{Composer, Instrument, Person, Work, WorkPart},
|
2024-06-01 13:49:13 +02:00
|
|
|
},
|
2024-05-31 13:39:27 +02:00
|
|
|
editor::{
|
|
|
|
|
instrument_selector_popover::MusicusInstrumentSelectorPopover,
|
2024-06-06 15:17:56 +02:00
|
|
|
person_editor::MusicusPersonEditor, person_selector_popover::MusicusPersonSelectorPopover,
|
2024-05-31 13:57:14 +02:00
|
|
|
translation_editor::MusicusTranslationEditor,
|
2024-05-31 13:39:27 +02:00
|
|
|
work_editor_composer_row::MusicusWorkEditorComposerRow,
|
|
|
|
|
},
|
|
|
|
|
library::MusicusLibrary,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
use adw::{prelude::*, subclass::prelude::*};
|
2024-06-01 13:49:13 +02:00
|
|
|
use gettextrs::gettext;
|
2024-05-31 13:39:27 +02:00
|
|
|
use gtk::glib::{self, clone, Properties};
|
|
|
|
|
|
|
|
|
|
use std::cell::{OnceCell, RefCell};
|
|
|
|
|
|
|
|
|
|
mod imp {
|
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Default, gtk::CompositeTemplate, Properties)]
|
|
|
|
|
#[properties(wrapper_type = super::MusicusWorkEditor)]
|
|
|
|
|
#[template(file = "data/ui/work_editor.blp")]
|
|
|
|
|
pub struct MusicusWorkEditor {
|
2024-06-05 19:03:04 +02:00
|
|
|
#[property(get, construct_only)]
|
|
|
|
|
pub navigation: OnceCell<adw::NavigationView>,
|
|
|
|
|
|
2024-05-31 13:39:27 +02:00
|
|
|
#[property(get, construct_only)]
|
|
|
|
|
pub library: OnceCell<MusicusLibrary>,
|
|
|
|
|
|
|
|
|
|
// Holding a reference to each composer row is the simplest way to enumerate all
|
|
|
|
|
// results when finishing the process of editing the work. The composer rows
|
|
|
|
|
// handle all state related to the composer.
|
|
|
|
|
pub composer_rows: RefCell<Vec<MusicusWorkEditorComposerRow>>,
|
2024-06-01 13:49:13 +02:00
|
|
|
pub parts: RefCell<Vec<WorkPart>>,
|
2024-05-31 13:39:27 +02:00
|
|
|
pub instruments: RefCell<Vec<Instrument>>,
|
|
|
|
|
|
|
|
|
|
pub persons_popover: OnceCell<MusicusPersonSelectorPopover>,
|
|
|
|
|
pub instruments_popover: OnceCell<MusicusInstrumentSelectorPopover>,
|
|
|
|
|
|
2024-06-05 19:03:04 +02:00
|
|
|
#[template_child]
|
|
|
|
|
pub name_editor: TemplateChild<MusicusTranslationEditor>,
|
2024-05-31 13:39:27 +02:00
|
|
|
#[template_child]
|
|
|
|
|
pub composer_list: TemplateChild<gtk::ListBox>,
|
|
|
|
|
#[template_child]
|
|
|
|
|
pub select_person_box: TemplateChild<gtk::Box>,
|
|
|
|
|
#[template_child]
|
2024-06-01 13:49:13 +02:00
|
|
|
pub part_list: TemplateChild<gtk::ListBox>,
|
|
|
|
|
#[template_child]
|
2024-05-31 13:39:27 +02:00
|
|
|
pub instrument_list: TemplateChild<gtk::ListBox>,
|
|
|
|
|
#[template_child]
|
|
|
|
|
pub select_instrument_box: TemplateChild<gtk::Box>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[glib::object_subclass]
|
|
|
|
|
impl ObjectSubclass for MusicusWorkEditor {
|
|
|
|
|
const NAME: &'static str = "MusicusWorkEditor";
|
|
|
|
|
type Type = super::MusicusWorkEditor;
|
|
|
|
|
type ParentType = adw::NavigationPage;
|
|
|
|
|
|
|
|
|
|
fn class_init(klass: &mut Self::Class) {
|
2024-05-31 13:57:14 +02:00
|
|
|
MusicusTranslationEditor::static_type();
|
2024-05-31 13:39:27 +02:00
|
|
|
klass.bind_template();
|
|
|
|
|
klass.bind_template_instance_callbacks();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn instance_init(obj: &glib::subclass::InitializingObject<Self>) {
|
|
|
|
|
obj.init_template();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[glib::derived_properties]
|
|
|
|
|
impl ObjectImpl for MusicusWorkEditor {
|
|
|
|
|
fn constructed(&self) {
|
|
|
|
|
self.parent_constructed();
|
|
|
|
|
|
|
|
|
|
let persons_popover = MusicusPersonSelectorPopover::new(self.library.get().unwrap());
|
|
|
|
|
|
|
|
|
|
let obj = self.obj().clone();
|
2024-06-06 15:17:56 +02:00
|
|
|
persons_popover.connect_person_selected(move |_, person| {
|
|
|
|
|
obj.add_composer(person);
|
|
|
|
|
});
|
2024-05-31 13:39:27 +02:00
|
|
|
|
2024-06-06 15:17:56 +02:00
|
|
|
let obj = self.obj().clone();
|
|
|
|
|
persons_popover.connect_create(move |_| {
|
|
|
|
|
let editor = MusicusPersonEditor::new(&obj.navigation(), &obj.library(), None);
|
2024-05-31 13:39:27 +02:00
|
|
|
|
2024-07-18 15:01:30 +02:00
|
|
|
editor.connect_created(clone!(
|
|
|
|
|
#[weak]
|
|
|
|
|
obj,
|
|
|
|
|
move |_, person| {
|
|
|
|
|
obj.add_composer(person);
|
|
|
|
|
}
|
|
|
|
|
));
|
2024-05-31 13:39:27 +02:00
|
|
|
|
2024-06-06 15:17:56 +02:00
|
|
|
obj.navigation().push(&editor);
|
|
|
|
|
});
|
2024-05-31 13:39:27 +02:00
|
|
|
|
|
|
|
|
self.select_person_box.append(&persons_popover);
|
|
|
|
|
self.persons_popover.set(persons_popover).unwrap();
|
|
|
|
|
|
|
|
|
|
let instruments_popover =
|
|
|
|
|
MusicusInstrumentSelectorPopover::new(self.library.get().unwrap());
|
|
|
|
|
|
|
|
|
|
let obj = self.obj().clone();
|
|
|
|
|
instruments_popover.connect_instrument_selected(
|
|
|
|
|
move |_: &MusicusInstrumentSelectorPopover, instrument: Instrument| {
|
|
|
|
|
let row = adw::ActionRow::builder()
|
|
|
|
|
.title(instrument.to_string())
|
|
|
|
|
.build();
|
|
|
|
|
|
|
|
|
|
let remove_button = gtk::Button::builder()
|
|
|
|
|
.icon_name("user-trash-symbolic")
|
|
|
|
|
.valign(gtk::Align::Center)
|
|
|
|
|
.css_classes(["flat"])
|
|
|
|
|
.build();
|
|
|
|
|
|
2024-07-18 15:01:30 +02:00
|
|
|
remove_button.connect_clicked(clone!(
|
|
|
|
|
#[weak]
|
|
|
|
|
obj,
|
|
|
|
|
#[weak]
|
|
|
|
|
row,
|
|
|
|
|
#[strong]
|
|
|
|
|
instrument,
|
|
|
|
|
move |_| {
|
2024-05-31 13:39:27 +02:00
|
|
|
obj.imp().instrument_list.remove(&row);
|
2024-07-18 15:01:30 +02:00
|
|
|
obj.imp()
|
|
|
|
|
.instruments
|
|
|
|
|
.borrow_mut()
|
|
|
|
|
.retain(|i| *i != instrument);
|
|
|
|
|
}
|
|
|
|
|
));
|
2024-05-31 13:39:27 +02:00
|
|
|
|
|
|
|
|
row.add_suffix(&remove_button);
|
|
|
|
|
|
|
|
|
|
obj.imp()
|
|
|
|
|
.instrument_list
|
|
|
|
|
.insert(&row, obj.imp().instruments.borrow().len() as i32);
|
|
|
|
|
|
|
|
|
|
obj.imp().instruments.borrow_mut().push(instrument);
|
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
self.select_instrument_box.append(&instruments_popover);
|
|
|
|
|
self.instruments_popover.set(instruments_popover).unwrap();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl WidgetImpl for MusicusWorkEditor {}
|
|
|
|
|
impl NavigationPageImpl for MusicusWorkEditor {}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
glib::wrapper! {
|
|
|
|
|
pub struct MusicusWorkEditor(ObjectSubclass<imp::MusicusWorkEditor>)
|
|
|
|
|
@extends gtk::Widget, adw::NavigationPage;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[gtk::template_callbacks]
|
|
|
|
|
impl MusicusWorkEditor {
|
2024-06-05 19:03:04 +02:00
|
|
|
pub fn new(
|
|
|
|
|
navigation: &adw::NavigationView,
|
|
|
|
|
library: &MusicusLibrary,
|
|
|
|
|
work: Option<&Work>,
|
|
|
|
|
) -> Self {
|
|
|
|
|
let obj: Self = glib::Object::builder()
|
|
|
|
|
.property("navigation", navigation)
|
|
|
|
|
.property("library", library)
|
|
|
|
|
.build();
|
|
|
|
|
|
|
|
|
|
if let Some(_work) = work {
|
|
|
|
|
// TODO: Initialize work data.
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
obj
|
2024-05-31 13:39:27 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[template_callback]
|
|
|
|
|
fn add_person(&self, _: &adw::ActionRow) {
|
|
|
|
|
self.imp().persons_popover.get().unwrap().popup();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[template_callback]
|
|
|
|
|
fn add_part(&self, _: &adw::ActionRow) {
|
2024-06-01 13:49:13 +02:00
|
|
|
let part = WorkPart {
|
|
|
|
|
work_id: db::generate_id(),
|
|
|
|
|
..Default::default()
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let row = adw::EntryRow::builder().title(gettext("Name")).build();
|
|
|
|
|
|
|
|
|
|
let remove_button = gtk::Button::builder()
|
|
|
|
|
.icon_name("user-trash-symbolic")
|
|
|
|
|
.valign(gtk::Align::Center)
|
|
|
|
|
.css_classes(["flat"])
|
|
|
|
|
.build();
|
|
|
|
|
|
2024-07-18 15:01:30 +02:00
|
|
|
remove_button.connect_clicked(clone!(
|
|
|
|
|
#[weak(rename_to = this)]
|
|
|
|
|
self,
|
|
|
|
|
#[weak]
|
|
|
|
|
row,
|
|
|
|
|
#[strong]
|
|
|
|
|
part,
|
|
|
|
|
move |_| {
|
|
|
|
|
this.imp().part_list.remove(&row);
|
|
|
|
|
this.imp().parts.borrow_mut().retain(|p| *p != part);
|
|
|
|
|
}
|
|
|
|
|
));
|
2024-06-01 13:49:13 +02:00
|
|
|
|
|
|
|
|
row.add_suffix(&remove_button);
|
|
|
|
|
|
|
|
|
|
self.imp()
|
|
|
|
|
.part_list
|
|
|
|
|
.insert(&row, self.imp().parts.borrow().len() as i32);
|
|
|
|
|
|
|
|
|
|
row.grab_focus();
|
|
|
|
|
|
|
|
|
|
self.imp().parts.borrow_mut().push(part);
|
2024-05-31 13:39:27 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[template_callback]
|
|
|
|
|
fn add_instrument(&self, _: &adw::ActionRow) {
|
|
|
|
|
self.imp().instruments_popover.get().unwrap().popup();
|
|
|
|
|
}
|
2024-06-06 15:17:56 +02:00
|
|
|
|
|
|
|
|
fn add_composer(&self, person: Person) {
|
|
|
|
|
let role = self.library().composer_default_role().unwrap();
|
|
|
|
|
let composer = Composer { person, role };
|
2024-07-18 15:01:30 +02:00
|
|
|
let row = MusicusWorkEditorComposerRow::new(&self.navigation(), &self.library(), composer);
|
|
|
|
|
|
|
|
|
|
row.connect_remove(clone!(
|
|
|
|
|
#[weak(rename_to = this)]
|
|
|
|
|
self,
|
|
|
|
|
move |row| {
|
|
|
|
|
this.imp().composer_list.remove(row);
|
|
|
|
|
this.imp().composer_rows.borrow_mut().retain(|c| c != row);
|
|
|
|
|
}
|
|
|
|
|
));
|
2024-06-06 15:17:56 +02:00
|
|
|
|
|
|
|
|
self.imp()
|
|
|
|
|
.composer_list
|
|
|
|
|
.insert(&row, self.imp().composer_rows.borrow().len() as i32);
|
|
|
|
|
|
|
|
|
|
self.imp().composer_rows.borrow_mut().push(row);
|
|
|
|
|
}
|
2024-05-31 13:39:27 +02:00
|
|
|
}
|