From b8c52790bca350819c967349339f5aff918342cb Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sat, 7 Dec 2013 13:41:27 +0530 Subject: [PATCH] Implement clicking on links in the preview panel Clicking links in the preview panel now opens the linked to document in an editor at the location pointed to by the link --- resources/compiled_coffeescript.zip | Bin 77601 -> 78402 bytes src/calibre/ebooks/oeb/polish/preview.coffee | 19 +++++++++++++- src/calibre/gui2/tweak_book/boss.py | 12 +++++++++ src/calibre/gui2/tweak_book/editor/text.py | 14 ++++++++++ src/calibre/gui2/tweak_book/editor/widget.py | 3 +++ src/calibre/gui2/tweak_book/preview.py | 26 +++++++++++++++---- 6 files changed, 68 insertions(+), 6 deletions(-) diff --git a/resources/compiled_coffeescript.zip b/resources/compiled_coffeescript.zip index 4b5ab2b97233cfe3c7769704a4ed62f8c2796061..6f4418df74e6e897d025e1ef21caf0fe31e34ed0 100644 GIT binary patch delta 750 zcmZ`%O=}ZD7|wK;ZIVK4zG^^A-xSGglFbG&A{Z8_MZ^z`;LTvyO)_mF$u4d;m1re? z96Sl?2!mLxqBko=d+_GLvlsgV>Y@IC9z8mftVRU~cA58io_XGP9_H1K{Q0|l;Nm$+ zP6Wi-YCo8tf3E6#Q-Of^4zlwant6ZxlY*BV({oJAt~VVQ3^2`F%WkM%v$d@MNzuFG z$*mtscY^M{lT$>4rb`wzXxrV62A-{f$?Gy*jP?73+mIDvd%9=2OT;UO!CyixNS&i| zp69k|U5}`^VR=?Ta5y{C3V*KkZ2rS|gx_ek+5}VrnG6s}8wNC-dbcCm)tp8TGN=B< zg;M1{LW?a7tE-R}?%6Ed`aK#9JE`ItVnn(j7ZandFu3o4Si>_4@YV*Ni;xKaNV%LURVopP8yh7x^qCrKGh z;_F@F_AYUv?m4cCtG3l4#w>=sZ^Jiu_6nwHGbxMXwBqCs{R)}a|IK+Y-cEF!R*rM#QR3jkqGN^SrE diff --git a/src/calibre/ebooks/oeb/polish/preview.coffee b/src/calibre/ebooks/oeb/polish/preview.coffee index 40fa2d4f97..81448bb1d5 100644 --- a/src/calibre/ebooks/oeb/polish/preview.coffee +++ b/src/calibre/ebooks/oeb/polish/preview.coffee @@ -105,9 +105,26 @@ class PreviewIntegration if this.in_split_mode this.report_split(event.target) else - window.py_bridge.request_sync(event.target.getAttribute("data-lnum")) + e = event.target + # Find the closest containing link, if any + lnum = e.getAttribute('data-lnum') + href = tn = '' + while e and e != document.body and e != document and (tn != 'a' or not href) + tn = e.tagName?.toLowerCase() + href = e.getAttribute('href') + e = e.parentNode + window.py_bridge.request_sync(tn, href, lnum) return false + go_to_anchor: (anchor, lnum) => + elem = document.getElementById(anchor) + if not elem + elem = document.querySelector('[name="' + anchor + '"]') + if elem + elem.scrollIntoView() + lnum = elem.getAttribute('data-lnum') + window.py_bridge.request_sync('', '', lnum) + window.calibre_preview_integration = new PreviewIntegration() window.onload = window.calibre_preview_integration.onload diff --git a/src/calibre/gui2/tweak_book/boss.py b/src/calibre/gui2/tweak_book/boss.py index 2c4853984c..dd9aa95aa4 100644 --- a/src/calibre/gui2/tweak_book/boss.py +++ b/src/calibre/gui2/tweak_book/boss.py @@ -91,6 +91,7 @@ class Boss(QObject): self.gui.preview.sync_requested.connect(self.sync_editor_to_preview) self.gui.preview.split_start_requested.connect(self.split_start_requested) self.gui.preview.split_requested.connect(self.split_requested) + self.gui.preview.link_clicked.connect(self.link_clicked) def preferences(self): p = Preferences(self.gui) @@ -668,6 +669,17 @@ class Boss(QObject): self.apply_container_update_to_gui() self.edit_file(bottom_name, 'html') + @in_thread_job + def link_clicked(self, name, anchor): + if name in editors: + editor = editors[name] + self.gui.central.show_editor(editor) + else: + syntax = syntax_from_mime(name, current_container().mime_map[name]) + editor = self.edit_file(name, syntax) + if anchor: + editor.go_to_anchor(anchor) + @in_thread_job def merge_requested(self, category, names, master): self.commit_all_editors_to_container() diff --git a/src/calibre/gui2/tweak_book/editor/text.py b/src/calibre/gui2/tweak_book/editor/text.py index 1c6f2c1764..de8e817f0e 100644 --- a/src/calibre/gui2/tweak_book/editor/text.py +++ b/src/calibre/gui2/tweak_book/editor/text.py @@ -287,6 +287,20 @@ class TextEdit(QPlainTextEdit): text = m.expand(template) c.insertText(text) return True + + def go_to_anchor(self, anchor): + base = r'''%%s\s*=\s*['"]{0,1}%s''' % regex.escape(anchor) + raw = unicode(self.toPlainText()) + m = regex.search(base % 'id', raw) + if m is None: + m = regex.search(base % 'name', raw) + if m is not None: + c = self.textCursor() + c.setPosition(m.start()) + self.setTextCursor(c) + return True + return False + # }}} # Line numbers and cursor line {{{ diff --git a/src/calibre/gui2/tweak_book/editor/widget.py b/src/calibre/gui2/tweak_book/editor/widget.py index a311905a0d..05520d258e 100644 --- a/src/calibre/gui2/tweak_book/editor/widget.py +++ b/src/calibre/gui2/tweak_book/editor/widget.py @@ -108,6 +108,9 @@ class Editor(QMainWindow): def all_in_marked(self, *args, **kwargs): return self.editor.all_in_marked(*args, **kwargs) + + def go_to_anchor(self, *args, **kwargs): + return self.editor.go_to_anchor(*args, **kwargs) # }}} @property diff --git a/src/calibre/gui2/tweak_book/preview.py b/src/calibre/gui2/tweak_book/preview.py index a95f3dd895..1371d4d2dd 100644 --- a/src/calibre/gui2/tweak_book/preview.py +++ b/src/calibre/gui2/tweak_book/preview.py @@ -14,6 +14,7 @@ from threading import Thread from Queue import Queue, Empty from collections import namedtuple from functools import partial +from urlparse import urlparse from PyQt4.Qt import ( QWidget, QVBoxLayout, QApplication, QSize, QNetworkAccessManager, QMenu, QIcon, @@ -273,7 +274,7 @@ def find_le(a, x): class WebPage(QWebPage): - sync_requested = pyqtSignal(object) + sync_requested = pyqtSignal(object, object, object) split_requested = pyqtSignal(object) def __init__(self, parent): @@ -312,13 +313,17 @@ class WebPage(QWebPage): mf.addToJavaScriptWindowObject("py_bridge", self) mf.evaluateJavaScript(self.js) - @pyqtSlot(str) - def request_sync(self, lnum): + @pyqtSlot(str, str, str) + def request_sync(self, tag_name, href, lnum): try: - self.sync_requested.emit(int(lnum)) + self.sync_requested.emit(unicode(tag_name), unicode(href), int(unicode(lnum))) except (TypeError, ValueError, OverflowError, AttributeError): pass + def go_to_anchor(self, anchor, lnum): + self.mainFrame().evaluateJavaScript('window.calibre_preview_integration.go_to_anchor(%s, %s)' % ( + json.dumps(anchor), json.dumps(str(lnum)))) + @pyqtSlot(str) def request_split(self, loc): actions['split-in-preview'].setChecked(False) @@ -414,6 +419,7 @@ class Preview(QWidget): sync_requested = pyqtSignal(object, object) split_requested = pyqtSignal(object, object) split_start_requested = pyqtSignal() + link_clicked = pyqtSignal(object, object) def __init__(self, parent=None): QWidget.__init__(self, parent) @@ -482,8 +488,18 @@ class Preview(QWidget): self.view.findText(text, QWebPage.FindWrapsAroundDocument | ( QWebPage.FindBackward if direction == 'prev' else QWebPage.FindFlags(0))) - def request_sync(self, lnum): + def request_sync(self, tagname, href, lnum): if self.current_name: + c = current_container() + if tagname == 'a' and href: + if href and href.startswith('#'): + name = self.current_name + else: + name = c.href_to_name(href, self.current_name) if href else None + if name == self.current_name: + return self.view.page().go_to_anchor(urlparse(href).fragment, lnum) + if name and c.exists(name) and c.mime_map[name] in OEB_DOCS: + return self.link_clicked.emit(name, urlparse(href).fragment) self.sync_requested.emit(self.current_name, lnum) def request_split(self, loc):