20 import shutil |
20 import shutil |
21 import tempfile |
21 import tempfile |
22 import sys |
22 import sys |
23 |
23 |
24 from PyQt5.QtCore import pyqtSlot, pyqtSignal, Qt, QUrl, QSize, QThread |
24 from PyQt5.QtCore import pyqtSlot, pyqtSignal, Qt, QUrl, QSize, QThread |
25 from PyQt5.QtWidgets import QWidget |
25 from PyQt5.QtWidgets import QWidget, QVBoxLayout, QLabel, QCheckBox, \ |
26 from PyQt5.QtWebKitWidgets import QWebPage |
26 QSizePolicy |
27 |
27 |
28 from E5Gui.E5Application import e5App |
28 from E5Gui.E5Application import e5App |
29 |
|
30 from .Ui_PreviewerHTML import Ui_PreviewerHTML |
|
31 |
29 |
32 import Utilities |
30 import Utilities |
33 import Preferences |
31 import Preferences |
34 |
32 |
35 |
33 |
36 class PreviewerHTML(QWidget, Ui_PreviewerHTML): |
34 class PreviewerHTML(QWidget): |
37 """ |
35 """ |
38 Class implementing a previewer widget for HTML, Markdown and ReST files. |
36 Class implementing a previewer widget for HTML, Markdown and ReST files. |
39 """ |
37 """ |
40 def __init__(self, parent=None): |
38 def __init__(self, parent=None): |
41 """ |
39 """ |
42 Constructor |
40 Constructor |
43 |
41 |
44 @param parent reference to the parent widget (QWidget) |
42 @param parent reference to the parent widget (QWidget) |
45 """ |
43 """ |
46 super(PreviewerHTML, self).__init__(parent) |
44 super(PreviewerHTML, self).__init__(parent) |
47 self.setupUi(self) |
45 |
|
46 self.__layout = QVBoxLayout(self) |
|
47 |
|
48 self.titleLabel = QLabel(self) |
|
49 self.titleLabel.setWordWrap(True) |
|
50 self.titleLabel.setTextInteractionFlags(Qt.NoTextInteraction) |
|
51 self.__layout.addWidget(self.titleLabel) |
|
52 |
|
53 try: |
|
54 from PyQt5.QtWebKitWidgets import QWebPage, QWebView |
|
55 self.previewView = QWebView(self) |
|
56 self.previewView.page().setLinkDelegationPolicy( |
|
57 QWebPage.DelegateAllLinks) |
|
58 self.__usesWebKit = True |
|
59 except ImportError: |
|
60 from PyQt5.QtWebEngineWidgets import QWebEngineView |
|
61 self.previewView = QWebEngineView(self) |
|
62 self.__usesWebKit = False |
|
63 |
|
64 sizePolicy = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Expanding) |
|
65 sizePolicy.setHorizontalStretch(0) |
|
66 sizePolicy.setVerticalStretch(0) |
|
67 sizePolicy.setHeightForWidth( |
|
68 self.previewView.sizePolicy().hasHeightForWidth()) |
|
69 self.previewView.setSizePolicy(sizePolicy) |
|
70 self.previewView.setContextMenuPolicy(Qt.NoContextMenu) |
|
71 self.previewView.setUrl(QUrl("about:blank")) |
|
72 self.__layout.addWidget(self.previewView) |
|
73 |
|
74 self.jsCheckBox = QCheckBox(self.tr("Enable JavaScript"), self) |
|
75 self.jsCheckBox.setToolTip(self.tr( |
|
76 "Select to enable JavaScript for HTML previews")) |
|
77 self.__layout.addWidget(self.jsCheckBox) |
|
78 |
|
79 self.ssiCheckBox = QCheckBox(self.tr("Enable Server Side Includes"), |
|
80 self) |
|
81 self.ssiCheckBox.setToolTip(self.tr( |
|
82 "Select to enable support for Server Side Includes")) |
|
83 self.__layout.addWidget(self.ssiCheckBox) |
|
84 |
|
85 self.jsCheckBox.clicked[bool].connect(self.on_jsCheckBox_clicked) |
|
86 self.ssiCheckBox.clicked[bool].connect(self.on_ssiCheckBox_clicked) |
|
87 self.previewView.titleChanged.connect(self.on_previewView_titleChanged) |
|
88 if self.__usesWebKit: |
|
89 self.previewView.linkClicked.connect( |
|
90 self.on_previewView_linkClicked) |
48 |
91 |
49 self.jsCheckBox.setChecked( |
92 self.jsCheckBox.setChecked( |
50 Preferences.getUI("ShowFilePreviewJS")) |
93 Preferences.getUI("ShowFilePreviewJS")) |
51 self.ssiCheckBox.setChecked( |
94 self.ssiCheckBox.setChecked( |
52 Preferences.getUI("ShowFilePreviewSSI")) |
95 Preferences.getUI("ShowFilePreviewSSI")) |
53 |
|
54 self.previewView.page().setLinkDelegationPolicy( |
|
55 QWebPage.DelegateAllLinks) |
|
56 |
96 |
57 self.__scrollBarPositions = {} |
97 self.__scrollBarPositions = {} |
58 self.__vScrollBarAtEnd = {} |
98 self.__vScrollBarAtEnd = {} |
59 self.__hScrollBarAtEnd = {} |
99 self.__hScrollBarAtEnd = {} |
60 |
100 |
156 positions. |
196 positions. |
157 |
197 |
158 @param filePath file path of the previewed editor (string) |
198 @param filePath file path of the previewed editor (string) |
159 @param html processed HTML text ready to be shown (string) |
199 @param html processed HTML text ready to be shown (string) |
160 """ |
200 """ |
161 self.__saveScrollBarPositions() |
|
162 self.__previewedPath = Utilities.normcasepath( |
201 self.__previewedPath = Utilities.normcasepath( |
163 Utilities.fromNativeSeparators(filePath)) |
202 Utilities.fromNativeSeparators(filePath)) |
164 self.previewView.page().mainFrame().contentsSizeChanged.connect( |
203 self.__saveScrollBarPositions() |
165 self.__restoreScrollBarPositions) |
204 if self.__usesWebKit: |
|
205 self.previewView.page().mainFrame().contentsSizeChanged.connect( |
|
206 self.__restoreScrollBarPositions) |
|
207 else: |
|
208 self.previewView.page().loadFinished.connect( |
|
209 self.__restoreScrollBarPositions) |
166 self.previewView.setHtml(html, baseUrl=QUrl.fromLocalFile(filePath)) |
210 self.previewView.setHtml(html, baseUrl=QUrl.fromLocalFile(filePath)) |
167 |
211 |
168 @pyqtSlot(str) |
212 @pyqtSlot(str) |
169 def on_previewView_titleChanged(self, title): |
213 def on_previewView_titleChanged(self, title): |
170 """ |
214 """ |
179 |
223 |
180 def __saveScrollBarPositions(self): |
224 def __saveScrollBarPositions(self): |
181 """ |
225 """ |
182 Private method to save scroll bar positions for a previewed editor. |
226 Private method to save scroll bar positions for a previewed editor. |
183 """ |
227 """ |
184 frame = self.previewView.page().mainFrame() |
228 if self.__usesWebKit: |
185 if frame.contentsSize() == QSize(0, 0): |
229 frame = self.previewView.page().mainFrame() |
186 return # no valid data, nothing to save |
230 if frame.contentsSize() == QSize(0, 0): |
187 |
231 return # no valid data, nothing to save |
188 pos = frame.scrollPosition() |
232 |
189 self.__scrollBarPositions[self.__previewedPath] = pos |
233 pos = frame.scrollPosition() |
190 self.__hScrollBarAtEnd[self.__previewedPath] = \ |
234 self.__scrollBarPositions[self.__previewedPath] = pos |
191 frame.scrollBarMaximum(Qt.Horizontal) == pos.x() |
235 self.__hScrollBarAtEnd[self.__previewedPath] = \ |
192 self.__vScrollBarAtEnd[self.__previewedPath] = \ |
236 frame.scrollBarMaximum(Qt.Horizontal) == pos.x() |
193 frame.scrollBarMaximum(Qt.Vertical) == pos.y() |
237 self.__vScrollBarAtEnd[self.__previewedPath] = \ |
|
238 frame.scrollBarMaximum(Qt.Vertical) == pos.y() |
|
239 else: |
|
240 from PyQt5.QtCore import QPoint |
|
241 pos = self.__execJavaScript( |
|
242 "(function() {" |
|
243 "var res = {" |
|
244 " x: 0," |
|
245 " y: 0," |
|
246 "};" |
|
247 "res.x = window.scrollX;" |
|
248 "res.y = window.scrollY;" |
|
249 "return res;" |
|
250 "})()" |
|
251 ) |
|
252 pos = QPoint(pos["x"], pos["y"]) |
|
253 self.__scrollBarPositions[self.__previewedPath] = pos |
|
254 self.__hScrollBarAtEnd[self.__previewedPath] = False |
|
255 self.__vScrollBarAtEnd[self.__previewedPath] = False |
194 |
256 |
195 def __restoreScrollBarPositions(self): |
257 def __restoreScrollBarPositions(self): |
196 """ |
258 """ |
197 Private method to restore scroll bar positions for a previewed editor. |
259 Private method to restore scroll bar positions for a previewed editor. |
198 """ |
260 """ |
199 try: |
261 if self.__usesWebKit: |
200 self.previewView.page().mainFrame().contentsSizeChanged.disconnect( |
262 try: |
201 self.__restoreScrollBarPositions) |
263 self.previewView.page().mainFrame().contentsSizeChanged.\ |
202 except TypeError: |
264 disconnect(self.__restoreScrollBarPositions) |
203 # not connected, simply ignore it |
265 except TypeError: |
204 pass |
266 # not connected, simply ignore it |
205 |
267 pass |
206 if self.__previewedPath not in self.__scrollBarPositions: |
268 |
207 return |
269 if self.__previewedPath not in self.__scrollBarPositions: |
208 |
270 return |
209 frame = self.previewView.page().mainFrame() |
271 |
210 frame.setScrollPosition( |
272 frame = self.previewView.page().mainFrame() |
211 self.__scrollBarPositions[self.__previewedPath]) |
273 frame.setScrollPosition( |
212 |
274 self.__scrollBarPositions[self.__previewedPath]) |
213 if self.__hScrollBarAtEnd[self.__previewedPath]: |
275 |
214 frame.setScrollBarValue( |
276 if self.__hScrollBarAtEnd[self.__previewedPath]: |
215 Qt.Horizontal, frame.scrollBarMaximum(Qt.Horizontal)) |
277 frame.setScrollBarValue( |
216 |
278 Qt.Horizontal, frame.scrollBarMaximum(Qt.Horizontal)) |
217 if self.__vScrollBarAtEnd[self.__previewedPath]: |
279 |
218 frame.setScrollBarValue( |
280 if self.__vScrollBarAtEnd[self.__previewedPath]: |
219 Qt.Vertical, frame.scrollBarMaximum(Qt.Vertical)) |
281 frame.setScrollBarValue( |
|
282 Qt.Vertical, frame.scrollBarMaximum(Qt.Vertical)) |
|
283 else: |
|
284 if self.__previewedPath not in self.__scrollBarPositions: |
|
285 return |
|
286 |
|
287 pos = self.__scrollBarPositions[self.__previewedPath] |
|
288 self.previewView.page().runJavaScript( |
|
289 "window.scrollTo({0}, {1});".format(pos.x(), pos.y())) |
220 |
290 |
221 @pyqtSlot(QUrl) |
291 @pyqtSlot(QUrl) |
222 def on_previewView_linkClicked(self, url): |
292 def on_previewView_linkClicked(self, url): |
223 """ |
293 """ |
224 Private slot handling the clicking of a link. |
294 Private slot handling the clicking of a link. |
225 |
295 |
226 @param url url of the clicked link (QUrl) |
296 @param url url of the clicked link (QUrl) |
227 """ |
297 """ |
228 e5App().getObject("UserInterface").launchHelpViewer(url.toString()) |
298 e5App().getObject("UserInterface").launchHelpViewer(url.toString()) |
|
299 |
|
300 def __execJavaScript(self, script): |
|
301 """ |
|
302 Private function to execute a JavaScript function Synchroneously. |
|
303 |
|
304 @param script JavaScript script source to be executed |
|
305 @type str |
|
306 @return result of the script |
|
307 @rtype depending upon script result |
|
308 """ |
|
309 from PyQt5.QtCore import QEventLoop |
|
310 loop = QEventLoop() |
|
311 resultDict = {"res": None} |
|
312 |
|
313 def resultCallback(res, resDict=resultDict): |
|
314 if loop and loop.isRunning(): |
|
315 resDict["res"] = res |
|
316 loop.quit() |
|
317 |
|
318 self.previewView.page().runJavaScript( |
|
319 script, resultCallback) |
|
320 |
|
321 loop.exec_() |
|
322 return resultDict["res"] |
229 |
323 |
230 |
324 |
231 class PreviewProcessingThread(QThread): |
325 class PreviewProcessingThread(QThread): |
232 """ |
326 """ |
233 Class implementing a thread to process some text into HTML usable by the |
327 Class implementing a thread to process some text into HTML usable by the |