Copy to library: Add a action to show a dialog that allows for easy selection of libraries for copy/move. Useful when there are a large number of libraries to choose from. Fixes #1706198 [[Enhancement] Provide users the ability to remove/hide popup menu option copy to library, leaving only Delete After Copy option visible](https://bugs.launchpad.net/calibre/+bug/1706198)

This commit is contained in:
Kovid Goyal 2017-08-03 11:25:45 +05:30
parent 0f6d873070
commit a74d0546eb
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C

View File

@ -12,8 +12,9 @@ from contextlib import closing
from collections import defaultdict from collections import defaultdict
from PyQt5.Qt import ( from PyQt5.Qt import (
QToolButton, QDialog, QGridLayout, QIcon, QLabel, QDialogButtonBox, QApplication, QToolButton, QDialog, QGridLayout, QIcon, QLabel, QDialogButtonBox,
QFormLayout, QCheckBox, QWidget, QScrollArea, QVBoxLayout, Qt, QListWidgetItem, QListWidget) QApplication, QLineEdit, QHBoxLayout, QFormLayout, QCheckBox, QWidget,
QScrollArea, QVBoxLayout, Qt, QListWidgetItem, QListWidget, QSize)
from calibre import as_unicode from calibre import as_unicode
from calibre.constants import isosx from calibre.constants import isosx
@ -22,10 +23,10 @@ from calibre.gui2.actions import InterfaceAction
from calibre.gui2 import (error_dialog, Dispatcher, warning_dialog, gprefs, from calibre.gui2 import (error_dialog, Dispatcher, warning_dialog, gprefs,
info_dialog, choose_dir) info_dialog, choose_dir)
from calibre.gui2.dialogs.progress import ProgressDialog from calibre.gui2.dialogs.progress import ProgressDialog
from calibre.gui2.widgets import HistoryLineEdit from calibre.gui2.widgets2 import Dialog
from calibre.utils.config import prefs, tweaks from calibre.utils.config import prefs, tweaks
from calibre.utils.date import now from calibre.utils.date import now
from calibre.utils.icu import sort_key from calibre.utils.icu import sort_key, numeric_sort_key
def ask_about_cc_mismatch(gui, db, newdb, missing_cols, incompatible_cols): # {{{ def ask_about_cc_mismatch(gui, db, newdb, missing_cols, incompatible_cols): # {{{
@ -257,28 +258,52 @@ class Worker(Thread): # {{{
# }}} # }}}
class ChooseLibrary(QDialog): # {{{ class ChooseLibrary(Dialog): # {{{
def __init__(self, parent): def __init__(self, parent, locations):
super(ChooseLibrary, self).__init__(parent) self.locations = locations
d = self Dialog.__init__(self, _('Choose library'), 'copy_to_choose_library_dialog', parent)
d.l = l = QGridLayout() self.resort()
d.setLayout(l) self.current_changed()
d.setWindowTitle(_('Choose library'))
la = d.la = QLabel(_('Library &path:')) def resort(self):
l.addWidget(la, 0, 0) if self.sort_alphabetically.isChecked():
le = d.le = HistoryLineEdit(d) sorted_locations = sorted(self.locations, key=lambda (name, loc): numeric_sort_key(name))
le.initialize('choose_library_for_copy') else:
l.addWidget(le, 0, 1) sorted_locations = self.locations
self.items.clear()
for name, loc in sorted_locations:
i = QListWidgetItem(name, self.items)
i.setData(Qt.UserRole, loc)
self.items.setCurrentRow(0)
def setup_ui(self):
self.l = l = QGridLayout(self)
self.items = i = QListWidget(self)
i.setSelectionMode(i.SingleSelection)
i.currentItemChanged.connect(self.current_changed)
l.addWidget(i)
self.v = v = QVBoxLayout()
l.addLayout(v, 0, 1)
self.sort_alphabetically = sa = QCheckBox(_('&Sort libraries alphabetically'))
v.addWidget(sa)
sa.setChecked(bool(gprefs.get('copy_to_library_choose_library_sort_alphabetically', True)))
sa.stateChanged.connect(self.resort)
sa.stateChanged.connect(lambda: gprefs.set('copy_to_library_choose_library_sort_alphabetically', bool(self.sort_alphabetically.isChecked())))
la = self.la = QLabel(_('Library &path:'))
v.addWidget(la)
le = self.le = QLineEdit(self)
la.setBuddy(le) la.setBuddy(le)
b = d.b = QToolButton(d) b = self.b = QToolButton(self)
b.setIcon(QIcon(I('document_open.png'))) b.setIcon(QIcon(I('document_open.png')))
b.setToolTip(_('Browse for library')) b.setToolTip(_('Browse for library'))
b.clicked.connect(self.browse) b.clicked.connect(self.browse)
l.addWidget(b, 0, 2) h = QHBoxLayout()
self.bb = bb = QDialogButtonBox(QDialogButtonBox.Cancel) h.addWidget(le), h.addWidget(b)
bb.accepted.connect(self.accept) v.addLayout(h)
bb.rejected.connect(self.reject) v.addStretch(10)
bb = self.bb
bb.setStandardButtons(QDialogButtonBox.Cancel)
self.delete_after_copy = False self.delete_after_copy = False
b = bb.addButton(_('&Copy'), bb.AcceptRole) b = bb.addButton(_('&Copy'), bb.AcceptRole)
b.setIcon(QIcon(I('edit-copy.png'))) b.setIcon(QIcon(I('edit-copy.png')))
@ -288,9 +313,16 @@ class ChooseLibrary(QDialog): # {{{
b2.setIcon(QIcon(I('edit-cut.png'))) b2.setIcon(QIcon(I('edit-cut.png')))
b2.setToolTip(_('Copy to the specified library and delete from the current library')) b2.setToolTip(_('Copy to the specified library and delete from the current library'))
b.setDefault(True) b.setDefault(True)
l.addWidget(bb, 1, 0, 1, 3) l.addWidget(bb, 1, 0, 1, 2)
le.setMinimumWidth(350) self.items.setFocus(Qt.OtherFocusReason)
self.resize(self.sizeHint())
def sizeHint(self):
return QSize(800, 550)
def current_changed(self):
i = self.items.currentItem() or self.items.item(0)
loc = i.data(Qt.UserRole)
self.le.setText(loc)
def browse(self): def browse(self):
d = choose_dir(self, 'choose_library_for_copy', d = choose_dir(self, 'choose_library_for_copy',
@ -396,9 +428,8 @@ class CopyToLibraryAction(InterfaceAction):
return return
db = self.gui.library_view.model().db db = self.gui.library_view.model().db
locations = list(self.stats.locations(db)) locations = list(self.stats.locations(db))
if len(locations) > 50: self.menu.addAction(_('Choose library...'), self.choose_library)
self.menu.addAction(_('Choose library by path...'), self.choose_library) self.menu.addSeparator()
self.menu.addSeparator()
for name, loc in locations: for name, loc in locations:
name = name.replace('&', '&&') name = name.replace('&', '&&')
self.menu.addAction(name, partial(self.copy_to_library, self.menu.addAction(name, partial(self.copy_to_library,
@ -407,15 +438,15 @@ class CopyToLibraryAction(InterfaceAction):
partial(self.copy_to_library, loc, delete_after=True)) partial(self.copy_to_library, loc, delete_after=True))
self.menu.addSeparator() self.menu.addSeparator()
if len(locations) <= 50:
self.menu.addAction(_('Choose library by path...'), self.choose_library)
self.qaction.setVisible(bool(locations)) self.qaction.setVisible(bool(locations))
if isosx: if isosx:
# The cloned action has to have its menu updated # The cloned action has to have its menu updated
self.qaction.changed.emit() self.qaction.changed.emit()
def choose_library(self): def choose_library(self):
d = ChooseLibrary(self.gui) db = self.gui.library_view.model().db
locations = list(self.stats.locations(db))
d = ChooseLibrary(self.gui, locations)
if d.exec_() == d.Accepted: if d.exec_() == d.Accepted:
path, delete_after = d.args path, delete_after = d.args
if not path: if not path: