diff --git a/src/calibre/gui2/tweak_book/boss.py b/src/calibre/gui2/tweak_book/boss.py index 3004c963b2..8e4abe890b 100644 --- a/src/calibre/gui2/tweak_book/boss.py +++ b/src/calibre/gui2/tweak_book/boss.py @@ -55,6 +55,7 @@ class Boss(QObject): self.gui.central.current_editor_changed.connect(self.apply_current_editor_state) self.gui.central.close_requested.connect(self.editor_close_requested) self.gui.central.search_panel.search_triggered.connect(self.search) + self.gui.preview.sync_requested.connect(self.sync_editor_to_preview) def mkdtemp(self, prefix=''): self.container_count += 1 @@ -492,6 +493,10 @@ class Boss(QObject): _('Saving of the book failed. Click "Show Details"' ' for more information.'), det_msg=tb, show=True) + def sync_editor_to_preview(self, name, lnum): + editor = self.edit_file(name, 'html') + editor.go_to_line(lnum) + def init_editor(self, name, editor, data=None, use_template=False): editor.undo_redo_state_changed.connect(self.editor_undo_redo_state_changed) editor.data_changed.connect(self.editor_data_changed) @@ -514,6 +519,7 @@ class Boss(QObject): data = use_template self.init_editor(name, editor, data, use_template=bool(use_template)) self.show_editor(name) + return editor def show_editor(self, name): self.gui.central.show_editor(editors[name]) diff --git a/src/calibre/gui2/tweak_book/editor/text.py b/src/calibre/gui2/tweak_book/editor/text.py index 6ae6513a43..e45d6df3e8 100644 --- a/src/calibre/gui2/tweak_book/editor/text.py +++ b/src/calibre/gui2/tweak_book/editor/text.py @@ -136,6 +136,22 @@ class TextEdit(QPlainTextEdit): self.setTextCursor(c) self.ensureCursorVisible() + def go_to_line(self, lnum): + lnum = max(1, min(self.blockCount(), lnum)) + c = self.textCursor() + c.clearSelection() + c.movePosition(c.Start) + c.movePosition(c.NextBlock, n=lnum - 1) + c.movePosition(c.StartOfLine) + c.movePosition(c.EndOfLine, c.KeepAnchor) + text = unicode(c.selectedText()) + c.movePosition(c.StartOfLine) + lt = text.lstrip() + if text and lt and lt != text: + c.movePosition(c.NextWord) + self.setTextCursor(c) + self.ensureCursorVisible() + def update_extra_selections(self): sel = [] if self.current_cursor_line is not None: diff --git a/src/calibre/gui2/tweak_book/editor/widget.py b/src/calibre/gui2/tweak_book/editor/widget.py index 3e39c74a35..a31fe82498 100644 --- a/src/calibre/gui2/tweak_book/editor/widget.py +++ b/src/calibre/gui2/tweak_book/editor/widget.py @@ -51,6 +51,9 @@ class Editor(QMainWindow): def init_from_template(self, template): self.editor.load_text(template, syntax=self.syntax, process_template=True) + def go_to_line(self, lnum): + self.editor.go_to_line(lnum) + def get_raw_data(self): return unicode(self.editor.toPlainText()) diff --git a/src/calibre/gui2/tweak_book/preview.py b/src/calibre/gui2/tweak_book/preview.py index eabf1e22c9..97be56d9d8 100644 --- a/src/calibre/gui2/tweak_book/preview.py +++ b/src/calibre/gui2/tweak_book/preview.py @@ -12,8 +12,9 @@ from Queue import Queue, Empty from PyQt4.Qt import ( QWidget, QVBoxLayout, QApplication, QSize, QNetworkAccessManager, QMenu, QIcon, - QNetworkReply, QTimer, QNetworkRequest, QUrl, Qt, QNetworkDiskCache, QToolBar) -from PyQt4.QtWebKit import QWebView, QWebInspector + QNetworkReply, QTimer, QNetworkRequest, QUrl, Qt, QNetworkDiskCache, QToolBar, + pyqtSlot, pyqtSignal) +from PyQt4.QtWebKit import QWebView, QWebInspector, QWebPage from calibre import prints from calibre.constants import iswindows @@ -217,6 +218,41 @@ class NetworkAccessManager(QNetworkAccessManager): # }}} +class WebPage(QWebPage): + + sync_requested = pyqtSignal(object) + + def __init__(self, parent): + QWebPage.__init__(self, parent) + self.setNetworkAccessManager(NetworkAccessManager(self)) + self.setLinkDelegationPolicy(self.DelegateAllLinks) + self.mainFrame().javaScriptWindowObjectCleared.connect(self.init_javascript) + self.init_javascript() + + def javaScriptConsoleMessage(self, msg, lineno, source_id): + prints('preview js:%s:%s:'%(unicode(source_id), lineno), unicode(msg)) + + def init_javascript(self): + mf = self.mainFrame() + mf.addToJavaScriptWindowObject("py_bridge", self) + mf.evaluateJavaScript( + ''' + function handle_click(event) { + event.preventDefault(); + window.py_bridge.request_sync(event.target.getAttribute("lnum")); + } + window.onload = function() { + document.body.addEventListener('click', handle_click, true); + } + ''') + + @pyqtSlot(str) + def request_sync(self, lnum): + try: + self.sync_requested.emit(int(lnum)) + except (TypeError, ValueError, OverflowError, AttributeError): + pass + class WebView(QWebView): def __init__(self, parent=None): @@ -235,10 +271,8 @@ class WebView(QWebView): settings.setAttribute(settings.LinksIncludedInFocusChain, False) settings.setAttribute(settings.DeveloperExtrasEnabled, True) settings.setDefaultTextEncoding('utf-8') - - self.page().setNetworkAccessManager(NetworkAccessManager(self)) - self.page().setLinkDelegationPolicy(self.page().DelegateAllLinks) - + self._page = WebPage(self) + self.setPage(self._page) self.clear() def sizeHint(self): @@ -274,12 +308,15 @@ class WebView(QWebView): class Preview(QWidget): + sync_requested = pyqtSignal(object, object) + def __init__(self, parent=None): QWidget.__init__(self, parent) self.l = l = QVBoxLayout() self.setLayout(l) l.setContentsMargins(0, 0, 0, 0) self.view = WebView(self) + self.view.page().sync_requested.connect(self.request_sync) self.inspector = self.view.inspector self.inspector.setPage(self.view.page()) l.addWidget(self.view) @@ -305,6 +342,10 @@ class Preview(QWidget): self.refresh_timer.timeout.connect(self.refresh) parse_worker.start() + def request_sync(self, lnum): + if self.current_name: + self.sync_requested.emit(self.current_name, lnum) + def show(self, name): if name != self.current_name: self.refresh_timer.stop()