mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-08 10:44:09 -04:00
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:
parent
0f6d873070
commit
a74d0546eb
@ -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:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user