diff --git a/src/calibre/gui2/viewer/toolbars.py b/src/calibre/gui2/viewer/toolbars.py index a4c2d390b4..d82c138e66 100644 --- a/src/calibre/gui2/viewer/toolbars.py +++ b/src/calibre/gui2/viewer/toolbars.py @@ -44,7 +44,7 @@ def all_actions(): 'back': Action('back.png', _('Back')), 'forward': Action('forward.png', _('Forward')), 'open': Action('document_open.png', _('Open e-book')), - 'copy': Action('edit-copy.png', _('Copy to clipboard')), + 'copy': Action('edit-copy.png', _('Copy to clipboard'), 'copy_to_clipboard'), 'increase_font_size': Action('font_size_larger.png', _('Increase font size'), 'increase_font_size'), 'decrease_font_size': Action('font_size_smaller.png', _('Decrease font size'), 'decrease_font_size'), 'fullscreen': Action('page.png', _('Toggle full screen'), 'toggle_full_screen'), @@ -150,8 +150,7 @@ class ActionsToolBar(ToolBar): a.setMenu(m) m.aboutToShow.connect(self.populate_open_menu) connect_lambda(a.triggered, self, lambda self: self.open_book_at_path.emit(None)) - self.copy_action = a = page.action(QWebEnginePage.Copy) - a.setIcon(aa.copy.icon), a.setText(aa.copy.text) + self.copy_action = shortcut_action('copy') self.increase_font_size_action = shortcut_action('increase_font_size') self.decrease_font_size_action = shortcut_action('decrease_font_size') self.fullscreen_action = shortcut_action('fullscreen') diff --git a/src/calibre/gui2/viewer/web_view.py b/src/calibre/gui2/viewer/web_view.py index 7006fa9f1c..7928484737 100644 --- a/src/calibre/gui2/viewer/web_view.py +++ b/src/calibre/gui2/viewer/web_view.py @@ -7,10 +7,9 @@ import os import shutil import sys from itertools import count - from PyQt5.Qt import ( - QApplication, QBuffer, QByteArray, QFontDatabase, QFontInfo, QHBoxLayout, QSize, QT_VERSION, - Qt, QTimer, QUrl, QWidget, pyqtSignal + QT_VERSION, QApplication, QBuffer, QByteArray, QFontDatabase, QFontInfo, + QHBoxLayout, QMimeData, QSize, Qt, QTimer, QUrl, QWidget, pyqtSignal ) from PyQt5.QtWebEngineCore import QWebEngineUrlSchemeHandler from PyQt5.QtWebEngineWidgets import ( @@ -251,7 +250,7 @@ class ViewerBridge(Bridge): ask_for_open = from_js(object) selection_changed = from_js(object, object) autoscroll_state_changed = from_js(object) - copy_selection = from_js(object) + copy_selection = from_js(object, object) view_image = from_js(object) copy_image = from_js(object) change_background_image = from_js(object) @@ -339,11 +338,13 @@ class WebPage(QWebEnginePage): self.bridge = ViewerBridge(self) self.bridge.copy_selection.connect(self.trigger_copy) - def trigger_copy(self, what): - if what: - QApplication.instance().clipboard().setText(what) - else: - self.triggerAction(self.Copy) + def trigger_copy(self, text, html): + if text: + md = QMimeData() + md.setText(text) + if html: + md.setHtml(html) + QApplication.instance().clipboard().setMimeData(md) def javaScriptConsoleMessage(self, level, msg, linenumber, source_id): prefix = {QWebEnginePage.InfoMessageLevel: 'INFO', QWebEnginePage.WarningMessageLevel: 'WARNING'}.get( diff --git a/src/pyj/read_book/iframe.pyj b/src/pyj/read_book/iframe.pyj index 3e7c17be63..1564fa46a8 100644 --- a/src/pyj/read_book/iframe.pyj +++ b/src/pyj/read_book/iframe.pyj @@ -862,9 +862,13 @@ class IframeBoss: select_crw(crw) def copy_selection(self): - text = window.getSelection().toString() + s = window.getSelection() + text = s.toString() if text: - self.send_message('copy_text_to_clipboard', text=text) + container = document.createElement('div') + for i in range(s.rangeCount): + container.appendChild(s.getRangeAt(i).cloneContents()) + self.send_message('copy_text_to_clipboard', text=text, html=container.innerHTML) def main(): main.boss = IframeBoss() diff --git a/src/pyj/read_book/selection_bar.pyj b/src/pyj/read_book/selection_bar.pyj index 62473a82c6..2b9fd10002 100644 --- a/src/pyj/read_book/selection_bar.pyj +++ b/src/pyj/read_book/selection_bar.pyj @@ -556,6 +556,8 @@ class SelectionBar: sc_name = shortcut_for_key_event(ev, self.view.keyboard_shortcut_map) if sc_name is 'show_chrome': self.clear_selection() + elif sc_name is 'copy_to_clipboard': + self.copy_to_clipboard() elif sc_name in ('up', 'down', 'pageup', 'pagedown', 'left', 'right'): self.send_message('trigger-shortcut', name=sc_name) elif sc_name is 'start_search': @@ -876,8 +878,7 @@ class SelectionBar: # Actions {{{ def copy_to_clipboard(self): - if self.view.currently_showing.selection.text: - ui_operations.copy_selection(self.view.currently_showing.selection.text) + self.view.copy_to_clipboard() def lookup(self): if ui_operations.toggle_lookup: diff --git a/src/pyj/read_book/shortcuts.pyj b/src/pyj/read_book/shortcuts.pyj index dd7e0a41b2..32514179a5 100644 --- a/src/pyj/read_book/shortcuts.pyj +++ b/src/pyj/read_book/shortcuts.pyj @@ -190,6 +190,12 @@ def shortcuts_definition(): _('Show/hide Table of Contents'), ), + 'copy_to_clipboard': desc( + 'Ctrl+C', + 'ui', + _('Copy to clipboard'), + ), + 'start_search': desc( v"['/', 'Ctrl+f']", 'ui', diff --git a/src/pyj/read_book/ui.pyj b/src/pyj/read_book/ui.pyj index 476f2d087b..437f528161 100644 --- a/src/pyj/read_book/ui.pyj +++ b/src/pyj/read_book/ui.pyj @@ -83,7 +83,7 @@ class ReadUI: ui_operations.close_book = self.close_book.bind(self) ui_operations.open_url = def(url): window.open(url, '_blank') - ui_operations.copy_selection = def(text): + ui_operations.copy_selection = def(text, html): if not window.navigator.clipboard: return error_dialog(_('No clipboard access'), _( 'Your browser requires you to access the Content server over an HTTPS connection' diff --git a/src/pyj/read_book/view.pyj b/src/pyj/read_book/view.pyj index 11cc2cfbf5..c58326c9e6 100644 --- a/src/pyj/read_book/view.pyj +++ b/src/pyj/read_book/view.pyj @@ -292,7 +292,7 @@ class View: , 'annotations': self.on_annotations_message, 'copy_text_to_clipboard': def(data): - ui_operations.copy_selection(data.text) + ui_operations.copy_selection(data.text, data.html) , 'view_image': def(data): if ui_operations.view_image: @@ -326,6 +326,9 @@ class View: def reference_mode_overlay(self): return document.getElementById('reference-mode-overlay') + def copy_to_clipboard(self): + self.iframe_wrapper.send_message('copy_selection') + def set_scrollbar_visibility(self, visible): sd = get_session_data() sd.set('book_scrollbar', bool(visible)) @@ -460,6 +463,8 @@ class View: ui_operations.toggle_highlights() elif data.name is 'new_bookmark': self.new_bookmark() + elif data.name is 'copy_to_clipboard': + self.copy_to_clipboard() elif data.name is 'toggle_inspector': ui_operations.toggle_inspector() elif data.name is 'toggle_lookup': diff --git a/src/pyj/viewer-main.pyj b/src/pyj/viewer-main.pyj index 6fe5b36d25..80e23722dc 100644 --- a/src/pyj/viewer-main.pyj +++ b/src/pyj/viewer-main.pyj @@ -362,8 +362,8 @@ if window is window.top: to_python.report_cfi(request_id, data) ui_operations.ask_for_open = def(path): to_python.ask_for_open(path) - ui_operations.copy_selection = def(text): - to_python.copy_selection(text or None) + ui_operations.copy_selection = def(text, html): + to_python.copy_selection(text or None, html or None) ui_operations.view_image = def(name): to_python.view_image(name) ui_operations.copy_image = def(name):