[gnome-music/wip/mschraal/tageditor: 11/21] tageditordialog: Initial songs support
- From: Marinus Schraal <mschraal src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-music/wip/mschraal/tageditor: 11/21] tageditordialog: Initial songs support
- Date: Fri, 22 Nov 2019 15:57:03 +0000 (UTC)
commit 01692b8dccda894234b9609e8e81954ceab02c65
Author: Sumaid Syed <sumaidsyed gmail com>
Date: Sun Aug 4 23:02:28 2019 +0530
tageditordialog: Initial songs support
Based on the patches by Jean Felder
https://gitlab.gnome.org/GNOME/gnome-music/issues/293
data/org.gnome.Music.css | 5 +
data/org.gnome.Music.gresource.xml | 1 +
data/ui/SelectionToolbar.ui | 13 +
data/ui/TagEditorDialog.ui | 708 +++++++++++++++++++++++++++++++++
gnomemusic/widgets/selectiontoolbar.py | 41 +-
gnomemusic/widgets/tageditordialog.py | 274 +++++++++++++
gnomemusic/window.py | 23 ++
po/POTFILES.in | 2 +
8 files changed, 1062 insertions(+), 5 deletions(-)
---
diff --git a/data/org.gnome.Music.css b/data/org.gnome.Music.css
index 9242fc57..b5de896a 100644
--- a/data/org.gnome.Music.css
+++ b/data/org.gnome.Music.css
@@ -24,6 +24,11 @@
padding: 12px 0;
}
+/* Suggested Tags in Tag Editor Dialog*/
+.suggestion-label {
+ color: #3680DB;
+}
+
/* ArtistAlbumsWidget */
box#ArtistAlbumsWidget .artist-label {
font-weight: bold;
diff --git a/data/org.gnome.Music.gresource.xml b/data/org.gnome.Music.gresource.xml
index 0132a33b..32699343 100644
--- a/data/org.gnome.Music.gresource.xml
+++ b/data/org.gnome.Music.gresource.xml
@@ -28,6 +28,7 @@
<file preprocess="xml-stripblanks">ui/SelectionBarMenuButton.ui</file>
<file preprocess="xml-stripblanks">ui/SelectionToolbar.ui</file>
<file preprocess="xml-stripblanks">ui/SongWidget.ui</file>
+ <file preprocess="xml-stripblanks">ui/TagEditorDialog.ui</file>
<file preprocess="xml-stripblanks">ui/TwoLineTip.ui</file>
<file preprocess="xml-stripblanks">ui/Window.ui</file>
</gresource>
diff --git a/data/ui/SelectionToolbar.ui b/data/ui/SelectionToolbar.ui
index ec64876c..4f488e7a 100644
--- a/data/ui/SelectionToolbar.ui
+++ b/data/ui/SelectionToolbar.ui
@@ -17,5 +17,18 @@
</style>
</object>
</child>
+ <child>
+ <object class="GtkButton" id="_edit_details_button">
+ <property name="label" translatable="yes">Edit Details</property>
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <signal name="clicked" handler="_on_edit_tags_button_clicked" swapped="no"/>
+ <style>
+ <class name="text-button"/>
+ </style>
+ </object>
+ </child>
</template>
</interface>
diff --git a/data/ui/TagEditorDialog.ui b/data/ui/TagEditorDialog.ui
new file mode 100644
index 00000000..c4db8d85
--- /dev/null
+++ b/data/ui/TagEditorDialog.ui
@@ -0,0 +1,708 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <requires lib="gtk+" version="3.16"/>
+ <template class="TagEditorDialog" parent="GtkDialog">
+ <property name="can_focus">False</property>
+ <property name="resizable">False</property>
+ <property name="modal">True</property>
+ <property name="window_position">center-on-parent</property>
+ <property name="destroy_with_parent">True</property>
+ <property name="type_hint">dialog</property>
+ <property name="skip_taskbar_hint">True</property>
+ <child internal-child="vbox">
+ <object class="GtkBox" id="dialog_vbox">
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <child internal-child="action_area">
+ <object class="GtkButtonBox">
+ <property name="can_focus">False</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkOverlay" id="overlay">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child type="overlay">
+ <object class="NotificationsPopup" id="_notifications_popup">
+ <property name="halign">center</property>
+ <property name="transition_type">slide-down</property>
+ <property name="valign">start</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkGrid" id="media_details">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="halign">start</property>
+ <property name="margin">12</property>
+ <property name="row_spacing">5</property>
+ <child>
+ <object class="GtkBox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <property name="vexpand">True</property>
+ <property name="valign">fill</property>
+ <child>
+ <object class="GtkBox" id="coverart_box">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="halign">start</property>
+ <property name="valign">fill</property>
+ <property name="margin_right">20</property>
+ <child>
+ <object class="CoverStack" id="_cover_stack">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="valign">fill</property>
+ </object>
+ </child>
+ <style>
+ <class name="album-cover"/>
+ </style>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="width">1</property>
+ <property name="height">4</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="location">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="justify">right</property>
+ <property name="margin_right">20</property>
+ <property name="label" translatable="yes">Location</property>
+ <property name="xalign">1</property>
+ <attributes>
+ <attribute name="weight" value="bold"/>
+ </attributes>
+ <style>
+ <class name="dim-label"/>
+ </style>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">4</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="_url">
+ <property name="can_focus">False</property>
+ <property name="halign">start</property>
+ <property name="xalign">0</property>
+ <property name="max_width_chars">45</property>
+ <property name="wrap">True</property>
+ <style>
+ <class name="dim-label"/>
+ </style>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">4</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">12</property>
+ <child>
+ <object class="GtkBox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkBox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkLabel" id="title">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_right">36</property>
+ <property name="label" translatable="yes">Title</property>
+ <property name="xalign">1</property>
+ <attributes>
+ <attribute name="weight" value="bold"/>
+ </attributes>
+ <style>
+ <class name="dim-label"/>
+ </style>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="_title_suggestion">
+ <property name="can_focus">False</property>
+ <property name="halign">start</property>
+ <property name="ellipsize">end</property>
+ <property name="max_width_chars">30</property>
+ <style>
+ <class name="suggestion-label"/>
+ </style>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="pack_type">end</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkEntry" id="_title_entry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="width_chars">45</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkBox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkBox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkLabel" id="album">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_right">36</property>
+ <property name="label" translatable="yes">Album</property>
+ <property name="xalign">1</property>
+ <attributes>
+ <attribute name="weight" value="bold"/>
+ </attributes>
+ <style>
+ <class name="dim-label"/>
+ </style>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="_album_suggestion">
+ <property name="can_focus">False</property>
+ <property name="halign">start</property>
+ <property name="ellipsize">end</property>
+ <property name="max_width_chars">30</property>
+ <style>
+ <class name="suggestion-label"/>
+ </style>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="pack_type">end</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkEntry" id="_album_entry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="width_chars">45</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkBox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkBox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkLabel" id="artist">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_right">36</property>
+ <property name="label" translatable="yes">Artist</property>
+ <property name="xalign">1</property>
+ <attributes>
+ <attribute name="weight" value="bold"/>
+ </attributes>
+ <style>
+ <class name="dim-label"/>
+ </style>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="_artist_suggestion">
+ <property name="can_focus">False</property>
+ <property name="halign">start</property>
+ <property name="ellipsize">end</property>
+ <property name="max_width_chars">30</property>
+ <style>
+ <class name="suggestion-label"/>
+ </style>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="pack_type">end</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkEntry" id="_artist_entry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="width_chars">45</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkBox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="halign">fill</property>
+ <property name="homogeneous">True</property>
+ <property name="spacing">12</property>
+ <child>
+ <object class="GtkBox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">6</property>
+ <property name="halign">start</property>
+ <child>
+ <object class="GtkBox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkLabel" id="track">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Song</property>
+ <property name="xalign">1</property>
+ <attributes>
+ <attribute name="weight" value="bold"/>
+ </attributes>
+ <style>
+ <class name="dim-label"/>
+ </style>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="_track_suggestion">
+ <property name="can_focus">False</property>
+ <property name="halign">end</property>
+ <style>
+ <class name="suggestion-label"/>
+ </style>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="pack_type">end</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkEntry" id="_track_entry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="width_chars">10</property>
+ <property name="input_purpose">number</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkBox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">6</property>
+ <property name="halign">start</property>
+ <child>
+ <object class="GtkBox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkLabel" id="disc">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="halign">start</property>
+ <property name="hexpand">True</property>
+ <property name="label" translatable="yes">Disc</property>
+ <attributes>
+ <attribute name="weight" value="bold"/>
+ </attributes>
+ <style>
+ <class name="dim-label"/>
+ </style>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="_disc_suggestion">
+ <property name="can_focus">False</property>
+ <property name="halign">start</property>
+ <property name="margin_right">2</property>
+ <style>
+ <class name="suggestion-label"/>
+ </style>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkEntry" id="_disc_entry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="width_chars">10</property>
+ <property name="input_purpose">number</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkBox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">6</property>
+ <property name="halign">start</property>
+ <child>
+ <object class="GtkBox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkLabel" id="year">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="halign">start</property>
+ <property name="hexpand">True</property>
+ <property name="label" translatable="yes">Year</property>
+ <attributes>
+ <attribute name="weight" value="bold"/>
+ </attributes>
+ <style>
+ <class name="dim-label"/>
+ </style>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="_year_suggestion">
+ <property name="can_focus">False</property>
+ <property name="halign">start</property>
+ <style>
+ <class name="suggestion-label"/>
+ </style>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkEntry" id="_year_entry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="width_chars">10</property>
+ <property name="input_purpose">number</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkBox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_bottom">20</property>
+ <property name="margin_left">20</property>
+ <child>
+ <object class="GtkBox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="valign">end</property>
+ <property name="spacing">6</property>
+ <property name="baseline_position">bottom</property>
+ <child>
+ <object class="GtkSpinner" id="_spinner">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="halign">start</property>
+ <property name="active">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="_spinner_label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Fetching metadata...</property>
+ <style>
+ <class name="dim-label"/>
+ </style>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkBox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="halign">end</property>
+ <property name="hexpand">True</property>
+ <property name="spacing">6</property>
+ <property name="margin_right">10</property>
+ <child>
+ <object class="GtkBox">
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkButton">
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="halign">end</property>
+ <child>
+ <object class="GtkImage">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="icon_name">go-next-symbolic-rtl</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton">
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="halign">start</property>
+ <child>
+ <object class="GtkImage">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="icon_name">go-next-symbolic</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <style>
+ <class name="linked"/>
+ </style>
+ </object>
+ </child>
+ <child>
+ <object class="GtkBox" id="_prev_next_box">
+ <property name="visible">True</property>
+ <property name="orientation">horizontal</property>
+ <property name="homogeneous">True</property>
+ <style>
+ <class name="linked"/>
+ </style>
+ <child>
+ <object class="GtkButton" id="_prev_button">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="valign">center</property>
+ <property name="sensitive">False</property>
+ <property name="tooltip_text" translatable="yes">Previous Suggestion</property>
+ <signal name="clicked" handler="_on_prev_button_clicked" swapped="no"/>
+ <style>
+ <class name="image-button"/>
+ </style>
+ <child>
+ <object class="GtkImage" id="_prev_button_image">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="icon_name">go-previous-symbolic</property>
+ <property name="icon_size">1</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkButton" id="_next_button">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="valign">center</property>
+ <property name="sensitive">False</property>
+ <property name="tooltip_text" translatable="yes">Next Suggestion</property>
+ <signal name="clicked" handler="_on_next_button_clicked" swapped="no"/>
+ <style>
+ <class name="image-button"/>
+ </style>
+ <child>
+ <object class="GtkImage" id="_next_button_image">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="icon_name">go-next-symbolic</property>
+ <property name="icon_size">1</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkButton" id="_use_suggestion_button">
+ <property name="label" translatable="yes">Use Suggestion</property>
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <signal name="clicked" handler="_on_use_suggestion_clicked" swapped="no"/>
+ <style>
+ <class name="suggested-action"/>
+ </style>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ <property name="pack_type">end</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="_submit_button">
+ <property name="label" translatable="yes">Submit</property>
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <signal name="clicked" handler="_on_submit_clicked" swapped="no"/>
+ <style>
+ <class name="suggested-action"/>
+ </style>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ <property name="pack_type">end</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </template>
+ <object class="GtkHeaderBar" id="_title_bar">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="title" translatable="yes">Edit Details</property>
+ <property name="show_close_button">True</property>
+ </object>
+</interface>
diff --git a/gnomemusic/widgets/selectiontoolbar.py b/gnomemusic/widgets/selectiontoolbar.py
index 16c9472b..799c56bb 100644
--- a/gnomemusic/widgets/selectiontoolbar.py
+++ b/gnomemusic/widgets/selectiontoolbar.py
@@ -33,9 +33,11 @@ class SelectionToolbar(Gtk.ActionBar):
__gtype_name__ = 'SelectionToolbar'
_add_to_playlist_button = Gtk.Template.Child()
+ _edit_details_button = Gtk.Template.Child()
__gsignals__ = {
- 'add-to-playlist': (GObject.SignalFlags.RUN_FIRST, None, ())
+ 'add-to-playlist': (GObject.SignalFlags.RUN_FIRST, None, ()),
+ "edit-details": (GObject.SignalFlags.RUN_FIRST, None, ())
}
selected_items_count = GObject.Property(type=int, default=0, minimum=0)
@@ -47,6 +49,8 @@ class SelectionToolbar(Gtk.ActionBar):
def __init__(self):
super().__init__()
+ self._stack = None
+
self.connect(
'notify::selected-items-count', self._on_item_selection_changed)
@@ -57,9 +61,36 @@ class SelectionToolbar(Gtk.ActionBar):
def _on_add_to_playlist_button_clicked(self, widget):
self.emit('add-to-playlist')
+ @Gtk.Template.Callback()
+ def _on_edit_tags_button_clicked(self, widget):
+ self.emit("edit-details")
+
@log
def _on_item_selection_changed(self, widget, data):
- if self.props.selected_items_count > 0:
- self._add_to_playlist_button.props.sensitive = True
- else:
- self._add_to_playlist_button.props.sensitive = False
+ selection_size = self.props.selected_items_count
+
+ self._add_to_playlist_button.props.sensitive = (selection_size > 0)
+ self._edit_details_button.props.sensitive = (selection_size == 1)
+
+ # FIXME: This is a workaround for not being able to pass the stack
+ # object via init when using Gtk.Builder.
+ @GObject.Property(type=Gtk.Stack, default=None)
+ def stack(self):
+ """The main GtkStack of the app
+
+ :return: main stack object
+ :rtype: GtkStack
+ """
+ return self._stack
+
+ @stack.setter
+ def stack(self, stack):
+ """Set the GtkStack object used
+
+ :param GtkStack stack: The GtkStack to use
+ """
+ if (stack is None
+ or (self._stack is not None
+ and self._stack != stack)):
+ return
+ self._stack = stack
diff --git a/gnomemusic/widgets/tageditordialog.py b/gnomemusic/widgets/tageditordialog.py
new file mode 100644
index 00000000..4f3c9cea
--- /dev/null
+++ b/gnomemusic/widgets/tageditordialog.py
@@ -0,0 +1,274 @@
+# Copyright 2019 The GNOME Music Developers
+#
+# GNOME Music is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# GNOME Music is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with GNOME Music; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+# The GNOME Music authors hereby grant permission for non-GPL compatible
+# GStreamer plugins to be used and distributed together with GStreamer
+# and GNOME Music. This permission is above and beyond the permissions
+# granted by the GPL license by which GNOME Music is covered. If you
+# modify this code, you may extend this exception to your version of the
+# code, but you are not obligated to do so. If you do not wish to do so,
+# delete this exception statement from your version.
+
+import logging
+from gettext import gettext as _
+
+from gi.repository import Grl, Gtk, Gio, GObject, GLib
+
+from gnomemusic.albumartcache import Art
+from gnomemusic.widgets.notificationspopup import TagEditorNotification
+
+import gnomemusic.utils as utils
+
+logger = logging.getLogger(__name__)
+
+
+@Gtk.Template(resource_path="/org/gnome/Music/ui/TagEditorDialog.ui")
+class TagEditorDialog(Gtk.Dialog):
+ """Tag editor widget
+ A tag editor dialog box that allows storing metadata for music files
+ through editing entries manually or applying automatic fetched tags.
+ """
+
+ __gtype_name__ = "TagEditorDialog"
+
+ _notifications_popup = Gtk.Template.Child()
+
+ _cover_stack = Gtk.Template.Child()
+ _spinner = Gtk.Template.Child()
+ _spinner_label = Gtk.Template.Child()
+ _title_bar = Gtk.Template.Child()
+
+ # tags entries and labels
+ _album_entry = Gtk.Template.Child()
+ _album_suggestion = Gtk.Template.Child()
+ _artist_entry = Gtk.Template.Child()
+ _artist_suggestion = Gtk.Template.Child()
+ _disc_entry = Gtk.Template.Child()
+ _disc_suggestion = Gtk.Template.Child()
+ _title_entry = Gtk.Template.Child()
+ _title_suggestion = Gtk.Template.Child()
+ _track_entry = Gtk.Template.Child()
+ _track_suggestion = Gtk.Template.Child()
+ _year_entry = Gtk.Template.Child()
+ _year_suggestion = Gtk.Template.Child()
+ _prev_button = Gtk.Template.Child()
+ _next_button = Gtk.Template.Child()
+ _use_suggestion_button = Gtk.Template.Child()
+ _submit_button = Gtk.Template.Child()
+
+ _url = Gtk.Template.Child()
+
+ __gsignals__ = {
+ "no-tags": (GObject.SignalFlags.RUN_FIRST, None, ()),
+ }
+
+ def __repr__(self):
+ return "<TagEditorDialog>"
+
+ def __init__(self, parent, selected_song, grilo, coreselection):
+ """Initialize the tag editor
+ :param parent: The parent widget calling the editor dialog box
+ :param selected_song: The current selected track which is being edited
+ """
+ super().__init__()
+
+ self._grilo = grilo
+ self._coreselection = coreselection
+
+ self.props.transient_for = parent
+ self.set_titlebar(self._title_bar)
+
+ self._cover_stack.props.size = Art.Size.LARGE
+ self._cover_stack.update(selected_song)
+
+ music_dir = GLib.UserDirectory.DIRECTORY_MUSIC
+ self._music_directory = GLib.get_user_special_dir(music_dir)
+
+ self._coresong = selected_song
+ self._init_labels()
+
+ self._notification = None
+ self._notification_finished_id = None
+ self._notification_undo_id = None
+
+ self._previous_tags = {}
+ self._suggestions = []
+ self._pointer = -1
+ self._search_tags()
+
+ def _init_labels(self):
+ for field in utils.fields_getter:
+ entry = getattr(self, "_" + field + "_entry")
+ value = utils.fields_getter[field](self._coresong.props.media)
+ if value:
+ entry.props.text = value
+ entry.connect("notify::text", self._on_entries_changed)
+
+ file_ = Gio.File.new_for_uri(self._coresong.props.url)
+ file_path = file_.get_path()
+ if file_path.startswith(self._music_directory):
+ baselength = len(self._music_directory) + 1
+ self._url.set_text(file_path[baselength:])
+ self._url.props.tooltip_text = file_path[baselength:]
+ else:
+ self._url.set_text(file_path)
+ self._url.props.tooltip_text = file_path
+
+ self._url.props.has_tooltip = True
+ self._url.props.visible = True
+
+ def _start_spinner(self, text):
+ self._spinner.start()
+ self._spinner_label.props.label = text
+
+ def _stop_spinner(self):
+ self._spinner.stop()
+ self._spinner_label.props.label = ""
+
+ def _search_tags(self):
+ self._start_spinner(_("Fetching metadata…"))
+ new_media = Grl.Media.audio_new()
+ new_media.set_url(self._coresong.props.url)
+ self._grilo.get_tags_from_musicbrainz(new_media, self._tags_found)
+
+ def _suggestion_sort_func(self, media):
+ creation_date = media.get_creation_date()
+ if creation_date is not None:
+ return (creation_date.get_year(), media.get_album())
+ return (GLib.DateTime.new_now_utc().get_year(), media.get_album())
+
+ def _tags_found(self, media, count=0):
+ if media is None:
+ logger.warning("Unable to find tags for song {}".format(
+ self._coresong.props.url))
+ self._stop_spinner()
+ self._create_notification(TagEditorNotification.Type.NONE)
+ return
+
+ self._suggestions.append(media)
+
+ if count == 0:
+ self._suggestions.sort(key=self._suggestion_sort_func)
+ self._stop_spinner()
+ self._pointer = 0
+ self._update_suggestion()
+ self._on_entries_changed()
+
+ def _update_suggestion(self):
+ media = self._suggestions[self._pointer]
+ for field in utils.fields_getter:
+ suggestion = getattr(self, "_" + field + "_suggestion")
+ value = utils.fields_getter[field](media)
+ if value:
+ suggestion.props.label = value
+ suggestion.props.visible = True
+ suggestion.props.has_tooltip = True
+ suggestion.props.tooltip_text = value
+
+ self._next_button.props.sensitive = (
+ self._pointer < len(self._suggestions) - 1)
+ self._prev_button.props.sensitive = (self._pointer > 0)
+
+ def _on_entries_changed(self, widget=None, param=None):
+ if self._pointer >= 0:
+ media = self._suggestions[self._pointer]
+ self._use_suggestion_button.props.sensitive = False
+ self._submit_button.props.sensitive = False
+
+ for field in utils.fields_getter:
+ entry = getattr(self, "_" + field + "_entry")
+ value = utils.fields_getter[field](self._coresong.props.media)
+ typed_value = entry.props.text.strip()
+ if (typed_value
+ and value != typed_value):
+ self._submit_button.props.sensitive = True
+ if self._pointer >= 0:
+ suggested_value = utils.fields_getter[field](media)
+ if (typed_value
+ and suggested_value
+ and typed_value != suggested_value):
+ self._use_suggestion_button.props.sensitive = True
+
+ @Gtk.Template.Callback()
+ def _on_next_button_clicked(self, widget):
+ self._pointer += 1
+ self._update_suggestion()
+ self._on_entries_changed()
+
+ @Gtk.Template.Callback()
+ def _on_prev_button_clicked(self, widget):
+ self._pointer -= 1
+ self._update_suggestion()
+ self._on_entries_changed()
+
+ @Gtk.Template.Callback()
+ def _on_use_suggestion_clicked(self, widget):
+ suggested_media = self._suggestions[self._pointer]
+ self._previous_tags.clear()
+ for field in utils.fields_getter:
+ entry = getattr(self, "_" + field + "_entry")
+ self._previous_tags[field] = entry.props.text
+ suggested_value = utils.fields_getter[field](suggested_media)
+ if suggested_value:
+ entry.props.text = suggested_value
+
+ self._create_notification(TagEditorNotification.Type.SONG)
+ self._on_entries_changed()
+
+ def _create_notification(self, notification_type):
+ self._notification = TagEditorNotification(
+ self._notifications_popup, notification_type)
+ if notification_type != TagEditorNotification.Type.NONE:
+ self._notification_undo_id = self._notification.connect(
+ "undo-fill", self._undo_use_suggestion)
+
+ self._notification_finished_id = self._notification.connect(
+ "finished", self._delete_notification)
+
+ def _delete_notification(self, notification=None):
+ if self._notification is None:
+ return
+
+ if self._notification_undo_id is not None:
+ self._notification.disconnect(self._notification_undo_id)
+ self._notification_undo_id = None
+
+ self._notification.disconnect(self._notification_finished_id)
+ self._notification_finished_id = None
+ self._notification = None
+
+ def _undo_use_suggestion(self, notification):
+ """Revert tags filling
+
+ :param TagEditorNotification notification: the notification
+ """
+ for field in utils.fields_getter:
+ entry = getattr(self, "_" + field + "_entry")
+ entry.props.text = self._previous_tags[field]
+
+ self._delete_notification()
+
+ @Gtk.Template.Callback()
+ def _on_submit_clicked(self, widget):
+ self._delete_notification()
+
+ for field in self._coresong.fields_setter:
+ entry = getattr(self, "_" + field + "_entry")
+ entry_text = entry.props.text
+ if entry_text:
+ self._coresong.fields_setter[field](entry_text)
+
+ self.destroy()
diff --git a/gnomemusic/window.py b/gnomemusic/window.py
index a7750a65..7ffd44be 100644
--- a/gnomemusic/window.py
+++ b/gnomemusic/window.py
@@ -44,6 +44,7 @@ from gnomemusic.widgets.playertoolbar import PlayerToolbar
from gnomemusic.widgets.playlistdialog import PlaylistDialog
from gnomemusic.widgets.searchheaderbar import SearchHeaderBar
from gnomemusic.widgets.selectiontoolbar import SelectionToolbar # noqa: F401
+from gnomemusic.widgets.tageditordialog import TagEditorDialog
from gnomemusic.windowplacement import WindowPlacement
import logging
@@ -134,6 +135,8 @@ class Window(Gtk.ApplicationWindow):
self._player_toolbar = PlayerToolbar()
self._player_toolbar.props.player = self._player
+ self._selection_toolbar.props.stack = self._stack
+
self._headerbar.connect(
'back-button-clicked', self._switch_back_from_childview)
@@ -180,6 +183,7 @@ class Window(Gtk.ApplicationWindow):
self._selection_toolbar.connect(
'add-to-playlist', self._on_add_to_playlist)
+ self._selection_toolbar.connect("edit-details", self._on_edit_tags)
self._search.connect("notify::state", self._on_search_state_changed)
self._headerbar.props.state = HeaderBar.State.MAIN
@@ -526,3 +530,22 @@ class Window(Gtk.ApplicationWindow):
:param bool visible: actionbar visibility
"""
self._player_toolbar.set_visible(visible)
+
+ @log
+ def _on_edit_tags(self, widget):
+ if self._stack.get_visible_child() == self.views[View.PLAYLIST]:
+ return
+
+ selected_songs = self._app._coreselection.props.selected_items
+
+ if len(selected_songs) < 1:
+ return
+
+ tags_editor_dialog = TagEditorDialog(self, selected_songs[0],
+ self._app.props.coremodel._grilo,
+ self._app._coreselection)
+ if tags_editor_dialog.run() == Gtk.ResponseType.ACCEPT:
+ return
+
+ self.props.selection_mode = False
+ tags_editor_dialog.destroy()
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 2df13b28..fd81907e 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -16,6 +16,7 @@ data/ui/SearchHeaderBar.ui
data/ui/SearchView.ui
data/ui/SelectionBarMenuButton.ui
data/ui/SelectionToolbar.ui
+data/ui/TagEditorDialog.ui
gnomemusic/__init__.py
gnomemusic/albumartcache.py
gnomemusic/application.py
@@ -39,4 +40,5 @@ gnomemusic/widgets/playertoolbar.py
gnomemusic/widgets/playlistcontrols.py
gnomemusic/widgets/playlistdialog.py
gnomemusic/widgets/starhandlerwidget.py
+gnomemusic/widgets/tageditordialog.py
gnomemusic/window.py
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]