diff --git a/src/calibre/gui2/tweak_book/boss.py b/src/calibre/gui2/tweak_book/boss.py index 2ea8e11093..9e9062409a 100644 --- a/src/calibre/gui2/tweak_book/boss.py +++ b/src/calibre/gui2/tweak_book/boss.py @@ -64,12 +64,14 @@ from calibre.gui2.tweak_book.widgets import ( AddCover, BusyCursor, FilterCSS, ImportForeign, InsertLink, InsertSemantics, InsertTag, MultiSplit, QuickOpen, RationalizeFolders ) -from calibre.ptempfile import TemporaryDirectory +from calibre.ptempfile import PersistentTemporaryDirectory, TemporaryDirectory from calibre.utils.config import JSONConfig from calibre.utils.icu import numeric_sort_key from calibre.utils.imghdr import identify from calibre.utils.tdir_in_cache import tdir_in_cache -from polyglot.builtins import iteritems, itervalues, string_or_bytes, map, unicode_type +from polyglot.builtins import ( + iteritems, itervalues, map, string_or_bytes, unicode_type +) from polyglot.urllib import urlparse _diff_dialogs = [] @@ -138,6 +140,7 @@ class Boss(QObject): fl.link_stylesheets_requested.connect(self.link_stylesheets_requested) fl.initiate_file_copy.connect(self.copy_files_to_clipboard) fl.initiate_file_paste.connect(self.paste_files_from_clipboard) + fl.open_file_with.connect(self.open_file_with) self.gui.central.current_editor_changed.connect(self.apply_current_editor_state) self.gui.central.close_requested.connect(self.editor_close_requested) self.gui.central.search_panel.search_triggered.connect(self.search) @@ -1361,6 +1364,29 @@ class Boss(QObject): raise self.export_file(name, dest) + def open_file_with(self, file_name, fmt, entry): + if file_name in editors and not editors[file_name].is_synced_to_container: + self.commit_editor_to_container(file_name) + with current_container().open(file_name) as src: + tdir = PersistentTemporaryDirectory(suffix='-ee-ow') + with open(os.path.join(tdir, os.path.basename(file_name)), 'wb') as dest: + shutil.copyfileobj(src, dest) + from calibre.gui2.open_with import run_program + run_program(entry, dest.name, self) + if question_dialog(self.gui, _('File opened'), _( + 'When you are done editing {0} click "Update" to update' + ' the file in the book or "Discard" to lose any changes.').format(file_name), + yes_text=_('Import'), no_text=_('Discard') + ): + self.add_savepoint(_('Before: Replace %s') % file_name) + with open(dest.name, 'rb') as src, current_container().open(file_name, 'wb') as cdest: + shutil.copyfileobj(src, cdest) + self.apply_container_update_to_gui() + try: + shutil.rmtree(tdir) + except Exception: + pass + @in_thread_job def copy_files_to_clipboard(self, names): names = tuple(names) diff --git a/src/calibre/gui2/tweak_book/file_list.py b/src/calibre/gui2/tweak_book/file_list.py index f675225d96..3fffe3c3c4 100644 --- a/src/calibre/gui2/tweak_book/file_list.py +++ b/src/calibre/gui2/tweak_book/file_list.py @@ -202,6 +202,7 @@ class FileList(QTreeWidget): link_stylesheets_requested = pyqtSignal(object, object, object) initiate_file_copy = pyqtSignal(object) initiate_file_paste = pyqtSignal() + open_file_with = pyqtSignal(object, object, object) def __init__(self, parent=None): QTreeWidget.__init__(self, parent) @@ -540,6 +541,8 @@ class FileList(QTreeWidget): m.addAction(_('Replace %s with file...') % n, partial(self.replace, cn)) if num > 1: m.addAction(QIcon(I('save.png')), _('Export all %d selected files') % num, self.export_selected) + if cn not in container.names_that_must_not_be_changed: + self.add_open_with_actions(m, cn) m.addSeparator() @@ -583,6 +586,35 @@ class FileList(QTreeWidget): if len(list(m.actions())) > 0: m.popup(self.mapToGlobal(point)) + def add_open_with_actions(self, menu, file_name): + from calibre.gui2.open_with import populate_menu, edit_programs + fmt = file_name.rpartition('.')[-1].lower() + if not fmt: + return + m = QMenu(_('Open %s with...') % file_name) + + def connect_action(ac, entry): + connect_lambda(ac.triggered, self, lambda self: self.open_with(file_name, fmt, entry)) + + populate_menu(m, connect_action, fmt) + if len(m.actions()) == 0: + menu.addAction(_('Open %s with...') % file_name, partial(self.choose_open_with, file_name, fmt)) + else: + m.addSeparator() + m.addAction(_('Add other application for %s files...') % fmt.upper(), partial(self.choose_open_with, file_name, fmt)) + m.addAction(_('Edit Open With applications...'), partial(edit_programs, fmt, file_name)) + menu.addMenu(m) + menu.ow = m + + def choose_open_with(self, file_name, fmt): + from calibre.gui2.open_with import choose_program + entry = choose_program(fmt, self) + if entry is not None: + self.open_with(file_name, fmt, entry) + + def open_with(self, file_name, fmt, entry): + self.open_file_with.emit(file_name, fmt, entry) + def index_of_name(self, name): for category, parent in iteritems(self.categories): for i in range(parent.childCount()):