Helpviewer/HelpBrowserWV.py

changeset 1934
ad6b7c30fb9f
parent 1823
21d988eaf1bf
child 1945
47016f5af3b8
equal deleted inserted replaced
1933:14c58eda4a35 1934:ad6b7c30fb9f
7 """ 7 """
8 Module implementing the helpbrowser using QWebView. 8 Module implementing the helpbrowser using QWebView.
9 """ 9 """
10 10
11 from PyQt4.QtCore import pyqtSlot, pyqtSignal, QObject, QT_TRANSLATE_NOOP, QUrl, \ 11 from PyQt4.QtCore import pyqtSlot, pyqtSignal, QObject, QT_TRANSLATE_NOOP, QUrl, \
12 QBuffer, QIODevice, QFileInfo, Qt, QTimer, QEvent, QRect, QFile 12 QBuffer, QIODevice, QFileInfo, Qt, QTimer, QEvent, QRect, QFile, QPoint
13 from PyQt4.QtGui import qApp, QDesktopServices, QStyle, QMenu, QApplication, \ 13 from PyQt4.QtGui import qApp, QDesktopServices, QStyle, QMenu, QApplication, \
14 QInputDialog, QLineEdit, QClipboard, QMouseEvent, QLabel, QToolTip, QColor, \ 14 QInputDialog, QLineEdit, QClipboard, QMouseEvent, QLabel, QToolTip, QColor, \
15 QPalette, QFrame, QPrinter, QPrintDialog, QDialog 15 QPalette, QFrame, QPrinter, QPrintDialog, QDialog
16 from PyQt4 import QtWebKit 16 from PyQt4 import QtWebKit
17 from PyQt4.QtWebKit import QWebView, QWebPage, QWebSettings 17 from PyQt4.QtWebKit import QWebView, QWebPage, QWebSettings
20 except ImportError: 20 except ImportError:
21 pass 21 pass
22 from PyQt4.QtNetwork import QNetworkReply, QNetworkRequest 22 from PyQt4.QtNetwork import QNetworkReply, QNetworkRequest
23 import sip 23 import sip
24 24
25 from E5Gui import E5MessageBox 25 from E5Gui import E5MessageBox, E5FileDialog
26 26
27 import Preferences 27 import Preferences
28 import UI.PixmapCache 28 import UI.PixmapCache
29 29
30 from .Bookmarks.AddBookmarkDialog import AddBookmarkDialog 30 from .Bookmarks.AddBookmarkDialog import AddBookmarkDialog
256 @param extension extension to be executed (QWebPage.Extension) 256 @param extension extension to be executed (QWebPage.Extension)
257 @param option provides input to the extension (QWebPage.ExtensionOption) 257 @param option provides input to the extension (QWebPage.ExtensionOption)
258 @param output stores the output results (QWebPage.ExtensionReturn) 258 @param output stores the output results (QWebPage.ExtensionReturn)
259 @return flag indicating a successful call of the extension (boolean) 259 @return flag indicating a successful call of the extension (boolean)
260 """ 260 """
261 try: 261 if extension == QWebPage.ChooseMultipleFilesExtension:
262 if extension == QWebPage.ErrorPageExtension: 262 info = sip.cast(option, QWebPage.ChooseMultipleFilesExtensionOption)
263 info = sip.cast(option, QWebPage.ErrorPageExtensionOption) 263 files = sip.cast(output, QWebPage.ChooseMultipleFilesExtensionReturn)
264 if info.error == 102: 264 if info is None or files is None:
265 # this is something of a hack; hopefully it will work in the future 265 return super().extension(extension, option, output)
266
267 suggestedFileName = ""
268 if info.suggestedFileNames:
269 suggestedFileName = info.suggestedFileNames[0]
270
271 files.fileNames = E5FileDialog.getOpenFileNames(
272 None,
273 self.trUtf8("Select files to upload..."),
274 suggestedFileName)
275 return True
276
277 ## if (extension == ChooseMultipleFilesExtension) {
278 ## const QWebPage::ChooseMultipleFilesExtensionOption* exOption = static_cast<const QWebPage::ChooseMultipleFilesExtensionOption*>(option);
279 ## QWebPage::ChooseMultipleFilesExtensionReturn* exReturn = static_cast<QWebPage::ChooseMultipleFilesExtensionReturn*>(output);
280 ##
281 ## if (!exOption || !exReturn) {
282 ## return QWebPage::extension(extension, option, output);
283 ## }
284 ##
285 ## QString suggestedFileName;
286 ## if (!exOption->suggestedFileNames.isEmpty()) {
287 ## suggestedFileName = exOption->suggestedFileNames.at(0);
288 ## }
289 ##
290 ## exReturn->fileNames = QFileDialog::getOpenFileNames(0, tr("Select files to upload..."), suggestedFileName);
291 ## return true;
292 ## }
293 if extension == QWebPage.ErrorPageExtension:
294 info = sip.cast(option, QWebPage.ErrorPageExtensionOption)
295 if info.error == 102:
296 # this is something of a hack; hopefully it will work in the future
297 return False
298
299 errorPage = sip.cast(output, QWebPage.ErrorPageExtensionReturn)
300 urlString = bytes(info.url.toEncoded()).decode()
301 errorPage.baseUrl = info.url
302 if info.domain == QWebPage.QtNetwork and \
303 info.error == QNetworkReply.ContentAccessDenied and \
304 info.errorString.startswith("AdBlockRule:"):
305 if info.frame != info.frame.page().mainFrame():
306 # content in <iframe>
307 docElement = info.frame.page().mainFrame().documentElement()
308 for element in docElement.findAll("iframe"):
309 src = element.attribute("src")
310 if src in info.url.toString():
311 element.setAttribute("style", "display:none;")
266 return False 312 return False
267 313 else:
268 errorPage = sip.cast(output, QWebPage.ErrorPageExtensionReturn) 314 # the whole page is blocked
269 urlString = bytes(info.url.toEncoded()).decode() 315 rule = info.errorString.replace("AdBlockRule:", "")
270 errorPage.baseUrl = info.url 316 title = self.trUtf8("Content blocked by AdBlock Plus")
271 if info.domain == QWebPage.QtNetwork and \ 317 message = self.trUtf8("Blocked by rule: <i>{0}</i>").format(rule)
272 info.error == QNetworkReply.ContentAccessDenied and \ 318
273 info.errorString.startswith("AdBlockRule:"): 319 htmlFile = QFile(":/html/adblockPage.html")
274 if info.frame != info.frame.page().mainFrame(): 320 htmlFile.open(QFile.ReadOnly)
275 # content in <iframe> 321 html = htmlFile.readAll()
276 docElement = info.frame.page().mainFrame().documentElement() 322 html = html.replace("@FAVICON@", "qrc:icons/adBlockPlus16.png")
277 for element in docElement.findAll("iframe"): 323 html = html.replace("@IMAGE@", "qrc:icons/adBlockPlus64.png")
278 src = element.attribute("src") 324 html = html.replace("@TITLE@", title.encode("utf8"))
279 if src in info.url.toString(): 325 html = html.replace("@MESSAGE@", message.encode("utf8"))
280 element.setAttribute("style", "display:none;") 326 errorPage.content = html
281 return False 327 return True
282 else: 328
283 # the whole page is blocked 329 title = self.trUtf8("Error loading page: {0}").format(urlString)
284 rule = info.errorString.replace("AdBlockRule:", "") 330 htmlFile = QFile(":/html/notFoundPage.html")
285 title = self.trUtf8("Content blocked by AdBlock Plus") 331 htmlFile.open(QFile.ReadOnly)
286 message = self.trUtf8("Blocked by rule: <i>{0}</i>").format(rule) 332 html = htmlFile.readAll()
287 333 pixmap = qApp.style()\
288 htmlFile = QFile(":/html/adblockPage.html") 334 .standardIcon(QStyle.SP_MessageBoxWarning).pixmap(48, 48)
289 htmlFile.open(QFile.ReadOnly) 335 imageBuffer = QBuffer()
290 html = htmlFile.readAll() 336 imageBuffer.open(QIODevice.ReadWrite)
291 html = html.replace("@FAVICON@", "qrc:icons/adBlockPlus16.png") 337 if pixmap.save(imageBuffer, "PNG"):
292 html = html.replace("@IMAGE@", "qrc:icons/adBlockPlus64.png") 338 html = html.replace("@IMAGE@", imageBuffer.buffer().toBase64())
293 html = html.replace("@TITLE@", title.encode("utf8")) 339 pixmap = qApp.style()\
294 html = html.replace("@MESSAGE@", message.encode("utf8")) 340 .standardIcon(QStyle.SP_MessageBoxWarning).pixmap(16, 16)
295 errorPage.content = html 341 imageBuffer = QBuffer()
296 return True 342 imageBuffer.open(QIODevice.ReadWrite)
297 343 if pixmap.save(imageBuffer, "PNG"):
298 title = self.trUtf8("Error loading page: {0}").format(urlString) 344 html = html.replace("@FAVICON@", imageBuffer.buffer().toBase64())
299 htmlFile = QFile(":/html/notFoundPage.html") 345 html = html.replace("@TITLE@", title.encode("utf8"))
300 htmlFile.open(QFile.ReadOnly) 346 html = html.replace("@H1@", info.errorString.encode("utf8"))
301 html = htmlFile.readAll() 347 html = html.replace("@H2@", self.trUtf8("When connecting to: {0}.")\
302 pixmap = qApp.style()\ 348 .format(urlString).encode("utf8"))
303 .standardIcon(QStyle.SP_MessageBoxWarning).pixmap(48, 48) 349 html = html.replace("@LI-1@",
304 imageBuffer = QBuffer() 350 self.trUtf8("Check the address for errors such as "
305 imageBuffer.open(QIODevice.ReadWrite) 351 "<b>ww</b>.example.org instead of "
306 if pixmap.save(imageBuffer, "PNG"): 352 "<b>www</b>.example.org").encode("utf8"))
307 html = html.replace("@IMAGE@", imageBuffer.buffer().toBase64()) 353 html = html.replace("@LI-2@",
308 pixmap = qApp.style()\ 354 self.trUtf8("If the address is correct, try checking the network "
309 .standardIcon(QStyle.SP_MessageBoxWarning).pixmap(16, 16) 355 "connection.").encode("utf8"))
310 imageBuffer = QBuffer() 356 html = html.replace("@LI-3@",
311 imageBuffer.open(QIODevice.ReadWrite) 357 self.trUtf8("If your computer or network is protected by a firewall "
312 if pixmap.save(imageBuffer, "PNG"): 358 "or proxy, make sure that the browser is permitted to "
313 html = html.replace("@FAVICON@", imageBuffer.buffer().toBase64()) 359 "access the network.").encode("utf8"))
314 html = html.replace("@TITLE@", title.encode("utf8")) 360 html = html.replace("@LI-4@",
315 html = html.replace("@H1@", info.errorString.encode("utf8")) 361 self.trUtf8("If your cache policy is set to offline browsing,"
316 html = html.replace("@H2@", self.trUtf8("When connecting to: {0}.")\ 362 "only pages in the local cache are available.")\
317 .format(urlString).encode("utf8")) 363 .encode("utf8"))
318 html = html.replace("@LI-1@", 364 html = html.replace("@BUTTON@", self.trUtf8("Try Again").encode("utf8"))
319 self.trUtf8("Check the address for errors such as " 365 errorPage.content = html
320 "<b>ww</b>.example.org instead of " 366 return True
321 "<b>www</b>.example.org").encode("utf8"))
322 html = html.replace("@LI-2@",
323 self.trUtf8("If the address is correct, try checking the network "
324 "connection.").encode("utf8"))
325 html = html.replace("@LI-3@",
326 self.trUtf8("If your computer or network is protected by a firewall "
327 "or proxy, make sure that the browser is permitted to "
328 "access the network.").encode("utf8"))
329 html = html.replace("@LI-4@",
330 self.trUtf8("If your cache policy is set to offline browsing,"
331 "only pages in the local cache are available.")\
332 .encode("utf8"))
333 html = html.replace("@BUTTON@", self.trUtf8("Try Again").encode("utf8"))
334 errorPage.content = html
335 return True
336 except AttributeError:
337 pass
338 367
339 return QWebPage.extension(self, extension, option, output) 368 return QWebPage.extension(self, extension, option, output)
340 369
341 def userAgent(self, resolveEmpty=False): 370 def userAgent(self, resolveEmpty=False):
342 """ 371 """
473 """ 502 """
474 if cls._webPluginFactory is None: 503 if cls._webPluginFactory is None:
475 cls._webPluginFactory = WebPluginFactory() 504 cls._webPluginFactory = WebPluginFactory()
476 505
477 return cls._webPluginFactory 506 return cls._webPluginFactory
507
508 def event(self, evt):
509 """
510 Protected method implementing the event handler.
511
512 @param evt reference to the event (QEvent)
513 @return flag indicating that the event was handled (boolean)
514 """
515 if evt.type() == QEvent.Leave:
516 # Fake a mouse move event just outside of the widget to trigger
517 # the WebKit event handler's mouseMoved function. This implements
518 # the interesting mouse-out behavior like invalidating scrollbars.
519 fakeEvent = QMouseEvent(QEvent.MouseMove, QPoint(0, -1),
520 Qt.NoButton, Qt.NoButton, Qt.NoModifier)
521 return super().event(fakeEvent)
522
523 return super().event(evt)
478 524
479 ########################################################################################## 525 ##########################################################################################
480 526
481 527
482 class HelpBrowser(QWebView): 528 class HelpBrowser(QWebView):
891 except AttributeError: 937 except AttributeError:
892 pass 938 pass
893 939
894 return self.findText(txt, findFlags) 940 return self.findText(txt, findFlags)
895 941
942 def __isMediaElement(self, element):
943 """
944 Private method to check, if the given element is a media element.
945
946 @param element element to be checked (QWebElement)
947 @return flag indicating a media element (boolean)
948 """
949 return element.tagName().lower() in ["video", "audio"]
950
896 def contextMenuEvent(self, evt): 951 def contextMenuEvent(self, evt):
897 """ 952 """
898 Protected method called to create a context menu. 953 Protected method called to create a context menu.
899 954
900 This method is overridden from QWebView. 955 This method is overridden from QWebView.
937 menu.addAction(UI.PixmapCache.getIcon("download.png"), 992 menu.addAction(UI.PixmapCache.getIcon("download.png"),
938 self.trUtf8("Save Image"), self.__downloadImage) 993 self.trUtf8("Save Image"), self.__downloadImage)
939 menu.addAction(self.trUtf8("Copy Image to Clipboard"), self.__copyImage) 994 menu.addAction(self.trUtf8("Copy Image to Clipboard"), self.__copyImage)
940 menu.addAction(UI.PixmapCache.getIcon("editCopy.png"), 995 menu.addAction(UI.PixmapCache.getIcon("editCopy.png"),
941 self.trUtf8("Copy Image Location to Clipboard"), 996 self.trUtf8("Copy Image Location to Clipboard"),
942 self.__copyImageLocation).setData(hit.imageUrl().toString()) 997 self.__copyLocation).setData(hit.imageUrl().toString())
943 menu.addAction(UI.PixmapCache.getIcon("mailSend.png"), 998 menu.addAction(UI.PixmapCache.getIcon("mailSend.png"),
944 self.trUtf8("Send Image Link"), self.__sendLink).setData(hit.imageUrl()) 999 self.trUtf8("Send Image Link"), self.__sendLink).setData(hit.imageUrl())
945 menu.addSeparator() 1000 menu.addSeparator()
946 menu.addAction(UI.PixmapCache.getIcon("adBlockPlus.png"), 1001 menu.addAction(UI.PixmapCache.getIcon("adBlockPlus.png"),
947 self.trUtf8("Block Image"), self.__blockImage)\ 1002 self.trUtf8("Block Image"), self.__blockImage)\
951 menu.addAction(UI.PixmapCache.getIcon("virustotal.png"), 1006 menu.addAction(UI.PixmapCache.getIcon("virustotal.png"),
952 self.trUtf8("Scan Image with VirusTotal"), self.__virusTotal)\ 1007 self.trUtf8("Scan Image with VirusTotal"), self.__virusTotal)\
953 .setData(hit.imageUrl()) 1008 .setData(hit.imageUrl())
954 1009
955 element = hit.element() 1010 element = hit.element()
956 if not element.isNull() and \ 1011 if not element.isNull():
957 element.tagName().lower() in ["input", "textarea", "video", "audio"]: 1012 if self.__isMediaElement(element):
958 if menu.isEmpty(): 1013 if not menu.isEmpty():
959 self.page().createStandardContextMenu().exec_(evt.globalPos()) 1014 menu.addSeparator()
960 return 1015
1016 self.__clickedMediaElement = element
1017
1018 paused = element.evaluateJavaScript("this.paused")
1019 muted = element.evaluateJavaScript("this.muted")
1020 videoUrl = QUrl(element.evaluateJavaScript("this.currentSrc"))
1021
1022 if paused:
1023 menu.addAction(UI.PixmapCache.getIcon("mediaPlaybackStart.png"),
1024 self.trUtf8("Play"), self.__pauseMedia)
1025 else:
1026 menu.addAction(UI.PixmapCache.getIcon("mediaPlaybackPause.png"),
1027 self.trUtf8("Pause"), self.__pauseMedia)
1028 if muted:
1029 menu.addAction(UI.PixmapCache.getIcon("audioVolumeHigh.png"),
1030 self.trUtf8("Unmute"), self.__muteMedia)
1031 else:
1032 menu.addAction(UI.PixmapCache.getIcon("audioVolumeMuted.png"),
1033 self.trUtf8("Mute"), self.__muteMedia)
1034 menu.addSeparator()
1035 menu.addAction(UI.PixmapCache.getIcon("editCopy.png"),
1036 self.trUtf8("Copy Media Address to Clipboard"),
1037 self.__copyLocation).setData(videoUrl.toString())
1038 menu.addAction(UI.PixmapCache.getIcon("mailSend.png"),
1039 self.trUtf8("Send Media Address"), self.__sendLink)\
1040 .setData(videoUrl)
1041 menu.addAction(UI.PixmapCache.getIcon("download.png"),
1042 self.trUtf8("Save Media"), self.__downloadMedia)\
1043 .setData(videoUrl)
1044
1045 if element.tagName().lower() in ["input", "textarea"]:
1046 if menu.isEmpty():
1047 self.page().createStandardContextMenu().exec_(evt.globalPos())
1048 return
961 1049
962 if not menu.isEmpty(): 1050 if not menu.isEmpty():
963 menu.addSeparator() 1051 menu.addSeparator()
964 menu.addAction(self.mw.newTabAct) 1052 menu.addAction(self.mw.newTabAct)
965 menu.addAction(self.mw.newAct) 1053 menu.addAction(self.mw.newAct)
1145 """ 1233 """
1146 Private slot to copy an image to the clipboard. 1234 Private slot to copy an image to the clipboard.
1147 """ 1235 """
1148 self.pageAction(QWebPage.CopyImageToClipboard).trigger() 1236 self.pageAction(QWebPage.CopyImageToClipboard).trigger()
1149 1237
1150 def __copyImageLocation(self): 1238 def __copyLocation(self):
1151 """ 1239 """
1152 Private slot to copy an image location to the clipboard. 1240 Private slot to copy an image or media location to the clipboard.
1153 """ 1241 """
1154 act = self.sender() 1242 act = self.sender()
1155 url = act.data() 1243 url = act.data()
1156 QApplication.clipboard().setText(url) 1244 QApplication.clipboard().setText(url)
1157 1245
1161 """ 1249 """
1162 act = self.sender() 1250 act = self.sender()
1163 url = act.data() 1251 url = act.data()
1164 dlg = Helpviewer.HelpWindow.HelpWindow.adblockManager().showDialog() 1252 dlg = Helpviewer.HelpWindow.HelpWindow.adblockManager().showDialog()
1165 dlg.addCustomRule(url) 1253 dlg.addCustomRule(url)
1254
1255 def __downloadMedia(self):
1256 """
1257 Private slot to download a media and save it to disk.
1258 """
1259 act = self.sender()
1260 url = act.data()
1261 self.mw.downloadManager().download(url, True, mainWindow=self.mw)
1262
1263 def __pauseMedia(self):
1264 """
1265 Private slot to pause or play the selected media.
1266 """
1267 paused = self.__clickedMediaElement.evaluateJavaScript("this.paused")
1268
1269 if paused:
1270 self.__clickedMediaElement.evaluateJavaScript("this.play()")
1271 else:
1272 self.__clickedMediaElement.evaluateJavaScript("this.pause()")
1273
1274 def __muteMedia(self):
1275 """
1276 Private slot to (un)mute the selected media.
1277 """
1278 muted = self.__clickedMediaElement.evaluateJavaScript("this.muted")
1279
1280 if muted:
1281 self.__clickedMediaElement.evaluateJavaScript("this.muted = false")
1282 else:
1283 self.__clickedMediaElement.evaluateJavaScript("this.muted = true")
1166 1284
1167 def __virusTotal(self): 1285 def __virusTotal(self):
1168 """ 1286 """
1169 Private slot to scan the selected URL with VirusTotal. 1287 Private slot to scan the selected URL with VirusTotal.
1170 """ 1288 """

eric ide

mercurial