1#!/usr/local/bin/python3.8 2# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai 3 4 5__license__ = 'GPL v3' 6__copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>' 7__docformat__ = 'restructuredtext en' 8 9import time 10 11from qt.core import QTimer, QDialog, QDialogButtonBox, QCheckBox, QVBoxLayout, QLabel, Qt 12 13from calibre.gui2 import error_dialog, question_dialog 14from calibre.gui2.actions import InterfaceAction 15 16 17class Choose(QDialog): 18 19 def __init__(self, fmts, parent=None): 20 QDialog.__init__(self, parent) 21 self.l = l = QVBoxLayout(self) 22 self.setLayout(l) 23 self.setWindowTitle(_('Choose format to edit')) 24 25 self.la = la = QLabel(_( 26 'This book has multiple formats that can be edited. Choose the format you want to edit.')) 27 l.addWidget(la) 28 29 self.rem = QCheckBox(_('Always ask when more than one format is available')) 30 self.rem.setChecked(True) 31 l.addWidget(self.rem) 32 33 self.bb = bb = QDialogButtonBox(self) 34 l.addWidget(bb) 35 bb.accepted.connect(self.accept) 36 bb.rejected.connect(self.reject) 37 self.buts = buts = [] 38 for fmt in fmts: 39 b = bb.addButton(fmt.upper(), QDialogButtonBox.ButtonRole.AcceptRole) 40 b.setObjectName(fmt) 41 connect_lambda(b.clicked, self, lambda self: self.chosen(self.sender().objectName())) 42 buts.append(b) 43 44 self.fmt = None 45 self.resize(self.sizeHint()) 46 47 def chosen(self, fmt): 48 self.fmt = fmt 49 50 def accept(self): 51 from calibre.gui2.tweak_book import tprefs 52 tprefs['choose_tweak_fmt'] = self.rem.isChecked() 53 QDialog.accept(self) 54 55 56class TweakEpubAction(InterfaceAction): 57 58 name = 'Tweak ePub' 59 action_spec = (_('Edit book'), 'edit_book.png', _('Edit books in the EPUB or AZW formats'), _('T')) 60 dont_add_to = frozenset(('context-menu-device',)) 61 action_type = 'current' 62 63 accepts_drops = True 64 65 def accept_enter_event(self, event, mime_data): 66 if mime_data.hasFormat("application/calibre+from_library"): 67 return True 68 return False 69 70 def accept_drag_move_event(self, event, mime_data): 71 if mime_data.hasFormat("application/calibre+from_library"): 72 return True 73 return False 74 75 def drop_event(self, event, mime_data): 76 mime = 'application/calibre+from_library' 77 if mime_data.hasFormat(mime): 78 self.dropped_ids = tuple(map(int, mime_data.data(mime).data().split())) 79 QTimer.singleShot(1, self.do_drop) 80 return True 81 return False 82 83 def do_drop(self): 84 book_ids = self.dropped_ids 85 del self.dropped_ids 86 if book_ids: 87 self.do_tweak(book_ids[0]) 88 89 def genesis(self): 90 self.qaction.triggered.connect(self.tweak_book) 91 92 def tweak_book(self): 93 row = self.gui.library_view.currentIndex() 94 if not row.isValid(): 95 return error_dialog(self.gui, _('Cannot Edit book'), 96 _('No book selected'), show=True) 97 98 book_id = self.gui.library_view.model().id(row) 99 self.do_tweak(book_id) 100 101 def do_tweak(self, book_id): 102 if self.gui.current_view() is not self.gui.library_view: 103 return error_dialog(self.gui, _('Cannot edit book'), _( 104 'Editing of books on the device is not supported'), show=True) 105 from calibre.ebooks.oeb.polish.main import SUPPORTED 106 db = self.gui.library_view.model().db 107 fmts = db.formats(book_id, index_is_id=True) or '' 108 fmts = [x.upper().strip() for x in fmts.split(',') if x] 109 tweakable_fmts = set(fmts).intersection(SUPPORTED) 110 if not tweakable_fmts: 111 if not fmts: 112 if not question_dialog(self.gui, _('No editable formats'), 113 _('Do you want to create an empty EPUB file to edit?')): 114 return 115 tweakable_fmts = {'EPUB'} 116 self.gui.iactions['Add Books'].add_empty_format_to_book(book_id, 'EPUB') 117 current_idx = self.gui.library_view.currentIndex() 118 if current_idx.isValid(): 119 self.gui.library_view.model().current_changed(current_idx, current_idx) 120 else: 121 return error_dialog(self.gui, _('Cannot edit book'), _( 122 'The book must be in the %s formats to edit.' 123 '\n\nFirst convert the book to one of these formats.' 124 ) % (_(' or ').join(SUPPORTED)), show=True) 125 from calibre.gui2.tweak_book import tprefs 126 tprefs.refresh() # In case they were changed in a Tweak Book process 127 if len(tweakable_fmts) > 1: 128 if tprefs['choose_tweak_fmt']: 129 d = Choose(sorted(tweakable_fmts, key=tprefs.defaults['tweak_fmt_order'].index), self.gui) 130 if d.exec() != QDialog.DialogCode.Accepted: 131 return 132 tweakable_fmts = {d.fmt} 133 else: 134 fmts = [f for f in tprefs['tweak_fmt_order'] if f in tweakable_fmts] 135 if not fmts: 136 fmts = [f for f in tprefs.defaults['tweak_fmt_order'] if f in tweakable_fmts] 137 tweakable_fmts = {fmts[0]} 138 139 fmt = tuple(tweakable_fmts)[0] 140 self.ebook_edit_format(book_id, fmt) 141 142 def ebook_edit_format(self, book_id, fmt): 143 ''' 144 Also called from edit_metadata formats list. In that context, 145 SUPPORTED check was already done. 146 ''' 147 db = self.gui.library_view.model().db 148 from calibre.gui2.tweak_book import tprefs 149 tprefs.refresh() # In case they were changed in a Tweak Book process 150 path = db.new_api.format_abspath(book_id, fmt) 151 if path is None: 152 return error_dialog(self.gui, _('File missing'), _( 153 'The %s format is missing from the calibre library. You should run' 154 ' library maintenance.') % fmt, show=True) 155 tweak = 'ebook-edit' 156 try: 157 self.gui.setCursor(Qt.CursorShape.BusyCursor) 158 if tprefs['update_metadata_from_calibre']: 159 db.new_api.embed_metadata((book_id,), only_fmts={fmt}) 160 notify = '%d:%s:%s:%s' % (book_id, fmt, db.library_id, db.library_path) 161 self.gui.job_manager.launch_gui_app(tweak, kwargs=dict(path=path, notify=notify)) 162 time.sleep(2) 163 finally: 164 self.gui.unsetCursor() 165