diff --git a/resources/compiled_coffeescript.zip b/resources/compiled_coffeescript.zip index 4b5ab2b972..6f4418df74 100644 Binary files a/resources/compiled_coffeescript.zip and b/resources/compiled_coffeescript.zip differ 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):