mirror of
https://github.com/johrpan/musicus.git
synced 2025-10-26 19:57:25 +01:00
Merge recording editor and selector into one dialog
This commit is contained in:
parent
3e34658b25
commit
99bbd9e58f
22 changed files with 1167 additions and 1032 deletions
|
|
@ -10,6 +10,7 @@ diesel_migrations = "1.4.0"
|
||||||
fragile = "1.0.0"
|
fragile = "1.0.0"
|
||||||
futures = "0.3.6"
|
futures = "0.3.6"
|
||||||
futures-channel = "0.3.5"
|
futures-channel = "0.3.5"
|
||||||
|
gdk = "0.13.2"
|
||||||
gettext-rs = "0.5.0"
|
gettext-rs = "0.5.0"
|
||||||
gio = "0.9.1"
|
gio = "0.9.1"
|
||||||
glib = "0.10.2"
|
glib = "0.10.2"
|
||||||
|
|
|
||||||
|
|
@ -37,12 +37,17 @@ src/dialogs/instrument_editor.rs
|
||||||
src/dialogs/instrument_selector.rs
|
src/dialogs/instrument_selector.rs
|
||||||
src/dialogs/mod.rs
|
src/dialogs/mod.rs
|
||||||
src/dialogs/part_editor.rs
|
src/dialogs/part_editor.rs
|
||||||
src/dialogs/performance_editor.rs
|
|
||||||
src/dialogs/person_editor.rs
|
src/dialogs/person_editor.rs
|
||||||
src/dialogs/person_selector.rs
|
src/dialogs/person_selector.rs
|
||||||
src/dialogs/preferences.rs
|
src/dialogs/preferences.rs
|
||||||
src/dialogs/recording_editor.rs
|
src/dialogs/recording/mod.rs
|
||||||
src/dialogs/recording_selector.rs
|
src/dialogs/recording/performance_editor.rs
|
||||||
|
src/dialogs/recording/recording_dialog.rs
|
||||||
|
src/dialogs/recording/recording_editor_dialog.rs
|
||||||
|
src/dialogs/recording/recording_editor.rs
|
||||||
|
src/dialogs/recording/recording_selector_person_screen.rs
|
||||||
|
src/dialogs/recording/recording_selector.rs
|
||||||
|
src/dialogs/recording/recording_selector_work_screen.rs
|
||||||
src/dialogs/section_editor.rs
|
src/dialogs/section_editor.rs
|
||||||
src/dialogs/track_editor.rs
|
src/dialogs/track_editor.rs
|
||||||
src/dialogs/tracks_editor.rs
|
src/dialogs/tracks_editor.rs
|
||||||
|
|
|
||||||
58
po/de.po
58
po/de.po
|
|
@ -7,7 +7,7 @@ msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: \n"
|
"Project-Id-Version: \n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2020-11-08 02:37+0100\n"
|
"POT-Creation-Date: 2020-11-08 12:11+0100\n"
|
||||||
"PO-Revision-Date: 2020-11-08 02:37+0100\n"
|
"PO-Revision-Date: 2020-11-08 02:37+0100\n"
|
||||||
"Last-Translator: \n"
|
"Last-Translator: \n"
|
||||||
"Language-Team: \n"
|
"Language-Team: \n"
|
||||||
|
|
@ -18,14 +18,14 @@ msgstr ""
|
||||||
"X-Generator: Poedit 2.4.1\n"
|
"X-Generator: Poedit 2.4.1\n"
|
||||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||||
|
|
||||||
#: res/ui/ensemble_editor.ui:20 res/ui/performance_editor.ui:197
|
#: res/ui/ensemble_editor.ui:20 res/ui/performance_editor.ui:196
|
||||||
#: res/ui/performance_editor.ui:228
|
#: res/ui/performance_editor.ui:227
|
||||||
msgid "Ensemble"
|
msgid "Ensemble"
|
||||||
msgstr "Ensemble"
|
msgstr "Ensemble"
|
||||||
|
|
||||||
#: res/ui/ensemble_editor.ui:23 res/ui/instrument_editor.ui:23
|
#: res/ui/ensemble_editor.ui:23 res/ui/instrument_editor.ui:23
|
||||||
#: res/ui/part_editor.ui:25 res/ui/performance_editor.ui:23
|
#: res/ui/part_editor.ui:25 res/ui/performance_editor.ui:23
|
||||||
#: res/ui/person_editor.ui:23 res/ui/recording_editor.ui:25
|
#: res/ui/person_editor.ui:23 res/ui/recording_editor.ui:17
|
||||||
#: res/ui/section_editor.ui:23 res/ui/track_editor.ui:24
|
#: res/ui/section_editor.ui:23 res/ui/track_editor.ui:24
|
||||||
#: res/ui/tracks_editor.ui:39 res/ui/work_editor.ui:25
|
#: res/ui/tracks_editor.ui:39 res/ui/work_editor.ui:25
|
||||||
msgid "Cancel"
|
msgid "Cancel"
|
||||||
|
|
@ -33,7 +33,7 @@ msgstr "Abbrechen"
|
||||||
|
|
||||||
#: res/ui/ensemble_editor.ui:31 res/ui/instrument_editor.ui:31
|
#: res/ui/ensemble_editor.ui:31 res/ui/instrument_editor.ui:31
|
||||||
#: res/ui/part_editor.ui:33 res/ui/performance_editor.ui:31
|
#: res/ui/part_editor.ui:33 res/ui/performance_editor.ui:31
|
||||||
#: res/ui/person_editor.ui:31 res/ui/recording_editor.ui:33
|
#: res/ui/person_editor.ui:31 res/ui/recording_editor.ui:25
|
||||||
#: res/ui/section_editor.ui:31 res/ui/track_editor.ui:32
|
#: res/ui/section_editor.ui:31 res/ui/track_editor.ui:32
|
||||||
#: res/ui/tracks_editor.ui:24 res/ui/work_editor.ui:33
|
#: res/ui/tracks_editor.ui:24 res/ui/work_editor.ui:33
|
||||||
msgid "Save"
|
msgid "Save"
|
||||||
|
|
@ -53,8 +53,9 @@ msgid "Recordings"
|
||||||
msgstr "Aufnahmen"
|
msgstr "Aufnahmen"
|
||||||
|
|
||||||
#: res/ui/ensemble_screen.ui:188 res/ui/work_screen.ui:188
|
#: res/ui/ensemble_screen.ui:188 res/ui/work_screen.ui:188
|
||||||
#: src/dialogs/recording_selector.rs:205 src/screens/ensemble_screen.rs:53
|
#: src/dialogs/recording/recording_selector_work_screen.rs:38
|
||||||
#: src/screens/person_screen.rs:77 src/screens/work_screen.rs:54
|
#: src/screens/ensemble_screen.rs:53 src/screens/person_screen.rs:77
|
||||||
|
#: src/screens/work_screen.rs:54
|
||||||
msgid "No recordings found."
|
msgid "No recordings found."
|
||||||
msgstr "Keine Aufnahmen gefunden."
|
msgstr "Keine Aufnahmen gefunden."
|
||||||
|
|
||||||
|
|
@ -93,15 +94,16 @@ msgid "Title"
|
||||||
msgstr "Titel"
|
msgstr "Titel"
|
||||||
|
|
||||||
#: res/ui/part_editor.ui:116 res/ui/performance_editor.ui:87
|
#: res/ui/part_editor.ui:116 res/ui/performance_editor.ui:87
|
||||||
#: res/ui/performance_editor.ui:171 res/ui/performance_editor.ui:215
|
#: res/ui/performance_editor.ui:170 res/ui/performance_editor.ui:214
|
||||||
#: res/ui/recording_editor.ui:89 res/ui/tracks_editor.ui:93
|
#: res/ui/recording_editor.ui:81 res/ui/tracks_editor.ui:93
|
||||||
#: res/ui/work_editor.ui:77 src/dialogs/part_editor.rs:99
|
#: res/ui/work_editor.ui:77 src/dialogs/part_editor.rs:99
|
||||||
#: src/dialogs/performance_editor.rs:112 src/dialogs/performance_editor.rs:122
|
#: src/dialogs/recording/performance_editor.rs:150
|
||||||
#: src/dialogs/performance_editor.rs:136
|
#: src/dialogs/recording/performance_editor.rs:160
|
||||||
|
#: src/dialogs/recording/performance_editor.rs:170
|
||||||
msgid "Select …"
|
msgid "Select …"
|
||||||
msgstr "Auswählen …"
|
msgstr "Auswählen …"
|
||||||
|
|
||||||
#: res/ui/part_editor.ui:160 res/ui/recording_editor.ui:126
|
#: res/ui/part_editor.ui:160 res/ui/recording_editor.ui:119
|
||||||
#: res/ui/work_editor.ui:126
|
#: res/ui/work_editor.ui:126
|
||||||
msgid "Overview"
|
msgid "Overview"
|
||||||
msgstr "Überblick"
|
msgstr "Überblick"
|
||||||
|
|
@ -122,11 +124,11 @@ msgstr "Auftritt"
|
||||||
msgid "Role"
|
msgid "Role"
|
||||||
msgstr "Rolle"
|
msgstr "Rolle"
|
||||||
|
|
||||||
#: res/ui/performance_editor.ui:129
|
#: res/ui/performance_editor.ui:128
|
||||||
msgid "Type"
|
msgid "Type"
|
||||||
msgstr "Typ"
|
msgstr "Typ"
|
||||||
|
|
||||||
#: res/ui/performance_editor.ui:152 res/ui/performance_editor.ui:184
|
#: res/ui/performance_editor.ui:151 res/ui/performance_editor.ui:183
|
||||||
#: res/ui/person_editor.ui:20
|
#: res/ui/person_editor.ui:20
|
||||||
msgid "Person"
|
msgid "Person"
|
||||||
msgstr "Person"
|
msgstr "Person"
|
||||||
|
|
@ -200,24 +202,20 @@ msgstr "Keiner ausgewählt"
|
||||||
msgid "Select"
|
msgid "Select"
|
||||||
msgstr "Auswählen"
|
msgstr "Auswählen"
|
||||||
|
|
||||||
#: res/ui/recording_editor.ui:22 res/ui/tracks_editor.ui:68
|
#: res/ui/recording_editor.ui:14 res/ui/tracks_editor.ui:68
|
||||||
msgid "Recording"
|
msgid "Recording"
|
||||||
msgstr "Aufnahme"
|
msgstr "Aufnahme"
|
||||||
|
|
||||||
#: res/ui/recording_editor.ui:71
|
#: res/ui/recording_editor.ui:63
|
||||||
msgid "Comment"
|
msgid "Comment"
|
||||||
msgstr "Kommentar"
|
msgstr "Kommentar"
|
||||||
|
|
||||||
#: res/ui/recording_editor.ui:113 res/ui/tracks_editor.ui:109
|
#: res/ui/recording_editor.ui:106 res/ui/tracks_editor.ui:109
|
||||||
#: res/ui/work_editor.ui:22
|
#: res/ui/work_editor.ui:22
|
||||||
msgid "Work"
|
msgid "Work"
|
||||||
msgstr "Werk"
|
msgstr "Werk"
|
||||||
|
|
||||||
#: res/ui/recording_editor.ui:157
|
#: res/ui/recording_editor.ui:226 res/ui/tracks_editor.ui:127
|
||||||
msgid "No performers added."
|
|
||||||
msgstr "Keine Interpreten hinzugefügt."
|
|
||||||
|
|
||||||
#: res/ui/recording_editor.ui:251 res/ui/tracks_editor.ui:127
|
|
||||||
msgid "Performers"
|
msgid "Performers"
|
||||||
msgstr "Interpreten"
|
msgstr "Interpreten"
|
||||||
|
|
||||||
|
|
@ -233,12 +231,7 @@ msgstr "Zur Wiedergabeliste hinzufügen"
|
||||||
msgid "Select a composer on the left."
|
msgid "Select a composer on the left."
|
||||||
msgstr "Wählen Sie einen Komponisten aus."
|
msgstr "Wählen Sie einen Komponisten aus."
|
||||||
|
|
||||||
#: res/ui/recording_selector.ui:71 res/ui/recording_selector.ui:186
|
#: res/ui/recording_selector.ui:51
|
||||||
#: src/dialogs/recording_selector.rs:109 src/screens/person_screen.rs:57
|
|
||||||
msgid "No works found."
|
|
||||||
msgstr "Keine Werke gefunden."
|
|
||||||
|
|
||||||
#: res/ui/recording_selector.ui:108
|
|
||||||
msgid "Select a recording"
|
msgid "Select a recording"
|
||||||
msgstr "Aufnahme auswählen"
|
msgstr "Aufnahme auswählen"
|
||||||
|
|
||||||
|
|
@ -323,6 +316,15 @@ msgstr "Weitere Informationen und Quellcode"
|
||||||
msgid "Select music library folder"
|
msgid "Select music library folder"
|
||||||
msgstr "Ordner der Musikbibliothek auswählen"
|
msgstr "Ordner der Musikbibliothek auswählen"
|
||||||
|
|
||||||
|
#: src/dialogs/recording/recording_editor.rs:54
|
||||||
|
msgid "No performers added."
|
||||||
|
msgstr "Keine Interpreten hinzugefügt."
|
||||||
|
|
||||||
|
#: src/dialogs/recording/recording_selector_person_screen.rs:39
|
||||||
|
#: src/screens/person_screen.rs:57
|
||||||
|
msgid "No works found."
|
||||||
|
msgstr "Keine Werke gefunden."
|
||||||
|
|
||||||
#: src/dialogs/tracks_editor.rs:60
|
#: src/dialogs/tracks_editor.rs:60
|
||||||
msgid "Add some tracks."
|
msgid "Add some tracks."
|
||||||
msgstr "Fügen Sie Tracks hinzu."
|
msgstr "Fügen Sie Tracks hinzu."
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: musicus\n"
|
"Project-Id-Version: musicus\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2020-11-08 02:37+0100\n"
|
"POT-Creation-Date: 2020-11-08 12:11+0100\n"
|
||||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||||
|
|
@ -17,14 +17,14 @@ msgstr ""
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
|
||||||
#: res/ui/ensemble_editor.ui:20 res/ui/performance_editor.ui:197
|
#: res/ui/ensemble_editor.ui:20 res/ui/performance_editor.ui:196
|
||||||
#: res/ui/performance_editor.ui:228
|
#: res/ui/performance_editor.ui:227
|
||||||
msgid "Ensemble"
|
msgid "Ensemble"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: res/ui/ensemble_editor.ui:23 res/ui/instrument_editor.ui:23
|
#: res/ui/ensemble_editor.ui:23 res/ui/instrument_editor.ui:23
|
||||||
#: res/ui/part_editor.ui:25 res/ui/performance_editor.ui:23
|
#: res/ui/part_editor.ui:25 res/ui/performance_editor.ui:23
|
||||||
#: res/ui/person_editor.ui:23 res/ui/recording_editor.ui:25
|
#: res/ui/person_editor.ui:23 res/ui/recording_editor.ui:17
|
||||||
#: res/ui/section_editor.ui:23 res/ui/track_editor.ui:24
|
#: res/ui/section_editor.ui:23 res/ui/track_editor.ui:24
|
||||||
#: res/ui/tracks_editor.ui:39 res/ui/work_editor.ui:25
|
#: res/ui/tracks_editor.ui:39 res/ui/work_editor.ui:25
|
||||||
msgid "Cancel"
|
msgid "Cancel"
|
||||||
|
|
@ -32,7 +32,7 @@ msgstr ""
|
||||||
|
|
||||||
#: res/ui/ensemble_editor.ui:31 res/ui/instrument_editor.ui:31
|
#: res/ui/ensemble_editor.ui:31 res/ui/instrument_editor.ui:31
|
||||||
#: res/ui/part_editor.ui:33 res/ui/performance_editor.ui:31
|
#: res/ui/part_editor.ui:33 res/ui/performance_editor.ui:31
|
||||||
#: res/ui/person_editor.ui:31 res/ui/recording_editor.ui:33
|
#: res/ui/person_editor.ui:31 res/ui/recording_editor.ui:25
|
||||||
#: res/ui/section_editor.ui:31 res/ui/track_editor.ui:32
|
#: res/ui/section_editor.ui:31 res/ui/track_editor.ui:32
|
||||||
#: res/ui/tracks_editor.ui:24 res/ui/work_editor.ui:33
|
#: res/ui/tracks_editor.ui:24 res/ui/work_editor.ui:33
|
||||||
msgid "Save"
|
msgid "Save"
|
||||||
|
|
@ -52,8 +52,9 @@ msgid "Recordings"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: res/ui/ensemble_screen.ui:188 res/ui/work_screen.ui:188
|
#: res/ui/ensemble_screen.ui:188 res/ui/work_screen.ui:188
|
||||||
#: src/dialogs/recording_selector.rs:205 src/screens/ensemble_screen.rs:53
|
#: src/dialogs/recording/recording_selector_work_screen.rs:38
|
||||||
#: src/screens/person_screen.rs:77 src/screens/work_screen.rs:54
|
#: src/screens/ensemble_screen.rs:53 src/screens/person_screen.rs:77
|
||||||
|
#: src/screens/work_screen.rs:54
|
||||||
msgid "No recordings found."
|
msgid "No recordings found."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
|
@ -92,15 +93,16 @@ msgid "Title"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: res/ui/part_editor.ui:116 res/ui/performance_editor.ui:87
|
#: res/ui/part_editor.ui:116 res/ui/performance_editor.ui:87
|
||||||
#: res/ui/performance_editor.ui:171 res/ui/performance_editor.ui:215
|
#: res/ui/performance_editor.ui:170 res/ui/performance_editor.ui:214
|
||||||
#: res/ui/recording_editor.ui:89 res/ui/tracks_editor.ui:93
|
#: res/ui/recording_editor.ui:81 res/ui/tracks_editor.ui:93
|
||||||
#: res/ui/work_editor.ui:77 src/dialogs/part_editor.rs:99
|
#: res/ui/work_editor.ui:77 src/dialogs/part_editor.rs:99
|
||||||
#: src/dialogs/performance_editor.rs:112 src/dialogs/performance_editor.rs:122
|
#: src/dialogs/recording/performance_editor.rs:150
|
||||||
#: src/dialogs/performance_editor.rs:136
|
#: src/dialogs/recording/performance_editor.rs:160
|
||||||
|
#: src/dialogs/recording/performance_editor.rs:170
|
||||||
msgid "Select …"
|
msgid "Select …"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: res/ui/part_editor.ui:160 res/ui/recording_editor.ui:126
|
#: res/ui/part_editor.ui:160 res/ui/recording_editor.ui:119
|
||||||
#: res/ui/work_editor.ui:126
|
#: res/ui/work_editor.ui:126
|
||||||
msgid "Overview"
|
msgid "Overview"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
@ -121,11 +123,11 @@ msgstr ""
|
||||||
msgid "Role"
|
msgid "Role"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: res/ui/performance_editor.ui:129
|
#: res/ui/performance_editor.ui:128
|
||||||
msgid "Type"
|
msgid "Type"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: res/ui/performance_editor.ui:152 res/ui/performance_editor.ui:184
|
#: res/ui/performance_editor.ui:151 res/ui/performance_editor.ui:183
|
||||||
#: res/ui/person_editor.ui:20
|
#: res/ui/person_editor.ui:20
|
||||||
msgid "Person"
|
msgid "Person"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
@ -199,24 +201,20 @@ msgstr ""
|
||||||
msgid "Select"
|
msgid "Select"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: res/ui/recording_editor.ui:22 res/ui/tracks_editor.ui:68
|
#: res/ui/recording_editor.ui:14 res/ui/tracks_editor.ui:68
|
||||||
msgid "Recording"
|
msgid "Recording"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: res/ui/recording_editor.ui:71
|
#: res/ui/recording_editor.ui:63
|
||||||
msgid "Comment"
|
msgid "Comment"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: res/ui/recording_editor.ui:113 res/ui/tracks_editor.ui:109
|
#: res/ui/recording_editor.ui:106 res/ui/tracks_editor.ui:109
|
||||||
#: res/ui/work_editor.ui:22
|
#: res/ui/work_editor.ui:22
|
||||||
msgid "Work"
|
msgid "Work"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: res/ui/recording_editor.ui:157
|
#: res/ui/recording_editor.ui:226 res/ui/tracks_editor.ui:127
|
||||||
msgid "No performers added."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: res/ui/recording_editor.ui:251 res/ui/tracks_editor.ui:127
|
|
||||||
msgid "Performers"
|
msgid "Performers"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
|
@ -232,12 +230,7 @@ msgstr ""
|
||||||
msgid "Select a composer on the left."
|
msgid "Select a composer on the left."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: res/ui/recording_selector.ui:71 res/ui/recording_selector.ui:186
|
#: res/ui/recording_selector.ui:51
|
||||||
#: src/dialogs/recording_selector.rs:109 src/screens/person_screen.rs:57
|
|
||||||
msgid "No works found."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: res/ui/recording_selector.ui:108
|
|
||||||
msgid "Select a recording"
|
msgid "Select a recording"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
|
@ -317,6 +310,15 @@ msgstr ""
|
||||||
msgid "Select music library folder"
|
msgid "Select music library folder"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/dialogs/recording/recording_editor.rs:54
|
||||||
|
msgid "No performers added."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/dialogs/recording/recording_selector_person_screen.rs:39
|
||||||
|
#: src/screens/person_screen.rs:57
|
||||||
|
msgid "No works found."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/dialogs/tracks_editor.rs:60
|
#: src/dialogs/tracks_editor.rs:60
|
||||||
msgid "Add some tracks."
|
msgid "Add some tracks."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
|
||||||
|
|
@ -96,7 +96,6 @@
|
||||||
</child>
|
</child>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkButton" id="reset_role_button">
|
<object class="GtkButton" id="reset_role_button">
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can-focus">True</property>
|
<property name="can-focus">True</property>
|
||||||
<property name="receives-default">True</property>
|
<property name="receives-default">True</property>
|
||||||
<child>
|
<child>
|
||||||
|
|
|
||||||
|
|
@ -3,15 +3,7 @@
|
||||||
<interface>
|
<interface>
|
||||||
<requires lib="gtk+" version="3.22"/>
|
<requires lib="gtk+" version="3.22"/>
|
||||||
<requires lib="libhandy" version="0.0"/>
|
<requires lib="libhandy" version="0.0"/>
|
||||||
<object class="HdyWindow" id="window">
|
<object class="GtkBox" id="widget">
|
||||||
<property name="can-focus">False</property>
|
|
||||||
<property name="modal">True</property>
|
|
||||||
<property name="default-width">500</property>
|
|
||||||
<property name="default-height">450</property>
|
|
||||||
<property name="destroy-with-parent">True</property>
|
|
||||||
<property name="type-hint">dialog</property>
|
|
||||||
<child>
|
|
||||||
<object class="GtkBox">
|
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can-focus">False</property>
|
<property name="can-focus">False</property>
|
||||||
<property name="orientation">vertical</property>
|
<property name="orientation">vertical</property>
|
||||||
|
|
@ -87,6 +79,7 @@
|
||||||
<property name="can-focus">False</property>
|
<property name="can-focus">False</property>
|
||||||
<property name="halign">start</property>
|
<property name="halign">start</property>
|
||||||
<property name="label" translatable="yes">Select …</property>
|
<property name="label" translatable="yes">Select …</property>
|
||||||
|
<property name="ellipsize">end</property>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
</object>
|
</object>
|
||||||
|
|
@ -136,30 +129,12 @@
|
||||||
<property name="border-width">18</property>
|
<property name="border-width">18</property>
|
||||||
<property name="spacing">6</property>
|
<property name="spacing">6</property>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkScrolledWindow">
|
<object class="GtkScrolledWindow" id="scroll">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can-focus">True</property>
|
<property name="can-focus">True</property>
|
||||||
<property name="shadow-type">in</property>
|
<property name="shadow-type">in</property>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkViewport">
|
<placeholder/>
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can-focus">False</property>
|
|
||||||
<child>
|
|
||||||
<object class="GtkListBox" id="performer_list">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can-focus">False</property>
|
|
||||||
<property name="hexpand">True</property>
|
|
||||||
<property name="vexpand">True</property>
|
|
||||||
<child type="placeholder">
|
|
||||||
<object class="GtkLabel">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can-focus">False</property>
|
|
||||||
<property name="label" translatable="yes">No performers added.</property>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
</child>
|
</child>
|
||||||
</object>
|
</object>
|
||||||
<packing>
|
<packing>
|
||||||
|
|
@ -263,6 +238,4 @@
|
||||||
</packing>
|
</packing>
|
||||||
</child>
|
</child>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
</interface>
|
</interface>
|
||||||
|
|
|
||||||
|
|
@ -33,64 +33,7 @@
|
||||||
</packing>
|
</packing>
|
||||||
</child>
|
</child>
|
||||||
</object>
|
</object>
|
||||||
<object class="GtkBox" id="person_screen">
|
<object class="HdyLeaflet" id="widget">
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can-focus">False</property>
|
|
||||||
<property name="orientation">vertical</property>
|
|
||||||
<child>
|
|
||||||
<object class="HdyHeaderBar" id="person_screen_header">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can-focus">False</property>
|
|
||||||
<property name="hexpand">True</property>
|
|
||||||
<property name="show-close-button">True</property>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="expand">False</property>
|
|
||||||
<property name="fill">True</property>
|
|
||||||
<property name="position">0</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="GtkStack" id="person_screen_stack">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can-focus">False</property>
|
|
||||||
<child>
|
|
||||||
<object class="GtkSpinner">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can-focus">False</property>
|
|
||||||
<property name="active">True</property>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="name">loading</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="GtkLabel">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can-focus">False</property>
|
|
||||||
<property name="label" translatable="yes">No works found.</property>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="name">empty</property>
|
|
||||||
<property name="position">1</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="expand">True</property>
|
|
||||||
<property name="fill">True</property>
|
|
||||||
<property name="position">1</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
<object class="HdyWindow" id="window">
|
|
||||||
<property name="can-focus">False</property>
|
|
||||||
<property name="modal">True</property>
|
|
||||||
<property name="default-width">600</property>
|
|
||||||
<property name="default-height">424</property>
|
|
||||||
<property name="type-hint">dialog</property>
|
|
||||||
<child>
|
|
||||||
<object class="HdyLeaflet" id="leaflet">
|
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can-focus">False</property>
|
<property name="can-focus">False</property>
|
||||||
<property name="visible-child">sidebar_box</property>
|
<property name="visible-child">sidebar_box</property>
|
||||||
|
|
@ -106,7 +49,7 @@
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can-focus">False</property>
|
<property name="can-focus">False</property>
|
||||||
<property name="title" translatable="yes">Select a recording</property>
|
<property name="title" translatable="yes">Select a recording</property>
|
||||||
<property name="show-close-button" bind-source="leaflet" bind-property="folded" bind-flags="sync-create">False</property>
|
<property name="show-close-button" bind-source="widget" bind-property="folded" bind-flags="sync-create">False</property>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkButton" id="add_button">
|
<object class="GtkButton" id="add_button">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
|
|
@ -147,55 +90,4 @@
|
||||||
</packing>
|
</packing>
|
||||||
</child>
|
</child>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
<object class="GtkBox" id="work_screen">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can-focus">False</property>
|
|
||||||
<property name="orientation">vertical</property>
|
|
||||||
<child>
|
|
||||||
<object class="HdyHeaderBar" id="work_screen_header">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can-focus">False</property>
|
|
||||||
<property name="hexpand">True</property>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="expand">False</property>
|
|
||||||
<property name="fill">True</property>
|
|
||||||
<property name="position">0</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="GtkStack" id="work_screen_stack">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can-focus">False</property>
|
|
||||||
<child>
|
|
||||||
<object class="GtkSpinner">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can-focus">False</property>
|
|
||||||
<property name="active">True</property>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="name">loading</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="GtkLabel">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can-focus">False</property>
|
|
||||||
<property name="label" translatable="yes">No works found.</property>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="name">empty</property>
|
|
||||||
<property name="position">1</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="expand">True</property>
|
|
||||||
<property name="fill">True</property>
|
|
||||||
<property name="position">1</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
</interface>
|
</interface>
|
||||||
|
|
|
||||||
|
|
@ -16,9 +16,6 @@ pub use instrument_selector::*;
|
||||||
pub mod part_editor;
|
pub mod part_editor;
|
||||||
pub use part_editor::*;
|
pub use part_editor::*;
|
||||||
|
|
||||||
pub mod performance_editor;
|
|
||||||
pub use performance_editor::*;
|
|
||||||
|
|
||||||
pub mod person_editor;
|
pub mod person_editor;
|
||||||
pub use person_editor::*;
|
pub use person_editor::*;
|
||||||
|
|
||||||
|
|
@ -28,11 +25,8 @@ pub use person_selector::*;
|
||||||
pub mod preferences;
|
pub mod preferences;
|
||||||
pub use preferences::*;
|
pub use preferences::*;
|
||||||
|
|
||||||
pub mod recording_editor;
|
pub mod recording;
|
||||||
pub use recording_editor::*;
|
pub use recording::*;
|
||||||
|
|
||||||
pub mod recording_selector;
|
|
||||||
pub use recording_selector::*;
|
|
||||||
|
|
||||||
pub mod section_editor;
|
pub mod section_editor;
|
||||||
pub use section_editor::*;
|
pub use section_editor::*;
|
||||||
|
|
|
||||||
|
|
@ -1,147 +0,0 @@
|
||||||
use super::*;
|
|
||||||
use crate::backend::Backend;
|
|
||||||
use crate::database::*;
|
|
||||||
use gettextrs::gettext;
|
|
||||||
use glib::clone;
|
|
||||||
use gtk::prelude::*;
|
|
||||||
use gtk_macros::get_widget;
|
|
||||||
use std::cell::RefCell;
|
|
||||||
use std::rc::Rc;
|
|
||||||
|
|
||||||
pub struct PerformanceEditor<F>
|
|
||||||
where
|
|
||||||
F: Fn(PerformanceDescription) -> () + 'static,
|
|
||||||
{
|
|
||||||
backend: Rc<Backend>,
|
|
||||||
window: libhandy::Window,
|
|
||||||
callback: F,
|
|
||||||
save_button: gtk::Button,
|
|
||||||
person_label: gtk::Label,
|
|
||||||
ensemble_label: gtk::Label,
|
|
||||||
role_label: gtk::Label,
|
|
||||||
person: RefCell<Option<Person>>,
|
|
||||||
ensemble: RefCell<Option<Ensemble>>,
|
|
||||||
role: RefCell<Option<Instrument>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<F> PerformanceEditor<F>
|
|
||||||
where
|
|
||||||
F: Fn(PerformanceDescription) -> () + 'static,
|
|
||||||
{
|
|
||||||
pub fn new<P: IsA<gtk::Window>>(
|
|
||||||
backend: Rc<Backend>,
|
|
||||||
parent: &P,
|
|
||||||
performance: Option<PerformanceDescription>,
|
|
||||||
callback: F,
|
|
||||||
) -> Rc<Self> {
|
|
||||||
let builder =
|
|
||||||
gtk::Builder::from_resource("/de/johrpan/musicus/ui/performance_editor.ui");
|
|
||||||
|
|
||||||
get_widget!(builder, libhandy::Window, window);
|
|
||||||
get_widget!(builder, gtk::Button, cancel_button);
|
|
||||||
get_widget!(builder, gtk::Button, save_button);
|
|
||||||
get_widget!(builder, gtk::Button, person_button);
|
|
||||||
get_widget!(builder, gtk::Button, ensemble_button);
|
|
||||||
get_widget!(builder, gtk::Button, role_button);
|
|
||||||
get_widget!(builder, gtk::Button, reset_role_button);
|
|
||||||
get_widget!(builder, gtk::Label, person_label);
|
|
||||||
get_widget!(builder, gtk::Label, ensemble_label);
|
|
||||||
get_widget!(builder, gtk::Label, role_label);
|
|
||||||
|
|
||||||
let (person, ensemble, role) = match performance {
|
|
||||||
Some(performance) => {
|
|
||||||
match performance.person.clone() {
|
|
||||||
Some(person) => {
|
|
||||||
person_label.set_text(&person.name_fl());
|
|
||||||
save_button.set_sensitive(true);
|
|
||||||
}
|
|
||||||
None => (),
|
|
||||||
}
|
|
||||||
|
|
||||||
match performance.ensemble.clone() {
|
|
||||||
Some(ensemble) => {
|
|
||||||
ensemble_label.set_text(&ensemble.name);
|
|
||||||
save_button.set_sensitive(true);
|
|
||||||
}
|
|
||||||
None => (),
|
|
||||||
}
|
|
||||||
|
|
||||||
match performance.role.clone() {
|
|
||||||
Some(role) => role_label.set_text(&role.name),
|
|
||||||
None => (),
|
|
||||||
}
|
|
||||||
|
|
||||||
(performance.person, performance.ensemble, performance.role)
|
|
||||||
}
|
|
||||||
None => (None, None, None),
|
|
||||||
};
|
|
||||||
|
|
||||||
let result = Rc::new(PerformanceEditor {
|
|
||||||
backend: backend,
|
|
||||||
window: window,
|
|
||||||
callback: callback,
|
|
||||||
save_button: save_button,
|
|
||||||
person_label: person_label,
|
|
||||||
ensemble_label: ensemble_label,
|
|
||||||
role_label: role_label,
|
|
||||||
person: RefCell::new(person),
|
|
||||||
ensemble: RefCell::new(ensemble),
|
|
||||||
role: RefCell::new(role),
|
|
||||||
});
|
|
||||||
|
|
||||||
cancel_button.connect_clicked(clone!(@strong result => move |_| {
|
|
||||||
result.window.close();
|
|
||||||
}));
|
|
||||||
|
|
||||||
result
|
|
||||||
.save_button
|
|
||||||
.connect_clicked(clone!(@strong result => move |_| {
|
|
||||||
(result.callback)(PerformanceDescription {
|
|
||||||
person: result.person.borrow().clone(),
|
|
||||||
ensemble: result.ensemble.borrow().clone(),
|
|
||||||
role: result.role.borrow().clone(),
|
|
||||||
});
|
|
||||||
result.window.close();
|
|
||||||
}));
|
|
||||||
|
|
||||||
person_button.connect_clicked(clone!(@strong result => move |_| {
|
|
||||||
PersonSelector::new(result.backend.clone(), &result.window, clone!(@strong result => move |person| {
|
|
||||||
result.person.replace(Some(person.clone()));
|
|
||||||
result.person_label.set_text(&person.name_fl());
|
|
||||||
result.ensemble.replace(None);
|
|
||||||
result.ensemble_label.set_text(&gettext("Select …"));
|
|
||||||
result.save_button.set_sensitive(true);
|
|
||||||
})).show();
|
|
||||||
}));
|
|
||||||
|
|
||||||
ensemble_button.connect_clicked(clone!(@strong result => move |_| {
|
|
||||||
EnsembleSelector::new(result.backend.clone(), &result.window, clone!(@strong result => move |ensemble| {
|
|
||||||
result.ensemble.replace(Some(ensemble.clone()));
|
|
||||||
result.ensemble_label.set_text(&ensemble.name);
|
|
||||||
result.person.replace(None);
|
|
||||||
result.person_label.set_text(&gettext("Select …"));
|
|
||||||
result.save_button.set_sensitive(true);
|
|
||||||
})).show();
|
|
||||||
}));
|
|
||||||
|
|
||||||
role_button.connect_clicked(clone!(@strong result => move |_| {
|
|
||||||
InstrumentSelector::new(result.backend.clone(), &result.window, clone!(@strong result => move |role| {
|
|
||||||
result.role.replace(Some(role.clone()));
|
|
||||||
result.role_label.set_text(&role.name);
|
|
||||||
})).show();
|
|
||||||
}));
|
|
||||||
|
|
||||||
reset_role_button.connect_clicked(clone!(@strong result => move |_| {
|
|
||||||
result.role.replace(None);
|
|
||||||
result.role_label.set_text(&gettext("Select …"));
|
|
||||||
}));
|
|
||||||
|
|
||||||
result.window.set_transient_for(Some(parent));
|
|
||||||
|
|
||||||
result
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn show(&self) {
|
|
||||||
self.window.show();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
11
src/dialogs/recording/mod.rs
Normal file
11
src/dialogs/recording/mod.rs
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
pub mod recording_dialog;
|
||||||
|
pub use recording_dialog::*;
|
||||||
|
|
||||||
|
pub mod recording_editor_dialog;
|
||||||
|
pub use recording_editor_dialog::*;
|
||||||
|
|
||||||
|
mod performance_editor;
|
||||||
|
mod recording_editor;
|
||||||
|
mod recording_selector;
|
||||||
|
mod recording_selector_person_screen;
|
||||||
|
mod recording_selector_work_screen;
|
||||||
174
src/dialogs/recording/performance_editor.rs
Normal file
174
src/dialogs/recording/performance_editor.rs
Normal file
|
|
@ -0,0 +1,174 @@
|
||||||
|
use crate::backend::*;
|
||||||
|
use crate::database::*;
|
||||||
|
use crate::dialogs::*;
|
||||||
|
use gettextrs::gettext;
|
||||||
|
use glib::clone;
|
||||||
|
use gtk::prelude::*;
|
||||||
|
use gtk_macros::get_widget;
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
/// A dialog for editing a performance within a recording.
|
||||||
|
pub struct PerformanceEditor {
|
||||||
|
backend: Rc<Backend>,
|
||||||
|
window: libhandy::Window,
|
||||||
|
save_button: gtk::Button,
|
||||||
|
person_label: gtk::Label,
|
||||||
|
ensemble_label: gtk::Label,
|
||||||
|
role_label: gtk::Label,
|
||||||
|
reset_role_button: gtk::Button,
|
||||||
|
person: RefCell<Option<Person>>,
|
||||||
|
ensemble: RefCell<Option<Ensemble>>,
|
||||||
|
role: RefCell<Option<Instrument>>,
|
||||||
|
selected_cb: RefCell<Option<Box<dyn Fn(PerformanceDescription) -> ()>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PerformanceEditor {
|
||||||
|
/// Create a new performance editor.
|
||||||
|
pub fn new<P: IsA<gtk::Window>>(
|
||||||
|
backend: Rc<Backend>,
|
||||||
|
parent: &P,
|
||||||
|
performance: Option<PerformanceDescription>,
|
||||||
|
) -> Rc<Self> {
|
||||||
|
// Create UI
|
||||||
|
|
||||||
|
let builder = gtk::Builder::from_resource("/de/johrpan/musicus/ui/performance_editor.ui");
|
||||||
|
|
||||||
|
get_widget!(builder, libhandy::Window, window);
|
||||||
|
get_widget!(builder, gtk::Button, cancel_button);
|
||||||
|
get_widget!(builder, gtk::Button, save_button);
|
||||||
|
get_widget!(builder, gtk::Button, person_button);
|
||||||
|
get_widget!(builder, gtk::Button, ensemble_button);
|
||||||
|
get_widget!(builder, gtk::Button, role_button);
|
||||||
|
get_widget!(builder, gtk::Button, reset_role_button);
|
||||||
|
get_widget!(builder, gtk::Label, person_label);
|
||||||
|
get_widget!(builder, gtk::Label, ensemble_label);
|
||||||
|
get_widget!(builder, gtk::Label, role_label);
|
||||||
|
|
||||||
|
window.set_transient_for(Some(parent));
|
||||||
|
|
||||||
|
let this = Rc::new(PerformanceEditor {
|
||||||
|
backend,
|
||||||
|
window,
|
||||||
|
save_button,
|
||||||
|
person_label,
|
||||||
|
ensemble_label,
|
||||||
|
role_label,
|
||||||
|
reset_role_button,
|
||||||
|
person: RefCell::new(None),
|
||||||
|
ensemble: RefCell::new(None),
|
||||||
|
role: RefCell::new(None),
|
||||||
|
selected_cb: RefCell::new(None),
|
||||||
|
});
|
||||||
|
|
||||||
|
// Connect signals and callbacks
|
||||||
|
|
||||||
|
cancel_button.connect_clicked(clone!(@strong this => move |_| {
|
||||||
|
this.window.close();
|
||||||
|
}));
|
||||||
|
|
||||||
|
this.save_button
|
||||||
|
.connect_clicked(clone!(@strong this => move |_| {
|
||||||
|
if let Some(cb) = &*this.selected_cb.borrow() {
|
||||||
|
cb(PerformanceDescription {
|
||||||
|
person: this.person.borrow().clone(),
|
||||||
|
ensemble: this.ensemble.borrow().clone(),
|
||||||
|
role: this.role.borrow().clone(),
|
||||||
|
});
|
||||||
|
|
||||||
|
this.window.close();
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
person_button.connect_clicked(clone!(@strong this => move |_| {
|
||||||
|
PersonSelector::new(this.backend.clone(), &this.window, clone!(@strong this => move |person| {
|
||||||
|
this.show_person(Some(&person));
|
||||||
|
this.person.replace(Some(person));
|
||||||
|
this.show_ensemble(None);
|
||||||
|
this.ensemble.replace(None);
|
||||||
|
})).show();
|
||||||
|
}));
|
||||||
|
|
||||||
|
ensemble_button.connect_clicked(clone!(@strong this => move |_| {
|
||||||
|
EnsembleSelector::new(this.backend.clone(), &this.window, clone!(@strong this => move |ensemble| {
|
||||||
|
this.show_person(None);
|
||||||
|
this.person.replace(None);
|
||||||
|
this.show_ensemble(Some(&ensemble));
|
||||||
|
this.ensemble.replace(Some(ensemble));
|
||||||
|
})).show();
|
||||||
|
}));
|
||||||
|
|
||||||
|
role_button.connect_clicked(clone!(@strong this => move |_| {
|
||||||
|
InstrumentSelector::new(this.backend.clone(), &this.window, clone!(@strong this => move |role| {
|
||||||
|
this.show_role(Some(&role));
|
||||||
|
this.role.replace(Some(role));
|
||||||
|
})).show();
|
||||||
|
}));
|
||||||
|
|
||||||
|
this.reset_role_button
|
||||||
|
.connect_clicked(clone!(@strong this => move |_| {
|
||||||
|
this.show_role(None);
|
||||||
|
this.role.replace(None);
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Initialize
|
||||||
|
|
||||||
|
if let Some(performance) = performance {
|
||||||
|
if let Some(person) = performance.person {
|
||||||
|
this.show_person(Some(&person));
|
||||||
|
this.person.replace(Some(person));
|
||||||
|
} else if let Some(ensemble) = performance.ensemble {
|
||||||
|
this.show_ensemble(Some(&ensemble));
|
||||||
|
this.ensemble.replace(Some(ensemble));
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(role) = performance.role {
|
||||||
|
this.show_role(Some(&role));
|
||||||
|
this.role.replace(Some(role));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set a closure to be called when the user has chosen to save the performance.
|
||||||
|
pub fn set_selected_cb<F: Fn(PerformanceDescription) -> () + 'static>(&self, cb: F) {
|
||||||
|
self.selected_cb.replace(Some(Box::new(cb)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Show the performance editor.
|
||||||
|
pub fn show(&self) {
|
||||||
|
self.window.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Update the UI according to person.
|
||||||
|
fn show_person(&self, person: Option<&Person>) {
|
||||||
|
if let Some(person) = person {
|
||||||
|
self.person_label.set_text(&person.name_fl());
|
||||||
|
self.save_button.set_sensitive(true);
|
||||||
|
} else {
|
||||||
|
self.person_label.set_text(&gettext("Select …"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Update the UI according to ensemble.
|
||||||
|
fn show_ensemble(&self, ensemble: Option<&Ensemble>) {
|
||||||
|
if let Some(ensemble) = ensemble {
|
||||||
|
self.ensemble_label.set_text(&ensemble.name);
|
||||||
|
self.save_button.set_sensitive(true);
|
||||||
|
} else {
|
||||||
|
self.ensemble_label.set_text(&gettext("Select …"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Update the UI according to role.
|
||||||
|
fn show_role(&self, role: Option<&Instrument>) {
|
||||||
|
if let Some(role) = role {
|
||||||
|
self.role_label.set_text(&role.name);
|
||||||
|
self.reset_role_button.show();
|
||||||
|
} else {
|
||||||
|
self.role_label.set_text(&gettext("Select …"));
|
||||||
|
self.reset_role_button.hide();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
86
src/dialogs/recording/recording_dialog.rs
Normal file
86
src/dialogs/recording/recording_dialog.rs
Normal file
|
|
@ -0,0 +1,86 @@
|
||||||
|
use super::recording_editor::*;
|
||||||
|
use super::recording_selector::*;
|
||||||
|
use crate::backend::*;
|
||||||
|
use crate::database::*;
|
||||||
|
use glib::clone;
|
||||||
|
use gtk::prelude::*;
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
/// A dialog for selecting and creating a recording.
|
||||||
|
pub struct RecordingDialog {
|
||||||
|
pub window: libhandy::Window,
|
||||||
|
stack: gtk::Stack,
|
||||||
|
selector: Rc<RecordingSelector>,
|
||||||
|
editor: Rc<RecordingEditor>,
|
||||||
|
selected_cb: RefCell<Option<Box<dyn Fn(RecordingDescription) -> ()>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RecordingDialog {
|
||||||
|
/// Create a new recording dialog.
|
||||||
|
pub fn new<W: IsA<gtk::Window>>(backend: Rc<Backend>, parent: &W) -> Rc<Self> {
|
||||||
|
// Create UI
|
||||||
|
|
||||||
|
let window = libhandy::Window::new();
|
||||||
|
window.set_type_hint(gdk::WindowTypeHint::Dialog);
|
||||||
|
window.set_modal(true);
|
||||||
|
window.set_transient_for(Some(parent));
|
||||||
|
window.set_default_size(600, 424);
|
||||||
|
|
||||||
|
let selector = RecordingSelector::new(backend.clone());
|
||||||
|
let editor = RecordingEditor::new(backend.clone(), &window, None);
|
||||||
|
|
||||||
|
let stack = gtk::Stack::new();
|
||||||
|
stack.set_transition_type(gtk::StackTransitionType::Crossfade);
|
||||||
|
stack.add(&selector.widget);
|
||||||
|
stack.add(&editor.widget);
|
||||||
|
window.add(&stack);
|
||||||
|
window.show_all();
|
||||||
|
|
||||||
|
let this = Rc::new(Self {
|
||||||
|
window,
|
||||||
|
stack,
|
||||||
|
selector,
|
||||||
|
editor,
|
||||||
|
selected_cb: RefCell::new(None),
|
||||||
|
});
|
||||||
|
|
||||||
|
// Connect signals and callbacks
|
||||||
|
|
||||||
|
this.selector.set_add_cb(clone!(@strong this => move || {
|
||||||
|
this.stack.set_visible_child(&this.editor.widget);
|
||||||
|
}));
|
||||||
|
|
||||||
|
this.selector
|
||||||
|
.set_selected_cb(clone!(@strong this => move |recording| {
|
||||||
|
if let Some(cb) = &*this.selected_cb.borrow() {
|
||||||
|
cb(recording);
|
||||||
|
this.window.close();
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
this.editor.set_back_cb(clone!(@strong this => move || {
|
||||||
|
this.stack.set_visible_child(&this.selector.widget);
|
||||||
|
}));
|
||||||
|
|
||||||
|
this.editor
|
||||||
|
.set_selected_cb(clone!(@strong this => move |recording| {
|
||||||
|
if let Some(cb) = &*this.selected_cb.borrow() {
|
||||||
|
cb(recording);
|
||||||
|
this.window.close();
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
this
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the closure to be called when the user has selected or created a recording.
|
||||||
|
pub fn set_selected_cb<F: Fn(RecordingDescription) -> () + 'static>(&self, cb: F) {
|
||||||
|
self.selected_cb.replace(Some(Box::new(cb)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Show the recording dialog.
|
||||||
|
pub fn show(&self) {
|
||||||
|
self.window.show();
|
||||||
|
}
|
||||||
|
}
|
||||||
197
src/dialogs/recording/recording_editor.rs
Normal file
197
src/dialogs/recording/recording_editor.rs
Normal file
|
|
@ -0,0 +1,197 @@
|
||||||
|
use super::performance_editor::*;
|
||||||
|
use crate::backend::*;
|
||||||
|
use crate::database::*;
|
||||||
|
use crate::dialogs::*;
|
||||||
|
use crate::widgets::*;
|
||||||
|
use gettextrs::gettext;
|
||||||
|
use glib::clone;
|
||||||
|
use gtk::prelude::*;
|
||||||
|
use gtk_macros::get_widget;
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
/// A widget for creating or editing a recording.
|
||||||
|
// TODO: Disable buttons if no performance is selected.
|
||||||
|
pub struct RecordingEditor {
|
||||||
|
pub widget: gtk::Box,
|
||||||
|
backend: Rc<Backend>,
|
||||||
|
parent: gtk::Window,
|
||||||
|
save_button: gtk::Button,
|
||||||
|
work_label: gtk::Label,
|
||||||
|
comment_entry: gtk::Entry,
|
||||||
|
performance_list: Rc<List<PerformanceDescription>>,
|
||||||
|
id: i64,
|
||||||
|
work: RefCell<Option<WorkDescription>>,
|
||||||
|
performances: RefCell<Vec<PerformanceDescription>>,
|
||||||
|
selected_cb: RefCell<Option<Box<dyn Fn(RecordingDescription) -> ()>>>,
|
||||||
|
back_cb: RefCell<Option<Box<dyn Fn() -> ()>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RecordingEditor {
|
||||||
|
/// Create a new recording editor widget and optionally initialize it. The parent window is
|
||||||
|
/// used as the parent for newly created dialogs.
|
||||||
|
pub fn new<W: IsA<gtk::Window>>(
|
||||||
|
backend: Rc<Backend>,
|
||||||
|
parent: &W,
|
||||||
|
recording: Option<RecordingDescription>,
|
||||||
|
) -> Rc<Self> {
|
||||||
|
// Create UI
|
||||||
|
|
||||||
|
let builder =
|
||||||
|
gtk::Builder::from_resource("/de/johrpan/musicus/ui/recording_editor.ui");
|
||||||
|
|
||||||
|
get_widget!(builder, gtk::Box, widget);
|
||||||
|
get_widget!(builder, gtk::Button, cancel_button);
|
||||||
|
get_widget!(builder, gtk::Button, save_button);
|
||||||
|
get_widget!(builder, gtk::Button, work_button);
|
||||||
|
get_widget!(builder, gtk::Label, work_label);
|
||||||
|
get_widget!(builder, gtk::Entry, comment_entry);
|
||||||
|
get_widget!(builder, gtk::ScrolledWindow, scroll);
|
||||||
|
get_widget!(builder, gtk::Button, add_performer_button);
|
||||||
|
get_widget!(builder, gtk::Button, edit_performer_button);
|
||||||
|
get_widget!(builder, gtk::Button, remove_performer_button);
|
||||||
|
|
||||||
|
let performance_list = List::new(&gettext("No performers added."));
|
||||||
|
scroll.add(&performance_list.widget);
|
||||||
|
|
||||||
|
let (id, work, performances) = match recording {
|
||||||
|
Some(recording) => (recording.id, Some(recording.work), recording.performances),
|
||||||
|
None => (rand::random::<u32>().into(), None, Vec::new()),
|
||||||
|
};
|
||||||
|
|
||||||
|
let this = Rc::new(RecordingEditor {
|
||||||
|
widget,
|
||||||
|
backend,
|
||||||
|
parent: parent.clone().upcast(),
|
||||||
|
save_button,
|
||||||
|
work_label,
|
||||||
|
comment_entry,
|
||||||
|
performance_list,
|
||||||
|
id,
|
||||||
|
work: RefCell::new(work),
|
||||||
|
performances: RefCell::new(performances),
|
||||||
|
selected_cb: RefCell::new(None),
|
||||||
|
back_cb: RefCell::new(None),
|
||||||
|
});
|
||||||
|
|
||||||
|
// Connect signals and callbacks
|
||||||
|
|
||||||
|
cancel_button.connect_clicked(clone!(@strong this => move |_| {
|
||||||
|
if let Some(cb) = &*this.back_cb.borrow() {
|
||||||
|
cb();
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
this.save_button
|
||||||
|
.connect_clicked(clone!(@strong this => move |_| {
|
||||||
|
let recording = RecordingDescription {
|
||||||
|
id: this.id,
|
||||||
|
work: this.work.borrow().clone().expect("Tried to create recording without work!"),
|
||||||
|
comment: this.comment_entry.get_text().to_string(),
|
||||||
|
performances: this.performances.borrow().clone(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let c = glib::MainContext::default();
|
||||||
|
let clone = this.clone();
|
||||||
|
c.spawn_local(async move {
|
||||||
|
clone.backend.update_recording(recording.clone().into()).await.unwrap();
|
||||||
|
if let Some(cb) = &*clone.selected_cb.borrow() {
|
||||||
|
cb(recording.clone());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
work_button.connect_clicked(clone!(@strong this => move |_| {
|
||||||
|
WorkSelector::new(this.backend.clone(), &this.parent, clone!(@strong this => move |work| {
|
||||||
|
this.work_selected(&work);
|
||||||
|
this.work.replace(Some(work));
|
||||||
|
})).show();
|
||||||
|
}));
|
||||||
|
|
||||||
|
this.performance_list.set_make_widget(|performance| {
|
||||||
|
let label = gtk::Label::new(Some(&performance.get_title()));
|
||||||
|
label.set_ellipsize(pango::EllipsizeMode::End);
|
||||||
|
label.set_halign(gtk::Align::Start);
|
||||||
|
label.set_margin_start(6);
|
||||||
|
label.set_margin_end(6);
|
||||||
|
label.set_margin_top(6);
|
||||||
|
label.set_margin_bottom(6);
|
||||||
|
label.upcast()
|
||||||
|
});
|
||||||
|
|
||||||
|
add_performer_button.connect_clicked(clone!(@strong this => move |_| {
|
||||||
|
let editor = PerformanceEditor::new(this.backend.clone(), &this.parent, None);
|
||||||
|
|
||||||
|
editor.set_selected_cb(clone!(@strong this => move |performance| {
|
||||||
|
let mut performances = this.performances.borrow_mut();
|
||||||
|
|
||||||
|
let index = match this.performance_list.get_selected_index() {
|
||||||
|
Some(index) => index + 1,
|
||||||
|
None => performances.len(),
|
||||||
|
};
|
||||||
|
|
||||||
|
performances.push(performance);
|
||||||
|
this.performance_list.show_items(performances.clone());
|
||||||
|
this.performance_list.select_index(index);
|
||||||
|
}));
|
||||||
|
|
||||||
|
editor.show();
|
||||||
|
}));
|
||||||
|
|
||||||
|
edit_performer_button.connect_clicked(clone!(@strong this => move |_| {
|
||||||
|
if let Some(index) = this.performance_list.get_selected_index() {
|
||||||
|
let performance = &this.performances.borrow()[index];
|
||||||
|
|
||||||
|
let editor = PerformanceEditor::new(
|
||||||
|
this.backend.clone(),
|
||||||
|
&this.parent,
|
||||||
|
Some(performance.clone()),
|
||||||
|
);
|
||||||
|
|
||||||
|
editor.set_selected_cb(clone!(@strong this => move |performance| {
|
||||||
|
let mut performances = this.performances.borrow_mut();
|
||||||
|
performances[index] = performance;
|
||||||
|
this.performance_list.show_items(performances.clone());
|
||||||
|
this.performance_list.select_index(index);
|
||||||
|
}));
|
||||||
|
|
||||||
|
editor.show();
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
remove_performer_button.connect_clicked(clone!(@strong this => move |_| {
|
||||||
|
if let Some(index) = this.performance_list.get_selected_index() {
|
||||||
|
let mut performances = this.performances.borrow_mut();
|
||||||
|
performances.remove(index);
|
||||||
|
this.performance_list.show_items(performances.clone());
|
||||||
|
this.performance_list.select_index(index);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Initialize
|
||||||
|
|
||||||
|
if let Some(work) = &*this.work.borrow() {
|
||||||
|
this.work_selected(work);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.performance_list.show_items(this.performances.borrow().clone());
|
||||||
|
|
||||||
|
this
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the closure to be called if the editor is canceled.
|
||||||
|
pub fn set_back_cb<F: Fn() -> () + 'static>(&self, cb: F) {
|
||||||
|
self.back_cb.replace(Some(Box::new(cb)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the closure to be called if the recording was created.
|
||||||
|
pub fn set_selected_cb<F: Fn(RecordingDescription) -> () + 'static>(&self, cb: F) {
|
||||||
|
self.selected_cb.replace(Some(Box::new(cb)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Update the UI according to work.
|
||||||
|
fn work_selected(&self, work: &WorkDescription) {
|
||||||
|
self.work_label.set_text(&format!("{}: {}", work.composer.name_fl(), work.title));
|
||||||
|
self.save_button.set_sensitive(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
63
src/dialogs/recording/recording_editor_dialog.rs
Normal file
63
src/dialogs/recording/recording_editor_dialog.rs
Normal file
|
|
@ -0,0 +1,63 @@
|
||||||
|
use super::recording_editor::*;
|
||||||
|
use crate::backend::*;
|
||||||
|
use crate::database::*;
|
||||||
|
use glib::clone;
|
||||||
|
use gtk::prelude::*;
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
/// A dialog for creating or editing a recording.
|
||||||
|
pub struct RecordingEditorDialog {
|
||||||
|
pub window: libhandy::Window,
|
||||||
|
selected_cb: RefCell<Option<Box<dyn Fn(RecordingDescription) -> ()>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RecordingEditorDialog {
|
||||||
|
/// Create a new recording editor dialog and optionally initialize it.
|
||||||
|
pub fn new<W: IsA<gtk::Window>>(
|
||||||
|
backend: Rc<Backend>,
|
||||||
|
parent: &W,
|
||||||
|
recording: Option<RecordingDescription>,
|
||||||
|
) -> Rc<Self> {
|
||||||
|
// Create UI
|
||||||
|
|
||||||
|
let window = libhandy::Window::new();
|
||||||
|
window.set_type_hint(gdk::WindowTypeHint::Dialog);
|
||||||
|
window.set_modal(true);
|
||||||
|
window.set_transient_for(Some(parent));
|
||||||
|
|
||||||
|
let editor = RecordingEditor::new(backend.clone(), &window, recording);
|
||||||
|
window.add(&editor.widget);
|
||||||
|
window.show_all();
|
||||||
|
|
||||||
|
let this = Rc::new(Self {
|
||||||
|
window,
|
||||||
|
selected_cb: RefCell::new(None),
|
||||||
|
});
|
||||||
|
|
||||||
|
// Connect signals and callbacks
|
||||||
|
|
||||||
|
editor.set_back_cb(clone!(@strong this => move || {
|
||||||
|
this.window.close();
|
||||||
|
}));
|
||||||
|
|
||||||
|
editor.set_selected_cb(clone!(@strong this => move |recording| {
|
||||||
|
if let Some(cb) = &*this.selected_cb.borrow() {
|
||||||
|
cb(recording);
|
||||||
|
this.window.close();
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
this
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the closure to be called when the user edited or created a recording.
|
||||||
|
pub fn set_selected_cb<F: Fn(RecordingDescription) -> () + 'static>(&self, cb: F) {
|
||||||
|
self.selected_cb.replace(Some(Box::new(cb)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Show the recording editor dialog.
|
||||||
|
pub fn show(&self) {
|
||||||
|
self.window.show();
|
||||||
|
}
|
||||||
|
}
|
||||||
89
src/dialogs/recording/recording_selector.rs
Normal file
89
src/dialogs/recording/recording_selector.rs
Normal file
|
|
@ -0,0 +1,89 @@
|
||||||
|
use super::recording_selector_person_screen::*;
|
||||||
|
use crate::backend::Backend;
|
||||||
|
use crate::database::*;
|
||||||
|
use crate::widgets::*;
|
||||||
|
use glib::clone;
|
||||||
|
use gtk::prelude::*;
|
||||||
|
use gtk_macros::get_widget;
|
||||||
|
use libhandy::prelude::*;
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
/// A widget for selecting a recording from a list of existing ones.
|
||||||
|
pub struct RecordingSelector {
|
||||||
|
pub widget: libhandy::Leaflet,
|
||||||
|
backend: Rc<Backend>,
|
||||||
|
sidebar_box: gtk::Box,
|
||||||
|
selected_cb: RefCell<Option<Box<dyn Fn(RecordingDescription) -> ()>>>,
|
||||||
|
add_cb: RefCell<Option<Box<dyn Fn() -> ()>>>,
|
||||||
|
navigator: Rc<Navigator>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RecordingSelector {
|
||||||
|
/// Create a new recording selector.
|
||||||
|
pub fn new(backend: Rc<Backend>) -> Rc<Self> {
|
||||||
|
// Create UI
|
||||||
|
|
||||||
|
let builder = gtk::Builder::from_resource("/de/johrpan/musicus/ui/recording_selector.ui");
|
||||||
|
|
||||||
|
get_widget!(builder, libhandy::Leaflet, widget);
|
||||||
|
get_widget!(builder, gtk::Button, add_button);
|
||||||
|
get_widget!(builder, gtk::Box, sidebar_box);
|
||||||
|
get_widget!(builder, gtk::Box, empty_screen);
|
||||||
|
|
||||||
|
let person_list = PersonList::new(backend.clone());
|
||||||
|
sidebar_box.pack_start(&person_list.widget, true, true, 0);
|
||||||
|
|
||||||
|
let navigator = Navigator::new(&empty_screen);
|
||||||
|
widget.add(&navigator.widget);
|
||||||
|
|
||||||
|
let this = Rc::new(Self {
|
||||||
|
widget,
|
||||||
|
backend,
|
||||||
|
sidebar_box,
|
||||||
|
selected_cb: RefCell::new(None),
|
||||||
|
add_cb: RefCell::new(None),
|
||||||
|
navigator,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Connect signals and callbacks
|
||||||
|
|
||||||
|
add_button.connect_clicked(clone!(@strong this => move |_| {
|
||||||
|
if let Some(cb) = &*this.add_cb.borrow() {
|
||||||
|
cb();
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
person_list.set_selected(clone!(@strong this => move |person| {
|
||||||
|
let person_screen = RecordingSelectorPersonScreen::new(
|
||||||
|
this.backend.clone(),
|
||||||
|
person.clone(),
|
||||||
|
);
|
||||||
|
|
||||||
|
person_screen.set_selected_cb(clone!(@strong this => move |recording| {
|
||||||
|
if let Some(cb) = &*this.selected_cb.borrow() {
|
||||||
|
cb(recording);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
this.navigator.clone().push(person_screen);
|
||||||
|
this.widget.set_visible_child(&this.navigator.widget);
|
||||||
|
}));
|
||||||
|
|
||||||
|
this.navigator.set_back_cb(clone!(@strong this => move || {
|
||||||
|
this.widget.set_visible_child(&this.sidebar_box);
|
||||||
|
}));
|
||||||
|
|
||||||
|
this
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the closure to be called if the editor is user wants to add a new recording.
|
||||||
|
pub fn set_add_cb<F: Fn() -> () + 'static>(&self, cb: F) {
|
||||||
|
self.add_cb.replace(Some(Box::new(cb)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the closure to be called when the user has selected a recording.
|
||||||
|
pub fn set_selected_cb<F: Fn(RecordingDescription) -> () + 'static>(&self, cb: F) {
|
||||||
|
self.selected_cb.replace(Some(Box::new(cb)));
|
||||||
|
}
|
||||||
|
}
|
||||||
126
src/dialogs/recording/recording_selector_person_screen.rs
Normal file
126
src/dialogs/recording/recording_selector_person_screen.rs
Normal file
|
|
@ -0,0 +1,126 @@
|
||||||
|
use super::recording_selector_work_screen::*;
|
||||||
|
use crate::backend::*;
|
||||||
|
use crate::database::*;
|
||||||
|
use crate::widgets::*;
|
||||||
|
use gettextrs::gettext;
|
||||||
|
use glib::clone;
|
||||||
|
use gtk::prelude::*;
|
||||||
|
use gtk_macros::get_widget;
|
||||||
|
use libhandy::HeaderBarExt;
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
/// A screen within the recording selector that presents a list of persons
|
||||||
|
/// and switches to a work screen on selection.
|
||||||
|
pub struct RecordingSelectorPersonScreen {
|
||||||
|
backend: Rc<Backend>,
|
||||||
|
widget: gtk::Box,
|
||||||
|
stack: gtk::Stack,
|
||||||
|
work_list: Rc<List<WorkDescription>>,
|
||||||
|
selected_cb: RefCell<Option<Box<dyn Fn(RecordingDescription) -> ()>>>,
|
||||||
|
navigator: RefCell<Option<Rc<Navigator>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RecordingSelectorPersonScreen {
|
||||||
|
/// Create a new recording selector person screen.
|
||||||
|
pub fn new(backend: Rc<Backend>, person: Person) -> Rc<Self> {
|
||||||
|
// Create UI
|
||||||
|
|
||||||
|
let builder =
|
||||||
|
gtk::Builder::from_resource("/de/johrpan/musicus/ui/recording_selector_screen.ui");
|
||||||
|
|
||||||
|
get_widget!(builder, gtk::Box, widget);
|
||||||
|
get_widget!(builder, libhandy::HeaderBar, header);
|
||||||
|
get_widget!(builder, gtk::Button, back_button);
|
||||||
|
get_widget!(builder, gtk::Stack, stack);
|
||||||
|
|
||||||
|
header.set_title(Some(&person.name_fl()));
|
||||||
|
|
||||||
|
let work_list = List::new(&gettext("No works found."));
|
||||||
|
stack.add_named(&work_list.widget, "content");
|
||||||
|
|
||||||
|
let this = Rc::new(Self {
|
||||||
|
backend,
|
||||||
|
widget,
|
||||||
|
stack,
|
||||||
|
work_list,
|
||||||
|
selected_cb: RefCell::new(None),
|
||||||
|
navigator: RefCell::new(None),
|
||||||
|
});
|
||||||
|
|
||||||
|
// Connect signals and callbacks
|
||||||
|
|
||||||
|
back_button.connect_clicked(clone!(@strong this => move |_| {
|
||||||
|
let navigator = this.navigator.borrow().clone();
|
||||||
|
if let Some(navigator) = navigator {
|
||||||
|
navigator.pop();
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
this.work_list.set_make_widget(|work: &WorkDescription| {
|
||||||
|
let label = gtk::Label::new(Some(&work.title));
|
||||||
|
label.set_ellipsize(pango::EllipsizeMode::End);
|
||||||
|
label.set_halign(gtk::Align::Start);
|
||||||
|
label.set_margin_start(6);
|
||||||
|
label.set_margin_end(6);
|
||||||
|
label.set_margin_top(6);
|
||||||
|
label.set_margin_bottom(6);
|
||||||
|
label.upcast()
|
||||||
|
});
|
||||||
|
|
||||||
|
this.work_list
|
||||||
|
.set_selected(clone!(@strong this => move |work| {
|
||||||
|
let navigator = this.navigator.borrow().clone();
|
||||||
|
if let Some(navigator) = navigator {
|
||||||
|
let work_screen = RecordingSelectorWorkScreen::new(
|
||||||
|
this.backend.clone(),
|
||||||
|
work.clone(),
|
||||||
|
);
|
||||||
|
|
||||||
|
work_screen.set_selected_cb(clone!(@strong this => move |recording| {
|
||||||
|
if let Some(cb) = &*this.selected_cb.borrow() {
|
||||||
|
cb(recording);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
navigator.push(work_screen);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Initialize
|
||||||
|
|
||||||
|
let context = glib::MainContext::default();
|
||||||
|
let clone = this.clone();
|
||||||
|
context.spawn_local(async move {
|
||||||
|
let works = clone
|
||||||
|
.backend
|
||||||
|
.get_work_descriptions(person.id)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
clone.work_list.show_items(works);
|
||||||
|
clone.stack.set_visible_child_name("content");
|
||||||
|
});
|
||||||
|
|
||||||
|
this
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets a closure to be called when the user has selected a recording.
|
||||||
|
pub fn set_selected_cb<F: Fn(RecordingDescription) -> () + 'static>(&self, cb: F) {
|
||||||
|
self.selected_cb.replace(Some(Box::new(cb)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NavigatorScreen for RecordingSelectorPersonScreen {
|
||||||
|
fn attach_navigator(&self, navigator: Rc<Navigator>) {
|
||||||
|
self.navigator.replace(Some(navigator));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_widget(&self) -> gtk::Widget {
|
||||||
|
self.widget.clone().upcast()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn detach_navigator(&self) {
|
||||||
|
self.navigator.replace(None);
|
||||||
|
}
|
||||||
|
}
|
||||||
120
src/dialogs/recording/recording_selector_work_screen.rs
Normal file
120
src/dialogs/recording/recording_selector_work_screen.rs
Normal file
|
|
@ -0,0 +1,120 @@
|
||||||
|
use crate::backend::*;
|
||||||
|
use crate::database::*;
|
||||||
|
use crate::widgets::*;
|
||||||
|
use gettextrs::gettext;
|
||||||
|
use glib::clone;
|
||||||
|
use gtk::prelude::*;
|
||||||
|
use gtk_macros::get_widget;
|
||||||
|
use libhandy::HeaderBarExt;
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
/// A screen within the recording selector presenting a list of recordings for a work.
|
||||||
|
pub struct RecordingSelectorWorkScreen {
|
||||||
|
backend: Rc<Backend>,
|
||||||
|
widget: gtk::Box,
|
||||||
|
stack: gtk::Stack,
|
||||||
|
recording_list: Rc<List<RecordingDescription>>,
|
||||||
|
selected_cb: RefCell<Option<Box<dyn Fn(RecordingDescription) -> ()>>>,
|
||||||
|
navigator: RefCell<Option<Rc<Navigator>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RecordingSelectorWorkScreen {
|
||||||
|
/// Create a new recording selector work screen.
|
||||||
|
pub fn new(backend: Rc<Backend>, work: WorkDescription) -> Rc<Self> {
|
||||||
|
// Create UI
|
||||||
|
|
||||||
|
let builder =
|
||||||
|
gtk::Builder::from_resource("/de/johrpan/musicus/ui/recording_selector_screen.ui");
|
||||||
|
|
||||||
|
get_widget!(builder, gtk::Box, widget);
|
||||||
|
get_widget!(builder, libhandy::HeaderBar, header);
|
||||||
|
get_widget!(builder, gtk::Button, back_button);
|
||||||
|
get_widget!(builder, gtk::Stack, stack);
|
||||||
|
|
||||||
|
header.set_title(Some(&work.title));
|
||||||
|
header.set_subtitle(Some(&work.composer.name_fl()));
|
||||||
|
|
||||||
|
let recording_list = List::new(&gettext("No recordings found."));
|
||||||
|
stack.add_named(&recording_list.widget, "content");
|
||||||
|
|
||||||
|
let this = Rc::new(Self {
|
||||||
|
backend,
|
||||||
|
widget,
|
||||||
|
stack,
|
||||||
|
recording_list,
|
||||||
|
selected_cb: RefCell::new(None),
|
||||||
|
navigator: RefCell::new(None),
|
||||||
|
});
|
||||||
|
|
||||||
|
// Connect signals and callbacks
|
||||||
|
|
||||||
|
back_button.connect_clicked(clone!(@strong this => move |_| {
|
||||||
|
let navigator = this.navigator.borrow().clone();
|
||||||
|
if let Some(navigator) = navigator {
|
||||||
|
navigator.pop();
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
this.recording_list.set_make_widget(|recording: &RecordingDescription| {
|
||||||
|
let work_label = gtk::Label::new(Some(&recording.work.get_title()));
|
||||||
|
work_label.set_ellipsize(pango::EllipsizeMode::End);
|
||||||
|
work_label.set_halign(gtk::Align::Start);
|
||||||
|
|
||||||
|
let performers_label = gtk::Label::new(Some(&recording.get_performers()));
|
||||||
|
performers_label.set_ellipsize(pango::EllipsizeMode::End);
|
||||||
|
performers_label.set_opacity(0.5);
|
||||||
|
performers_label.set_halign(gtk::Align::Start);
|
||||||
|
|
||||||
|
let vbox = gtk::Box::new(gtk::Orientation::Vertical, 0);
|
||||||
|
vbox.set_border_width(6);
|
||||||
|
vbox.add(&work_label);
|
||||||
|
vbox.add(&performers_label);
|
||||||
|
|
||||||
|
vbox.upcast()
|
||||||
|
});
|
||||||
|
|
||||||
|
this.recording_list
|
||||||
|
.set_selected(clone!(@strong this => move |recording| {
|
||||||
|
if let Some(cb) = &*this.selected_cb.borrow() {
|
||||||
|
cb(recording.clone());
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Initialize
|
||||||
|
|
||||||
|
let context = glib::MainContext::default();
|
||||||
|
let clone = this.clone();
|
||||||
|
context.spawn_local(async move {
|
||||||
|
let recordings = clone
|
||||||
|
.backend
|
||||||
|
.get_recordings_for_work(work.id)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
clone.recording_list.show_items(recordings);
|
||||||
|
clone.stack.set_visible_child_name("content");
|
||||||
|
});
|
||||||
|
|
||||||
|
this
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets a closure to be called when the user has selected a recording.
|
||||||
|
pub fn set_selected_cb<F: Fn(RecordingDescription) -> () + 'static>(&self, cb: F) {
|
||||||
|
self.selected_cb.replace(Some(Box::new(cb)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NavigatorScreen for RecordingSelectorWorkScreen {
|
||||||
|
fn attach_navigator(&self, navigator: Rc<Navigator>) {
|
||||||
|
self.navigator.replace(Some(navigator));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_widget(&self) -> gtk::Widget {
|
||||||
|
self.widget.clone().upcast()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn detach_navigator(&self) {
|
||||||
|
self.navigator.replace(None);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,186 +0,0 @@
|
||||||
use super::*;
|
|
||||||
use crate::backend::Backend;
|
|
||||||
use crate::database::*;
|
|
||||||
use crate::widgets::*;
|
|
||||||
use glib::clone;
|
|
||||||
use gtk::prelude::*;
|
|
||||||
use gtk_macros::get_widget;
|
|
||||||
use std::cell::RefCell;
|
|
||||||
use std::rc::Rc;
|
|
||||||
use std::convert::TryInto;
|
|
||||||
|
|
||||||
pub struct RecordingEditor<F>
|
|
||||||
where
|
|
||||||
F: Fn(RecordingDescription) -> () + 'static,
|
|
||||||
{
|
|
||||||
backend: Rc<Backend>,
|
|
||||||
window: libhandy::Window,
|
|
||||||
callback: F,
|
|
||||||
id: i64,
|
|
||||||
save_button: gtk::Button,
|
|
||||||
work_label: gtk::Label,
|
|
||||||
work: RefCell<Option<WorkDescription>>,
|
|
||||||
comment_entry: gtk::Entry,
|
|
||||||
performers: RefCell<Vec<PerformanceDescription>>,
|
|
||||||
performer_list: gtk::ListBox,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<F> RecordingEditor<F>
|
|
||||||
where
|
|
||||||
F: Fn(RecordingDescription) -> () + 'static,
|
|
||||||
{
|
|
||||||
pub fn new<P: IsA<gtk::Window>>(
|
|
||||||
backend: Rc<Backend>,
|
|
||||||
parent: &P,
|
|
||||||
recording: Option<RecordingDescription>,
|
|
||||||
callback: F,
|
|
||||||
) -> Rc<Self> {
|
|
||||||
let builder =
|
|
||||||
gtk::Builder::from_resource("/de/johrpan/musicus/ui/recording_editor.ui");
|
|
||||||
|
|
||||||
get_widget!(builder, libhandy::Window, window);
|
|
||||||
get_widget!(builder, gtk::Button, cancel_button);
|
|
||||||
get_widget!(builder, gtk::Button, save_button);
|
|
||||||
get_widget!(builder, gtk::Button, work_button);
|
|
||||||
get_widget!(builder, gtk::Label, work_label);
|
|
||||||
get_widget!(builder, gtk::Entry, comment_entry);
|
|
||||||
get_widget!(builder, gtk::ListBox, performer_list);
|
|
||||||
get_widget!(builder, gtk::Button, add_performer_button);
|
|
||||||
get_widget!(builder, gtk::Button, edit_performer_button);
|
|
||||||
get_widget!(builder, gtk::Button, remove_performer_button);
|
|
||||||
|
|
||||||
let (id, work, performers) = match recording {
|
|
||||||
Some(recording) => {
|
|
||||||
save_button.set_sensitive(true);
|
|
||||||
work_label.set_text(&recording.work.get_title());
|
|
||||||
comment_entry.set_text(&recording.comment);
|
|
||||||
(recording.id, Some(recording.work), recording.performances)
|
|
||||||
}
|
|
||||||
None => (rand::random::<u32>().into(), None, Vec::new()),
|
|
||||||
};
|
|
||||||
|
|
||||||
let result = Rc::new(RecordingEditor {
|
|
||||||
backend: backend,
|
|
||||||
window: window,
|
|
||||||
callback: callback,
|
|
||||||
id: id,
|
|
||||||
save_button: save_button,
|
|
||||||
work_label: work_label,
|
|
||||||
work: RefCell::new(work),
|
|
||||||
comment_entry: comment_entry,
|
|
||||||
performers: RefCell::new(performers),
|
|
||||||
performer_list: performer_list,
|
|
||||||
});
|
|
||||||
|
|
||||||
cancel_button.connect_clicked(clone!(@strong result => move |_| {
|
|
||||||
result.window.close();
|
|
||||||
}));
|
|
||||||
|
|
||||||
result
|
|
||||||
.save_button
|
|
||||||
.connect_clicked(clone!(@strong result => move |_| {
|
|
||||||
let recording = RecordingDescription {
|
|
||||||
id: result.id,
|
|
||||||
work: result.work.borrow().clone().expect("Tried to create recording without work!"),
|
|
||||||
comment: result.comment_entry.get_text().to_string(),
|
|
||||||
performances: result.performers.borrow().to_vec(),
|
|
||||||
};
|
|
||||||
|
|
||||||
let c = glib::MainContext::default();
|
|
||||||
let clone = result.clone();
|
|
||||||
c.spawn_local(async move {
|
|
||||||
clone.backend.update_recording(recording.clone().into()).await.unwrap();
|
|
||||||
clone.window.close();
|
|
||||||
(clone.callback)(recording.clone());
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
work_button.connect_clicked(clone!(@strong result => move |_| {
|
|
||||||
WorkSelector::new(result.backend.clone(), &result.window, clone!(@strong result => move |work| {
|
|
||||||
result.work.replace(Some(work.clone()));
|
|
||||||
result.work_label.set_text(&format!("{}: {}", work.composer.name_fl(), work.title));
|
|
||||||
result.save_button.set_sensitive(true);
|
|
||||||
})).show();
|
|
||||||
}));
|
|
||||||
|
|
||||||
add_performer_button.connect_clicked(clone!(@strong result => move |_| {
|
|
||||||
PerformanceEditor::new(result.backend.clone(), &result.window, None, clone!(@strong result => move |performance| {
|
|
||||||
{
|
|
||||||
let mut performers = result.performers.borrow_mut();
|
|
||||||
performers.push(performance);
|
|
||||||
}
|
|
||||||
|
|
||||||
result.show_performers();
|
|
||||||
})).show();
|
|
||||||
}));
|
|
||||||
|
|
||||||
edit_performer_button.connect_clicked(clone!(@strong result => move |_| {
|
|
||||||
let row = result.get_selected_performer_row();
|
|
||||||
match row {
|
|
||||||
Some(row) => {
|
|
||||||
let index = row.get_index();
|
|
||||||
let index: usize = index.try_into().unwrap();
|
|
||||||
let performer = result.performers.borrow()[index].clone();
|
|
||||||
|
|
||||||
PerformanceEditor::new(result.backend.clone(), &result.window, Some(performer), clone!(@strong result => move |performer| {
|
|
||||||
result.performers.borrow_mut()[index] = performer;
|
|
||||||
result.show_performers();
|
|
||||||
})).show();
|
|
||||||
}
|
|
||||||
None => (),
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
|
|
||||||
remove_performer_button.connect_clicked(clone!(@strong result => move |_| {
|
|
||||||
let row = result.get_selected_performer_row();
|
|
||||||
match row {
|
|
||||||
Some(row) => {
|
|
||||||
let index = row.get_index();
|
|
||||||
let index: usize = index.try_into().unwrap();
|
|
||||||
result.performers.borrow_mut().remove(index);
|
|
||||||
result.show_performers();
|
|
||||||
}
|
|
||||||
None => (),
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
|
|
||||||
result.window.set_transient_for(Some(parent));
|
|
||||||
|
|
||||||
result.show_performers();
|
|
||||||
|
|
||||||
result
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn show(&self) {
|
|
||||||
self.window.show();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn show_performers(&self) {
|
|
||||||
for child in self.performer_list.get_children() {
|
|
||||||
self.performer_list.remove(&child);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (index, performer) in self.performers.borrow().iter().enumerate() {
|
|
||||||
let label = gtk::Label::new(Some(&performer.get_title()));
|
|
||||||
label.set_halign(gtk::Align::Start);
|
|
||||||
label.set_margin_start(6);
|
|
||||||
label.set_margin_end(6);
|
|
||||||
label.set_margin_top(6);
|
|
||||||
label.set_margin_bottom(6);
|
|
||||||
|
|
||||||
let row = SelectorRow::new(index.try_into().unwrap(), &label);
|
|
||||||
row.show_all();
|
|
||||||
self.performer_list.insert(&row, -1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_selected_performer_row(&self) -> Option<SelectorRow> {
|
|
||||||
match self.performer_list.get_selected_rows().first() {
|
|
||||||
Some(row) => match row.get_child() {
|
|
||||||
Some(child) => Some(child.downcast().unwrap()),
|
|
||||||
None => None,
|
|
||||||
},
|
|
||||||
None => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,279 +0,0 @@
|
||||||
use super::*;
|
|
||||||
use crate::backend::Backend;
|
|
||||||
use crate::database::*;
|
|
||||||
use crate::widgets::*;
|
|
||||||
use gettextrs::gettext;
|
|
||||||
use gio::prelude::*;
|
|
||||||
use glib::clone;
|
|
||||||
use gtk::prelude::*;
|
|
||||||
use gtk_macros::get_widget;
|
|
||||||
use libhandy::prelude::*;
|
|
||||||
use libhandy::HeaderBarExt;
|
|
||||||
use std::cell::RefCell;
|
|
||||||
use std::rc::Rc;
|
|
||||||
|
|
||||||
pub struct RecordingSelector {
|
|
||||||
backend: Rc<Backend>,
|
|
||||||
window: libhandy::Window,
|
|
||||||
callback: Box<dyn Fn(RecordingDescription) -> () + 'static>,
|
|
||||||
leaflet: libhandy::Leaflet,
|
|
||||||
navigator: Rc<Navigator>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RecordingSelector {
|
|
||||||
pub fn new<P, F>(backend: Rc<Backend>, parent: &P, callback: F) -> Rc<Self>
|
|
||||||
where
|
|
||||||
P: IsA<gtk::Window>,
|
|
||||||
F: Fn(RecordingDescription) -> () + 'static,
|
|
||||||
{
|
|
||||||
let builder = gtk::Builder::from_resource("/de/johrpan/musicus/ui/recording_selector.ui");
|
|
||||||
|
|
||||||
get_widget!(builder, libhandy::Window, window);
|
|
||||||
get_widget!(builder, libhandy::Leaflet, leaflet);
|
|
||||||
get_widget!(builder, gtk::Button, add_button);
|
|
||||||
get_widget!(builder, gtk::Box, sidebar_box);
|
|
||||||
get_widget!(builder, gtk::Box, empty_screen);
|
|
||||||
|
|
||||||
let person_list = PersonList::new(backend.clone());
|
|
||||||
sidebar_box.pack_start(&person_list.widget, true, true, 0);
|
|
||||||
|
|
||||||
let navigator = Navigator::new(&empty_screen);
|
|
||||||
leaflet.add(&navigator.widget);
|
|
||||||
|
|
||||||
navigator.set_back_cb(clone!(@strong leaflet, @strong sidebar_box => move || {
|
|
||||||
leaflet.set_visible_child(&sidebar_box);
|
|
||||||
}));
|
|
||||||
|
|
||||||
let result = Rc::new(Self {
|
|
||||||
backend: backend,
|
|
||||||
window: window,
|
|
||||||
callback: Box::new(callback),
|
|
||||||
leaflet: leaflet,
|
|
||||||
navigator: navigator,
|
|
||||||
});
|
|
||||||
|
|
||||||
add_button.connect_clicked(clone!(@strong result => move |_| {
|
|
||||||
let editor = RecordingEditor::new(
|
|
||||||
result.backend.clone(),
|
|
||||||
&result.window,
|
|
||||||
None,
|
|
||||||
clone!(@strong result => move |recording| {
|
|
||||||
result.select(recording);
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
editor.show();
|
|
||||||
}));
|
|
||||||
|
|
||||||
person_list.set_selected(clone!(@strong result => move |person| {
|
|
||||||
result.navigator.clone().replace(RecordingSelectorPersonScreen::new(result.backend.clone(), result.clone(), person.clone()));
|
|
||||||
result.leaflet.set_visible_child(&result.navigator.widget);
|
|
||||||
}));
|
|
||||||
|
|
||||||
result.window.set_transient_for(Some(parent));
|
|
||||||
|
|
||||||
result
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn show(&self) {
|
|
||||||
self.window.show();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn select(&self, recording: RecordingDescription) {
|
|
||||||
self.window.close();
|
|
||||||
(self.callback)(recording);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct RecordingSelectorPersonScreen {
|
|
||||||
backend: Rc<Backend>,
|
|
||||||
selector: Rc<RecordingSelector>,
|
|
||||||
widget: gtk::Box,
|
|
||||||
stack: gtk::Stack,
|
|
||||||
work_list: Rc<List<WorkDescription>>,
|
|
||||||
navigator: RefCell<Option<Rc<Navigator>>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RecordingSelectorPersonScreen {
|
|
||||||
pub fn new(backend: Rc<Backend>, selector: Rc<RecordingSelector>, person: Person) -> Rc<Self> {
|
|
||||||
let builder =
|
|
||||||
gtk::Builder::from_resource("/de/johrpan/musicus/ui/recording_selector_screen.ui");
|
|
||||||
|
|
||||||
get_widget!(builder, gtk::Box, widget);
|
|
||||||
get_widget!(builder, libhandy::HeaderBar, header);
|
|
||||||
get_widget!(builder, gtk::Button, back_button);
|
|
||||||
get_widget!(builder, gtk::Stack, stack);
|
|
||||||
|
|
||||||
header.set_title(Some(&person.name_fl()));
|
|
||||||
|
|
||||||
let work_list = List::new(&gettext("No works found."));
|
|
||||||
|
|
||||||
work_list.set_make_widget(|work: &WorkDescription| {
|
|
||||||
let label = gtk::Label::new(Some(&work.title));
|
|
||||||
label.set_halign(gtk::Align::Start);
|
|
||||||
label.set_margin_start(6);
|
|
||||||
label.set_margin_end(6);
|
|
||||||
label.set_margin_top(6);
|
|
||||||
label.set_margin_bottom(6);
|
|
||||||
label.upcast()
|
|
||||||
});
|
|
||||||
|
|
||||||
stack.add_named(&work_list.widget, "content");
|
|
||||||
|
|
||||||
let result = Rc::new(Self {
|
|
||||||
backend,
|
|
||||||
selector,
|
|
||||||
widget,
|
|
||||||
stack,
|
|
||||||
work_list,
|
|
||||||
navigator: RefCell::new(None),
|
|
||||||
});
|
|
||||||
|
|
||||||
back_button.connect_clicked(clone!(@strong result => move |_| {
|
|
||||||
let navigator = result.navigator.borrow().clone();
|
|
||||||
if let Some(navigator) = navigator {
|
|
||||||
navigator.clone().pop();
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
|
|
||||||
result
|
|
||||||
.work_list
|
|
||||||
.set_selected(clone!(@strong result => move |work| {
|
|
||||||
let navigator = result.navigator.borrow().clone();
|
|
||||||
if let Some(navigator) = navigator {
|
|
||||||
navigator.push(RecordingSelectorWorkScreen::new(result.backend.clone(), result.selector.clone(), work.clone()));
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
|
|
||||||
let context = glib::MainContext::default();
|
|
||||||
let clone = result.clone();
|
|
||||||
context.spawn_local(async move {
|
|
||||||
let works = clone
|
|
||||||
.backend
|
|
||||||
.get_work_descriptions(person.id)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
clone.work_list.show_items(works);
|
|
||||||
clone.stack.set_visible_child_name("content");
|
|
||||||
});
|
|
||||||
|
|
||||||
result
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl NavigatorScreen for RecordingSelectorPersonScreen {
|
|
||||||
fn attach_navigator(&self, navigator: Rc<Navigator>) {
|
|
||||||
self.navigator.replace(Some(navigator));
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_widget(&self) -> gtk::Widget {
|
|
||||||
self.widget.clone().upcast()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn detach_navigator(&self) {
|
|
||||||
self.navigator.replace(None);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct RecordingSelectorWorkScreen {
|
|
||||||
backend: Rc<Backend>,
|
|
||||||
selector: Rc<RecordingSelector>,
|
|
||||||
widget: gtk::Box,
|
|
||||||
stack: gtk::Stack,
|
|
||||||
recording_list: Rc<List<RecordingDescription>>,
|
|
||||||
navigator: RefCell<Option<Rc<Navigator>>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RecordingSelectorWorkScreen {
|
|
||||||
pub fn new(
|
|
||||||
backend: Rc<Backend>,
|
|
||||||
selector: Rc<RecordingSelector>,
|
|
||||||
work: WorkDescription,
|
|
||||||
) -> Rc<Self> {
|
|
||||||
let builder =
|
|
||||||
gtk::Builder::from_resource("/de/johrpan/musicus/ui/recording_selector_screen.ui");
|
|
||||||
|
|
||||||
get_widget!(builder, gtk::Box, widget);
|
|
||||||
get_widget!(builder, libhandy::HeaderBar, header);
|
|
||||||
get_widget!(builder, gtk::Button, back_button);
|
|
||||||
get_widget!(builder, gtk::Stack, stack);
|
|
||||||
|
|
||||||
header.set_title(Some(&work.title));
|
|
||||||
header.set_subtitle(Some(&work.composer.name_fl()));
|
|
||||||
|
|
||||||
let recording_list = List::new(&gettext("No recordings found."));
|
|
||||||
|
|
||||||
recording_list.set_make_widget(|recording: &RecordingDescription| {
|
|
||||||
let work_label = gtk::Label::new(Some(&recording.work.get_title()));
|
|
||||||
|
|
||||||
work_label.set_ellipsize(pango::EllipsizeMode::End);
|
|
||||||
work_label.set_halign(gtk::Align::Start);
|
|
||||||
|
|
||||||
let performers_label = gtk::Label::new(Some(&recording.get_performers()));
|
|
||||||
performers_label.set_ellipsize(pango::EllipsizeMode::End);
|
|
||||||
performers_label.set_opacity(0.5);
|
|
||||||
performers_label.set_halign(gtk::Align::Start);
|
|
||||||
|
|
||||||
let vbox = gtk::Box::new(gtk::Orientation::Vertical, 0);
|
|
||||||
vbox.set_border_width(6);
|
|
||||||
vbox.add(&work_label);
|
|
||||||
vbox.add(&performers_label);
|
|
||||||
|
|
||||||
vbox.upcast()
|
|
||||||
});
|
|
||||||
|
|
||||||
stack.add_named(&recording_list.widget, "content");
|
|
||||||
|
|
||||||
let result = Rc::new(Self {
|
|
||||||
backend,
|
|
||||||
selector,
|
|
||||||
widget,
|
|
||||||
stack,
|
|
||||||
recording_list,
|
|
||||||
navigator: RefCell::new(None),
|
|
||||||
});
|
|
||||||
|
|
||||||
back_button.connect_clicked(clone!(@strong result => move |_| {
|
|
||||||
let navigator = result.navigator.borrow().clone();
|
|
||||||
if let Some(navigator) = navigator {
|
|
||||||
navigator.clone().pop();
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
|
|
||||||
result
|
|
||||||
.recording_list
|
|
||||||
.set_selected(clone!(@strong result => move |recording| {
|
|
||||||
result.selector.select(recording.clone());
|
|
||||||
}));
|
|
||||||
|
|
||||||
let context = glib::MainContext::default();
|
|
||||||
let clone = result.clone();
|
|
||||||
context.spawn_local(async move {
|
|
||||||
let recordings = clone
|
|
||||||
.backend
|
|
||||||
.get_recordings_for_work(work.id)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
clone.recording_list.show_items(recordings);
|
|
||||||
clone.stack.set_visible_child_name("content");
|
|
||||||
});
|
|
||||||
|
|
||||||
result
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl NavigatorScreen for RecordingSelectorWorkScreen {
|
|
||||||
fn attach_navigator(&self, navigator: Rc<Navigator>) {
|
|
||||||
self.navigator.replace(Some(navigator));
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_widget(&self) -> gtk::Widget {
|
|
||||||
self.widget.clone().upcast()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn detach_navigator(&self) {
|
|
||||||
self.navigator.replace(None);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -95,14 +95,14 @@ impl TracksEditor {
|
||||||
}));
|
}));
|
||||||
|
|
||||||
recording_button.connect_clicked(clone!(@strong this => move |_| {
|
recording_button.connect_clicked(clone!(@strong this => move |_| {
|
||||||
RecordingSelector::new(
|
let dialog = RecordingDialog::new(this.backend.clone(), &this.window);
|
||||||
this.backend.clone(),
|
|
||||||
&this.window,
|
dialog.set_selected_cb(clone!(@strong this => move |recording| {
|
||||||
clone!(@strong this => move |recording| {
|
|
||||||
this.recording_selected(&recording);
|
this.recording_selected(&recording);
|
||||||
this.recording.replace(Some(recording));
|
this.recording.replace(Some(recording));
|
||||||
}),
|
}));
|
||||||
).show();
|
|
||||||
|
dialog.show();
|
||||||
}
|
}
|
||||||
));
|
));
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -45,12 +45,17 @@ sources = files(
|
||||||
'dialogs/instrument_selector.rs',
|
'dialogs/instrument_selector.rs',
|
||||||
'dialogs/mod.rs',
|
'dialogs/mod.rs',
|
||||||
'dialogs/part_editor.rs',
|
'dialogs/part_editor.rs',
|
||||||
'dialogs/performance_editor.rs',
|
|
||||||
'dialogs/person_editor.rs',
|
'dialogs/person_editor.rs',
|
||||||
'dialogs/person_selector.rs',
|
'dialogs/person_selector.rs',
|
||||||
'dialogs/preferences.rs',
|
'dialogs/preferences.rs',
|
||||||
'dialogs/recording_editor.rs',
|
'dialogs/recording/mod.rs',
|
||||||
'dialogs/recording_selector.rs',
|
'dialogs/recording/performance_editor.rs',
|
||||||
|
'dialogs/recording/recording_dialog.rs',
|
||||||
|
'dialogs/recording/recording_editor_dialog.rs',
|
||||||
|
'dialogs/recording/recording_editor.rs',
|
||||||
|
'dialogs/recording/recording_selector_person_screen.rs',
|
||||||
|
'dialogs/recording/recording_selector.rs',
|
||||||
|
'dialogs/recording/recording_selector_work_screen.rs',
|
||||||
'dialogs/section_editor.rs',
|
'dialogs/section_editor.rs',
|
||||||
'dialogs/track_editor.rs',
|
'dialogs/track_editor.rs',
|
||||||
'dialogs/tracks_editor.rs',
|
'dialogs/tracks_editor.rs',
|
||||||
|
|
|
||||||
|
|
@ -166,9 +166,13 @@ impl Window {
|
||||||
result.window,
|
result.window,
|
||||||
"add-recording",
|
"add-recording",
|
||||||
clone!(@strong result => move |_, _| {
|
clone!(@strong result => move |_, _| {
|
||||||
RecordingEditor::new(result.backend.clone(), &result.window, None, clone!(@strong result => move |_| {
|
let dialog = RecordingDialog::new(result.backend.clone(), &result.window);
|
||||||
|
|
||||||
|
dialog.set_selected_cb(clone!(@strong result => move |_| {
|
||||||
result.reload();
|
result.reload();
|
||||||
})).show();
|
}));
|
||||||
|
|
||||||
|
dialog.show();
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -292,9 +296,13 @@ impl Window {
|
||||||
let c = glib::MainContext::default();
|
let c = glib::MainContext::default();
|
||||||
c.spawn_local(async move {
|
c.spawn_local(async move {
|
||||||
let recording = result.backend.get_recording_description(id).await.unwrap();
|
let recording = result.backend.get_recording_description(id).await.unwrap();
|
||||||
RecordingEditor::new(result.backend.clone(), &result.window, Some(recording), clone!(@strong result => move |_| {
|
let dialog = RecordingEditorDialog::new(result.backend.clone(), &result.window, Some(recording));
|
||||||
|
|
||||||
|
dialog.set_selected_cb(clone!(@strong result => move |_| {
|
||||||
result.reload();
|
result.reload();
|
||||||
})).show();
|
}));
|
||||||
|
|
||||||
|
dialog.show();
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue