diff --git a/picard/__init__.py b/picard/__init__.py
index 919b16b0a..62fbead25 100644
--- a/picard/__init__.py
+++ b/picard/__init__.py
@@ -41,7 +41,7 @@ PICARD_APP_NAME = "Picard"
PICARD_DISPLAY_NAME = "MusicBrainz Picard"
PICARD_APP_ID = "org.musicbrainz.Picard"
PICARD_DESKTOP_NAME = PICARD_APP_ID + ".desktop"
-PICARD_VERSION = Version(2, 4, 0, 'beta', 2)
+PICARD_VERSION = Version(2, 4, 0, 'beta', 3)
# optional build version
diff --git a/picard/config_upgrade.py b/picard/config_upgrade.py
index 1d6d30557..266aa75ab 100644
--- a/picard/config_upgrade.py
+++ b/picard/config_upgrade.py
@@ -5,7 +5,7 @@
# Copyright (C) 2013-2014 Michael Wiencek
# Copyright (C) 2013-2016, 2018-2019 Laurent Monin
# Copyright (C) 2014, 2017 Lukáš Lalinský
-# Copyright (C) 2014, 2018-2019 Philipp Wolfer
+# Copyright (C) 2014, 2018-2020 Philipp Wolfer
# Copyright (C) 2015 Ohm Patel
# Copyright (C) 2016 Suhas
# Copyright (C) 2016-2017 Sambhav Kothari
@@ -296,6 +296,13 @@ def upgrade_to_v2_2_0_dev_4(config):
_s["file_naming_format"] = DEFAULT_FILE_NAMING_FORMAT
+def upgrade_to_v2_4_0_beta_3(config):
+ """Improved default file naming script"""
+ _s = config.setting
+ opt = 'preserved_tags'
+ _s[opt] = [t.strip() for t in _s.raw_value(opt, qtype='QString').split(',')]
+
+
def rename_option(config, old_opt, new_opt, option_type, default):
_s = config.setting
if old_opt in _s:
@@ -319,4 +326,5 @@ def upgrade_config(config):
cfg.register_upgrade_hook(upgrade_to_v2_0_0_dev_3)
cfg.register_upgrade_hook(upgrade_to_v2_1_0_dev_1)
cfg.register_upgrade_hook(upgrade_to_v2_2_0_dev_3)
+ cfg.register_upgrade_hook(upgrade_to_v2_4_0_beta_3)
cfg.run_upgrade_hooks(log.debug)
diff --git a/picard/ui/options/tags.py b/picard/ui/options/tags.py
index 2a6bca8ac..61681d921 100644
--- a/picard/ui/options/tags.py
+++ b/picard/ui/options/tags.py
@@ -4,7 +4,7 @@
#
# Copyright (C) 2006-2007, 2011 Lukáš Lalinský
# Copyright (C) 2009 Nikolai Prokoschenko
-# Copyright (C) 2009-2010, 2018-2019 Philipp Wolfer
+# Copyright (C) 2009-2010, 2018-2020 Philipp Wolfer
# Copyright (C) 2012 Erik Wasser
# Copyright (C) 2012 Johannes Weißl
# Copyright (C) 2012-2013 Michael Wiencek
@@ -27,15 +27,7 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-import re
-
-from PyQt5 import (
- QtCore,
- QtWidgets,
-)
-
from picard import config
-from picard.util.tags import TAG_NAMES
from picard.ui.options import (
OptionsPage,
@@ -58,18 +50,13 @@ class TagsOptionsPage(OptionsPage):
config.BoolOption("setting", "clear_existing_tags", False),
config.BoolOption("setting", "remove_id3_from_flac", False),
config.BoolOption("setting", "remove_ape_from_mp3", False),
- config.TextOption("setting", "preserved_tags", ""),
+ config.ListOption("setting", "preserved_tags", []),
]
def __init__(self, parent=None):
super().__init__(parent)
self.ui = Ui_TagsOptionsPage()
self.ui.setupUi(self)
- self.completer = QtWidgets.QCompleter(sorted(TAG_NAMES.keys()), self)
- self.completer.setCaseSensitivity(QtCore.Qt.CaseInsensitive)
- self.completer.setWidget(self.ui.preserved_tags)
- self.ui.preserved_tags.textEdited.connect(self.preserved_tags_edited)
- self.completer.activated.connect(self.completer_activated)
def load(self):
self.ui.write_tags.setChecked(not config.setting["dont_write_tags"])
@@ -77,7 +64,8 @@ class TagsOptionsPage(OptionsPage):
self.ui.clear_existing_tags.setChecked(config.setting["clear_existing_tags"])
self.ui.remove_ape_from_mp3.setChecked(config.setting["remove_ape_from_mp3"])
self.ui.remove_id3_from_flac.setChecked(config.setting["remove_id3_from_flac"])
- self.ui.preserved_tags.setText(config.setting["preserved_tags"])
+ self.ui.preserved_tags.update(config.setting["preserved_tags"])
+ self.ui.preserved_tags.set_user_sortable(False)
def save(self):
config.setting["dont_write_tags"] = not self.ui.write_tags.isChecked()
@@ -88,30 +76,8 @@ class TagsOptionsPage(OptionsPage):
self.tagger.window.metadata_box.update()
config.setting["remove_ape_from_mp3"] = self.ui.remove_ape_from_mp3.isChecked()
config.setting["remove_id3_from_flac"] = self.ui.remove_id3_from_flac.isChecked()
- config.setting["preserved_tags"] = re.sub(r"[,\s]+$", "", self.ui.preserved_tags.text())
+ config.setting["preserved_tags"] = list(self.ui.preserved_tags.tags)
self.tagger.window.enable_tag_saving_action.setChecked(not config.setting["dont_write_tags"])
- def preserved_tags_edited(self, text):
- prefix = text[:self.ui.preserved_tags.cursorPosition()].split(",")[-1]
- self.completer.setCompletionPrefix(prefix.strip())
- if prefix:
- self.completer.complete()
- else:
- self.completer.popup().hide()
-
- def completer_activated(self, text):
- input_field = self.ui.preserved_tags
- current = input_field.text()
- cursor_pos = input_field.cursorPosition()
- prefix_len = len(self.completer.completionPrefix())
- leading_text = current[:cursor_pos - prefix_len].rstrip()
- trailing_text = current[cursor_pos:].lstrip()
- # Replace the autocompletion prefix with the autocompleted text,
- # append a comma so the user can easily enter the next entry
- replacement = ("%s %s, " % (leading_text, text)).lstrip()
- input_field.setText(replacement + trailing_text)
- # Set cursor position to end of autocompleted input
- input_field.setCursorPosition(len(replacement))
-
register_options_page(TagsOptionsPage)
diff --git a/picard/ui/ui_options_tags.py b/picard/ui/ui_options_tags.py
index 7ade0c25a..f11d227c1 100644
--- a/picard/ui/ui_options_tags.py
+++ b/picard/ui/ui_options_tags.py
@@ -39,15 +39,15 @@ class Ui_TagsOptionsPage(object):
self.preserved_tags_label = QtWidgets.QLabel(self.before_tagging)
self.preserved_tags_label.setObjectName("preserved_tags_label")
self.vboxlayout1.addWidget(self.preserved_tags_label)
- self.preserved_tags = QtWidgets.QLineEdit(self.before_tagging)
+ self.preserved_tags = TagListEditor(self.before_tagging)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Expanding)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.preserved_tags.sizePolicy().hasHeightForWidth())
+ self.preserved_tags.setSizePolicy(sizePolicy)
self.preserved_tags.setObjectName("preserved_tags")
self.vboxlayout1.addWidget(self.preserved_tags)
- self.preserved_tags_help = QtWidgets.QLabel(self.before_tagging)
- self.preserved_tags_help.setObjectName("preserved_tags_help")
- self.vboxlayout1.addWidget(self.preserved_tags_help)
self.vboxlayout.addWidget(self.before_tagging)
- spacerItem1 = QtWidgets.QSpacerItem(274, 41, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
- self.vboxlayout.addItem(spacerItem1)
self.retranslateUi(TagsOptionsPage)
QtCore.QMetaObject.connectSlotsByName(TagsOptionsPage)
@@ -66,4 +66,4 @@ class Ui_TagsOptionsPage(object):
self.remove_id3_from_flac.setText(_("Remove ID3 tags from FLAC files"))
self.remove_ape_from_mp3.setText(_("Remove APEv2 tags from MP3 files"))
self.preserved_tags_label.setText(_("Preserve these tags from being cleared or overwritten with MusicBrainz data:"))
- self.preserved_tags_help.setText(_("Tags are separated by commas and are case-insensitive."))
+from picard.ui.widgets.taglisteditor import TagListEditor
diff --git a/picard/util/preservedtags.py b/picard/util/preservedtags.py
index 35af784ae..b18f834f0 100644
--- a/picard/util/preservedtags.py
+++ b/picard/util/preservedtags.py
@@ -31,10 +31,10 @@ class PreservedTags:
self._tags = self._from_config()
def _to_config(self):
- config.setting[self.opt_name] = ", ".join(sorted(self._tags))
+ config.setting[self.opt_name] = sorted(self._tags)
def _from_config(self):
- tags = config.setting[self.opt_name].split(',')
+ tags = config.setting[self.opt_name]
return set(filter(bool, map(self._normalize_tag, tags)))
@staticmethod
diff --git a/test/test_util_preservedtags.py b/test/test_util_preservedtags.py
index 15fb10ac3..79e6eaacf 100644
--- a/test/test_util_preservedtags.py
+++ b/test/test_util_preservedtags.py
@@ -28,7 +28,7 @@ from picard.util.preservedtags import PreservedTags
class PreservedTagsTest(PicardTestCase):
def setUp(self):
super().setUp()
- config.setting[PreservedTags.opt_name] = "tag1, tag2"
+ config.setting[PreservedTags.opt_name] = ["tag1", "tag2"]
def test_load_and_contains(self):
preserved = PreservedTags()
@@ -71,4 +71,4 @@ class PreservedTagsTest(PicardTestCase):
preserved.add('tag2')
preserved.add('tag1')
preserved.discard('tag2')
- self.assertEqual(config.setting[PreservedTags.opt_name], 'tag1, tag3')
+ self.assertEqual(config.setting[PreservedTags.opt_name], ['tag1', 'tag3'])
diff --git a/ui/options_tags.ui b/ui/options_tags.ui
index bb571cbad..8136d1670 100644
--- a/ui/options_tags.ui
+++ b/ui/options_tags.ui
@@ -85,33 +85,28 @@
-
-
-
- -
-
-
- Tags are separated by commas and are case-insensitive.
+
+
+
+ 0
+ 0
+
- -
-
-
- Qt::Vertical
-
-
-
- 274
- 41
-
-
-
-
+
+
+ TagListEditor
+ QWidget
+ picard.ui.widgets.taglisteditor
+ 1
+
+
write_tags
preserve_timestamps