Edit book: Add an "Open with" action to the context menu of the files browser to easily open files with external programs. Fixes #1860462 [[Enhancement] External Editor Needed](https://bugs.launchpad.net/calibre/+bug/1860462)

This commit is contained in:
Kovid Goyal 2020-01-22 09:13:00 +05:30
parent ffef6f303a
commit d671ccaa49
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
2 changed files with 60 additions and 2 deletions

View File

@ -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)

View File

@ -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()):