diff --git a/src/calibre/gui2/viewer/toolbars.py b/src/calibre/gui2/viewer/toolbars.py index 07a1c7b081..0296d10218 100644 --- a/src/calibre/gui2/viewer/toolbars.py +++ b/src/calibre/gui2/viewer/toolbars.py @@ -68,6 +68,7 @@ def all_actions(): 'toggle_read_aloud': Action('bullhorn.png', _('Read aloud'), 'toggle_read_aloud'), 'toggle_highlights': Action('highlight_only_on.png', _('Browse highlights in book'), 'toggle_highlights'), 'select_all': Action('edit-select-all.png', _('Select all text in the current file')), + 'edit_book': Action('edit_book.png', _('Edit this book'), 'edit_book'), } all_actions.ans = Actions(amap) return all_actions.ans @@ -193,6 +194,7 @@ class ActionsToolBar(ToolBar): self.print_action = shortcut_action('print') self.preferences_action = shortcut_action('preferences') self.metadata_action = shortcut_action('metadata') + self.edit_book_action = shortcut_action('edit_book') self.update_mode_action() self.color_scheme_action = a = QAction(aa.color_scheme.icon, aa.color_scheme.text, self) self.color_scheme_menu = m = QMenu(self) diff --git a/src/calibre/gui2/viewer/ui.py b/src/calibre/gui2/viewer/ui.py index f6c0448b51..2a76b1d8be 100644 --- a/src/calibre/gui2/viewer/ui.py +++ b/src/calibre/gui2/viewer/ui.py @@ -17,9 +17,10 @@ from qt.core import ( from threading import Thread from calibre import prints +from calibre.constants import ismacos from calibre.customize.ui import available_input_formats from calibre.db.annotations import merge_annotations -from calibre.gui2 import choose_files, error_dialog +from calibre.gui2 import choose_files, error_dialog, sanitize_env_vars from calibre.gui2.dialogs.drm_error import DRMErrorMessage from calibre.gui2.image_popup import ImagePopup from calibre.gui2.main_window import MainWindow @@ -189,6 +190,7 @@ class EbookViewer(MainWindow): self.web_view.scrollbar_context_menu.connect(self.scrollbar_context_menu) self.web_view.close_prep_finished.connect(self.close_prep_finished) self.web_view.highlights_changed.connect(self.highlights_changed) + self.web_view.edit_book.connect(self.edit_book) self.actions_toolbar.initialize(self.web_view, self.search_dock.toggleViewAction()) self.setCentralWidget(self.web_view) self.loading_overlay = LoadingOverlay(self) @@ -639,6 +641,29 @@ class EbookViewer(MainWindow): self.highlights_widget.refresh(highlights) self.save_annotations() + def edit_book(self, file_name, progress_frac): + import subprocess + from calibre.ebooks.oeb.polish.main import SUPPORTED + from calibre.utils.ipc.launch import exe_path, macos_edit_book_bundle_path + try: + path = set_book_path.pathtoebook + except AttributeError: + return error_dialog(self, _('Cannot edit book'), _( + 'No book is currently open'), show=True) + fmt = path.rpartition('.')[-1].upper().replace('ORIGINAL_', '') + if fmt not in SUPPORTED: + return error_dialog(self, _('Cannot edit book'), _( + 'The book must be in the %s formats to edit.' + '\n\nFirst convert the book to one of these formats.' + ) % (_(' or ').join(SUPPORTED)), show=True) + exe = 'ebook-edit' + if ismacos: + exe = os.path.join(macos_edit_book_bundle_path(), exe) + else: + exe = exe_path(exe) + with sanitize_env_vars(): + subprocess.Popen([exe, path, file_name]) + def save_state(self): with vprefs: vprefs['main_window_state'] = bytearray(self.saveState(self.MAIN_WINDOW_STATE_VERSION)) diff --git a/src/calibre/gui2/viewer/web_view.py b/src/calibre/gui2/viewer/web_view.py index 8efb55b079..3344e00591 100644 --- a/src/calibre/gui2/viewer/web_view.py +++ b/src/calibre/gui2/viewer/web_view.py @@ -273,6 +273,7 @@ class ViewerBridge(Bridge): open_url = from_js(object) speak_simple_text = from_js(object) tts = from_js(object, object) + edit_book = from_js(object, object) create_view = to_js() start_book_load = to_js() @@ -472,6 +473,7 @@ class WebView(RestartingWebEngineView): scrollbar_context_menu = pyqtSignal(object, object, object) close_prep_finished = pyqtSignal(object) highlights_changed = pyqtSignal(object) + edit_book = pyqtSignal(object, object) shortcuts_changed = pyqtSignal(object) paged_mode_changed = pyqtSignal() standalone_misc_settings_changed = pyqtSignal(object) @@ -532,6 +534,7 @@ class WebView(RestartingWebEngineView): self.bridge.scrollbar_context_menu.connect(self.scrollbar_context_menu) self.bridge.close_prep_finished.connect(self.close_prep_finished) self.bridge.highlights_changed.connect(self.highlights_changed) + self.bridge.edit_book.connect(self.edit_book) self.bridge.open_url.connect(safe_open_url) self.bridge.speak_simple_text.connect(self.tts.speak_simple_text) self.bridge.tts.connect(self.tts.action) diff --git a/src/calibre/utils/ipc/launch.py b/src/calibre/utils/ipc/launch.py index 2302d9bf9e..9619e7966a 100644 --- a/src/calibre/utils/ipc/launch.py +++ b/src/calibre/utils/ipc/launch.py @@ -30,6 +30,33 @@ def renice(niceness): pass +def macos_edit_book_bundle_path(): + base = os.path.dirname(sys.executables_location) + return os.path.join(base, 'ebook-viewer.app/Contents/ebook-edit.app/Contents/MacOS/') + + +def exe_path(exe_name): + if hasattr(sys, 'running_from_setup'): + return [sys.executable, os.path.join(sys.setup_dir, 'run-calibre-worker.py')] + if getattr(sys, 'run_local', False): + return [sys.executable, sys.run_local, exe_name] + e = exe_name + if iswindows: + return os.path.join(os.path.dirname(sys.executable), + e+'.exe' if isfrozen else 'Scripts\\%s.exe'%e) + if ismacos: + return os.path.join(sys.executables_location, e) + + if isfrozen: + return os.path.join(sys.executables_location, e) + + if hasattr(sys, 'executables_location'): + c = os.path.join(sys.executables_location, e) + if os.access(c, os.X_OK): + return c + return e + + class Worker: ''' Platform independent object for launching child processes. All processes @@ -47,25 +74,7 @@ class Worker: @property def executable(self): - if hasattr(sys, 'running_from_setup'): - return [sys.executable, os.path.join(sys.setup_dir, 'run-calibre-worker.py')] - if getattr(sys, 'run_local', False): - return [sys.executable, sys.run_local, self.exe_name] - e = self.exe_name - if iswindows: - return os.path.join(os.path.dirname(sys.executable), - e+'.exe' if isfrozen else 'Scripts\\%s.exe'%e) - if ismacos: - return os.path.join(sys.executables_location, e) - - if isfrozen: - return os.path.join(sys.executables_location, e) - - if hasattr(sys, 'executables_location'): - c = os.path.join(sys.executables_location, e) - if os.access(c, os.X_OK): - return c - return e + return exe_path(self.exe_name) @property def gui_executable(self): @@ -74,8 +83,7 @@ class Worker: base = os.path.dirname(sys.executables_location) return os.path.join(base, 'ebook-viewer.app/Contents/MacOS/', self.exe_name) if self.job_name == 'ebook-edit': - base = os.path.dirname(sys.executables_location) - return os.path.join(base, 'ebook-viewer.app/Contents/ebook-edit.app/Contents/MacOS/', self.exe_name) + return os.path.join(macos_edit_book_bundle_path(), self.exe_name) return os.path.join(sys.executables_location, self.exe_name) diff --git a/src/pyj/read_book/shortcuts.pyj b/src/pyj/read_book/shortcuts.pyj index 8042f2960e..e8109c8119 100644 --- a/src/pyj/read_book/shortcuts.pyj +++ b/src/pyj/read_book/shortcuts.pyj @@ -432,6 +432,13 @@ def add_standalone_viewer_shortcuts(sc): _('Toggle the highlights panel') ) + sc['edit_book'] = desc( + "Ctrl+Alt+e", + 'ui', + _('Edit this book') + ) + + def create_shortcut_map(custom_shortcuts): ans = {} diff --git a/src/pyj/read_book/view.pyj b/src/pyj/read_book/view.pyj index 863707fe41..5a2fe3b72c 100644 --- a/src/pyj/read_book/view.pyj +++ b/src/pyj/read_book/view.pyj @@ -17,7 +17,7 @@ from read_book.annotations import AnnotationsManager from read_book.bookmarks import create_new_bookmark from read_book.content_popup import ContentPopupOverlay from read_book.globals import ( - current_book, is_dark_theme, rtl_page_progression, runtime, + current_book, current_spine_item, is_dark_theme, rtl_page_progression, runtime, set_current_spine_item, ui_operations ) from read_book.goto import get_next_section @@ -555,6 +555,8 @@ class View: self.show_chrome({'initial_panel': 'show_prefs'}) elif data.name is 'metadata': self.overlay.show_metadata() + elif data.name is 'edit_book': + ui_operations.edit_book(current_spine_item(), self.current_file_progress_frac) elif data.name is 'goto_location': self.overlay.show_ask_for_location() elif data.name is 'shrink_selection_by_word': diff --git a/src/pyj/viewer-main.pyj b/src/pyj/viewer-main.pyj index c35ff2a80c..735a58b117 100644 --- a/src/pyj/viewer-main.pyj +++ b/src/pyj/viewer-main.pyj @@ -419,6 +419,8 @@ if window is window.top: to_python.speak_simple_text(text) ui_operations.tts = def(action, data): to_python.tts(action, data or v'{}') + ui_operations.edit_book = def (spine_name, frac): + to_python.edit_book(spine_name, frac) document.body.appendChild(E.div(id='view')) window.onerror = onerror