From 28fded9122683c8ad00cceb09b943ac02ce95cb8 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sat, 27 Jul 2024 10:50:45 +0530 Subject: [PATCH] macOS: Fix opening multiple books from Finder with the editor only opening one of the books --- src/calibre/gui2/__init__.py | 17 ++++++++---- src/calibre/gui2/tweak_book/main.py | 40 ++++++++++++++++++----------- 2 files changed, 37 insertions(+), 20 deletions(-) diff --git a/src/calibre/gui2/__init__.py b/src/calibre/gui2/__init__.py index 22e1f59adf..290b572d2f 100644 --- a/src/calibre/gui2/__init__.py +++ b/src/calibre/gui2/__init__.py @@ -1199,6 +1199,8 @@ class Application(QApplication): self.headless = headless from calibre_extensions import progress_indicator self.pi = progress_indicator + self._file_open_paths = [] + self._file_open_lock = RLock() QApplication.setOrganizationName('calibre-ebook.com') QApplication.setOrganizationDomain(QApplication.organizationName()) QApplication.setApplicationVersion(__version__) @@ -1258,8 +1260,6 @@ class Application(QApplication): self._translator = None self.load_translations() qt_app = self - self._file_open_paths = [] - self._file_open_lock = RLock() if not ismacos: # OS X uses a native color dialog that does not support custom @@ -1375,9 +1375,16 @@ class Application(QApplication): def _send_file_open_events(self): with self._file_open_lock: if self._file_open_paths: - self.file_event_hook(self._file_open_paths) + if callable(self.file_event_hook): + self.file_event_hook(self._file_open_paths) self._file_open_paths = [] + def get_pending_file_open_events(self): + with self._file_open_lock: + ans = self._file_open_paths + self._file_open_paths = [] + return ans + def load_translations(self): if self._translator is not None: self.removeTranslator(self._translator) @@ -1386,7 +1393,7 @@ class Application(QApplication): def event(self, e): etype = e.type() - if callable(self.file_event_hook) and etype == QEvent.Type.FileOpen: + if etype == QEvent.Type.FileOpen: url = e.url().toString(QUrl.ComponentFormattingOption.FullyEncoded) if url and url.startswith('calibre://'): with self._file_open_lock: @@ -1394,7 +1401,7 @@ class Application(QApplication): QTimer.singleShot(1000, self._send_file_open_events) return True path = str(e.file()) - if os.access(path, os.R_OK): + if path and os.access(path, os.R_OK): with self._file_open_lock: self._file_open_paths.append(path) QTimer.singleShot(1000, self._send_file_open_events) diff --git a/src/calibre/gui2/tweak_book/main.py b/src/calibre/gui2/tweak_book/main.py index b5056ccb24..47f3a6e255 100644 --- a/src/calibre/gui2/tweak_book/main.py +++ b/src/calibre/gui2/tweak_book/main.py @@ -8,7 +8,7 @@ import time from qt.core import QIcon -from calibre.constants import EDITOR_APP_UID, islinux +from calibre.constants import EDITOR_APP_UID, islinux, ismacos from calibre.ebooks.oeb.polish.check.css import shutdown as shutdown_css_check_pool from calibre.gui2 import Application, decouple, set_gui_prefs, setup_gui_option_parser from calibre.ptempfile import reset_base_dir @@ -34,19 +34,23 @@ files inside the book which will be opened for editing automatically. return parser -class EventAccumulator: - - def __init__(self): - self.events = [] - - def __call__(self, ev): - self.events.append(ev) - - def gui_main(path=None, notify=None): _run(['ebook-edit', path], notify=notify) +def open_path_in_new_editor_instance(path: str): + import subprocess + + from calibre.gui2 import sanitize_env_vars + with sanitize_env_vars(): + if ismacos: + from calibre.utils.ipc.launch import macos_edit_book_bundle_path + bundle = os.path.dirname(os.path.dirname(macos_edit_book_bundle_path().rstrip('/'))) + subprocess.Popen(['open', '-n', '-a', bundle, path]) + else: + subprocess.Popen([sys.executable, path]) + + def _run(args, notify=None): from calibre.utils.webengine import setup_fake_protocol # Ensure we can continue to function if GUI is closed @@ -68,7 +72,6 @@ def _run(args, notify=None): app = Application(args, override_program_name=override, color_prefs=tprefs, windows_app_uid=EDITOR_APP_UID) from calibre.utils.webengine import setup_default_profile setup_default_profile() - app.file_event_hook = EventAccumulator() app.load_builtin_fonts() app.setWindowIcon(QIcon.ic('tweak.png')) main = Main(opts, notify=notify) @@ -78,10 +81,17 @@ def _run(args, notify=None): if len(args) > 1: main.boss.open_book(args[1], edit_file=args[2:], clear_notify_data=False, search_text=opts.select_text) else: - for path in reversed(app.file_event_hook.events): - main.boss.open_book(path) - break - app.file_event_hook = main.boss.open_book + paths = app.get_pending_file_open_events() + if paths: + if len(paths) > 1: + for path in paths[1:]: + try: + open_path_in_new_editor_instance(path) + except Exception: + import traceback + traceback.print_exc() + main.boss.open_book(paths[0]) + app.file_event_hook = main.boss.open_book app.exec() # Ensure that the parse worker has quit so that temp files can be deleted # on windows