Peter Surda
976af4b3cd
MessageView does not currently load external resources (QTextBrowser by default interprets all external resources as local file names and tries to load them like that. This can, in the future, be implemented. For example, if SOCKS (Tor) is used, the resource could be loaded through the SOCKS too. This commit is a skeleton for it that does not actually do anything and can be filled with an implementation that does the loading.
124 lines
5.6 KiB
Python
124 lines
5.6 KiB
Python
from PyQt4 import QtCore, QtGui
|
|
|
|
from urlparse import urlparse
|
|
from safehtmlparser import *
|
|
|
|
class MessageView(QtGui.QTextBrowser):
|
|
MODE_PLAIN = 0
|
|
MODE_HTML = 1
|
|
TEXT_PLAIN = "HTML detected, click here to display"
|
|
TEXT_HTML = "Click here to disable HTML"
|
|
CONFIRM_TITLE = "Follow external link"
|
|
CONFIRM_TEXT = "The link \"%1\" will open in a browser. It may be a security risk, it could de-anonymise you or download malicious data. Are you sure?"
|
|
|
|
def __init__(self, parent = 0):
|
|
super(MessageView, self).__init__(parent)
|
|
self.mode = MessageView.MODE_PLAIN
|
|
self.html = None
|
|
self.setOpenExternalLinks(False)
|
|
self.setOpenLinks(False)
|
|
self.anchorClicked.connect(self.confirmURL)
|
|
self.out = ""
|
|
self.outpos = 0
|
|
self.document().setUndoRedoEnabled(False)
|
|
self.rendering = False
|
|
self.defaultFontPointSize = self.currentFont().pointSize()
|
|
self.verticalScrollBar().valueChanged.connect(self.lazyRender)
|
|
|
|
def mousePressEvent(self, event):
|
|
#text = textCursor.block().text()
|
|
if event.button() == QtCore.Qt.LeftButton and self.html and self.html.has_html and self.cursorForPosition(event.pos()).block().blockNumber() == 0:
|
|
if self.mode == MessageView.MODE_PLAIN:
|
|
self.showHTML()
|
|
else:
|
|
self.showPlain()
|
|
else:
|
|
super(MessageView, self).mousePressEvent(event)
|
|
|
|
def wheelEvent(self, event):
|
|
# super will actually automatically take care of zooming
|
|
super(MessageView, self).wheelEvent(event)
|
|
if (QtGui.QApplication.queryKeyboardModifiers() & QtCore.Qt.ControlModifier) == QtCore.Qt.ControlModifier and event.orientation() == QtCore.Qt.Vertical:
|
|
zoom = self.currentFont().pointSize() * 100 / self.defaultFontPointSize
|
|
QtGui.QApplication.activeWindow().statusBar().showMessage(QtGui.QApplication.translate("MainWindow", "Zoom level %1%").arg(str(zoom)))
|
|
|
|
def confirmURL(self, link):
|
|
if link.scheme() == "mailto":
|
|
QtGui.QApplication.activeWindow().ui.lineEditTo.setText(link.path())
|
|
if link.hasQueryItem("subject"):
|
|
QtGui.QApplication.activeWindow().ui.lineEditSubject.setText(link.queryItemValue("subject"))
|
|
if link.hasQueryItem("body"):
|
|
QtGui.QApplication.activeWindow().ui.textEditMessage.setText(link.queryItemValue("body"))
|
|
QtGui.QApplication.activeWindow().ui.tabWidgetSend.setCurrentIndex(0)
|
|
QtGui.QApplication.activeWindow().ui.tabWidget.setCurrentIndex(1)
|
|
QtGui.QApplication.activeWindow().ui.textEditMessage.setFocus()
|
|
return
|
|
reply = QtGui.QMessageBox.warning(self,
|
|
QtGui.QApplication.translate(type(self).__name__, MessageView.CONFIRM_TITLE),
|
|
QtGui.QApplication.translate(type(self).__name__, MessageView.CONFIRM_TEXT).arg(str(link.toString())),
|
|
QtGui.QMessageBox.Yes, QtGui.QMessageBox.No)
|
|
if reply == QtGui.QMessageBox.Yes:
|
|
QtGui.QDesktopServices.openUrl(link)
|
|
|
|
def loadResource (restype, name):
|
|
if restype == QtGui.QTextDocument.ImageResource and name.scheme() == "bmmsg":
|
|
pass
|
|
# QImage correctImage;
|
|
# lookup the correct QImage from a cache
|
|
# return QVariant::fromValue(correctImage);
|
|
# elif restype == QtGui.QTextDocument.HtmlResource:
|
|
# elif restype == QtGui.QTextDocument.ImageResource:
|
|
# elif restype == QtGui.QTextDocument.StyleSheetResource:
|
|
# elif restype == QtGui.QTextDocument.UserResource:
|
|
else:
|
|
pass
|
|
# by default, this will interpret it as a local file
|
|
# QtGui.QTextBrowser.loadResource(restype, name)
|
|
|
|
def lazyRender(self):
|
|
if self.rendering:
|
|
return
|
|
self.rendering = True
|
|
position = self.verticalScrollBar().value()
|
|
cursor = QtGui.QTextCursor(self.document())
|
|
while self.outpos < len(self.out) and self.verticalScrollBar().value() >= self.document().size().height() - 2 * self.size().height():
|
|
startpos = self.outpos
|
|
self.outpos += 10240
|
|
# find next end of tag
|
|
if self.mode == MessageView.MODE_HTML:
|
|
pos = self.out.find(">", self.outpos)
|
|
if pos > self.outpos:
|
|
self.outpos = pos
|
|
cursor.movePosition(QtGui.QTextCursor.End, QtGui.QTextCursor.MoveAnchor)
|
|
cursor.insertHtml(QtCore.QString(self.out[startpos:self.outpos]))
|
|
self.verticalScrollBar().setValue(position)
|
|
self.rendering = False
|
|
|
|
def showPlain(self):
|
|
self.mode = MessageView.MODE_PLAIN
|
|
out = self.html.raw
|
|
if self.html.has_html:
|
|
out = "<div align=\"center\" style=\"text-decoration: underline;\"><b>" + str(QtGui.QApplication.translate(type(self).__name__, MessageView.TEXT_PLAIN)) + "</b></div><br/>" + out
|
|
self.out = out
|
|
self.outpos = 0
|
|
self.setHtml("")
|
|
self.lazyRender()
|
|
|
|
def showHTML(self):
|
|
self.mode = MessageView.MODE_HTML
|
|
out = self.html.sanitised
|
|
out = "<div align=\"center\" style=\"text-decoration: underline;\"><b>" + str(QtGui.QApplication.translate(type(self).__name__, MessageView.TEXT_HTML)) + "</b></div><br/>" + out
|
|
self.out = out
|
|
self.outpos = 0
|
|
self.setHtml("")
|
|
self.lazyRender()
|
|
|
|
def setContent(self, data):
|
|
self.html = SafeHTMLParser()
|
|
self.html.reset()
|
|
self.html.reset_safe()
|
|
self.html.allow_picture = True
|
|
self.html.feed(data)
|
|
self.html.close()
|
|
self.showPlain()
|