From 0d5cf9d78c11c1807760a1a9ab316e49ccedaa2b Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 29 Jan 2014 14:34:35 +0530 Subject: [PATCH] Allow comparing the ORIGINAL_EPUB version of a book to the EPUB version by right clicking on the ORIGINAL_EPUB format in the book details panel. --- src/calibre/db/cache.py | 5 +++-- src/calibre/gui2/book_details.py | 25 ++++++++++++++++++++---- src/calibre/gui2/init.py | 13 ++++++++++++ src/calibre/gui2/tweak_book/diff/main.py | 19 +++++++++++++++--- 4 files changed, 53 insertions(+), 9 deletions(-) diff --git a/src/calibre/db/cache.py b/src/calibre/db/cache.py index 8da2f335dd..aee2e792df 100644 --- a/src/calibre/db/cache.py +++ b/src/calibre/db/cache.py @@ -671,8 +671,9 @@ class Cache(object): ''' Return absolute path to the ebook file of format `format` - Currently used only in calibredb list, the viewer, edit book and the - catalogs (via get_data_as_dict()). + Currently used only in calibredb list, the viewer, edit book, + compare_format to original format and the catalogs (via + get_data_as_dict()). Apart from the viewer, I don't believe any of the others do any file I/O with the results of this call. diff --git a/src/calibre/gui2/book_details.py b/src/calibre/gui2/book_details.py index 7beef6e8ef..e2a40079f3 100644 --- a/src/calibre/gui2/book_details.py +++ b/src/calibre/gui2/book_details.py @@ -413,6 +413,7 @@ class BookInfo(QWebView): remove_format = pyqtSignal(int, object) save_format = pyqtSignal(int, object) restore_format = pyqtSignal(int, object) + compare_format = pyqtSignal(int, object) copy_link = pyqtSignal(object) manage_author = pyqtSignal(object) @@ -433,7 +434,7 @@ class BookInfo(QWebView): for x, icon in [ ('remove_format', 'trash.png'), ('save_format', 'save.png'), ('restore_format', 'edit-undo.png'), ('copy_link','edit-copy.png'), - ('manage_author', 'user_profile.png')]: + ('manage_author', 'user_profile.png'), ('compare_format', 'diff.png')]: ac = QAction(QIcon(I(icon)), '', self) ac.current_fmt = None ac.current_url = None @@ -458,6 +459,9 @@ class BookInfo(QWebView): def restore_format_triggerred(self): self.context_action_triggered('restore_format') + def compare_format_triggerred(self): + self.context_action_triggered('compare_format') + def copy_link_triggerred(self): self.context_action_triggered('copy_link') @@ -517,20 +521,31 @@ class BookInfo(QWebView): if url.startswith('format:'): parts = url.split(':') try: - book_id, fmt = int(parts[1]), parts[2] + book_id, fmt = int(parts[1]), parts[2].upper() except: import traceback traceback.print_exc() else: + from calibre.gui2.ui import get_gui + db = get_gui().current_db.new_api + ofmt = fmt.upper() if fmt.startswith('ORIGINAL_') else 'ORIGINAL_' + fmt + fmts = {x.upper() for x in db.formats(book_id)} for a, t in [('remove', _('Delete the %s format')), ('save', _('Save the %s format to disk')), ('restore', _('Restore the %s format')), + ('compare', ''), ]: - if a == 'restore' and not fmt.upper().startswith('ORIGINAL_'): + if a == 'restore' and not fmt.startswith('ORIGINAL_'): continue + if a == 'compare': + if ofmt not in fmts: + continue + t = _('Compare to the %s format') % (fmt[9:] if fmt.startswith('ORIGINAL_') else ofmt) + else: + t = t % fmt ac = getattr(self, '%s_format_action'%a) ac.current_fmt = (book_id, fmt) - ac.setText(t%parts[2]) + ac.setText(t) menu.addAction(ac) if len(menu.actions()) > 0: menu.exec_(ev.globalPos()) @@ -635,6 +650,7 @@ class BookDetails(QWidget): # {{{ remove_specific_format = pyqtSignal(int, object) save_specific_format = pyqtSignal(int, object) restore_specific_format = pyqtSignal(int, object) + compare_specific_format = pyqtSignal(int, object) copy_link = pyqtSignal(object) remote_file_dropped = pyqtSignal(object, object) files_dropped = pyqtSignal(object, object) @@ -706,6 +722,7 @@ class BookDetails(QWidget): # {{{ self.book_info.remove_format.connect(self.remove_specific_format) self.book_info.save_format.connect(self.save_specific_format) self.book_info.restore_format.connect(self.restore_specific_format) + self.book_info.compare_format.connect(self.compare_specific_format) self.book_info.copy_link.connect(self.copy_link) self.book_info.manage_author.connect(self.manage_author) self.setCursor(Qt.PointingHandCursor) diff --git a/src/calibre/gui2/init.py b/src/calibre/gui2/init.py index 4d15279f63..9a71ca41bc 100644 --- a/src/calibre/gui2/init.py +++ b/src/calibre/gui2/init.py @@ -449,6 +449,7 @@ class LayoutMixin(object): # {{{ self.book_details.view_device_book.connect( self.iactions['View'].view_device_book) self.book_details.manage_author.connect(lambda author:self.do_author_sort_edit(self, author, select_sort=False, select_link=False)) + self.book_details.compare_specific_format.connect(self.compare_format) m = self.library_view.model() if m.rowCount(None) > 0: @@ -475,6 +476,18 @@ class LayoutMixin(object): # {{{ if url: QApplication.clipboard().setText(url) + def compare_format(self, book_id, fmt): + db = self.current_db.new_api + ofmt = fmt + if fmt.startswith('ORIGINAL_'): + fmt = fmt.partition('_')[-1] + else: + ofmt = 'ORIGINAL_' + fmt + path1, path2 = db.format_abspath(book_id, ofmt), db.format_abspath(book_id, fmt) + from calibre.gui2.tweak_book.diff.main import compare_books + compare_books(path1, path2, parent=self, revert_msg=_('Restore %s') % ofmt, revert_callback=partial( + self.iactions['Remove Books'].restore_format, book_id, ofmt)) + def save_layout_state(self): for x in ('library', 'memory', 'card_a', 'card_b'): getattr(self, x+'_view').save_state() diff --git a/src/calibre/gui2/tweak_book/diff/main.py b/src/calibre/gui2/tweak_book/diff/main.py index 5edb9e7780..f2c9b3f5ef 100644 --- a/src/calibre/gui2/tweak_book/diff/main.py +++ b/src/calibre/gui2/tweak_book/diff/main.py @@ -11,7 +11,7 @@ from functools import partial from PyQt4.Qt import ( QGridLayout, QToolButton, QIcon, QRadioButton, QMenu, QApplication, Qt, QSize, QWidget, QLabel, QStackedLayout, QPainter, QRect, QVBoxLayout, - QCursor, QEventLoop, QKeySequence, pyqtSignal) + QCursor, QEventLoop, QKeySequence, pyqtSignal, QTimer) from calibre.ebooks.oeb.polish.container import Container from calibre.gui2 import info_dialog @@ -107,8 +107,8 @@ def container_diff(left, right): left_names -= samefile_names right_names -= samefile_names - cache, changed_names, renamed_names, removed_names, added_names = changed_files( - left_names, right_names, left.raw_data, right.raw_data) + cache, changed_names, renamed_names, removed_names, added_names = changed_files( + left_names, right_names, left.raw_data, right.raw_data) def syntax(container, name): mt = container.mime_map[name] @@ -141,6 +141,7 @@ class Diff(Dialog): return QSize(int(0.9 * geom.width()), int(0.8 * geom.height())) def setup_ui(self): + self.setWindowIcon(QIcon(I('diff.png'))) self.stacks = st = QStackedLayout(self) self.busy = BusyWidget(self) self.w = QWidget(self) @@ -300,6 +301,18 @@ class Diff(Dialog): return return Dialog.keyPressEvent(self, ev) +def compare_books(path1, path2, revert_msg=None, revert_callback=None, parent=None): + d = Diff(parent=parent, revert_button_msg=revert_msg) + if revert_msg is not None: + d.revert_requested.connect(revert_callback) + QTimer.singleShot(0, partial(d.ebook_diff, path1, path2)) + d.exec_() + try: + d.revert_requested.disconnect() + except: + pass + d.break_cycles() + if __name__ == '__main__': import sys app = QApplication([])