diff --git a/picard/ui/checkbox_list_item.py b/picard/ui/checkbox_list_item.py
new file mode 100644
index 000000000..d318fbefa
--- /dev/null
+++ b/picard/ui/checkbox_list_item.py
@@ -0,0 +1,34 @@
+# -*- coding: utf-8 -*-
+#
+# Picard, the next-generation MusicBrainz tagger
+# Copyright (C) 2018 Sambhav Kothari
+#
+# This program 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.
+#
+# This program 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 this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+from PyQt5.QtCore import Qt
+from PyQt5.QtWidgets import QListWidgetItem
+
+class CheckboxListItem(QListWidgetItem):
+
+ def __init__(self, text='', checked=False, data=None):
+ super().__init__()
+ self.setText(text)
+ self.setFlags(self.flags() | Qt.ItemIsUserCheckable)
+ self.setCheckState(Qt.Checked if checked else Qt.Unchecked)
+ self.data = data
+
+ @property
+ def checked(self):
+ return self.checkState() == Qt.Checked
diff --git a/picard/ui/moveable_list_view.py b/picard/ui/moveable_list_view.py
new file mode 100644
index 000000000..348656245
--- /dev/null
+++ b/picard/ui/moveable_list_view.py
@@ -0,0 +1,51 @@
+# -*- coding: utf-8 -*-
+#
+# Picard, the next-generation MusicBrainz tagger
+# Copyright (C) 2018 Sambhav Kothari
+#
+# This program 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.
+#
+# This program 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 this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+from functools import partial
+from PyQt5 import QtWidgets, QtCore
+
+class MoveableListView:
+
+ def __init__(self, list_widget, up_button, down_button, callback=None):
+ self.list_widget = list_widget
+ self.up_button = up_button
+ self.down_button = down_button
+ self.update_callback = callback
+ self.up_button.clicked.connect(partial(self.move_item, 1))
+ self.down_button.clicked.connect(partial(self.move_item, -1))
+ self.list_widget.currentRowChanged.connect(self.update_buttons)
+ self.list_widget.setDragDropMode(QtWidgets.QAbstractItemView.DragDrop)
+ self.list_widget.setDefaultDropAction(QtCore.Qt.MoveAction)
+
+ def move_item(self, offset):
+ current_index = self.list_widget.currentRow()
+ offset_index = current_index - offset
+ offset_item = self.list_widget.item(offset_index)
+ if offset_item:
+ current_item = self.list_widget.takeItem(current_index)
+ self.list_widget.insertItem(offset_index, current_item)
+ self.list_widget.setCurrentItem(current_item)
+ self.update_buttons()
+
+ def update_buttons(self):
+ current_row = self.list_widget.currentRow()
+ self.up_button.setEnabled(current_row > 0)
+ self.down_button.setEnabled(current_row < self.list_widget.count() - 1)
+ if self.update_callback:
+ self.update_callback()
diff --git a/picard/ui/options/cover.py b/picard/ui/options/cover.py
index fe1ffdea1..e60989535 100644
--- a/picard/ui/options/cover.py
+++ b/picard/ui/options/cover.py
@@ -21,10 +21,8 @@ from picard import config
from picard.ui.options import OptionsPage, register_options_page
from picard.ui.ui_options_cover import Ui_CoverOptionsPage
from picard.coverart.providers import cover_art_providers, is_provider_enabled
-from picard.ui.sortablecheckboxlist import (
- SortableCheckboxListWidget,
- SortableCheckboxListItem
-)
+from picard.ui.moveable_list_view import MoveableListView
+from picard.ui.checkbox_list_item import CheckboxListItem
class CoverOptionsPage(OptionsPage):
@@ -56,9 +54,8 @@ class CoverOptionsPage(OptionsPage):
self.ui.setupUi(self)
self.ui.save_images_to_files.clicked.connect(self.update_filename)
self.ui.save_images_to_tags.clicked.connect(self.update_save_images_to_tags)
- self.provider_list_widget = SortableCheckboxListWidget()
- self.ui.ca_providers_list.insertWidget(0, self.provider_list_widget)
- self.ca_providers = []
+ self.move_view = MoveableListView(self.ui.ca_providers_list, self.ui.up_button,
+ self.ui.down_button)
def load_cover_art_providers(self):
"""Load available providers, initialize provider-specific options, restore state of each
@@ -70,25 +67,28 @@ class CoverOptionsPage(OptionsPage):
except AttributeError:
title = provider.NAME
checked = is_provider_enabled(provider.NAME)
- self.provider_list_widget.addItem(SortableCheckboxListItem(title, checked=checked, data=provider.NAME))
-
- def update_providers_options(items):
- self.ca_providers = [(item.data, item.checked) for item in items]
- self.provider_list_widget.changed.connect(update_providers_options)
+ self.ui.ca_providers_list.addItem(CheckboxListItem(title, checked=checked, data=provider.NAME))
def restore_defaults(self):
# Remove previous entries
self.provider_list_widget.clear()
super().restore_defaults()
+ def ca_providers(self):
+ items = []
+ for i in range(self.ui.ca_providers_list.count()):
+ item = self.ui.ca_providers_list.item(i)
+ items.append((item.data, item.checked))
+ return items
+
def load(self):
self.ui.save_images_to_tags.setChecked(config.setting["save_images_to_tags"])
self.ui.cb_embed_front_only.setChecked(config.setting["embed_only_one_front_image"])
self.ui.save_images_to_files.setChecked(config.setting["save_images_to_files"])
self.ui.cover_image_filename.setText(config.setting["cover_image_filename"])
self.ui.save_images_overwrite.setChecked(config.setting["save_images_overwrite"])
- self.ca_providers = config.setting["ca_providers"]
self.load_cover_art_providers()
+ self.ui.ca_providers_list.setCurrentRow(0)
self.update_all()
def save(self):
@@ -97,7 +97,7 @@ class CoverOptionsPage(OptionsPage):
config.setting["save_images_to_files"] = self.ui.save_images_to_files.isChecked()
config.setting["cover_image_filename"] = self.ui.cover_image_filename.text()
config.setting["save_images_overwrite"] = self.ui.save_images_overwrite.isChecked()
- config.setting["ca_providers"] = self.ca_providers
+ config.setting["ca_providers"] = self.ca_providers()
def update_all(self):
self.update_filename()
diff --git a/picard/ui/options/interface.py b/picard/ui/options/interface.py
index db421b127..b60d13a3f 100644
--- a/picard/ui/options/interface.py
+++ b/picard/ui/options/interface.py
@@ -23,6 +23,7 @@ from PyQt5 import QtCore, QtWidgets
from PyQt5.QtCore import QStandardPaths
from picard import config
from picard.util import icontheme
+from picard.ui.moveable_list_view import MoveableListView
from picard.ui.options import OptionsPage, register_options_page
from picard.ui.ui_options_interface import Ui_InterfaceOptionsPage
from picard.ui.util import enabledSlot
@@ -151,11 +152,9 @@ class InterfaceOptionsPage(OptionsPage):
self.ui.add_button.clicked.connect(self.add_to_toolbar)
self.ui.insert_separator_button.clicked.connect(self.insert_separator)
self.ui.remove_button.clicked.connect(self.remove_action)
- self.ui.up_button.clicked.connect(partial(self.move_item, 1))
- self.ui.down_button.clicked.connect(partial(self.move_item, -1))
- self.ui.toolbar_layout_list.currentRowChanged.connect(self.update_buttons)
- self.ui.toolbar_layout_list.setDragDropMode(QtWidgets.QAbstractItemView.DragDrop)
- self.ui.toolbar_layout_list.setDefaultDropAction(QtCore.Qt.MoveAction)
+ self.move_view = MoveableListView(self.ui.toolbar_layout_list, self.ui.up_button,
+ self.ui.down_button, self.update_action_buttons)
+ self.update_buttons = self.move_view.update_buttons
def load(self):
self.ui.toolbar_show_labels.setChecked(config.setting["toolbar_show_labels"])
@@ -234,11 +233,8 @@ class InterfaceOptionsPage(OptionsPage):
if name in self.ACTION_NAMES or name == 'separator':
self._insert_item(name)
- def update_buttons(self):
+ def update_action_buttons(self):
self.ui.add_button.setEnabled(self._added_actions() != self.ACTION_NAMES)
- current_row = self.ui.toolbar_layout_list.currentRow()
- self.ui.up_button.setEnabled(current_row > 0)
- self.ui.down_button.setEnabled(current_row < self.ui.toolbar_layout_list.count() - 1)
def add_to_toolbar(self):
display_list = set.difference(self.ACTION_NAMES, self._added_actions())
@@ -252,16 +248,6 @@ class InterfaceOptionsPage(OptionsPage):
insert_index = self.ui.toolbar_layout_list.currentRow() + 1
self._insert_item('separator', insert_index)
- def move_item(self, offset):
- current_index = self.ui.toolbar_layout_list.currentRow()
- offset_index = current_index - offset
- offset_item = self.ui.toolbar_layout_list.item(offset_index)
- if offset_item:
- current_item = self.ui.toolbar_layout_list.takeItem(current_index)
- self.ui.toolbar_layout_list.insertItem(offset_index, current_item)
- self.ui.toolbar_layout_list.setCurrentItem(current_item)
- self.update_buttons()
-
def remove_action(self):
item = self.ui.toolbar_layout_list.takeItem(self.ui.toolbar_layout_list.currentRow())
del item
diff --git a/picard/ui/sortablecheckboxlist.py b/picard/ui/sortablecheckboxlist.py
deleted file mode 100644
index 2f47c6f82..000000000
--- a/picard/ui/sortablecheckboxlist.py
+++ /dev/null
@@ -1,148 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Picard, the next-generation MusicBrainz tagger
-# Copyright (C) 2015 Laurent Monin
-#
-# This program 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.
-#
-# This program 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 this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-
-from functools import partial
-from PyQt5 import QtCore, QtWidgets
-from PyQt5.QtCore import pyqtSignal
-
-
-class SortableCheckboxListWidget(QtWidgets.QWidget):
- _CHECKBOX_POS = 0
- _BUTTON_UP = 1
- _BUTTON_DOWN = 2
-
- __no_emit = False
- changed = pyqtSignal(list)
-
- def __init__(self, parent=None):
- super().__init__(parent)
- layout = QtWidgets.QGridLayout()
- layout.setHorizontalSpacing(5)
- layout.setVerticalSpacing(2)
- layout.setContentsMargins(0, 0, 0, 0)
- self.setLayout(layout)
- self.__items = []
-
- def addItems(self, items):
- for item in items:
- self.addItem(item)
-
- def setSignals(self, row):
- layout = self.layout()
- checkbox = layout.itemAtPosition(row, self._CHECKBOX_POS).widget()
- up = layout.itemAtPosition(row, self._BUTTON_UP).widget()
- down = layout.itemAtPosition(row, self._BUTTON_DOWN).widget()
- checkbox.stateChanged.connect(partial(self.checkbox_toggled, row))
- up.clicked.connect(partial(self.move_button_clicked, row, up=True))
- down.clicked.connect(partial(self.move_button_clicked, row, up=False))
-
- def moveItem(self, from_row, to_row):
- to_row = to_row % len(self.__items)
- self.__items[to_row], self.__items[from_row] = \
- self.__items[from_row], self.__items[to_row]
- self.updateRow(to_row)
- self.updateRow(from_row)
- self._emit_changed()
-
- def checkbox_toggled(self, row, state):
- self.__items[row].setChecked(state == QtCore.Qt.Checked)
- self._emit_changed()
-
- def move_button_clicked(self, row, up):
- if up:
- to = row - 1
- else:
- to = row + 1
- self.moveItem(row, to)
-
- def updateRow(self, row):
- self.__no_emit = True
- item = self.__items[row]
- layout = self.layout()
- checkbox = layout.itemAtPosition(row, self._CHECKBOX_POS).widget()
- checkbox.setText(item.text)
- checkbox.setChecked(item.checked)
- self.__no_emit = False
-
- def addItem(self, item):
- self.__items.append(item)
- row = len(self.__items) - 1
- layout = self.layout()
- layout.addWidget(QtWidgets.QCheckBox(), row, self._CHECKBOX_POS)
- self.updateRow(row)
- up_button = QtWidgets.QToolButton()
- up_button.setArrowType(QtCore.Qt.UpArrow)
- up_button.setMaximumSize(QtCore.QSize(16, 16))
- down_button = QtWidgets.QToolButton()
- down_button.setArrowType(QtCore.Qt.DownArrow)
- down_button.setMaximumSize(QtCore.QSize(16, 16))
- layout.addWidget(up_button, row, self._BUTTON_UP)
- layout.addWidget(down_button, row, self._BUTTON_DOWN)
- self.setSignals(row)
-
- def _emit_changed(self):
- if not self.__no_emit:
- self.changed.emit(self.__items)
-
- def clear(self):
- for i in reversed(range(len(self.__items))):
- self._remove(i)
- self.__items = []
-
- def _remove(self, row):
- self.layout().itemAtPosition(row, self._CHECKBOX_POS).widget().setParent(None)
- self.layout().itemAtPosition(row, self._BUTTON_UP).widget().setParent(None)
- self.layout().itemAtPosition(row, self._BUTTON_DOWN).widget().setParent(None)
-
-
-class SortableCheckboxListItem(object):
-
- def __init__(self, text='', checked=False, data=None):
- self._checked = checked
- self._text = text
- self._data = data
-
- @property
- def text(self):
- return self._text
-
- def setText(self, text):
- self._text = text
-
- @property
- def checked(self):
- return self._checked
-
- def setChecked(self, state):
- self._checked = state
-
- @property
- def data(self):
- return self._data
-
- def setData(self, data):
- self._data = data
-
- def __repr__(self):
- params = []
- params.append('text=' + repr(self.text))
- params.append('checked=' + repr(self.checked))
- if self.data is not None:
- params.append('data=' + repr(self.data))
- return "%s(%s)" % (self.__class__.__name__, ", ".join(params))
diff --git a/picard/ui/ui_options_cover.py b/picard/ui/ui_options_cover.py
index 7b3a7c24b..9bf99d03f 100644
--- a/picard/ui/ui_options_cover.py
+++ b/picard/ui/ui_options_cover.py
@@ -45,12 +45,32 @@ class Ui_CoverOptionsPage(object):
self.ca_providers_groupbox.setObjectName("ca_providers_groupbox")
self.ca_providers_layout = QtWidgets.QVBoxLayout(self.ca_providers_groupbox)
self.ca_providers_layout.setObjectName("ca_providers_layout")
- self.ca_providers_list = QtWidgets.QHBoxLayout()
+ self.ca_providers_list = QtWidgets.QListWidget(self.ca_providers_groupbox)
self.ca_providers_list.setObjectName("ca_providers_list")
+ self.ca_providers_layout.addWidget(self.ca_providers_list)
+ self.ca_layout = QtWidgets.QHBoxLayout()
+ self.ca_layout.setObjectName("ca_layout")
+ self.move_label = QtWidgets.QLabel(self.ca_providers_groupbox)
+ self.move_label.setObjectName("move_label")
+ self.ca_layout.addWidget(self.move_label)
+ self.up_button = QtWidgets.QToolButton(self.ca_providers_groupbox)
+ self.up_button.setLayoutDirection(QtCore.Qt.LeftToRight)
+ self.up_button.setText("")
+ self.up_button.setToolButtonStyle(QtCore.Qt.ToolButtonIconOnly)
+ self.up_button.setAutoRaise(False)
+ self.up_button.setArrowType(QtCore.Qt.UpArrow)
+ self.up_button.setObjectName("up_button")
+ self.ca_layout.addWidget(self.up_button)
+ self.down_button = QtWidgets.QToolButton(self.ca_providers_groupbox)
+ self.down_button.setText("")
+ self.down_button.setToolButtonStyle(QtCore.Qt.ToolButtonIconOnly)
+ self.down_button.setArrowType(QtCore.Qt.DownArrow)
+ self.down_button.setObjectName("down_button")
+ self.ca_layout.addWidget(self.down_button)
spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
- self.ca_providers_list.addItem(spacerItem)
- self.ca_providers_layout.addLayout(self.ca_providers_list)
- self.verticalLayout.addWidget(self.ca_providers_groupbox)
+ self.ca_layout.addItem(spacerItem)
+ self.ca_providers_layout.addLayout(self.ca_layout)
+ self.verticalLayout.addWidget(self.ca_providers_groupbox, 0, QtCore.Qt.AlignTop)
spacerItem1 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
self.verticalLayout.addItem(spacerItem1)
@@ -70,4 +90,7 @@ class Ui_CoverOptionsPage(object):
self.label_use_filename.setText(_("Use the following file name for images:"))
self.save_images_overwrite.setText(_("Overwrite the file if it already exists"))
self.ca_providers_groupbox.setTitle(_("Cover Art Providers"))
+ self.move_label.setText(_("Reorder Priority: "))
+ self.up_button.setToolTip(_("Move selected item up"))
+ self.down_button.setToolTip(_("Move selected item down"))
diff --git a/ui/options_cover.ui b/ui/options_cover.ui
index 2c7c94f2c..9ae4f6380 100644
--- a/ui/options_cover.ui
+++ b/ui/options_cover.ui
@@ -20,7 +20,16 @@
2
-
+
+ 9
+
+
+ 9
+
+
+ 9
+
+
9
-
@@ -64,7 +73,7 @@
- -
+
-
@@ -77,7 +86,55 @@
-
-
+
+
+ -
+
+
-
+
+
+ Reorder Priority:
+
+
+
+ -
+
+
+ Move selected item up
+
+
+ Qt::LeftToRight
+
+
+
+
+
+ Qt::ToolButtonIconOnly
+
+
+ false
+
+
+ Qt::UpArrow
+
+
+
+ -
+
+
+ Move selected item down
+
+
+
+
+
+ Qt::ToolButtonIconOnly
+
+
+ Qt::DownArrow
+
+
+
-