mirror of
https://github.com/ankitects/anki.git
synced 2025-09-18 22:12:21 -04:00
Merge pull request #1356 from tatsumoto-ren/editor_will_process_mime
Add a hook for modifiying pasted MIME data
This commit is contained in:
commit
c9574ec133
3 changed files with 48 additions and 21 deletions
|
@ -84,6 +84,7 @@ cherryblossom <github.com/cherryblossom000>
|
||||||
Hikaru Yoshiga <github.com/hikaru-y>
|
Hikaru Yoshiga <github.com/hikaru-y>
|
||||||
Thore Tyborski <github.com/ThoreBor>
|
Thore Tyborski <github.com/ThoreBor>
|
||||||
Alexander Giorev <alex.giorev@gmail.com>
|
Alexander Giorev <alex.giorev@gmail.com>
|
||||||
|
Ren Tatsumoto <tatsu@autistici.org>
|
||||||
|
|
||||||
********************
|
********************
|
||||||
|
|
||||||
|
|
|
@ -863,13 +863,16 @@ $editorToolbar.then(({{ toolbar }}) => toolbar.appendGroup({{
|
||||||
self.web.eval(f"pasteHTML({json.dumps(html)}, {json.dumps(internal)}, {ext});")
|
self.web.eval(f"pasteHTML({json.dumps(html)}, {json.dumps(internal)}, {ext});")
|
||||||
gui_hooks.editor_did_paste(self, html, internal, extended)
|
gui_hooks.editor_did_paste(self, html, internal, extended)
|
||||||
|
|
||||||
def doDrop(self, html: str, internal: bool, extended: bool = False) -> None:
|
def doDrop(
|
||||||
|
self, html: str, internal: bool, extended: bool, cursor_pos: QPoint
|
||||||
|
) -> None:
|
||||||
def pasteIfField(ret: bool) -> None:
|
def pasteIfField(ret: bool) -> None:
|
||||||
if ret:
|
if ret:
|
||||||
self.doPaste(html, internal, extended)
|
self.doPaste(html, internal, extended)
|
||||||
|
|
||||||
p = self.web.mapFromGlobal(QCursor.pos())
|
self.web.evalWithCallback(
|
||||||
self.web.evalWithCallback(f"focusIfField({p.x()}, {p.y()});", pasteIfField)
|
f"focusIfField({cursor_pos.x()}, {cursor_pos.y()});", pasteIfField
|
||||||
|
)
|
||||||
|
|
||||||
def onPaste(self) -> None:
|
def onPaste(self) -> None:
|
||||||
self.web.onPaste()
|
self.web.onPaste()
|
||||||
|
@ -1138,30 +1141,39 @@ class EditorWebView(AnkiWebView):
|
||||||
def dropEvent(self, evt: QDropEvent) -> None:
|
def dropEvent(self, evt: QDropEvent) -> None:
|
||||||
extended = self._wantsExtendedPaste()
|
extended = self._wantsExtendedPaste()
|
||||||
mime = evt.mimeData()
|
mime = evt.mimeData()
|
||||||
|
cursor_pos = self.mapFromGlobal(QCursor.pos())
|
||||||
|
|
||||||
if evt.source() and mime.hasHtml():
|
if evt.source() and mime.hasHtml():
|
||||||
# don't filter html from other fields
|
# don't filter html from other fields
|
||||||
html, internal = mime.html(), True
|
html, internal = mime.html(), True
|
||||||
else:
|
else:
|
||||||
html, internal = self._processMime(mime, extended)
|
html, internal = self._processMime(mime, extended, drop_event=True)
|
||||||
|
|
||||||
if not html:
|
if not html:
|
||||||
return
|
return
|
||||||
|
|
||||||
self.editor.doDrop(html, internal, extended)
|
self.editor.doDrop(html, internal, extended, cursor_pos)
|
||||||
|
|
||||||
# returns (html, isInternal)
|
# returns (html, isInternal)
|
||||||
def _processMime(self, mime: QMimeData, extended: bool = False) -> Tuple[str, bool]:
|
def _processMime(
|
||||||
|
self, mime: QMimeData, extended: bool = False, drop_event: bool = False
|
||||||
|
) -> Tuple[str, bool]:
|
||||||
# print("html=%s image=%s urls=%s txt=%s" % (
|
# print("html=%s image=%s urls=%s txt=%s" % (
|
||||||
# mime.hasHtml(), mime.hasImage(), mime.hasUrls(), mime.hasText()))
|
# mime.hasHtml(), mime.hasImage(), mime.hasUrls(), mime.hasText()))
|
||||||
# print("html", mime.html())
|
# print("html", mime.html())
|
||||||
# print("urls", mime.urls())
|
# print("urls", mime.urls())
|
||||||
# print("text", mime.text())
|
# print("text", mime.text())
|
||||||
|
|
||||||
|
internal = mime.html().startswith("<!--anki-->")
|
||||||
|
|
||||||
|
mime = gui_hooks.editor_will_process_mime(
|
||||||
|
mime, self, internal, extended, drop_event
|
||||||
|
)
|
||||||
|
|
||||||
# try various content types in turn
|
# try various content types in turn
|
||||||
html, internal = self._processHtml(mime)
|
if mime.hasHtml():
|
||||||
if html:
|
html_content = mime.html()[11:] if internal else mime.html()
|
||||||
return html, internal
|
return html_content, internal
|
||||||
|
|
||||||
# favour url if it's a local link
|
# favour url if it's a local link
|
||||||
if mime.hasUrls() and mime.urls()[0].toString().startswith("file://"):
|
if mime.hasUrls() and mime.urls()[0].toString().startswith("file://"):
|
||||||
|
@ -1227,17 +1239,6 @@ class EditorWebView(AnkiWebView):
|
||||||
processed.pop()
|
processed.pop()
|
||||||
return "".join(processed)
|
return "".join(processed)
|
||||||
|
|
||||||
def _processHtml(self, mime: QMimeData) -> Tuple[Optional[str], bool]:
|
|
||||||
if not mime.hasHtml():
|
|
||||||
return None, False
|
|
||||||
html = mime.html()
|
|
||||||
|
|
||||||
# no filtering required for internal pastes
|
|
||||||
if html.startswith("<!--anki-->"):
|
|
||||||
return html[11:], True
|
|
||||||
|
|
||||||
return html, False
|
|
||||||
|
|
||||||
def _processImage(self, mime: QMimeData, extended: bool = False) -> Optional[str]:
|
def _processImage(self, mime: QMimeData, extended: bool = False) -> Optional[str]:
|
||||||
if not mime.hasImage():
|
if not mime.hasImage():
|
||||||
return None
|
return None
|
||||||
|
|
|
@ -29,7 +29,7 @@ from anki.decks import DeckDict, DeckConfigDict
|
||||||
from anki.hooks import runFilter, runHook
|
from anki.hooks import runFilter, runHook
|
||||||
from anki.models import NotetypeDict
|
from anki.models import NotetypeDict
|
||||||
from anki.collection import OpChangesAfterUndo
|
from anki.collection import OpChangesAfterUndo
|
||||||
from aqt.qt import QDialog, QEvent, QMenu, QModelIndex, QWidget
|
from aqt.qt import QDialog, QEvent, QMenu, QModelIndex, QWidget, QMimeData
|
||||||
from aqt.tagedit import TagEdit
|
from aqt.tagedit import TagEdit
|
||||||
from aqt.undo import UndoActionsInfo
|
from aqt.undo import UndoActionsInfo
|
||||||
"""
|
"""
|
||||||
|
@ -864,6 +864,31 @@ gui_hooks.webview_did_inject_style_into_page.append(mytest)
|
||||||
],
|
],
|
||||||
doc="""Called after some data is pasted by python into an editor field.""",
|
doc="""Called after some data is pasted by python into an editor field.""",
|
||||||
),
|
),
|
||||||
|
Hook(
|
||||||
|
name="editor_will_process_mime",
|
||||||
|
args=[
|
||||||
|
"mime: QMimeData",
|
||||||
|
"editor_web_view: aqt.editor.EditorWebView",
|
||||||
|
"internal: bool",
|
||||||
|
"extended: bool",
|
||||||
|
"drop_event: bool",
|
||||||
|
],
|
||||||
|
return_type="QMimeData",
|
||||||
|
doc="""
|
||||||
|
Used to modify MIME data stored in the clipboard after a drop or a paste.
|
||||||
|
Called after the user pastes or drag-and-drops something to Anki
|
||||||
|
before Anki processes the data.
|
||||||
|
|
||||||
|
The function should return a new or existing QMimeData object.
|
||||||
|
|
||||||
|
"mime" contains the corresponding QMimeData object.
|
||||||
|
"internal" indicates whether the drop or paste is performed between Anki fields.
|
||||||
|
Most likely you want to skip processing if "internal" was set to True.
|
||||||
|
"extended" indicates whether the user requested an extended paste.
|
||||||
|
"drop_event" indicates whether the event was triggered by a drag-and-drop
|
||||||
|
or by a right-click paste.
|
||||||
|
""",
|
||||||
|
),
|
||||||
# Tag
|
# Tag
|
||||||
###################
|
###################
|
||||||
Hook(name="tag_editor_did_process_key", args=["tag_edit: TagEdit", "evt: QEvent"]),
|
Hook(name="tag_editor_did_process_key", args=["tag_edit: TagEdit", "evt: QEvent"]),
|
||||||
|
|
Loading…
Reference in a new issue