diff --git a/src/calibre/gui2/comments_editor.py b/src/calibre/gui2/comments_editor.py
index 41f92b509a..c043fccba4 100644
--- a/src/calibre/gui2/comments_editor.py
+++ b/src/calibre/gui2/comments_editor.py
@@ -66,6 +66,7 @@ class EditorWidget(QWebView): # {{{
def __init__(self, parent=None):
QWebView.__init__(self, parent)
+ self.readonly = False
self.comments_pat = re.compile(r'', re.DOTALL)
@@ -163,7 +164,11 @@ class EditorWidget(QWebView): # {{{
self.page().linkClicked.connect(self.link_clicked)
self.setHtml('')
- self.page().setContentEditable(True)
+ self.set_readonly(False)
+
+ def set_readonly(self, what):
+ self.readonly = what
+ self.page().setContentEditable(not self.readonly)
def clear_text(self, *args):
us = self.page().undoStack()
@@ -313,7 +318,7 @@ class EditorWidget(QWebView): # {{{
# toList() is needed because PyQt on Debian is old/broken
for body in self.page().mainFrame().documentElement().findAll('body').toList():
body.setAttribute('style', style)
- self.page().setContentEditable(True)
+ self.page().setContentEditable(not self.readonly)
def keyPressEvent(self, ev):
if ev.key() in (Qt.Key_Tab, Qt.Key_Escape, Qt.Key_Backtab):
@@ -585,6 +590,7 @@ class Editor(QWidget): # {{{
self.tabs.addTab(self.code_edit, _('HTML Source'))
self.tabs.currentChanged[int].connect(self.change_tab)
self.highlighter = Highlighter(self.code_edit.document())
+ self.layout().setContentsMargins(0, 0, 0, 0)
# toolbar1 {{{
self.toolbar1.addAction(self.editor.action_undo)
@@ -666,6 +672,12 @@ class Editor(QWidget): # {{{
self.toolbar2.setVisible(False)
self.toolbar3.setVisible(False)
+ def set_readonly(self, what):
+ self.editor.set_readonly(what)
+
+ def hide_tabs(self):
+ self.tabs.tabBar().setVisible(False)
+
# }}}
if __name__ == '__main__':
diff --git a/src/calibre/gui2/metadata/basic_widgets.py b/src/calibre/gui2/metadata/basic_widgets.py
index 984986affe..5544c52273 100644
--- a/src/calibre/gui2/metadata/basic_widgets.py
+++ b/src/calibre/gui2/metadata/basic_widgets.py
@@ -101,7 +101,7 @@ class TitleEdit(EnLineEdit):
getattr(db, 'set_'+ self.TITLE_ATTR)(id_, title, notify=False,
commit=False)
except (IOError, OSError) as err:
- if getattr(err, 'errno', None) == errno.EACCES: # Permission denied
+ if getattr(err, 'errno', None) == errno.EACCES: # Permission denied
import traceback
fname = getattr(err, 'filename', None)
p = 'Locked file: %s\n\n'%fname if fname else ''
@@ -273,7 +273,7 @@ class AuthorsEdit(EditWithComplete):
self.books_to_refresh |= db.set_authors(id_, authors, notify=False,
allow_case_change=True)
except (IOError, OSError) as err:
- if getattr(err, 'errno', None) == errno.EACCES: # Permission denied
+ if getattr(err, 'errno', None) == errno.EACCES: # Permission denied
import traceback
fname = getattr(err, 'filename', None)
p = 'Locked file: %s\n\n'%fname if fname else ''
@@ -485,7 +485,7 @@ class SeriesEdit(EditWithComplete):
def initialize(self, db, id_):
self.books_to_refresh = set([])
all_series = db.all_series()
- all_series.sort(key=lambda x : sort_key(x[1]))
+ all_series.sort(key=lambda x: sort_key(x[1]))
self.update_items_cache([x[1] for x in all_series])
series_id = db.series_id(id_, index_is_id=True)
inval = ''
@@ -586,7 +586,7 @@ class SeriesIndexEdit(QDoubleSpinBox):
# }}}
-class BuddyLabel(QLabel): # {{{
+class BuddyLabel(QLabel): # {{{
def __init__(self, buddy):
QLabel.__init__(self, buddy.LABEL)
@@ -698,11 +698,11 @@ class FormatsManager(QWidget):
self.formats.setIconSize(QSize(32, 32))
self.formats.setMaximumWidth(200)
- l.addWidget(self.cover_from_format_button, 0, 0, 1, 1)
+ l.addWidget(self.cover_from_format_button, 0, 0, 1, 1)
l.addWidget(self.metadata_from_format_button, 2, 0, 1, 1)
- l.addWidget(self.add_format_button, 0, 2, 1, 1)
- l.addWidget(self.remove_format_button, 2, 2, 1, 1)
- l.addWidget(self.formats, 0, 1, 3, 1)
+ l.addWidget(self.add_format_button, 0, 2, 1, 1)
+ l.addWidget(self.remove_format_button, 2, 2, 1, 1)
+ l.addWidget(self.formats, 0, 1, 3, 1)
self.temp_files = []
@@ -882,7 +882,7 @@ class FormatsManager(QWidget):
self.temp_files = []
# }}}
-class Cover(ImageView): # {{{
+class Cover(ImageView): # {{{
download_cover = pyqtSignal()
@@ -1052,7 +1052,7 @@ class Cover(ImageView): # {{{
# }}}
-class CommentsEdit(Editor): # {{{
+class CommentsEdit(Editor): # {{{
@dynamic_property
def current_val(self):
@@ -1076,7 +1076,7 @@ class CommentsEdit(Editor): # {{{
return True
# }}}
-class RatingEdit(QSpinBox): # {{{
+class RatingEdit(QSpinBox): # {{{
LABEL = _('&Rating:')
TOOLTIP = _('Rating of this book. 0-5 stars')
@@ -1120,7 +1120,7 @@ class RatingEdit(QSpinBox): # {{{
# }}}
-class TagsEdit(EditWithComplete): # {{{
+class TagsEdit(EditWithComplete): # {{{
LABEL = _('Ta&gs:')
TOOLTIP = '
'+_('Tags categorize the book. This is particularly '
'useful while searching.
They can be any words '
@@ -1174,7 +1174,6 @@ class TagsEdit(EditWithComplete): # {{{
self.current_val = d.tags
self.all_items = db.all_tags()
-
def commit(self, db, id_):
self.books_to_refresh |= db.set_tags(
id_, self.current_val, notify=False, commit=False,
@@ -1183,7 +1182,7 @@ class TagsEdit(EditWithComplete): # {{{
# }}}
-class LanguagesEdit(LE): # {{{
+class LanguagesEdit(LE): # {{{
LABEL = _('&Languages:')
TOOLTIP = _('A comma separated list of languages for this book')
@@ -1194,8 +1193,10 @@ class LanguagesEdit(LE): # {{{
@dynamic_property
def current_val(self):
- def fget(self): return self.lang_codes
- def fset(self, val): self.lang_codes = val
+ def fget(self):
+ return self.lang_codes
+ def fset(self, val):
+ self.lang_codes = val
return property(fget=fget, fset=fset)
def initialize(self, db, id_):
@@ -1221,7 +1222,7 @@ class LanguagesEdit(LE): # {{{
return True
# }}}
-class IdentifiersEdit(QLineEdit): # {{{
+class IdentifiersEdit(QLineEdit): # {{{
LABEL = _('I&ds:')
BASE_TT = _('Edit the identifiers for this book. '
'For example: \n\n%s')%(
@@ -1309,7 +1310,7 @@ class IdentifiersEdit(QLineEdit): # {{{
# }}}
-class ISBNDialog(QDialog) : # {{{
+class ISBNDialog(QDialog): # {{{
def __init__(self, parent, txt):
QDialog.__init__(self, parent)
@@ -1320,7 +1321,7 @@ class ISBNDialog(QDialog) : # {{{
l.addWidget(w, 0, 0, 1, 2)
w = QLabel(_('ISBN:'))
l.addWidget(w, 1, 0, 1, 1)
- self.line_edit = w = QLineEdit();
+ self.line_edit = w = QLineEdit()
w.setText(txt)
w.selectAll()
w.textChanged.connect(self.checkText)
@@ -1361,7 +1362,7 @@ class ISBNDialog(QDialog) : # {{{
# }}}
-class PublisherEdit(EditWithComplete): # {{{
+class PublisherEdit(EditWithComplete): # {{{
LABEL = _('&Publisher:')
def __init__(self, parent):
@@ -1388,7 +1389,7 @@ class PublisherEdit(EditWithComplete): # {{{
def initialize(self, db, id_):
self.books_to_refresh = set([])
all_publishers = db.all_publishers()
- all_publishers.sort(key=lambda x : sort_key(x[1]))
+ all_publishers.sort(key=lambda x: sort_key(x[1]))
self.update_items_cache([x[1] for x in all_publishers])
publisher_id = db.publisher_id(id_, index_is_id=True)
inval = ''
@@ -1421,7 +1422,7 @@ class DateEdit(QDateTimeEdit):
ATTR = 'timestamp'
TWEAK = 'gui_timestamp_display_format'
- def __init__(self, parent):
+ def __init__(self, parent, create_clear_button=True):
QDateTimeEdit.__init__(self, parent)
self.setToolTip(self.TOOLTIP)
self.setWhatsThis(self.TOOLTIP)
@@ -1435,10 +1436,11 @@ class DateEdit(QDateTimeEdit):
self.setCalendarWidget(self.cw)
self.setMinimumDateTime(UNDEFINED_QDATETIME)
self.setSpecialValueText(_('Undefined'))
- self.clear_button = QToolButton(parent)
- self.clear_button.setIcon(QIcon(I('trash.png')))
- self.clear_button.setToolTip(_('Clear date'))
- self.clear_button.clicked.connect(self.reset_date)
+ if create_clear_button:
+ self.clear_button = QToolButton(parent)
+ self.clear_button.setIcon(QIcon(I('trash.png')))
+ self.clear_button.setToolTip(_('Clear date'))
+ self.clear_button.clicked.connect(self.reset_date)
def reset_date(self, *args):
self.current_val = None
diff --git a/src/calibre/gui2/metadata/diff.py b/src/calibre/gui2/metadata/diff.py
index 2dfbaa2fb8..8c1b613a41 100644
--- a/src/calibre/gui2/metadata/diff.py
+++ b/src/calibre/gui2/metadata/diff.py
@@ -6,5 +6,477 @@ from __future__ import (unicode_literals, division, absolute_import,
__license__ = 'GPL v3'
__copyright__ = '2013, Kovid Goyal '
+import os
+from collections import OrderedDict, namedtuple
+from functools import partial
+from PyQt4.Qt import (
+ QDialog, QWidget, QGridLayout, QLineEdit, QLabel, QToolButton, QIcon,
+ QVBoxLayout, QDialogButtonBox, QApplication, pyqtSignal, QFont, QPixmap,
+ QSize, QPainter, Qt, QColor, QPen, QSizePolicy, QScrollArea, QFrame)
+
+from calibre import fit_image
+from calibre.ebooks.metadata import title_sort, authors_to_sort_string
+from calibre.gui2 import pixmap_to_data, gprefs
+from calibre.gui2.comments_editor import Editor
+from calibre.gui2.metadata.basic_widgets import PubdateEdit, RatingEdit
+from calibre.ptempfile import PersistentTemporaryFile
+from calibre.utils.date import UNDEFINED_DATE
+
+Widgets = namedtuple('Widgets', 'new old label button')
+
+# Widgets {{{
+
+class LineEdit(QLineEdit):
+
+ changed = pyqtSignal()
+
+ def __init__(self, field, is_new, parent, metadata, extra):
+ QLineEdit.__init__(self, parent)
+ self.is_new = is_new
+ self.field = field
+ self.metadata = metadata
+ if not is_new:
+ self.setReadOnly(True)
+ self.textChanged.connect(self.changed)
+
+ def from_mi(self, mi):
+ val = mi.get(self.field, default='') or ''
+ ism = self.metadata['is_multiple']
+ if ism:
+ if not val:
+ val = ''
+ else:
+ val = ism['list_to_ui'].join(val)
+ self.setText(val)
+ self.setCursorPosition(0)
+
+ def to_mi(self, mi):
+ val = unicode(self.text()).strip()
+ ism = self.metadata['is_multiple']
+ if ism:
+ if not val:
+ val = []
+ else:
+ val = [x.strip() for x in val.split(ism['list_to_ui']) if x.strip()]
+ mi.set(self.field, val)
+ if self.field == 'title':
+ mi.set('title_sort', title_sort(val, lang=mi.language))
+ elif self.field == 'authors':
+ mi.set('author_sort', authors_to_sort_string(val))
+
+ @dynamic_property
+ def current_val(self):
+ def fget(self):
+ return unicode(self.text())
+ def fset(self, val):
+ self.setText(val)
+ self.setCursorPosition(0)
+ return property(fget=fget, fset=fset)
+
+ @property
+ def is_blank(self):
+ return not self.current_val.strip()
+
+class RatingsEdit(RatingEdit):
+
+ changed = pyqtSignal()
+
+ def __init__(self, field, is_new, parent, metadata, extra):
+ RatingEdit.__init__(self, parent)
+ self.is_new = is_new
+ self.field = field
+ self.metadata = metadata
+ self.valueChanged.connect(self.changed)
+ if not is_new:
+ self.setReadOnly(True)
+
+ def from_mi(self, mi):
+ val = (mi.get(self.field, default=0) or 0)/2
+ self.setValue(val)
+
+ def to_mi(self, mi):
+ mi.set(self.field, self.value() * 2)
+
+ @property
+ def is_blank(self):
+ return self.value() == 0
+
+class DateEdit(PubdateEdit):
+
+ changed = pyqtSignal()
+
+ def __init__(self, field, is_new, parent, metadata, extra):
+ PubdateEdit.__init__(self, parent, create_clear_button=False)
+ self.is_new = is_new
+ self.field = field
+ self.metadata = metadata
+ self.setDisplayFormat(extra)
+ self.dateTimeChanged.connect(self.changed)
+ if not is_new:
+ self.setReadOnly(True)
+
+ def from_mi(self, mi):
+ self.current_val = mi.get(self.field, default=None)
+
+ def to_mi(self, mi):
+ mi.set(self.field, self.current_val)
+
+ @property
+ def is_blank(self):
+ return self.current_val == UNDEFINED_DATE
+
+class SeriesEdit(LineEdit):
+
+ def from_mi(self, mi):
+ series = mi.get(self.field, default='')
+ series_index = mi.get(self.field + '_index', default=1.0)
+ val = ''
+ if series:
+ val = '%s [%s]' % (series, mi.format_series_index(series_index))
+ self.setText(val)
+ self.setCursorPosition(0)
+
+ def to_mi(self, mi):
+ val = unicode(self.text()).strip()
+ try:
+ series_index = float(val.rpartition('[')[-1].rstrip(']').strip())
+ except:
+ series_index = 1.0
+ series = val.rpartition('[')[0].strip() or None
+ mi.set(self.field, series)
+ mi.set(self.field + '_index', series_index)
+
+class IdentifiersEdit(LineEdit):
+
+ def from_mi(self, mi):
+ val = ('%s:%s' % (k, v) for k, v in mi.identifiers.iteritems())
+ self.setText(', '.join(val))
+
+ def to_mi(self, mi):
+ parts = (x.strip() for x in self.current_val.split(',') if x.strip())
+ val = {x.partition(':')[0].strip():x.partition(':')[-1].strip() for x in parts}
+ mi.set_identifiers({k:v for k, v in val.iteritems() if k and v})
+
+class CommentsEdit(Editor):
+
+ changed = pyqtSignal()
+
+ def __init__(self, field, is_new, parent, metadata, extra):
+ Editor.__init__(self, parent, one_line_toolbar=False)
+ self.is_new = is_new
+ self.field = field
+ self.metadata = metadata
+ self.hide_tabs()
+ if not is_new:
+ self.hide_toolbars()
+ self.set_readonly(True)
+
+ @dynamic_property
+ def current_val(self):
+ def fget(self):
+ return self.html
+ def fset(self, val):
+ self.html = val or ''
+ self.changed.emit()
+ return property(fget=fget, fset=fset)
+
+ def from_mi(self, mi):
+ val = mi.get(self.field, default='')
+ self.current_val = val
+
+ def to_mi(self, mi):
+ mi.set(self.field, self.current_val)
+
+ def sizeHint(self):
+ return QSize(450, 200)
+
+ @property
+ def is_blank(self):
+ return not self.current_val.strip()
+
+class CoverView(QWidget):
+
+ changed = pyqtSignal()
+
+ def __init__(self, field, is_new, parent, metadata, extra):
+ QWidget.__init__(self, parent)
+ self.is_new = is_new
+ self.field = field
+ self.metadata = metadata
+ self.pixmap = None
+ self.blank = QPixmap(I('blank.png'))
+ self.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.GrowFlag|QSizePolicy.ExpandFlag)
+ self.sizePolicy().setHeightForWidth(True)
+
+ @property
+ def is_blank(self):
+ return self.pixmap is None
+
+ @dynamic_property
+ def current_val(self):
+ def fget(self):
+ return self.pixmap
+ def fset(self, val):
+ self.pixmap = val
+ self.changed.emit()
+ self.update()
+ return property(fget=fget, fset=fset)
+
+ def from_mi(self, mi):
+ p = getattr(mi, 'cover', None)
+ if p and os.path.exists(p):
+ pmap = QPixmap()
+ with open(p, 'rb') as f:
+ pmap.loadFromData(f.read())
+ if not pmap.isNull():
+ self.pixmap = pmap
+ self.update()
+ self.changed.emit()
+ return
+ cd = getattr(mi, 'cover_data', (None, None))
+ if cd and cd[1]:
+ pmap = QPixmap()
+ pmap.loadFromData(cd[1])
+ if not pmap.isNull():
+ self.pixmap = pmap
+ self.update()
+ self.changed.emit()
+ return
+ self.pixmap = None
+ self.update()
+ self.changed.emit()
+
+ def to_mi(self, mi):
+ mi.cover, mi.cover_data = None, (None, None)
+ if self.pixmap is not None and not self.pixmap.isNull():
+ with PersistentTemporaryFile('.jpg') as pt:
+ pt.write(pixmap_to_data(self.pixmap))
+ mi.cover = pt.name
+
+ def sizeHint(self):
+ return QSize(225, 300)
+
+ def paintEvent(self, event):
+ pmap = self.blank if self.pixmap is None or self.pixmap.isNull() else self.pixmap
+ target = self.rect()
+ scaled, width, height = fit_image(pmap.width(), pmap.height(), target.width(), target.height())
+ target.setRect(target.x(), target.y(), width, height)
+ p = QPainter(self)
+ p.setRenderHints(QPainter.Antialiasing | QPainter.SmoothPixmapTransform)
+ p.drawPixmap(target, pmap)
+
+ if self.pixmap is not None and not self.pixmap.isNull():
+ sztgt = target.adjusted(0, 0, 0, -4)
+ f = p.font()
+ f.setBold(True)
+ p.setFont(f)
+ sz = u'\u00a0%d x %d\u00a0'%(self.pixmap.width(), self.pixmap.height())
+ flags = Qt.AlignBottom|Qt.AlignRight|Qt.TextSingleLine
+ szrect = p.boundingRect(sztgt, flags, sz)
+ p.fillRect(szrect.adjusted(0, 0, 0, 4), QColor(0, 0, 0, 200))
+ p.setPen(QPen(QColor(255,255,255)))
+ p.drawText(sztgt, flags, sz)
+ p.end()
+# }}}
+
+class CompareSingle(QWidget):
+
+ def __init__(
+ self, field_metadata, parent=None, revert_tooltip=None,
+ datetime_fmt='MMMM yyyy', blank_as_equal=True,
+ fields=('title', 'authors', 'series', 'tags', 'rating', 'publisher', 'pubdate', 'identifiers', 'comments', 'cover')):
+ QWidget.__init__(self, parent)
+ self.l = l = QGridLayout()
+ l.setContentsMargins(0, 0, 0, 0)
+ self.setLayout(l)
+ revert_tooltip = revert_tooltip or _('Revert %s')
+ self.current_mi = None
+ self.changed_font = QFont(QApplication.font())
+ self.changed_font.setBold(True)
+ self.changed_font.setItalic(True)
+ self.blank_as_equal = blank_as_equal
+
+ self.widgets = OrderedDict()
+ row = 0
+
+ for field in fields:
+ m = field_metadata[field]
+ dt = m['datatype']
+ extra = None
+ if 'series' in {field, dt}:
+ cls = SeriesEdit
+ elif field == 'identifiers':
+ cls = IdentifiersEdit
+ elif 'comments' in {field, dt}:
+ cls = CommentsEdit
+ elif 'rating' in {field, dt}:
+ cls = RatingsEdit
+ elif dt == 'datetime':
+ extra = datetime_fmt
+ cls = DateEdit
+ elif field == 'cover':
+ cls = CoverView
+ elif dt in {'text', 'enum'}:
+ cls = LineEdit
+ else:
+ continue
+ neww = cls(field, True, self, m, extra)
+ neww.changed.connect(partial(self.changed, field))
+ oldw = cls(field, False, self, m, extra)
+ newl = QLabel('&%s:' % m['name'])
+ newl.setBuddy(neww)
+ button = QToolButton(self)
+ button.setIcon(QIcon(I('back.png')))
+ button.clicked.connect(partial(self.revert, field))
+ button.setToolTip(revert_tooltip % m['name'])
+ self.widgets[field] = Widgets(neww, oldw, newl, button)
+ for i, w in enumerate((newl, neww, button, oldw)):
+ c = i if i < 2 else i + 1
+ if w is oldw:
+ c += 1
+ l.addWidget(w, row, c)
+ row += 1
+
+ self.sep = f = QFrame(self)
+ f.setFrameShape(f.VLine)
+ l.addWidget(f, 0, 2, row, 1)
+ self.sep2 = f = QFrame(self)
+ f.setFrameShape(f.VLine)
+ l.addWidget(f, 0, 4, row, 1)
+
+ def changed(self, field):
+ w = self.widgets[field]
+ if w.new.current_val != w.old.current_val and (not self.blank_as_equal or not w.new.is_blank):
+ w.label.setFont(self.changed_font)
+ else:
+ w.label.setFont(QApplication.font())
+
+ def revert(self, field):
+ widgets = self.widgets[field]
+ neww, oldw = widgets[:2]
+ neww.current_val = oldw.current_val
+
+ def __call__(self, oldmi, newmi):
+ self.current_mi = newmi
+ self.initial_vals = {}
+ for field, widgets in self.widgets.iteritems():
+ widgets.old.from_mi(oldmi)
+ widgets.new.from_mi(newmi)
+ self.initial_vals[field] = widgets.new.current_val
+
+ def apply_changes(self):
+ changed = False
+ for field, widgets in self.widgets.iteritems():
+ val = widgets.new.current_val
+ if val != self.initial_vals[field]:
+ widgets.new.to_mi(self.current_mi)
+ changed = True
+ return changed
+
+class CompareMany(QDialog):
+
+ def __init__(self, ids, get_metadata, field_metadata, parent=None, window_title=None, skip_button_tooltip=None,
+ accept_all_tooltip=None, reject_all_tooltip=None, **kwargs):
+ QDialog.__init__(self, parent)
+ self.l = l = QVBoxLayout()
+ self.setLayout(l)
+ self.setWindowIcon(QIcon(I('auto_author_sort.png')))
+ self.get_metadata = get_metadata
+ self.ids = list(ids)
+ self.total = len(self.ids)
+ self.accepted = OrderedDict()
+ self.window_title = window_title or _('Compare metadata')
+
+ self.compare_widget = CompareSingle(field_metadata, parent=parent, **kwargs)
+ self.sa = sa = QScrollArea()
+ l.addWidget(sa)
+ sa.setWidget(self.compare_widget)
+ sa.setWidgetResizable(True)
+
+ self.bb = bb = QDialogButtonBox(QDialogButtonBox.Cancel)
+ bb.rejected.connect(self.reject)
+ self.aarb = b = bb.addButton(_('&Accept all remaining'), bb.YesRole)
+ if accept_all_tooltip:
+ b.setToolTip(accept_all_tooltip)
+ b.clicked.connect(self.accept_all_remaining)
+ self.rarb = b = bb.addButton(_('Re&ject all remaining'), bb.NoRole)
+ if reject_all_tooltip:
+ b.setToolTip(reject_all_tooltip)
+ b.clicked.connect(self.reject_all_remaining)
+ self.sb = b = bb.addButton(_('&Reject'), bb.ActionRole)
+ b.clicked.connect(partial(self.next_item, False))
+ if skip_button_tooltip:
+ b.setToolTip(skip_button_tooltip)
+ self.nb = b = bb.addButton(_('&Next'), bb.ActionRole)
+ b.setIcon(QIcon(I('forward.png')))
+ b.clicked.connect(partial(self.next_item, True))
+ b.setDefault(True)
+ l.addWidget(bb)
+
+ self.next_item(True)
+
+ desktop = QApplication.instance().desktop()
+ geom = desktop.availableGeometry(parent or self)
+ width = max(700, min(950, geom.width()-50))
+ height = max(650, min(1000, geom.height()-100))
+ self.resize(QSize(width, height))
+ geom = gprefs.get('diff_dialog_geom', None)
+ if geom is not None:
+ self.restoreGeometry(geom)
+
+ def accept(self):
+ gprefs.set('diff_dialog_geom', bytearray(self.saveGeometry()))
+ super(CompareMany, self).accept()
+
+ def reject(self):
+ gprefs.set('diff_dialog_geom', bytearray(self.saveGeometry()))
+ super(CompareMany, self).reject()
+
+ @property
+ def current_mi(self):
+ return self.compare_widget.current_mi
+
+ def next_item(self, accept):
+ if not self.ids:
+ return self.accept()
+ if self.current_mi is not None:
+ changed = self.compare_widget.apply_changes()
+ self.setWindowTitle(self.window_title + _(' [%(num)d of %(tot)d]') % dict(
+ num=(self.total - len(self.ids) + 1), tot=self.total))
+ oldmi, newmi = self.get_metadata(self.ids[0])
+ old_id = self.ids.pop(0)
+ if self.current_mi is not None:
+ self.accepted[old_id] = (changed, self.current_mi) if accept else (False, None)
+ self.compare_widget(oldmi, newmi)
+
+ def accept_all_remaining(self):
+ self.next_item(True)
+ for id_ in self.ids:
+ oldmi, newmi = self.get_metadata(id_)
+ self.accepted[id_] = (False, newmi)
+ self.ids = []
+ self.accept()
+
+ def reject_all_remaining(self):
+ self.next_item(False)
+ for id_ in self.ids:
+ oldmi, newmi = self.get_metadata(id_)
+ self.accepted[id_] = (False, None)
+ self.ids = []
+ self.accept()
+
+if __name__ == '__main__':
+ app = QApplication([])
+ from calibre.library import db
+ db = db()
+ ids = sorted(db.all_ids(), reverse=True)
+ ids = [(x, ids[i+1]) for i, x in enumerate(ids[0::2])]
+ gm = partial(db.get_metadata, index_is_id=True, get_cover=True, cover_as_data=True)
+ get_metadata = lambda x:map(gm, ids[x])
+ d = CompareMany(list(xrange(len(ids))), get_metadata, db.field_metadata)
+ if d.exec_() == d.Accepted:
+ for changed, mi in d.accepted.itervalues():
+ if changed and mi is not None:
+ print (mi)
diff --git a/src/calibre/library/field_metadata.py b/src/calibre/library/field_metadata.py
index 2b683da909..105b66a677 100644
--- a/src/calibre/library/field_metadata.py
+++ b/src/calibre/library/field_metadata.py
@@ -252,7 +252,7 @@ class FieldMetadata(dict):
'datatype':'int',
'is_multiple':{},
'kind':'field',
- 'name':None,
+ 'name':_('Cover'),
'search_terms':['cover'],
'is_custom':False,
'is_category':False,