diff --git a/ftl/core/editing.ftl b/ftl/core/editing.ftl index 2da97d539..64c0db0c1 100644 --- a/ftl/core/editing.ftl +++ b/ftl/core/editing.ftl @@ -36,6 +36,7 @@ editing-mathjax-chemistry = MathJax chemistry editing-mathjax-inline = MathJax inline editing-mathjax-placeholder = Press { $accept } to accept, { $newline } for new line. editing-media = Media +editing-open-image = Open image editing-show-in-folder = Show in folder editing-ordered-list = Ordered list editing-outdent = Decrease indent diff --git a/python/requirements.dev.txt b/python/requirements.dev.txt index acec9bc4b..c441f99b1 100644 --- a/python/requirements.dev.txt +++ b/python/requirements.dev.txt @@ -577,6 +577,10 @@ types-protobuf==5.27.0.20240626 \ # via # -r requirements.dev.in # mypy-protobuf +types-pywin32==306.0.0.20240822 \ + --hash=sha256:31a16f7eaf711166e8aec50ee1ddf0f16b4512e19ecc92a019ae7a0860b64bad \ + --hash=sha256:34d22b58aaa2cc86fe585b6e2e1eda88a60b010badea0e0e4a410ebe28744645 + # via -r requirements.dev.in types-requests==2.32.0.20240712 \ --hash=sha256:90c079ff05e549f6bf50e02e910210b98b8ff1ebdd18e19c873cd237737c1358 \ --hash=sha256:f754283e152c752e46e70942fa2a146b5bc70393522257bb85bd1ef7e019dcc3 diff --git a/qt/aqt/editor.py b/qt/aqt/editor.py index ebd1f5058..4f64c5f2f 100644 --- a/qt/aqt/editor.py +++ b/qt/aqt/editor.py @@ -37,7 +37,7 @@ from anki.httpclient import HttpClient from anki.models import NotetypeId, StockNotetype from anki.notes import Note, NoteFieldsCheckResult, NoteId from anki.utils import checksum, is_lin, is_mac, is_win, namedtmp -from aqt import AnkiQt, colors, gui_hooks, mw +from aqt import AnkiQt, colors, gui_hooks from aqt.operations import QueryOp from aqt.operations.note import update_note from aqt.operations.notetype import update_notetype_legacy @@ -49,13 +49,14 @@ from aqt.utils import ( KeyboardModifiersPressed, disable_help_button, getFile, + openFolder, openHelp, qtMenuShortcutWorkaround, restoreGeom, saveGeom, shortcut, + show_in_folder, showInfo, - showinFolder, showWarning, tooltip, tr, @@ -1592,7 +1593,7 @@ class EditorWebView(AnkiWebView): a = m.addAction(tr.editing_paste()) qconnect(a.triggered, self.onPaste) if self._opened_context_menu_on_image(): - self._add_image_menu(AnkiWebView(self), m) + self._add_image_menu(m) gui_hooks.editor_will_show_context_menu(self, m) m.popup(QCursor.pos()) @@ -1604,16 +1605,19 @@ class EditorWebView(AnkiWebView): a = menu.addAction(tr.actions_copy()) qconnect(a.triggered, self.onCopy) - def _add_image_menu(self, webview: AnkiWebView, menu: QMenu) -> None: + def _add_image_menu(self, menu: QMenu) -> None: a = menu.addAction(tr.editing_copy_image()) qconnect(a.triggered, self.on_copy_image) + url = self.lastContextMenuRequest().mediaUrl() + file_name = url.fileName() + path = os.path.join(self.editor.mw.col.media.dir(), file_name) + a = menu.addAction(tr.editing_open_image()) + qconnect(a.triggered, lambda: openFolder(path)) + if is_win or is_mac: - url = webview.lastContextMenuRequest().mediaUrl() - file_name = url.fileName() - path = os.path.join(mw.col.media.dir(), file_name) a = menu.addAction(tr.editing_show_in_folder()) - qconnect(a.triggered, lambda: showinFolder(path)) + qconnect(a.triggered, lambda: show_in_folder(path)) # QFont returns "Kozuka Gothic Pro L" but WebEngine expects "Kozuka Gothic Pro Light" diff --git a/qt/aqt/utils.py b/qt/aqt/utils.py index b265b2f6e..ac11694e1 100644 --- a/qt/aqt/utils.py +++ b/qt/aqt/utils.py @@ -886,14 +886,14 @@ def openFolder(path: str) -> None: QDesktopServices.openUrl(QUrl(f"file://{path}")) -def showinFolder(path: str) -> None: +def show_in_folder(path: str) -> None: if is_win: - call(["explorer", "/select,", f"file://{path}"]) + _show_in_folder_win32(path) elif is_mac: script = f""" tell application "Finder" activate - select POSIX file '{path}' + select POSIX file "{path}" end tell """ call(osascript_to_args(script)) @@ -903,6 +903,22 @@ def showinFolder(path: str) -> None: QDesktopServices.openUrl(QUrl(f"file://{path}")) +def _show_in_folder_win32(path: str) -> None: + import win32con # pylint: disable=import-error + import win32gui # pylint: disable=import-error + + from aqt import mw + + def focus_explorer(): + hwnd = win32gui.FindWindow("CabinetWClass", None) + if hwnd: + win32gui.ShowWindow(hwnd, win32con.SW_RESTORE) + win32gui.SetForegroundWindow(hwnd) + + subprocess.run(["explorer", f"/select,{path}"], check=False) + mw.progress.single_shot(500, focus_explorer) + + def osascript_to_args(script: str): args = [ item