def __init__(self, parent=None): """ Constructor @param parent reference to the parent object (QObject) """ super(AdBlockManager, self).__init__(parent) self.__loaded = False self.__subscriptionsLoaded = False self.__enabled = False self.__adBlockDialog = None self.__adBlockExceptionsDialog = None self.__adBlockNetwork = None self.__adBlockPage = None self.__subscriptions = [] self.__exceptedHosts = Preferences.getHelp("AdBlockExceptions") self.__saveTimer = AutoSaver(self, self.save) self.__defaultSubscriptionUrlString = \ "abp:subscribe?location=" \ "https://easylist-downloads.adblockplus.org/easylist.txt&"\ "title=EasyList" self.__customSubscriptionUrlString = \ bytes(self.__customSubscriptionUrl().toEncoded()).decode() self.rulesChanged.connect(self.__saveTimer.changeOccurred)
def __init__(self, parent=None): """ Constructor @param parent reference to the parent object (QObject) """ super(CookieJar, self).__init__(parent) self.__loaded = False self.__acceptCookies = self.AcceptOnlyFromSitesNavigatedTo self.__saveTimer = AutoSaver(self, self.__save) self.__cookiesFile = os.path.join(Utilities.getConfigDir(), "web_browser", "cookies.ini") self.__store = WebBrowserWindow.webProfile().cookieStore() try: self.__store.setCookieFilter(self.__cookieFilter) except AttributeError: # pre Qt 5.11 pass self.__store.cookieAdded.connect(self.__cookieAdded) self.__store.cookieRemoved.connect(self.__cookieRemoved) self.__load() self.__store.loadAllCookies()
def __init__(self, parent=None): """ Constructor @param parent reference to the parent object (QObject) """ super(PasswordManager, self).__init__(parent) # setup userscript to monitor forms script = QWebEngineScript() script.setName("_eric_passwordmonitor") script.setInjectionPoint(QWebEngineScript.DocumentReady) script.setWorldId(WebBrowserPage.SafeJsWorld) script.setRunsOnSubFrames(True) script.setSourceCode(Scripts.setupFormObserver()) profile = WebBrowser.WebBrowserWindow.WebBrowserWindow.webProfile() profile.scripts().insert(script) self.__logins = {} self.__loginForms = {} self.__never = [] self.__loaded = False self.__saveTimer = AutoSaver(self, self.save) self.changed.connect(self.__saveTimer.changeOccurred)
def __init__(self, parent=None): """ Constructor @param parent reference to the parent widget (QWidget) """ super(DownloadManager, self).__init__(parent) self.setupUi(self) self.setWindowFlags(Qt.Window) self.__saveTimer = AutoSaver(self, self.save) self.__model = DownloadModel(self) self.__manager = Helpviewer.HelpWindow.HelpWindow\ .networkAccessManager() self.__iconProvider = None self.__downloads = [] self.__downloadDirectory = "" self.__loaded = False self.setDownloadDirectory(Preferences.getUI("DownloadPath")) self.downloadsView.setShowGrid(False) self.downloadsView.verticalHeader().hide() self.downloadsView.horizontalHeader().hide() self.downloadsView.setAlternatingRowColors(True) self.downloadsView.horizontalHeader().setStretchLastSection(True) self.downloadsView.setModel(self.__model) self.downloadsView.setContextMenuPolicy(Qt.CustomContextMenu) self.downloadsView.customContextMenuRequested.connect( self.__customContextMenuRequested) self.__load()
def __init__(self, engine, parent=None): """ Constructor @param engine reference to the help engine (QHelpEngine) @param parent reference to the parent object (QObject) """ super(NetworkManager, self).__init__(parent) from E5Network.E5NetworkProxyFactory import E5NetworkProxyFactory self.__proxyFactory = E5NetworkProxyFactory() if Preferences.getUI("UseSystemProxy"): QNetworkProxyFactory.setUseSystemConfiguration(True) else: QNetworkProxyFactory.setApplicationProxyFactory( self.__proxyFactory) QNetworkProxyFactory.setUseSystemConfiguration(False) self.languagesChanged() if SSL_AVAILABLE: self.__sslErrorHandler = E5SslErrorHandler(self) self.sslErrors.connect(self.__sslErrorHandler.sslErrorsReplySlot) self.__temporarilyIgnoredSslErrors = {} self.__permanentlyIgnoredSslErrors = {} # dictionaries of permanently and temporarily ignored SSL errors self.__loaded = False self.__saveTimer = AutoSaver(self, self.__save) self.changed.connect(self.__saveTimer.changeOccurred) self.proxyAuthenticationRequired.connect(proxyAuthenticationRequired) self.authenticationRequired.connect( lambda reply, auth: self.authentication(reply.url(), auth)) from .EricSchemeHandler import EricSchemeHandler self.__ericSchemeHandler = EricSchemeHandler() WebBrowserWindow.webProfile().installUrlSchemeHandler( QByteArray(b"eric"), self.__ericSchemeHandler) if engine: from .QtHelpSchemeHandler import QtHelpSchemeHandler self.__qtHelpSchemeHandler = QtHelpSchemeHandler(engine) WebBrowserWindow.webProfile().installUrlSchemeHandler( QByteArray(b"qthelp"), self.__qtHelpSchemeHandler) self.__interceptor = NetworkUrlInterceptor(self) try: WebBrowserWindow.webProfile().setUrlRequestInterceptor( self.__interceptor) except AttributeError: # Qt < 5.13 WebBrowserWindow.webProfile().setRequestInterceptor( self.__interceptor) WebBrowserWindow.cookieJar()
def __init__(self, parent=None): """ Constructor @param parent reference to the parent object (QObject) """ super(BookmarksManager, self).__init__(parent) self.__saveTimer = AutoSaver(self, self.save) self.entryAdded.connect(self.__saveTimer.changeOccurred) self.entryRemoved.connect(self.__saveTimer.changeOccurred) self.entryChanged.connect(self.__saveTimer.changeOccurred) self.__initialize()
def __init__(self, parent=None): """ Constructor @param parent reference to the parent object (QObject) """ super(UserAgentManager, self).__init__(parent) self.__agents = {} # dictionary with agent strings indexed by host name self.__loaded = False self.__saveTimer = AutoSaver(self, self.save) self.changed.connect(self.__saveTimer.changeOccurred)
def __init__(self, parent=None): """ Constructor @param parent reference to the parent object (QObject) """ super(CookieJar, self).__init__(parent) self.__loaded = False self.__acceptCookies = self.AcceptOnlyFromSitesNavigatedTo self.__saveTimer = AutoSaver(self, self.save) self.__cookiesFile = os.path.join(Utilities.getConfigDir(), "browser", "cookies.ini")
def __init__(self, parent=None): """ Constructor @param parent reference to the parent object (QObject) """ super(PasswordManager, self).__init__(parent) self.__logins = {} self.__loginForms = {} self.__never = [] self.__loaded = False self.__saveTimer = AutoSaver(self, self.save) self.changed.connect(self.__saveTimer.changeOccurred)
def __init__(self, parent=None): """ Constructor @param parent reference to the parent object (QObject) """ super(ZoomManager, self).__init__(parent) self.__zoomDB = {} self.__saveTimer = AutoSaver(self, self.save) self.changed.connect(self.__saveTimer.changeOccurred) self.__loaded = False
def __init__(self, parent=None): """ Constructor @param parent reference to the parent widget (QWidget) """ super(DownloadManager, self).__init__(parent) self.setupUi(self) self.__saveTimer = AutoSaver(self, self.save) self.__model = DownloadModel(self) self.__manager = Helpviewer.HelpWindow.HelpWindow\ .networkAccessManager() self.__iconProvider = None self.__downloads = [] self.__downloadDirectory = "" self.__loaded = False self.setDownloadDirectory(Preferences.getUI("DownloadPath")) self.downloadsView.setShowGrid(False) self.downloadsView.verticalHeader().hide() self.downloadsView.horizontalHeader().hide() self.downloadsView.setAlternatingRowColors(True) self.downloadsView.horizontalHeader().setStretchLastSection(True) self.downloadsView.setModel(self.__model) self.downloadsView.setContextMenuPolicy(Qt.CustomContextMenu) self.downloadsView.customContextMenuRequested.connect( self.__customContextMenuRequested) self.__load()
def __init__(self, parent=None): """ Constructor @param parent reference to the parent object (QObject) """ super(IrcNetworkManager, self).__init__(parent) self.__loaded = False self.__saveTimer = AutoSaver(self, self.save) self.__settings = Preferences.Prefs.settings self.__networks = {} self.__identities = {} self.dataChanged.connect(self.__saveTimer.changeOccurred)
def __init__(self, parent=None): """ Constructor @param parent reference to the parent object (QObject) """ super(WebIconProvider, self).__init__(parent) self.__encoding = "iso-8859-1" self.__iconsFileName = "web_site_icons.json" self.__iconDatabasePath = "" # saving of icons disabled self.__iconsDB = {} self.__loaded = False self.__saveTimer = AutoSaver(self, self.save) self.changed.connect(self.__saveTimer.changeOccurred)
def __init__(self, parent=None): """ Constructor @param parent reference to the parent object @type QObject """ super(AdBlockManager, self).__init__(parent) self.__loaded = False self.__subscriptionsLoaded = False self.__enabled = False self.__adBlockDialog = None self.__adBlockExceptionsDialog = None self.__adBlockNetwork = None self.__adBlockPage = None self.__subscriptions = [] self.__exceptedHosts = Preferences.getWebBrowser("AdBlockExceptions") self.__saveTimer = AutoSaver(self, self.save) self.__limitedEasyList = Preferences.getWebBrowser( "AdBlockUseLimitedEasyList") self.__defaultSubscriptionUrlString = ( "abp:subscribe?location=" "https://easylist-downloads.adblockplus.org/easylist.txt&" "title=EasyList") self.__additionalDefaultSubscriptionUrlStrings = ( "abp:subscribe?location=https://raw.githubusercontent.com/" "hoshsadiq/adblock-nocoin-list/master/nocoin.txt&" "title=NoCoin", ) self.__customSubscriptionUrlString = (bytes( self.__customSubscriptionUrl().toEncoded()).decode()) self.__mutex = QMutex() self.__matcher = AdBlockMatcher(self) self.rulesChanged.connect(self.__saveTimer.changeOccurred) self.rulesChanged.connect(self.__rulesChanged) self.__interceptor = AdBlockUrlInterceptor(self) from WebBrowser.WebBrowserWindow import WebBrowserWindow WebBrowserWindow.networkManager().installUrlInterceptor( self.__interceptor)
def __init__(self, parent=None): """ Constructor @param parent reference to the parent widget (QWidget) """ super(DownloadManager, self).__init__(parent) self.setupUi(self) self.setWindowFlags(Qt.Window) self.__winTaskbarButton = None self.__saveTimer = AutoSaver(self, self.save) self.__model = DownloadModel(self) self.__manager = WebBrowserWindow.networkManager() self.__iconProvider = None self.__downloads = [] self.__downloadDirectory = "" self.__loaded = False self.__rowHeightMultiplier = 1.1 self.setDownloadDirectory(Preferences.getUI("DownloadPath")) self.downloadsView.setShowGrid(False) self.downloadsView.verticalHeader().hide() self.downloadsView.horizontalHeader().hide() self.downloadsView.setAlternatingRowColors(True) self.downloadsView.horizontalHeader().setStretchLastSection(True) self.downloadsView.setModel(self.__model) self.downloadsView.setContextMenuPolicy(Qt.CustomContextMenu) self.downloadsView.customContextMenuRequested.connect( self.__customContextMenuRequested) self.__clearShortcut = QShortcut(QKeySequence("Ctrl+L"), self) self.__clearShortcut.activated.connect(self.on_cleanupButton_clicked) self.__load() self.__updateTimer = QBasicTimer()
def __init__(self, parent=None): """ Constructor @param parent reference to the parent object (QObject) """ if parent is None: parent = e5App() super(OpenSearchManager, self).__init__(parent) self.__replies = [] self.__engines = {} self.__keywords = {} self.__current = "" self.__loading = False self.__saveTimer = AutoSaver(self, self.save) self.changed.connect(self.__saveTimer.changeOccurred) self.load()
def __init__(self, parent=None): """ Constructor @param parent reference to the parent object (QObject) """ super(SpeedDial, self).__init__(parent) self.__regenerateScript = True self.__webPages = [] self.__initialScript = "" self.__thumbnailsDirectory = "" self.__thumbnailers = [] self.__initialize() self.__saveTimer = AutoSaver(self, self.save) self.pagesChanged.connect(self.__saveTimer.changeOccurred)
def __init__(self, parent=None): """ Constructor @param parent reference to the parent object (QObject) """ super(HistoryManager, self).__init__(parent) self.__saveTimer = AutoSaver(self, self.save) self.__daysToExpire = Preferences.getHelp("HistoryLimit") self.__history = [] self.__lastSavedUrl = "" self.__expiredTimer = QTimer(self) self.__expiredTimer.setSingleShot(True) self.__expiredTimer.timeout.connect(self.__checkForExpired) self.__frequencyTimer = QTimer(self) self.__frequencyTimer.setSingleShot(True) self.__frequencyTimer.timeout.connect(self.__refreshFrequencies) self.entryAdded.connect(self.__saveTimer.changeOccurred) self.entryRemoved.connect(self.__saveTimer.changeOccurred) self.__load() from .HistoryModel import HistoryModel from .HistoryFilterModel import HistoryFilterModel from .HistoryTreeModel import HistoryTreeModel self.__historyModel = HistoryModel(self, self) self.__historyFilterModel = \ HistoryFilterModel(self.__historyModel, self) self.__historyTreeModel = \ HistoryTreeModel(self.__historyFilterModel, self) super(HistoryManager, self).setDefaultInterface(self) self.__startFrequencyTimer()
def __init__(self, parent = None): """ Constructor @param parent reference to the parent object (QObject) """ QNetworkCookieJar.__init__(self, parent) self.__loaded = False self.__acceptCookies = self.AcceptOnlyFromSitesNavigatedTo self.__saveTimer = AutoSaver(self, self.save) self.__cookiesFile = os.path.join(Utilities.getConfigDir(), "browser", "cookies.ini")
def __init__(self, parent = None): """ Constructor @param parent reference to the parent object (QObject) """ QObject.__init__(self, parent) self.__logins = {} self.__loginForms = {} self.__never = [] self.__loaded = False self.__saveTimer = AutoSaver(self, self.save) self.connect(self, SIGNAL("changed()"), self.__saveTimer.changeOccurred)
def __init__(self, parent = None): """ Constructor @param parent reference to the parent object (QObject) """ QObject.__init__(self, parent) self.__loaded = False self.__subscriptionsLoaded = False self.__enabled = False self.__adBlockDialog = None self.__adBlockNetwork = None self.__adBlockPage = None self.__subscriptions = [] self.__saveTimer = AutoSaver(self, self.save) self.connect(self, SIGNAL("rulesChanged()"), self.__saveTimer.changeOccurred)
def __init__(self, parent=None): """ Constructor @param parent reference to the parent object (QObject) """ if parent is None: parent = e4App() QObject.__init__(self, parent) self.__replies = [] self.__engines = {} self.__keywords = {} self.__current = QString() self.__loading = False self.__saveTimer = AutoSaver(self, self.save) self.connect(self, SIGNAL("changed()"), self.__saveTimer.changeOccurred) self.load()
def __init__(self, parent = None): """ Constructor @param parent reference to the parent object (QObject) """ QObject.__init__(self, parent) self.__loaded = False self.__saveTimer = AutoSaver(self, self.save) self.__bookmarkRootNode = None self.__toolbar = None self.__menu = None self.__bookmarksModel = None self.__commands = QUndoStack() self.connect(self, SIGNAL("entryAdded"), self.__saveTimer.changeOccurred) self.connect(self, SIGNAL("entryRemoved"), self.__saveTimer.changeOccurred) self.connect(self, SIGNAL("entryChanged"), self.__saveTimer.changeOccurred)
def __init__(self, parent=None): """ Constructor @param parent reference to the parent object (QObject) """ super(SpeedDial, self).__init__(parent) self.__regenerateScript = True self.__webPages = [] self.__webFrames = [] self.__initialScript = "" self.__thumbnailsDirectory = "" self.__thumbnailers = [] self.__initialize() self.pagesChanged.connect(self.__pagesChanged) self.__saveTimer = AutoSaver(self, self.save) self.pagesChanged.connect(self.__saveTimer.changeOccurred)
def __init__(self, parent = None): """ Constructor @param parent reference to the parent object (QObject) """ QWebHistoryInterface.__init__(self, parent) self.__saveTimer = AutoSaver(self, self.save) self.__daysToExpire = Preferences.getHelp("HistoryLimit") self.__history = [] self.__lastSavedUrl = QString() self.__expiredTimer = QTimer() self.__expiredTimer.setSingleShot(True) self.connect(self.__expiredTimer, SIGNAL("timeout()"), self.__checkForExpired) self.__frequencyTimer = QTimer() self.__frequencyTimer.setSingleShot(True) self.connect(self.__frequencyTimer, SIGNAL("timeout()"), self.__refreshFrequencies) self.connect(self, SIGNAL("entryAdded"), self.__saveTimer.changeOccurred) self.connect(self, SIGNAL("entryRemoved"), self.__saveTimer.changeOccurred) self.__load() self.__historyModel = HistoryModel(self, self) self.__historyFilterModel = HistoryFilterModel(self.__historyModel, self) self.__historyTreeModel = HistoryTreeModel(self.__historyFilterModel, self) QWebHistoryInterface.setDefaultInterface(self) self.__startFrequencyTimer()
class UserAgentManager(QObject): """ Class implementing a user agent manager. @signal changed() emitted to indicate a change @signal userAgentSettingsSaved() emitted after the user agent settings were saved """ changed = pyqtSignal() userAgentSettingsSaved = pyqtSignal() def __init__(self, parent=None): """ Constructor @param parent reference to the parent object (QObject) """ super(UserAgentManager, self).__init__(parent) self.__agents = {} # dictionary with agent strings indexed by host name self.__loaded = False self.__saveTimer = AutoSaver(self, self.save) self.changed.connect(self.__saveTimer.changeOccurred) def getFileName(self): """ Public method to get the file name of the user agents file. @return name of the user agents file (string) """ return os.path.join(Utilities.getConfigDir(), "web_browser", "userAgentSettings.xml") def save(self): """ Public slot to save the user agent entries to disk. """ if not self.__loaded: return from .UserAgentWriter import UserAgentWriter agentFile = self.getFileName() writer = UserAgentWriter() if not writer.write(agentFile, self.__agents): E5MessageBox.critical( None, self.tr("Saving user agent data"), self.tr("""<p>User agent data could not be saved to""" """ <b>{0}</b></p>""").format(agentFile)) else: self.userAgentSettingsSaved.emit() def __load(self): """ Private method to load the saved user agent settings. """ agentFile = self.getFileName() from .UserAgentReader import UserAgentReader reader = UserAgentReader() self.__agents = reader.read(agentFile) if reader.error() != QXmlStreamReader.NoError: E5MessageBox.warning( None, self.tr("Loading user agent data"), self.tr("""Error when loading user agent data on""" """ line {0}, column {1}:\n{2}""").format( reader.lineNumber(), reader.columnNumber(), reader.errorString())) self.__loaded = True def reload(self): """ Public method to reload the user agent settings. """ if not self.__loaded: return self.__agents = {} self.__load() def close(self): """ Public method to close the user agents manager. """ self.__saveTimer.saveIfNeccessary() def removeUserAgent(self, host): """ Public method to remove a user agent entry. @param host host name (string) """ if host in self.__agents: del self.__agents[host] self.changed.emit() def allHostNames(self): """ Public method to get a list of all host names we a user agent setting for. @return sorted list of all host names (list of strings) """ if not self.__loaded: self.__load() return sorted(self.__agents.keys()) def hostsCount(self): """ Public method to get the number of available user agent settings. @return number of user agent settings (integer) """ if not self.__loaded: self.__load() return len(self.__agents) def userAgent(self, host): """ Public method to get the user agent setting for a host. @param host host name (string) @return user agent string (string) """ if not self.__loaded: self.__load() for agentHost in self.__agents: if host.endswith(agentHost): return self.__agents[agentHost] return "" def setUserAgent(self, host, agent): """ Public method to set the user agent string for a host. @param host host name (string) @param agent user agent string (string) """ if host != "" and agent != "": self.__agents[host] = agent self.changed.emit() def userAgentForUrl(self, url): """ Public method to determine the user agent for the given URL. @param url URL to determine user agent for (QUrl) @return user agent string (string) """ if url.isValid(): host = url.host() return self.userAgent(host) return "" def setUserAgentForUrl(self, url, agent): """ Public method to set the user agent string for an URL. @param url URL to register user agent setting for (QUrl) @param agent new current user agent string (string) """ if url.isValid(): host = url.host() self.setUserAgent(host, agent)
class BookmarksManager(QObject): """ Class implementing the bookmarks manager. @signal entryAdded(BookmarkNode) emitted after a bookmark node has been added @signal entryRemoved(BookmarkNode, int, BookmarkNode) emitted after a bookmark node has been removed @signal entryChanged(BookmarkNode) emitted after a bookmark node has been changed @signal bookmarksSaved() emitted after the bookmarks were saved @signal bookmarksReloaded() emitted after the bookmarks were reloaded """ entryAdded = pyqtSignal(BookmarkNode) entryRemoved = pyqtSignal(BookmarkNode, int, BookmarkNode) entryChanged = pyqtSignal(BookmarkNode) bookmarksSaved = pyqtSignal() bookmarksReloaded = pyqtSignal() def __init__(self, parent=None): """ Constructor @param parent reference to the parent object (QObject) """ super(BookmarksManager, self).__init__(parent) self.__saveTimer = AutoSaver(self, self.save) self.entryAdded.connect(self.__saveTimer.changeOccurred) self.entryRemoved.connect(self.__saveTimer.changeOccurred) self.entryChanged.connect(self.__saveTimer.changeOccurred) self.__initialize() def __initialize(self): """ Private method to initialize some data. """ self.__loaded = False self.__bookmarkRootNode = None self.__toolbar = None self.__menu = None self.__bookmarksModel = None self.__commands = QUndoStack() @classmethod def getFileName(cls): """ Class method to get the file name of the bookmark file. @return name of the bookmark file (string) """ return os.path.join(Utilities.getConfigDir(), "browser", "bookmarks.xbel") def close(self): """ Public method to close the bookmark manager. """ self.__saveTimer.saveIfNeccessary() def undoRedoStack(self): """ Public method to get a reference to the undo stack. @return reference to the undo stack (QUndoStack) """ return self.__commands def changeExpanded(self): """ Public method to handle a change of the expanded state. """ self.__saveTimer.changeOccurred() def reload(self): """ Public method used to initiate a reloading of the bookmarks. """ self.__initialize() self.load() self.bookmarksReloaded.emit() def load(self): """ Public method to load the bookmarks. @exception RuntimeError raised to indicate an error loading the bookmarks """ if self.__loaded: return self.__loaded = True bookmarkFile = self.getFileName() if not QFile.exists(bookmarkFile): from . import DefaultBookmarks_rc # __IGNORE_WARNING__ bookmarkFile = QFile(":/DefaultBookmarks.xbel") bookmarkFile.open(QIODevice.ReadOnly) from .XbelReader import XbelReader reader = XbelReader() self.__bookmarkRootNode = reader.read(bookmarkFile) if reader.error() != QXmlStreamReader.NoError: E5MessageBox.warning( None, self.tr("Loading Bookmarks"), self.tr( """Error when loading bookmarks on line {0},""" """ column {1}:\n {2}""") .format(reader.lineNumber(), reader.columnNumber(), reader.errorString())) others = [] for index in range( len(self.__bookmarkRootNode.children()) - 1, -1, -1): node = self.__bookmarkRootNode.children()[index] if node.type() == BookmarkNode.Folder: if (node.title == self.tr("Toolbar Bookmarks") or node.title == BOOKMARKBAR) and \ self.__toolbar is None: node.title = self.tr(BOOKMARKBAR) self.__toolbar = node if (node.title == self.tr("Menu") or node.title == BOOKMARKMENU) and \ self.__menu is None: node.title = self.tr(BOOKMARKMENU) self.__menu = node else: others.append(node) self.__bookmarkRootNode.remove(node) if len(self.__bookmarkRootNode.children()) > 0: raise RuntimeError("Error loading bookmarks.") if self.__toolbar is None: self.__toolbar = BookmarkNode(BookmarkNode.Folder, self.__bookmarkRootNode) self.__toolbar.title = self.tr(BOOKMARKBAR) else: self.__bookmarkRootNode.add(self.__toolbar) if self.__menu is None: self.__menu = BookmarkNode(BookmarkNode.Folder, self.__bookmarkRootNode) self.__menu.title = self.tr(BOOKMARKMENU) else: self.__bookmarkRootNode.add(self.__menu) for node in others: self.__menu.add(node) self.__convertFromOldBookmarks() def save(self): """ Public method to save the bookmarks. """ if not self.__loaded: return from .XbelWriter import XbelWriter writer = XbelWriter() bookmarkFile = self.getFileName() # save root folder titles in English (i.e. not localized) self.__menu.title = BOOKMARKMENU self.__toolbar.title = BOOKMARKBAR if not writer.write(bookmarkFile, self.__bookmarkRootNode): E5MessageBox.warning( None, self.tr("Saving Bookmarks"), self.tr("""Error saving bookmarks to <b>{0}</b>.""") .format(bookmarkFile)) # restore localized titles self.__menu.title = self.tr(BOOKMARKMENU) self.__toolbar.title = self.tr(BOOKMARKBAR) self.bookmarksSaved.emit() def addBookmark(self, parent, node, row=-1): """ Public method to add a bookmark. @param parent reference to the node to add to (BookmarkNode) @param node reference to the node to add (BookmarkNode) @param row row number (integer) """ if not self.__loaded: return self.setTimestamp(node, BookmarkNode.TsAdded, QDateTime.currentDateTime()) command = InsertBookmarksCommand(self, parent, node, row) self.__commands.push(command) def removeBookmark(self, node): """ Public method to remove a bookmark. @param node reference to the node to be removed (BookmarkNode) """ if not self.__loaded: return parent = node.parent() row = parent.children().index(node) command = RemoveBookmarksCommand(self, parent, row) self.__commands.push(command) def setTitle(self, node, newTitle): """ Public method to set the title of a bookmark. @param node reference to the node to be changed (BookmarkNode) @param newTitle title to be set (string) """ if not self.__loaded: return command = ChangeBookmarkCommand(self, node, newTitle, True) self.__commands.push(command) def setUrl(self, node, newUrl): """ Public method to set the URL of a bookmark. @param node reference to the node to be changed (BookmarkNode) @param newUrl URL to be set (string) """ if not self.__loaded: return command = ChangeBookmarkCommand(self, node, newUrl, False) self.__commands.push(command) def setNodeChanged(self, node): """ Public method to signal changes of bookmarks other than title, URL or timestamp. @param node reference to the bookmark (BookmarkNode) """ self.__saveTimer.changeOccurred() def setTimestamp(self, node, timestampType, timestamp): """ Public method to set the URL of a bookmark. @param node reference to the node to be changed (BookmarkNode) @param timestampType type of the timestamp to set (BookmarkNode.TsAdded, BookmarkNode.TsModified, BookmarkNode.TsVisited) @param timestamp timestamp to set (QDateTime) """ if not self.__loaded: return assert timestampType in [BookmarkNode.TsAdded, BookmarkNode.TsModified, BookmarkNode.TsVisited] if timestampType == BookmarkNode.TsAdded: node.added = timestamp elif timestampType == BookmarkNode.TsModified: node.modified = timestamp elif timestampType == BookmarkNode.TsVisited: node.visited = timestamp self.__saveTimer.changeOccurred() def bookmarks(self): """ Public method to get a reference to the root bookmark node. @return reference to the root bookmark node (BookmarkNode) """ if not self.__loaded: self.load() return self.__bookmarkRootNode def menu(self): """ Public method to get a reference to the bookmarks menu node. @return reference to the bookmarks menu node (BookmarkNode) """ if not self.__loaded: self.load() return self.__menu def toolbar(self): """ Public method to get a reference to the bookmarks toolbar node. @return reference to the bookmarks toolbar node (BookmarkNode) """ if not self.__loaded: self.load() return self.__toolbar def bookmarksModel(self): """ Public method to get a reference to the bookmarks model. @return reference to the bookmarks model (BookmarksModel) """ if self.__bookmarksModel is None: from .BookmarksModel import BookmarksModel self.__bookmarksModel = BookmarksModel(self, self) return self.__bookmarksModel def importBookmarks(self): """ Public method to import bookmarks. """ from .BookmarksImportDialog import BookmarksImportDialog dlg = BookmarksImportDialog() if dlg.exec_() == QDialog.Accepted: importRootNode = dlg.getImportedBookmarks() if importRootNode is not None: self.addBookmark(self.menu(), importRootNode) def exportBookmarks(self): """ Public method to export the bookmarks. """ fileName, selectedFilter = E5FileDialog.getSaveFileNameAndFilter( None, self.tr("Export Bookmarks"), "eric6_bookmarks.xbel", self.tr("XBEL bookmarks (*.xbel);;" "XBEL bookmarks (*.xml);;" "HTML Bookmarks (*.html)")) if not fileName: return ext = QFileInfo(fileName).suffix() if not ext: ex = selectedFilter.split("(*")[1].split(")")[0] if ex: fileName += ex ext = QFileInfo(fileName).suffix() if ext == "html": from .NsHtmlWriter import NsHtmlWriter writer = NsHtmlWriter() else: from .XbelWriter import XbelWriter writer = XbelWriter() if not writer.write(fileName, self.__bookmarkRootNode): E5MessageBox.critical( None, self.tr("Exporting Bookmarks"), self.tr("""Error exporting bookmarks to <b>{0}</b>.""") .format(fileName)) def __convertFromOldBookmarks(self): """ Private method to convert the old bookmarks into the new ones. """ bmNames = Preferences.Prefs.settings.value('Bookmarks/Names') bmFiles = Preferences.Prefs.settings.value('Bookmarks/Files') if bmNames is not None and bmFiles is not None: if len(bmNames) == len(bmFiles): convertedRootNode = BookmarkNode(BookmarkNode.Folder) convertedRootNode.title = self.tr("Converted {0}")\ .format(QDate.currentDate().toString( Qt.SystemLocaleShortDate)) for i in range(len(bmNames)): node = BookmarkNode(BookmarkNode.Bookmark, convertedRootNode) node.title = bmNames[i] url = QUrl(bmFiles[i]) if not url.scheme(): url.setScheme("file") node.url = url.toString() self.addBookmark(self.menu(), convertedRootNode) Preferences.Prefs.settings.remove('Bookmarks') def iconChanged(self, url): """ Public slot to update the icon image for an URL. @param url URL of the icon to update (QUrl or string) """ if isinstance(url, QUrl): url = url.toString() nodes = self.bookmarksForUrl(url) for node in nodes: self.bookmarksModel().entryChanged(node) def bookmarkForUrl(self, url, start=StartRoot): """ Public method to get a bookmark node for a given URL. @param url URL of the bookmark to search for (QUrl or string) @keyparam start indicator for the start of the search (StartRoot, StartMenu, StartToolBar) @return bookmark node for the given url (BookmarkNode) """ if start == StartMenu: startNode = self.__menu elif start == StartToolBar: startNode = self.__toolbar else: startNode = self.__bookmarkRootNode if startNode is None: return None if isinstance(url, QUrl): url = url.toString() return self.__searchBookmark(url, startNode) def __searchBookmark(self, url, startNode): """ Private method get a bookmark node for a given URL. @param url URL of the bookmark to search for (string) @param startNode reference to the node to start searching (BookmarkNode) @return bookmark node for the given url (BookmarkNode) """ bm = None for node in startNode.children(): if node.type() == BookmarkNode.Folder: bm = self.__searchBookmark(url, node) elif node.type() == BookmarkNode.Bookmark: if node.url == url: bm = node if bm is not None: return bm return None def bookmarksForUrl(self, url, start=StartRoot): """ Public method to get a list of bookmark nodes for a given URL. @param url URL of the bookmarks to search for (QUrl or string) @keyparam start indicator for the start of the search (StartRoot, StartMenu, StartToolBar) @return list of bookmark nodes for the given url (list of BookmarkNode) """ if start == StartMenu: startNode = self.__menu elif start == StartToolBar: startNode = self.__toolbar else: startNode = self.__bookmarkRootNode if startNode is None: return None if isinstance(url, QUrl): url = url.toString() return self.__searchBookmarks(url, startNode) def __searchBookmarks(self, url, startNode): """ Private method get a list of bookmark nodes for a given URL. @param url URL of the bookmarks to search for (string) @param startNode reference to the node to start searching (BookmarkNode) @return list of bookmark nodes for the given url (list of BookmarkNode) """ bm = [] for node in startNode.children(): if node.type() == BookmarkNode.Folder: bm.extend(self.__searchBookmarks(url, node)) elif node.type() == BookmarkNode.Bookmark: if node.url == url: bm.append(node) return bm
class HistoryManager(QWebHistoryInterface): """ Class implementing the history manager. @signal historyCleared() emitted after the history has been cleared @signal historyReset() emitted after the history has been reset @signal entryAdded(HistoryEntry) emitted after a history entry has been added @signal entryRemoved(HistoryEntry) emitted after a history entry has been removed @signal entryUpdated(int) emitted after a history entry has been updated @signal historySaved() emitted after the history was saved """ historyCleared = pyqtSignal() historyReset = pyqtSignal() entryAdded = pyqtSignal(HistoryEntry) entryRemoved = pyqtSignal(HistoryEntry) entryUpdated = pyqtSignal(int) historySaved = pyqtSignal() def __init__(self, parent=None): """ Constructor @param parent reference to the parent object (QObject) """ super(HistoryManager, self).__init__(parent) self.__saveTimer = AutoSaver(self, self.save) self.__daysToExpire = Preferences.getHelp("HistoryLimit") self.__history = [] self.__lastSavedUrl = "" self.__expiredTimer = QTimer(self) self.__expiredTimer.setSingleShot(True) self.__expiredTimer.timeout.connect(self.__checkForExpired) self.__frequencyTimer = QTimer(self) self.__frequencyTimer.setSingleShot(True) self.__frequencyTimer.timeout.connect(self.__refreshFrequencies) self.entryAdded.connect(self.__saveTimer.changeOccurred) self.entryRemoved.connect(self.__saveTimer.changeOccurred) self.__load() from .HistoryModel import HistoryModel from .HistoryFilterModel import HistoryFilterModel from .HistoryTreeModel import HistoryTreeModel self.__historyModel = HistoryModel(self, self) self.__historyFilterModel = HistoryFilterModel(self.__historyModel, self) self.__historyTreeModel = HistoryTreeModel(self.__historyFilterModel, self) super(HistoryManager, self).setDefaultInterface(self) self.__startFrequencyTimer() def close(self): """ Public method to close the history manager. """ # remove history items on application exit if self.__daysToExpire == -2: self.clear() self.__saveTimer.saveIfNeccessary() def history(self): """ Public method to return the history. @return reference to the list of history entries (list of HistoryEntry) """ return self.__history[:] def setHistory(self, history, loadedAndSorted=False): """ Public method to set a new history. @param history reference to the list of history entries to be set (list of HistoryEntry) @param loadedAndSorted flag indicating that the list is sorted (boolean) """ self.__history = history[:] if not loadedAndSorted: self.__history.sort() self.__checkForExpired() if loadedAndSorted: try: self.__lastSavedUrl = self.__history[0].url except IndexError: self.__lastSavedUrl = "" else: self.__lastSavedUrl = "" self.__saveTimer.changeOccurred() self.historyReset.emit() def historyContains(self, url): """ Public method to check the history for an entry. @param url URL to check for (string) @return flag indicating success (boolean) """ return self.__historyFilterModel.historyContains(url) def _addHistoryEntry(self, itm): """ Protected method to add a history item. @param itm reference to the history item to add (HistoryEntry) """ globalSettings = QWebSettings.globalSettings() if globalSettings.testAttribute(QWebSettings.PrivateBrowsingEnabled): return self.__history.insert(0, itm) self.entryAdded.emit(itm) if len(self.__history) == 1: self.__checkForExpired() def _removeHistoryEntry(self, itm): """ Protected method to remove a history item. @param itm reference to the history item to remove (HistoryEntry) """ self.__lastSavedUrl = "" self.__history.remove(itm) self.entryRemoved.emit(itm) def addHistoryEntry(self, url): """ Public method to add a history entry. @param url URL to be added (string) """ cleanurl = QUrl(url) if cleanurl.scheme() not in ["eric", "about"]: if cleanurl.password(): # don't save the password in the history cleanurl.setPassword("") if cleanurl.host(): cleanurl.setHost(cleanurl.host().lower()) itm = HistoryEntry(cleanurl.toString(), QDateTime.currentDateTime()) self._addHistoryEntry(itm) def updateHistoryEntry(self, url, title): """ Public method to update a history entry. @param url URL of the entry to update (string) @param title title of the entry to update (string) """ cleanurl = QUrl(url) if cleanurl.scheme() not in ["eric", "about"]: for index in range(len(self.__history)): if url == self.__history[index].url: self.__history[index].title = title self.__saveTimer.changeOccurred() if not self.__lastSavedUrl: self.__lastSavedUrl = self.__history[index].url self.entryUpdated.emit(index) break def removeHistoryEntry(self, url, title=""): """ Public method to remove a history entry. @param url URL of the entry to remove (QUrl) @param title title of the entry to remove (string) """ for index in range(len(self.__history)): if url == QUrl(self.__history[index].url) and (not title or title == self.__history[index].title): self._removeHistoryEntry(self.__history[index]) break def historyModel(self): """ Public method to get a reference to the history model. @return reference to the history model (HistoryModel) """ return self.__historyModel def historyFilterModel(self): """ Public method to get a reference to the history filter model. @return reference to the history filter model (HistoryFilterModel) """ return self.__historyFilterModel def historyTreeModel(self): """ Public method to get a reference to the history tree model. @return reference to the history tree model (HistoryTreeModel) """ return self.__historyTreeModel def __checkForExpired(self): """ Private slot to check entries for expiration. """ if self.__daysToExpire < 0 or len(self.__history) == 0: return now = QDateTime.currentDateTime() nextTimeout = 0 while self.__history: checkForExpired = QDateTime(self.__history[-1].dateTime) checkForExpired.setDate(checkForExpired.date().addDays(self.__daysToExpire)) if now.daysTo(checkForExpired) > 7: nextTimeout = 7 * 86400 else: nextTimeout = now.secsTo(checkForExpired) if nextTimeout > 0: break itm = self.__history.pop(-1) self.__lastSavedUrl = "" self.entryRemoved.emit(itm) self.__saveTimer.saveIfNeccessary() if nextTimeout > 0: self.__expiredTimer.start(nextTimeout * 1000) def daysToExpire(self): """ Public method to get the days for entry expiration. @return days for entry expiration (integer) """ return self.__daysToExpire def setDaysToExpire(self, limit): """ Public method to set the days for entry expiration. @param limit days for entry expiration (integer) """ if self.__daysToExpire == limit: return self.__daysToExpire = limit self.__checkForExpired() self.__saveTimer.changeOccurred() def preferencesChanged(self): """ Public method to indicate a change of preferences. """ self.setDaysToExpire(Preferences.getHelp("HistoryLimit")) @pyqtSlot() def clear(self, period=0): """ Public slot to clear the complete history. @param period history period in milliseconds to be cleared (integer) """ if period == 0: self.__history = [] self.historyReset.emit() else: breakMS = QDateTime.currentMSecsSinceEpoch() - period while self.__history and (QDateTime(self.__history[0].dateTime).toMSecsSinceEpoch() > breakMS): itm = self.__history.pop(0) self.entryRemoved.emit(itm) self.__lastSavedUrl = "" self.__saveTimer.changeOccurred() self.__saveTimer.saveIfNeccessary() self.historyCleared.emit() def getFileName(self): """ Public method to get the file name of the history file. @return name of the history file (string) """ return os.path.join(Utilities.getConfigDir(), "browser", "history") def reload(self): """ Public method to reload the history. """ self.__load() def __load(self): """ Private method to load the saved history entries from disk. """ historyFile = QFile(self.getFileName()) if not historyFile.exists(): return if not historyFile.open(QIODevice.ReadOnly): E5MessageBox.warning( None, self.tr("Loading History"), self.tr("""<p>Unable to open history file <b>{0}</b>.<br/>""" """Reason: {1}</p>""").format( historyFile.fileName, historyFile.errorString() ), ) return history = [] # double check, that the history file is sorted as it is read needToSort = False lastInsertedItem = HistoryEntry() data = QByteArray(historyFile.readAll()) stream = QDataStream(data, QIODevice.ReadOnly) stream.setVersion(QDataStream.Qt_4_6) while not stream.atEnd(): ver = stream.readUInt32() if ver != HISTORY_VERSION: continue itm = HistoryEntry() itm.url = Utilities.readStringFromStream(stream) stream >> itm.dateTime itm.title = Utilities.readStringFromStream(stream) if not itm.dateTime.isValid(): continue if itm == lastInsertedItem: if not lastInsertedItem.title and len(history) > 0: history[0].title = itm.title continue if not needToSort and history and lastInsertedItem < itm: needToSort = True history.insert(0, itm) lastInsertedItem = itm historyFile.close() if needToSort: history.sort() self.setHistory(history, True) # if the history had to be sorted, rewrite the history sorted if needToSort: self.__lastSavedUrl = "" self.__saveTimer.changeOccurred() def save(self): """ Public slot to save the history entries to disk. """ historyFile = QFile(self.getFileName()) if not historyFile.exists(): self.__lastSavedUrl = "" saveAll = self.__lastSavedUrl == "" first = len(self.__history) - 1 if not saveAll: # find the first one to save for index in range(len(self.__history)): if self.__history[index].url == self.__lastSavedUrl: first = index - 1 break if first == len(self.__history) - 1: saveAll = True if saveAll: # use a temporary file when saving everything f = QTemporaryFile() f.setAutoRemove(False) opened = f.open() else: f = historyFile opened = f.open(QIODevice.Append) if not opened: E5MessageBox.warning( None, self.tr("Saving History"), self.tr("""<p>Unable to open history file <b>{0}</b>.<br/>""" """Reason: {1}</p>""").format( f.fileName(), f.errorString() ), ) return for index in range(first, -1, -1): data = QByteArray() stream = QDataStream(data, QIODevice.WriteOnly) stream.setVersion(QDataStream.Qt_4_6) itm = self.__history[index] stream.writeUInt32(HISTORY_VERSION) stream.writeString(itm.url.encode("utf-8")) stream << itm.dateTime stream.writeString(itm.title.encode("utf-8")) f.write(data) f.close() if saveAll: if historyFile.exists() and not historyFile.remove(): E5MessageBox.warning( None, self.tr("Saving History"), self.tr("""<p>Error removing old history file <b>{0}</b>.""" """<br/>Reason: {1}</p>""").format( historyFile.fileName(), historyFile.errorString() ), ) if not f.copy(historyFile.fileName()): E5MessageBox.warning( None, self.tr("Saving History"), self.tr( """<p>Error moving new history file over old one """ """(<b>{0}</b>).<br/>Reason: {1}</p>""" ).format(historyFile.fileName(), f.errorString()), ) self.historySaved.emit() try: self.__lastSavedUrl = self.__history[0].url except IndexError: self.__lastSavedUrl = "" def __refreshFrequencies(self): """ Private slot to recalculate the refresh frequencies. """ self.__historyFilterModel.recalculateFrequencies() self.__startFrequencyTimer() def __startFrequencyTimer(self): """ Private method to start the timer to recalculate the frequencies. """ tomorrow = QDateTime(QDate.currentDate().addDays(1), QTime(3, 0)) self.__frequencyTimer.start(QDateTime.currentDateTime().secsTo(tomorrow) * 1000)
class PasswordManager(QObject): """ Class implementing the password manager. @signal changed() emitted to indicate a change @signal passwordsSaved() emitted after the passwords were saved """ changed = pyqtSignal() passwordsSaved = pyqtSignal() SEPARATOR = "====================" FORMS = "=====FORMS=====" NEVER = "=====NEVER=====" def __init__(self, parent=None): """ Constructor @param parent reference to the parent object (QObject) """ super(PasswordManager, self).__init__(parent) self.__logins = {} self.__loginForms = {} self.__never = [] self.__loaded = False self.__saveTimer = AutoSaver(self, self.save) self.changed.connect(self.__saveTimer.changeOccurred) def clear(self): """ Public slot to clear the saved passwords. """ if not self.__loaded: self.__load() self.__logins = {} self.__loginForms = {} self.__never = [] self.__saveTimer.changeOccurred() self.__saveTimer.saveIfNeccessary() self.changed.emit() def getLogin(self, url, realm): """ Public method to get the login credentials. @param url URL to get the credentials for (QUrl) @param realm realm to get the credentials for (string) @return tuple containing the user name (string) and password (string) """ if not self.__loaded: self.__load() key = self.__createKey(url, realm) try: return self.__logins[key][0], Utilities.crypto.pwConvert( self.__logins[key][1], encode=False) except KeyError: return "", "" def setLogin(self, url, realm, username, password): """ Public method to set the login credentials. @param url URL to set the credentials for (QUrl) @param realm realm to set the credentials for (string) @param username username for the login (string) @param password password for the login (string) """ if not self.__loaded: self.__load() key = self.__createKey(url, realm) self.__logins[key] = ( username, Utilities.crypto.pwConvert(password, encode=True) ) self.changed.emit() def __createKey(self, url, realm): """ Private method to create the key string for the login credentials. @param url URL to get the credentials for (QUrl) @param realm realm to get the credentials for (string) @return key string (string) """ authority = url.authority() if authority.startswith("@"): authority = authority[1:] if realm: key = "{0}://{1} ({2})".format( url.scheme(), authority, realm) else: key = "{0}://{1}".format(url.scheme(), authority) return key def getFileName(self): """ Public method to get the file name of the passwords file. @return name of the passwords file (string) """ return os.path.join(Utilities.getConfigDir(), "browser", "logins.xml") def save(self): """ Public slot to save the login entries to disk. """ if not self.__loaded: return from .PasswordWriter import PasswordWriter loginFile = self.getFileName() writer = PasswordWriter() if not writer.write( loginFile, self.__logins, self.__loginForms, self.__never): E5MessageBox.critical( None, self.tr("Saving login data"), self.tr( """<p>Login data could not be saved to <b>{0}</b></p>""" ).format(loginFile)) else: self.passwordsSaved.emit() def __load(self): """ Private method to load the saved login credentials. """ loginFile = self.getFileName() if not os.path.exists(loginFile): self.__loadNonXml(os.path.splitext(loginFile)[0]) else: from .PasswordReader import PasswordReader reader = PasswordReader() self.__logins, self.__loginForms, self.__never = \ reader.read(loginFile) if reader.error() != QXmlStreamReader.NoError: E5MessageBox.warning( None, self.tr("Loading login data"), self.tr("""Error when loading login data on""" """ line {0}, column {1}:\n{2}""") .format(reader.lineNumber(), reader.columnNumber(), reader.errorString())) self.__loaded = True def __loadNonXml(self, loginFile): """ Private method to load non-XML password files. This method is to convert from the old, non-XML format to the new XML based format. @param loginFile name of the non-XML password file (string) """ if os.path.exists(loginFile): try: f = open(loginFile, "r", encoding="utf-8") lines = f.read() f.close() except IOError as err: E5MessageBox.critical( None, self.tr("Loading login data"), self.tr("""<p>Login data could not be loaded """ """from <b>{0}</b></p>""" """<p>Reason: {1}</p>""") .format(loginFile, str(err))) return data = [] section = 0 # 0 = login data, 1 = forms data, 2 = never store info for line in lines.splitlines(): if line == self.FORMS: section = 1 continue elif line == self.NEVER: section = 2 continue if section == 0: if line != self.SEPARATOR: data.append(line) else: if len(data) != 3: E5MessageBox.critical( None, self.tr("Loading login data"), self.tr( """<p>Login data could not be loaded """ """from <b>{0}</b></p>""" """<p>Reason: Wrong input format</p>""") .format(loginFile)) return self.__logins[data[0]] = (data[1], data[2]) data = [] elif section == 1: if line != self.SEPARATOR: data.append(line) else: from .LoginForm import LoginForm key = data[0] form = LoginForm() form.url = QUrl(data[1]) form.name = data[2] form.hasAPassword = data[3] == "True" for element in data[4:]: name, value = element.split(" = ", 1) form.elements.append((name, value)) self.__loginForms[key] = form data = [] elif section == 2: self.__never.append(line) os.remove(loginFile) self.__loaded = True # this does the conversion self.save() def reload(self): """ Public method to reload the login data. """ if not self.__loaded: return self.__load() def close(self): """ Public method to close the passwords manager. """ self.__saveTimer.saveIfNeccessary() def removePassword(self, site): """ Public method to remove a password entry. @param site web site name (string) """ if site in self.__logins: del self.__logins[site] if site in self.__loginForms: del self.__loginForms[site] self.changed.emit() def allSiteNames(self): """ Public method to get a list of all site names. @return sorted list of all site names (list of strings) """ if not self.__loaded: self.__load() return sorted(self.__logins.keys()) def sitesCount(self): """ Public method to get the number of available sites. @return number of sites (integer) """ if not self.__loaded: self.__load() return len(self.__logins) def siteInfo(self, site): """ Public method to get a reference to the named site. @param site web site name (string) @return tuple containing the user name (string) and password (string) """ if not self.__loaded: self.__load() if site not in self.__logins: return None return self.__logins[site][0], Utilities.crypto.pwConvert( self.__logins[site][1], encode=False) def post(self, request, data): """ Public method to check, if the data to be sent contains login data. @param request reference to the network request (QNetworkRequest) @param data data to be sent (QByteArray) """ # shall passwords be saved? if not Preferences.getUser("SavePasswords"): return # observe privacy if QWebSettings.globalSettings().testAttribute( QWebSettings.PrivateBrowsingEnabled): return if not self.__loaded: self.__load() # determine the url refererHeader = request.rawHeader(b"Referer") if refererHeader.isEmpty(): return url = QUrl.fromEncoded(refererHeader) url = self.__stripUrl(url) # check that url isn't in __never if url.toString() in self.__never: return # check the request type navType = request.attribute(QNetworkRequest.User + 101) if navType is None: return if navType != QWebPage.NavigationTypeFormSubmitted: return # determine the QWebPage webPage = request.attribute(QNetworkRequest.User + 100) if webPage is None: return # determine the requests content type contentTypeHeader = request.rawHeader(b"Content-Type") if contentTypeHeader.isEmpty(): return multipart = contentTypeHeader.startsWith(b"multipart/form-data") if multipart: boundary = contentTypeHeader.split(" ")[1].split("=")[1] else: boundary = None # find the matching form on the web page form = self.__findForm(webPage, data, boundary=boundary) if not form.isValid(): return form.url = QUrl(url) # check, if the form has a password if not form.hasAPassword: return # prompt, if the form has never be seen key = self.__createKey(url, "") if key not in self.__loginForms: mb = E5MessageBox.E5MessageBox( E5MessageBox.Question, self.tr("Save password"), self.tr( """<b>Would you like to save this password?</b><br/>""" """To review passwords you have saved and remove them, """ """use the password management dialog of the Settings""" """ menu."""), modal=True) neverButton = mb.addButton( self.tr("Never for this site"), E5MessageBox.DestructiveRole) noButton = mb.addButton( self.tr("Not now"), E5MessageBox.RejectRole) mb.addButton(E5MessageBox.Yes) mb.exec_() if mb.clickedButton() == neverButton: self.__never.append(url.toString()) return elif mb.clickedButton() == noButton: return # extract user name and password user = "" password = "" for index in range(len(form.elements)): element = form.elements[index] type_ = form.elementTypes[element[0]] if user == "" and \ type_ == "text": user = element[1] elif password == "" and \ type_ == "password": password = element[1] form.elements[index] = (element[0], "--PASSWORD--") if user and password: self.__logins[key] = \ (user, Utilities.crypto.pwConvert(password, encode=True)) self.__loginForms[key] = form self.changed.emit() def __stripUrl(self, url): """ Private method to strip off all unneeded parts of a URL. @param url URL to be stripped (QUrl) @return stripped URL (QUrl) """ cleanUrl = QUrl(url) if qVersion() >= "5.0.0": cleanUrl.setQuery("") else: cleanUrl.setQueryItems([]) cleanUrl.setUserInfo("") authority = cleanUrl.authority() if authority.startswith("@"): authority = authority[1:] cleanUrl = QUrl("{0}://{1}{2}".format( cleanUrl.scheme(), authority, cleanUrl.path())) cleanUrl.setFragment("") return cleanUrl def __findForm(self, webPage, data, boundary=None): """ Private method to find the form used for logging in. @param webPage reference to the web page (QWebPage) @param data data to be sent (QByteArray) @keyparam boundary boundary string (QByteArray) for multipart encoded data, None for urlencoded data @return parsed form (LoginForm) """ from .LoginForm import LoginForm form = LoginForm() if boundary is not None: args = self.__extractMultipartQueryItems(data, boundary) else: if qVersion() >= "5.0.0": from PyQt5.QtCore import QUrlQuery argsUrl = QUrl.fromEncoded( QByteArray(b"foo://bar.com/?" + QUrl.fromPercentEncoding( data.replace(b"+", b"%20")).encode("utf-8"))) encodedArgs = QUrlQuery(argsUrl).queryItems() else: argsUrl = QUrl.fromEncoded( QByteArray(b"foo://bar.com/?" + data.replace(b"+", b"%20")) ) encodedArgs = argsUrl.queryItems() args = set() for arg in encodedArgs: key = arg[0] value = arg[1] args.add((key, value)) # extract the forms from Helpviewer.JavaScriptResources import parseForms_js lst = webPage.mainFrame().evaluateJavaScript(parseForms_js) for map in lst: formHasPasswords = False formName = map["name"] formIndex = map["index"] if isinstance(formIndex, float) and formIndex.is_integer(): formIndex = int(formIndex) elements = map["elements"] formElements = set() formElementTypes = {} deadElements = set() for elementMap in elements: try: name = elementMap["name"] value = elementMap["value"] type_ = elementMap["type"] except KeyError: continue if type_ == "password": formHasPasswords = True t = (name, value) try: if elementMap["autocomplete"] == "off": deadElements.add(t) except KeyError: pass if name: formElements.add(t) formElementTypes[name] = type_ if formElements.intersection(args) == args: form.hasAPassword = formHasPasswords if not formName: form.name = formIndex else: form.name = formName args.difference_update(deadElements) for elt in deadElements: if elt[0] in formElementTypes: del formElementTypes[elt[0]] form.elements = list(args) form.elementTypes = formElementTypes break return form def __extractMultipartQueryItems(self, data, boundary): """ Private method to extract the query items for a post operation. @param data data to be sent (QByteArray) @param boundary boundary string (QByteArray) @return set of name, value pairs (set of tuple of string, string) """ args = set() dataStr = bytes(data).decode() boundaryStr = bytes(boundary).decode() parts = dataStr.split(boundaryStr + "\r\n") for part in parts: if part.startswith("Content-Disposition"): lines = part.split("\r\n") name = lines[0].split("=")[1][1:-1] value = lines[2] args.add((name, value)) return args def fill(self, page): """ Public slot to fill login forms with saved data. @param page reference to the web page (QWebPage) """ if page is None or page.mainFrame() is None: return if not self.__loaded: self.__load() url = page.mainFrame().url() url = self.__stripUrl(url) key = self.__createKey(url, "") if key not in self.__loginForms or \ key not in self.__logins: return form = self.__loginForms[key] if form.url != url: return if form.name == "": formName = "0" else: try: formName = "{0:d}".format(int(form.name)) except ValueError: formName = '"{0}"'.format(form.name) for element in form.elements: name = element[0] value = element[1] disabled = page.mainFrame().evaluateJavaScript( 'document.forms[{0}].elements["{1}"].disabled'.format( formName, name)) if disabled: continue readOnly = page.mainFrame().evaluateJavaScript( 'document.forms[{0}].elements["{1}"].readOnly'.format( formName, name)) if readOnly: continue type_ = page.mainFrame().evaluateJavaScript( 'document.forms[{0}].elements["{1}"].type'.format( formName, name)) if type_ == "" or \ type_ in ["hidden", "reset", "submit"]: continue if type_ == "password": value = Utilities.crypto.pwConvert( self.__logins[key][1], encode=False) setType = type_ == "checkbox" and "checked" or "value" value = value.replace("\\", "\\\\") value = value.replace('"', '\\"') javascript = \ 'document.forms[{0}].elements["{1}"].{2}="{3}";'.format( formName, name, setType, value) page.mainFrame().evaluateJavaScript(javascript) def masterPasswordChanged(self, oldPassword, newPassword): """ Public slot to handle the change of the master password. @param oldPassword current master password (string) @param newPassword new master password (string) """ if not self.__loaded: self.__load() progress = E5ProgressDialog( self.tr("Re-encoding saved passwords..."), None, 0, len(self.__logins), self.tr("%v/%m Passwords"), QApplication.activeModalWidget()) progress.setMinimumDuration(0) progress.setWindowTitle(self.tr("Passwords")) count = 0 for key in self.__logins: progress.setValue(count) QCoreApplication.processEvents() username, hash = self.__logins[key] hash = Utilities.crypto.pwRecode(hash, oldPassword, newPassword) self.__logins[key] = (username, hash) count += 1 progress.setValue(len(self.__logins)) QCoreApplication.processEvents() self.changed.emit()
class NetworkManager(QNetworkAccessManager): """ Class implementing a network manager. @signal changed() emitted to indicate a change """ changed = pyqtSignal() def __init__(self, engine, parent=None): """ Constructor @param engine reference to the help engine (QHelpEngine) @param parent reference to the parent object (QObject) """ super(NetworkManager, self).__init__(parent) from E5Network.E5NetworkProxyFactory import E5NetworkProxyFactory self.__proxyFactory = E5NetworkProxyFactory() if Preferences.getUI("UseSystemProxy"): QNetworkProxyFactory.setUseSystemConfiguration(True) else: QNetworkProxyFactory.setApplicationProxyFactory( self.__proxyFactory) QNetworkProxyFactory.setUseSystemConfiguration(False) self.languagesChanged() if SSL_AVAILABLE: self.__sslErrorHandler = E5SslErrorHandler(self) self.sslErrors.connect(self.__sslErrorHandler.sslErrorsReplySlot) self.__temporarilyIgnoredSslErrors = {} self.__permanentlyIgnoredSslErrors = {} # dictionaries of permanently and temporarily ignored SSL errors self.__loaded = False self.__saveTimer = AutoSaver(self, self.__save) self.changed.connect(self.__saveTimer.changeOccurred) self.proxyAuthenticationRequired.connect(proxyAuthenticationRequired) self.authenticationRequired.connect( lambda reply, auth: self.authentication(reply.url(), auth)) from .EricSchemeHandler import EricSchemeHandler self.__ericSchemeHandler = EricSchemeHandler() WebBrowserWindow.webProfile().installUrlSchemeHandler( QByteArray(b"eric"), self.__ericSchemeHandler) if engine: from .QtHelpSchemeHandler import QtHelpSchemeHandler self.__qtHelpSchemeHandler = QtHelpSchemeHandler(engine) WebBrowserWindow.webProfile().installUrlSchemeHandler( QByteArray(b"qthelp"), self.__qtHelpSchemeHandler) self.__interceptor = NetworkUrlInterceptor(self) try: WebBrowserWindow.webProfile().setUrlRequestInterceptor( self.__interceptor) except AttributeError: # Qt < 5.13 WebBrowserWindow.webProfile().setRequestInterceptor( self.__interceptor) WebBrowserWindow.cookieJar() def __save(self): """ Private slot to save the permanent SSL error exceptions. """ if not self.__loaded: return from WebBrowser.WebBrowserWindow import WebBrowserWindow if not WebBrowserWindow.isPrivate(): dbString = json.dumps(self.__permanentlyIgnoredSslErrors) Preferences.setWebBrowser("SslExceptionsDB", dbString) def __load(self): """ Private method to load the permanent SSL error exceptions. """ if self.__loaded: return dbString = Preferences.getWebBrowser("SslExceptionsDB") if dbString: try: db = json.loads(dbString) self.__permanentlyIgnoredSslErrors = db except ValueError: # ignore silently pass self.__loaded = True def shutdown(self): """ Public method to shut down the network manager. """ self.__saveTimer.saveIfNeccessary() self.__loaded = False self.__temporarilyIgnoredSslErrors = {} self.__permanentlyIgnoredSslErrors = {} # set proxy factory to None to avoid crashes QNetworkProxyFactory.setApplicationProxyFactory(None) def showSslErrorExceptionsDialog(self): """ Public method to show the SSL error exceptions dialog. """ self.__load() from .SslErrorExceptionsDialog import SslErrorExceptionsDialog dlg = SslErrorExceptionsDialog(self.__permanentlyIgnoredSslErrors) if dlg.exec_() == QDialog.Accepted: self.__permanentlyIgnoredSslErrors = dlg.getSslErrorExceptions() self.changed.emit() def clearSslExceptions(self): """ Public method to clear the permanent SSL certificate error exceptions. """ self.__load() self.__permanentlyIgnoredSslErrors = {} self.changed.emit() self.__saveTimer.saveIfNeccessary() def certificateError(self, error, view): """ Public method to handle SSL certificate errors. @param error object containing the certificate error information @type QWebEngineCertificateError @param view reference to a view to be used as parent for the dialog @type QWidget @return flag indicating to ignore this error @rtype bool """ self.__load() host = error.url().host() if ( host in self.__temporarilyIgnoredSslErrors and error.error() in self.__temporarilyIgnoredSslErrors[host] ): return True if ( host in self.__permanentlyIgnoredSslErrors and error.error() in self.__permanentlyIgnoredSslErrors[host] ): return True title = self.tr("SSL Certificate Error") msgBox = E5MessageBox.E5MessageBox( E5MessageBox.Warning, title, self.tr("""<b>{0}</b>""" """<p>The page you are trying to access has errors""" """ in the SSL certificate.</p>""" """<ul><li>{1}</li></ul>""" """<p>Would you like to make an exception?</p>""") .format(title, error.errorDescription()), modal=True, parent=view) permButton = msgBox.addButton(self.tr("&Permanent accept"), E5MessageBox.AcceptRole) tempButton = msgBox.addButton(self.tr("&Temporary accept"), E5MessageBox.AcceptRole) msgBox.addButton(self.tr("&Reject"), E5MessageBox.RejectRole) msgBox.exec_() if msgBox.clickedButton() == permButton: if host not in self.__permanentlyIgnoredSslErrors: self.__permanentlyIgnoredSslErrors[host] = [] self.__permanentlyIgnoredSslErrors[host].append(error.error()) self.changed.emit() return True elif msgBox.clickedButton() == tempButton: if host not in self.__temporarilyIgnoredSslErrors: self.__temporarilyIgnoredSslErrors[host] = [] self.__temporarilyIgnoredSslErrors[host].append(error.error()) return True else: return False def authentication(self, url, auth, page=None): """ Public slot to handle an authentication request. @param url URL requesting authentication @type QUrl @param auth reference to the authenticator object @type QAuthenticator @param page reference to the web page @type QWebEnginePage or None """ urlRoot = ( "{0}://{1}".format(url.scheme(), url.authority()) ) realm = auth.realm() if not realm and 'realm' in auth.options(): realm = auth.option("realm") if realm: info = self.tr( "<b>Enter username and password for '{0}', realm '{1}'</b>" ).format(urlRoot, realm) else: info = self.tr( "<b>Enter username and password for '{0}'</b>" ).format(urlRoot) from UI.AuthenticationDialog import AuthenticationDialog import WebBrowser.WebBrowserWindow dlg = AuthenticationDialog(info, auth.user(), Preferences.getUser("SavePasswords"), Preferences.getUser("SavePasswords")) if Preferences.getUser("SavePasswords"): username, password = ( WebBrowser.WebBrowserWindow.WebBrowserWindow .passwordManager().getLogin(url, realm) ) if username: dlg.setData(username, password) if dlg.exec_() == QDialog.Accepted: username, password = dlg.getData() auth.setUser(username) auth.setPassword(password) if Preferences.getUser("SavePasswords") and dlg.shallSave(): ( WebBrowser.WebBrowserWindow.WebBrowserWindow .passwordManager().setLogin( url, realm, username, password) ) else: if page is not None: self.__showAuthenticationErrorPage(page, url) def __showAuthenticationErrorPage(self, page, url): """ Private method to show an authentication error page. @param page reference to the page @type QWebEnginePage @param url reference to the URL requesting authentication @type QUrl """ html = readAllFileContents(":/html/authenticationErrorPage.html") html = html.replace("@IMAGE@", pixmapToDataUrl( qApp.style().standardIcon(QStyle.SP_MessageBoxCritical).pixmap( 48, 48)).toString()) html = html.replace("@FAVICON@", pixmapToDataUrl( qApp.style() .standardIcon(QStyle.SP_MessageBoxCritical).pixmap( 16, 16)).toString()) html = html.replace("@TITLE@", self.tr("Authentication required")) html = html.replace("@H1@", self.tr("Authentication required")) html = html.replace( "@LI-1@", self.tr("Authentication is required to access:")) html = html.replace( "@LI-2@", '<a href="{0}">{0}</a>'.format(url.toString())) page.setHtml(html, url) def proxyAuthentication(self, requestUrl, auth, proxyHost): """ Public slot to handle a proxy authentication request. @param requestUrl requested URL @type QUrl @param auth reference to the authenticator object @type QAuthenticator @param proxyHost name of the proxy host @type str """ proxy = QNetworkProxy.applicationProxy() if proxy.user() and proxy.password(): auth.setUser(proxy.user()) auth.setPassword(proxy.password()) return proxyAuthenticationRequired(proxy, auth) def languagesChanged(self): """ Public slot to (re-)load the list of accepted languages. """ from WebBrowser.WebBrowserLanguagesDialog import ( WebBrowserLanguagesDialog ) languages = Preferences.toList( Preferences.Prefs.settings.value( "WebBrowser/AcceptLanguages", WebBrowserLanguagesDialog.defaultAcceptLanguages())) self.__acceptLanguage = WebBrowserLanguagesDialog.httpString(languages) WebBrowserWindow.webProfile().setHttpAcceptLanguage( self.__acceptLanguage) def installUrlInterceptor(self, interceptor): """ Public method to install an URL interceptor. @param interceptor URL interceptor to be installed @type UrlInterceptor """ self.__interceptor.installUrlInterceptor(interceptor) def removeUrlInterceptor(self, interceptor): """ Public method to remove an URL interceptor. @param interceptor URL interceptor to be removed @type UrlInterceptor """ self.__interceptor.removeUrlInterceptor(interceptor) def preferencesChanged(self): """ Public slot to handle a change of preferences. """ self.__interceptor.preferencesChanged() if Preferences.getUI("UseSystemProxy"): QNetworkProxyFactory.setUseSystemConfiguration(True) else: QNetworkProxyFactory.setApplicationProxyFactory( self.__proxyFactory) QNetworkProxyFactory.setUseSystemConfiguration(False) def createRequest(self, op, request, data): """ Public method to launch a network action. @param op operation to be performed @type QNetworkAccessManager.Operation @param request request to be operated on @type QNetworkRequest @param data reference to the data to be sent @type QIODevice @return reference to the network reply @rtype QNetworkReply """ req = QNetworkRequest(request) req.setAttribute(QNetworkRequest.SpdyAllowedAttribute, True) req.setAttribute(QNetworkRequest.FollowRedirectsAttribute, True) return super(NetworkManager, self).createRequest(op, req, data)
class WebIconProvider(QObject): """ Class implementing a web site icon storage. @signal changed() emitted to indicate a change of the icons database """ changed = pyqtSignal() def __init__(self, parent=None): """ Constructor @param parent reference to the parent object (QObject) """ super(WebIconProvider, self).__init__(parent) self.__encoding = "iso-8859-1" self.__iconsFileName = "web_site_icons.json" self.__iconDatabasePath = "" # saving of icons disabled self.__iconsDB = {} self.__loaded = False self.__saveTimer = AutoSaver(self, self.save) self.changed.connect(self.__saveTimer.changeOccurred) def setIconDatabasePath(self, path): """ Public method to set the path for the web site icons store. @param path path to store the icons file to @type str """ if path != self.__iconDatabasePath: self.close() self.__iconDatabasePath = path def iconDatabasePath(self): """ Public method o get the path for the web site icons store. @return path to store the icons file to @rtype str """ return self.__iconDatabasePath def close(self): """ Public method to close the web icon provider. """ self.__saveTimer.saveIfNeccessary() self.__loaded = False self.__iconsDB = {} def load(self): """ Public method to load the bookmarks. """ if self.__loaded: return if self.__iconDatabasePath: filename = os.path.join(self.__iconDatabasePath, self.__iconsFileName) try: f = open(filename, "r") db = json.load(f) f.close() except (IOError, OSError): # ignore silentyl db = {} self.__iconsDB = {} for url, data in db.items(): self.__iconsDB[url] = QIcon( QPixmap.fromImage( QImage.fromData( QByteArray(data.encode(self.__encoding))))) self.__loaded = True def save(self): """ Public method to save the zoom values. """ if not self.__loaded: return from WebBrowser.WebBrowserWindow import WebBrowserWindow if not WebBrowserWindow.isPrivate() and bool(self.__iconDatabasePath): db = {} for url, icon in self.__iconsDB.items(): ba = QByteArray() buffer = QBuffer(ba) buffer.open(QIODevice.WriteOnly) icon.pixmap(32).toImage().save(buffer, "PNG") db[url] = bytes(buffer.data()).decode(self.__encoding) filename = os.path.join(self.__iconDatabasePath, self.__iconsFileName) try: f = open(filename, "w") json.dump(db, f) f.close() except (IOError, OSError): # ignore silentyl pass def saveIcon(self, view): """ Public method to save a web site icon. @param view reference to the view object @type WebBrowserView """ scheme = view.url().scheme() if scheme in ["eric", "about", "qthelp", "file", "abp", "ftp"]: return self.load() if view.mainWindow().isPrivate(): return urlStr = self.__urlToString(view.url()) self.__iconsDB[urlStr] = view.icon() self.changed.emit() def __urlToString(self, url): """ Private method to convert an URL to a string. @param url URL to be converted @type QUrl @return string representation of the URL @rtype str """ return url.toString(QUrl.PrettyDecoded | QUrl.RemoveUserInfo | QUrl.RemoveFragment) def iconForUrl(self, url): """ Public method to get an icon for an URL. @param url URL to get icon for @type QUrl @return icon for the URL @rtype QIcon """ scheme = url.scheme() if scheme in ["eric", "about"]: return UI.PixmapCache.getIcon("ericWeb.png") elif scheme == "qthelp": return UI.PixmapCache.getIcon("qthelp.png") elif scheme == "file": return UI.PixmapCache.getIcon("fileMisc.png") elif scheme == "abp": return UI.PixmapCache.getIcon("adBlockPlus.png") elif scheme == "ftp": return UI.PixmapCache.getIcon("network-server.png") self.load() urlStr = self.__urlToString(url) for iconUrlStr in self.__iconsDB: if iconUrlStr.startswith(urlStr): return self.__iconsDB[iconUrlStr] # try replacing http scheme with https scheme url = QUrl(url) if url.scheme() == "http": url.setScheme("https") urlStr = self.__urlToString(url) for iconUrlStr in self.__iconsDB: if iconUrlStr.startswith(urlStr): return self.__iconsDB[iconUrlStr] if scheme == "https": return UI.PixmapCache.getIcon("securityHigh32.png") else: return UI.PixmapCache.getIcon("defaultIcon.png") def clear(self): """ Public method to clear the icons cache. """ self.load() self.__iconsDB = {} self.changed.emit() self.__saveTimer.saveIfNeccessary() def showWebIconDialog(self): """ Public method to show a dialog to manage the Favicons. """ self.load() from .WebIconDialog import WebIconDialog dlg = WebIconDialog(self.__iconsDB) if dlg.exec_() == QDialog.Accepted: changed = False urls = dlg.getUrls() for url in list(self.__iconsDB.keys())[:]: if url not in urls: del self.__iconsDB[url] changed = True if changed: self.changed.emit()
class IrcNetworkManager(QObject): """ Class implementing the IRC identity object. @signal dataChanged() emitted after some data has changed @signal networksChanged() emitted after a network object has changed @signal identitiesChanged() emitted after an identity object has changed """ dataChanged = pyqtSignal() networksChanged = pyqtSignal() identitiesChanged = pyqtSignal() def __init__(self, parent=None): """ Constructor @param parent reference to the parent object (QObject) """ super(IrcNetworkManager, self).__init__(parent) self.__loaded = False self.__saveTimer = AutoSaver(self, self.save) self.__settings = Preferences.Prefs.settings self.__networks = {} self.__identities = {} self.dataChanged.connect(self.__saveTimer.changeOccurred) def close(self): """ Public method to close the open search engines manager. """ self.__saveTimer.saveIfNeccessary() def save(self): """ Public slot to save the IRC data. """ if not self.__loaded: return # save IRC data self.__settings.beginGroup("IRC") # identities self.__settings.remove("Identities") self.__settings.beginGroup("Identities") for key in self.__identities: self.__settings.beginGroup(key) self.__identities[key].save(self.__settings) self.__settings.endGroup() self.__settings.endGroup() # networks self.__settings.remove("Networks") self.__settings.beginGroup("Networks") for key in self.__networks: self.__settings.beginGroup(key) self.__networks[key].save(self.__settings) self.__settings.endGroup() self.__settings.endGroup() self.__settings.endGroup() def __load(self): """ Private slot to load the IRC data. """ if self.__loaded: return # load IRC data self.__settings.beginGroup("IRC") # identities self.__settings.beginGroup("Identities") for key in self.__settings.childGroups(): self.__identities[key] = IrcIdentity(key) self.__settings.beginGroup(key) self.__identities[key].load(self.__settings) self.__settings.endGroup() self.__settings.endGroup() # networks self.__settings.beginGroup("Networks") for key in self.__settings.childGroups(): self.__networks[key] = IrcNetwork(key) self.__settings.beginGroup(key) self.__networks[key].load(self.__settings) self.__settings.endGroup() self.__settings.endGroup() self.__settings.endGroup() if not self.__identities or \ not self.__networks: # data structures got corrupted; load defaults self.__loadDefaults() if IrcIdentity.DefaultIdentityName not in self.__identities: self.__loadDefaults(identityOnly=True) self.__loaded = True def __loadDefaults(self, identityOnly=False): """ Private method to load default values. @param identityOnly flag indicating to just load the default identity (boolean) """ if not identityOnly: self.__networks = {} self.__identities = {} # identity identity = IrcIdentity.createDefaultIdentity() self.__identities[identity.getName()] = identity if not identityOnly: network = IrcNetwork.createDefaultNetwork() self.__networks[network.getName()] = network network = IrcNetwork.createDefaultNetwork(True) self.__networks[network.getName()] = network self.dataChanged.emit() ################################################################## ## Identity related methods below ################################################################## def getIdentity(self, name, create=False): """ Public method to get an identity object. @param name name of the identity to get (string) @param create flag indicating to create a new object, if none exists (boolean) @return reference to the identity (IrcIdentity) """ if not name: return None if not self.__loaded: self.__load() if name in self.__identities: return self.__identities[name] elif create: id = IrcIdentity(name) self.__identities[name] = id self.dataChanged.emit() return id else: return None def getIdentities(self): """ Public method to get a copy of all identities. @return dictionary of all identities (dict of IrcIdentity) """ return copy.deepcopy(self.__identities) def setIdentities(self, identities): """ Public method to set the identities. @param identities dictionary of all identities (dict of IrcIdentity) """ self.__identities = copy.deepcopy(identities) self.identityChanged() # Check all networks, if the identity they use is still available. # If it isn't, change them to use the default identity. for network in self.__networks.values(): if network.getIdentityName() not in self.__identities: network.setIdentityName(IrcIdentity.DefaultIdentityName) def getIdentityNames(self): """ Public method to get the names of all identities. @return names of all identities (list of string) """ return list(self.__identities.keys()) def addIdentity(self, identity): """ Public method to add a new identity. @param identity reference to the identity to add (IrcIdentity) """ name = identity.getName() self.__identities[name] = identity self.identityChanged() def deleteIdentity(self, name): """ Public method to delete the given identity. @param name name of the identity to delete (string) """ if name in self.__identities and \ name != IrcIdentity.DefaultIdentityName: del self.__identities[name] self.identityChanged() def renameIdentity(self, oldName, newName): """ Public method to rename an identity. @param oldName old name of the identity (string) @param newName new name of the identity (string) """ if oldName in self.__identities: self.__identities[newName] = self.__identities[oldName] del self.__identities[oldName] for network in self.__networks: if network.getIdentityName() == oldName: network.setIdentityName(newName) self.identityChanged() def identityChanged(self): """ Public method to indicate a change of an identity object. """ self.dataChanged.emit() self.identitiesChanged.emit() ################################################################## ## Network related methods below ################################################################## def getNetwork(self, name): """ Public method to get a network object. @param name name of the network (string) @return reference to the network object (IrcNetwork) """ if not self.__loaded: self.__load() if name in self.__networks: return self.__networks[name] else: return None def setNetwork(self, network, networkName=""): """ Public method to set a network. @param network network object to set (IrcNetwork) @param networkName name the network was known for (string) """ name = network.getName() if networkName and name != networkName: # the network name has changed self.deleteNetwork(networkName) self.addNetwork(network) elif name in self.__networks: self.__networks[name] = network self.networkChanged() def addNetwork(self, network): """ Public method to add a network. @param network network object to add (IrcNetwork) """ name = network.getName() if name not in self.__networks: self.__networks[name] = network self.networkChanged() def deleteNetwork(self, name): """ Public method to delete the given network. @param name name of the network to delete (string) """ if name in self.__networks: del self.__networks[name] self.networkChanged() def networkChanged(self): """ Public method to indicate a change of a network object. """ self.dataChanged.emit() self.networksChanged.emit() def getNetworkNames(self): """ Public method to get a list of all known network names. @return list of network names (list of string) """ if not self.__loaded: self.__load() return list(sorted(self.__networks.keys()))
class DownloadManager(QDialog, Ui_DownloadManager): """ Class implementing the download manager. @signal downloadsCountChanged() emitted to indicate a change of the count of download items """ RemoveNever = 0 RemoveExit = 1 RemoveSuccessFullDownload = 2 UpdateTimerTimeout = 1000 downloadsCountChanged = pyqtSignal() def __init__(self, parent=None): """ Constructor @param parent reference to the parent widget (QWidget) """ super(DownloadManager, self).__init__(parent) self.setupUi(self) self.setWindowFlags(Qt.Window) self.__winTaskbarButton = None self.__saveTimer = AutoSaver(self, self.save) self.__model = DownloadModel(self) self.__manager = WebBrowserWindow.networkManager() self.__iconProvider = None self.__downloads = [] self.__downloadDirectory = "" self.__loaded = False self.__rowHeightMultiplier = 1.1 self.setDownloadDirectory(Preferences.getUI("DownloadPath")) self.downloadsView.setShowGrid(False) self.downloadsView.verticalHeader().hide() self.downloadsView.horizontalHeader().hide() self.downloadsView.setAlternatingRowColors(True) self.downloadsView.horizontalHeader().setStretchLastSection(True) self.downloadsView.setModel(self.__model) self.downloadsView.setContextMenuPolicy(Qt.CustomContextMenu) self.downloadsView.customContextMenuRequested.connect( self.__customContextMenuRequested) self.__clearShortcut = QShortcut(QKeySequence("Ctrl+L"), self) self.__clearShortcut.activated.connect(self.on_cleanupButton_clicked) self.__load() self.__updateTimer = QBasicTimer() def __customContextMenuRequested(self, pos): """ Private slot to handle the context menu request for the bookmarks tree. @param pos position the context menu was requested (QPoint) """ menu = QMenu() selectedRowsCount = len( self.downloadsView.selectionModel().selectedRows()) if selectedRowsCount == 1: row = self.downloadsView.selectionModel().selectedRows()[0].row() itm = self.__downloads[row] if itm.downloadedSuccessfully(): menu.addAction(UI.PixmapCache.getIcon("open.png"), self.tr("Open"), self.__contextMenuOpen) elif itm.downloading(): menu.addAction(UI.PixmapCache.getIcon("stopLoading.png"), self.tr("Cancel"), self.__contextMenuCancel) menu.addSeparator() menu.addAction(self.tr("Open Containing Folder"), self.__contextMenuOpenFolder) menu.addSeparator() menu.addAction(self.tr("Go to Download Page"), self.__contextMenuGotoPage) menu.addAction(self.tr("Copy Download Link"), self.__contextMenuCopyLink) menu.addSeparator() menu.addAction(self.tr("Select All"), self.__contextMenuSelectAll) if (selectedRowsCount > 1 or (selectedRowsCount == 1 and not self.__downloads[self.downloadsView.selectionModel( ).selectedRows()[0].row()].downloading())): menu.addSeparator() menu.addAction(self.tr("Remove From List"), self.__contextMenuRemoveSelected) menu.exec_(QCursor.pos()) def shutdown(self): """ Public method to stop the download manager. """ self.save() self.close() def activeDownloadsCount(self): """ Public method to get the number of active downloads. @return number of active downloads (integer) """ count = 0 for download in self.__downloads: if download.downloading(): count += 1 return count def allowQuit(self): """ Public method to check, if it is ok to quit. @return flag indicating allowance to quit (boolean) """ if self.activeDownloadsCount() > 0: res = E5MessageBox.yesNo( self, self.tr(""), self.tr( """There are %n downloads in progress.\n""" """Do you want to quit anyway?""", "", self.activeDownloadsCount()), icon=E5MessageBox.Warning) if not res: self.show() return False self.close() return True def __testWebBrowserView(self, view, url): """ Private method to test a web browser view against an URL. @param view reference to the web browser view to be tested @type WebBrowserView @param url URL to test against @type QUrl @return flag indicating, that the view is the one for the URL @rtype bool """ if view.tabWidget().count() < 2: return False page = view.page() if page.history().count() != 0: return False if (not page.url().isEmpty() and page.url().host() == url.host()): return True requestedUrl = page.requestedUrl() if requestedUrl.isEmpty(): requestedUrl = QUrl(view.tabWidget().urlBarForView(view).text()) return requestedUrl.isEmpty() or requestedUrl.host() == url.host() def __closeDownloadTab(self, url): """ Private method to close an empty tab, that was opened only for loading the download URL. @param url download URL @type QUrl """ if self.__testWebBrowserView( WebBrowserWindow.getWindow().currentBrowser(), url): WebBrowserWindow.getWindow().closeCurrentBrowser() return for window in WebBrowserWindow.mainWindows(): for browser in window.browsers(): if self.__testWebBrowserView(browser, url): window.closeBrowser(browser) return def download(self, downloadItem): """ Public method to download a file. @param downloadItem reference to the download object containing the download data. @type QWebEngineDownloadItem """ url = downloadItem.url() if url.isEmpty(): return self.__closeDownloadTab(url) # Safe Browsing from WebBrowser.SafeBrowsing.SafeBrowsingManager import ( SafeBrowsingManager) if SafeBrowsingManager.isEnabled(): threatLists = ( WebBrowserWindow.safeBrowsingManager().lookupUrl(url)[0]) if threatLists: threatMessages = (WebBrowserWindow.safeBrowsingManager(). getThreatMessages(threatLists)) res = E5MessageBox.warning( WebBrowserWindow.getWindow(), self.tr("Suspicuous URL detected"), self.tr("<p>The URL <b>{0}</b> was found in the Safe" " Browsing database.</p>{1}").format( url.toString(), "".join(threatMessages)), E5MessageBox.StandardButtons(E5MessageBox.Abort | E5MessageBox.Ignore), E5MessageBox.Abort) if res == E5MessageBox.Abort: downloadItem.cancel() return window = WebBrowserWindow.getWindow() if window: pageUrl = window.currentBrowser().url() else: pageUrl = QUrl() from .DownloadItem import DownloadItem itm = DownloadItem(downloadItem=downloadItem, pageUrl=pageUrl, parent=self) self.__addItem(itm) if Preferences.getWebBrowser("DownloadManagerAutoOpen"): self.show() else: self.__startUpdateTimer() def show(self): """ Public slot to show the download manager dialog. """ self.__startUpdateTimer() super(DownloadManager, self).show() self.activateWindow() self.raise_() def __addItem(self, itm, append=False): """ Private method to add a download to the list of downloads. @param itm reference to the download item @type DownloadItem @param append flag indicating to append the item @type bool """ itm.statusChanged.connect(lambda: self.__updateRow(itm)) itm.downloadFinished.connect(self.__finished) # insert at top of window if append: row = self.downloadsCount() else: row = 0 self.__model.beginInsertRows(QModelIndex(), row, row) if append: self.__downloads.append(itm) else: self.__downloads.insert(0, itm) self.__model.endInsertRows() self.downloadsView.setIndexWidget(self.__model.index(row, 0), itm) icon = self.style().standardIcon(QStyle.SP_FileIcon) itm.setIcon(icon) self.downloadsView.setRowHeight( row, itm.sizeHint().height() * self.__rowHeightMultiplier) # just in case the download finished before the constructor returned self.__updateRow(itm) self.changeOccurred() self.downloadsCountChanged.emit() def __updateRow(self, itm): """ Private slot to update a download item. @param itm reference to the download item @type DownloadItem """ if itm not in self.__downloads: return row = self.__downloads.index(itm) if self.__iconProvider is None: self.__iconProvider = QFileIconProvider() icon = self.__iconProvider.icon(QFileInfo(itm.fileName())) if icon.isNull(): icon = self.style().standardIcon(QStyle.SP_FileIcon) itm.setIcon(icon) self.downloadsView.setRowHeight( row, itm.minimumSizeHint().height() * self.__rowHeightMultiplier) remove = False if (itm.downloadedSuccessfully() and self.removePolicy() == DownloadManager.RemoveSuccessFullDownload): remove = True if remove: self.__model.removeRow(row) self.cleanupButton.setEnabled( (self.downloadsCount() - self.activeDownloadsCount()) > 0) # record the change self.changeOccurred() def removePolicy(self): """ Public method to get the remove policy. @return remove policy (integer) """ return Preferences.getWebBrowser("DownloadManagerRemovePolicy") def setRemovePolicy(self, policy): """ Public method to set the remove policy. @param policy policy to be set (DownloadManager.RemoveExit, DownloadManager.RemoveNever, DownloadManager.RemoveSuccessFullDownload) """ assert policy in (DownloadManager.RemoveExit, DownloadManager.RemoveNever, DownloadManager.RemoveSuccessFullDownload) if policy == self.removePolicy(): return Preferences.setWebBrowser("DownloadManagerRemovePolicy", self.policy) def save(self): """ Public method to save the download settings. """ if not self.__loaded: return Preferences.setWebBrowser("DownloadManagerSize", self.size()) Preferences.setWebBrowser("DownloadManagerPosition", self.pos()) if self.removePolicy() == DownloadManager.RemoveExit: return from WebBrowser.WebBrowserWindow import WebBrowserWindow if WebBrowserWindow.isPrivate(): return downloads = [] for download in self.__downloads: downloads.append(download.getData()) Preferences.setWebBrowser("DownloadManagerDownloads", downloads) def __load(self): """ Private method to load the download settings. """ if self.__loaded: return size = Preferences.getWebBrowser("DownloadManagerSize") if size.isValid(): self.resize(size) pos = Preferences.getWebBrowser("DownloadManagerPosition") self.move(pos) from WebBrowser.WebBrowserWindow import WebBrowserWindow if not WebBrowserWindow.isPrivate(): downloads = Preferences.getWebBrowser("DownloadManagerDownloads") for download in downloads: if (not download["URL"].isEmpty() and bool(download["Location"])): from .DownloadItem import DownloadItem itm = DownloadItem(parent=self) itm.setData(download) self.__addItem(itm, append=True) self.cleanupButton.setEnabled( (self.downloadsCount() - self.activeDownloadsCount()) > 0) self.__loaded = True self.downloadsCountChanged.emit() def closeEvent(self, evt): """ Protected event handler for the close event. @param evt reference to the close event @type QCloseEvent """ self.save() def cleanup(self): """ Public slot to cleanup the downloads. """ self.on_cleanupButton_clicked() @pyqtSlot() def on_cleanupButton_clicked(self): """ Private slot to cleanup the downloads. """ if self.downloadsCount() == 0: return self.__model.removeRows(0, self.downloadsCount()) if (self.downloadsCount() == 0 and self.__iconProvider is not None): self.__iconProvider = None self.changeOccurred() self.downloadsCountChanged.emit() def __finished(self, success): """ Private slot to handle a finished download. @param success flag indicating a successful download @type bool """ if self.isVisible(): QApplication.alert(self) self.downloadsCountChanged.emit() if self.activeDownloadsCount() == 0: # all active downloads are done if success and e5App().activeWindow() is not self: if WebBrowserWindow.notificationsEnabled(): WebBrowserWindow.showNotification( UI.PixmapCache.getPixmap("downloads48.png"), self.tr("Downloads finished"), self.tr("All files have been downloaded.")) if not Preferences.getWebBrowser("DownloadManagerAutoClose"): self.raise_() self.activateWindow() self.__stopUpdateTimer() self.infoLabel.clear() self.setWindowTitle(self.tr("Download Manager")) if Globals.isWindowsPlatform(): self.__taskbarButton().progress().hide() if Preferences.getWebBrowser("DownloadManagerAutoClose"): self.close() def setDownloadDirectory(self, directory): """ Public method to set the current download directory. @param directory current download directory (string) """ self.__downloadDirectory = directory if self.__downloadDirectory != "": self.__downloadDirectory += "/" def downloadDirectory(self): """ Public method to get the current download directory. @return current download directory (string) """ return self.__downloadDirectory def downloadsCount(self): """ Public method to get the number of downloads. @return number of downloads @rtype int """ return len(self.__downloads) def downloads(self): """ Public method to get a reference to the downloads. @return reference to the downloads (list of DownloadItem) """ return self.__downloads def changeOccurred(self): """ Public method to signal a change. """ self.__saveTimer.changeOccurred() def __taskbarButton(self): """ Private method to get a reference to the task bar button (Windows only). @return reference to the task bar button @rtype QWinTaskbarButton or None """ if Globals.isWindowsPlatform(): from PyQt5.QtWinExtras import QWinTaskbarButton if self.__winTaskbarButton is None: window = WebBrowserWindow.mainWindow() self.__winTaskbarButton = QWinTaskbarButton( window.windowHandle()) self.__winTaskbarButton.progress().setRange(0, 100) return self.__winTaskbarButton def timerEvent(self, evt): """ Protected event handler for timer events. @param evt reference to the timer event @type QTimerEvent """ if evt.timerId() == self.__updateTimer.timerId(): if self.activeDownloadsCount() == 0: self.__stopUpdateTimer() self.infoLabel.clear() self.setWindowTitle(self.tr("Download Manager")) if Globals.isWindowsPlatform(): self.__taskbarButton().progress().hide() else: progresses = [] for itm in self.__downloads: if (itm is None or itm.downloadCanceled() or not itm.downloading()): continue progresses.append( (itm.downloadProgress(), itm.remainingTime(), itm.currentSpeed())) if not progresses: return remaining = 0 progress = 0 speed = 0.0 for progressData in progresses: if progressData[1] > remaining: remaining = progressData[1] progress += progressData[0] speed += progressData[2] progress = progress / len(progresses) if self.isVisible(): self.infoLabel.setText( self.tr("{0}% of %n file(s) ({1}) {2}", "", len(progresses)).format( progress, speedString(speed), timeString(remaining), )) self.setWindowTitle(self.tr("{0}% - Download Manager")) if Globals.isWindowsPlatform(): self.__taskbarButton().progress().show() self.__taskbarButton().progress().setValue(progress) super(DownloadManager, self).timerEvent(evt) def __startUpdateTimer(self): """ Private slot to start the update timer. """ if self.activeDownloadsCount() and not self.__updateTimer.isActive(): self.__updateTimer.start(DownloadManager.UpdateTimerTimeout, self) def __stopUpdateTimer(self): """ Private slot to stop the update timer. """ self.__updateTimer.stop() ########################################################################### ## Context menu related methods below ########################################################################### def __currentItem(self): """ Private method to get a reference to the current item. @return reference to the current item (DownloadItem) """ index = self.downloadsView.currentIndex() if index and index.isValid(): row = index.row() return self.__downloads[row] return None def __contextMenuOpen(self): """ Private method to open the downloaded file. """ itm = self.__currentItem() if itm is not None: itm.openFile() def __contextMenuOpenFolder(self): """ Private method to open the folder containing the downloaded file. """ itm = self.__currentItem() if itm is not None: itm.openFolder() def __contextMenuCancel(self): """ Private method to cancel the current download. """ itm = self.__currentItem() if itm is not None: itm.cancelDownload() def __contextMenuGotoPage(self): """ Private method to open the download page. """ itm = self.__currentItem() if itm is not None: url = itm.getPageUrl() WebBrowserWindow.mainWindow().openUrl(url, "") def __contextMenuCopyLink(self): """ Private method to copy the download link to the clipboard. """ itm = self.__currentItem() if itm is not None: url = itm.getPageUrl().toDisplayString(QUrl.FullyDecoded) QApplication.clipboard().setText(url) def __contextMenuSelectAll(self): """ Private method to select all downloads. """ self.downloadsView.selectAll() def __contextMenuRemoveSelected(self): """ Private method to remove the selected downloads from the list. """ self.downloadsView.removeSelected()
class SpeedDial(QObject): """ Class implementing the speed dial. @signal pagesChanged() emitted after the list of pages changed @signal speedDialSaved() emitted after the speed dial data was saved """ pagesChanged = pyqtSignal() speedDialSaved = pyqtSignal() def __init__(self, parent=None): """ Constructor @param parent reference to the parent object (QObject) """ super(SpeedDial, self).__init__(parent) self.__regenerateScript = True self.__webPages = [] self.__webFrames = [] self.__initialScript = "" self.__thumbnailsDirectory = "" self.__thumbnailers = [] self.__initialize() self.pagesChanged.connect(self.__pagesChanged) self.__saveTimer = AutoSaver(self, self.save) self.pagesChanged.connect(self.__saveTimer.changeOccurred) def addWebFrame(self, frame): """ Public method to add a web frame. @param frame reference to the frame to be added (QWebFrame) """ if frame not in self.__webFrames: self.__webFrames.append(frame) def addPage(self, url, title): """ Public method to add a page for the given data. @param url URL of the page (QUrl) @param title title of the page (string) """ if url.isEmpty(): return from .Page import Page page = Page(url.toString(), title) self.__webPages.append(page) self.pagesChanged.emit() def removePage(self, url): """ Public method to remove a page. @param url URL of the page (QUrl) """ page = self.pageForUrl(url) if not page.url: return self.removeImageForUrl(page.url) self.__webPages.remove(page) self.pagesChanged.emit() def __imageFileName(self, url): """ Private method to generate the image file name for a URL. @param url URL to generate the file name from (string) @return name of the image file (string) """ return os.path.join( self.__thumbnailsDirectory, str(QCryptographicHash.hash(QByteArray(url.encode("utf-8")), QCryptographicHash.Md5).toHex(), encoding="utf-8") + ".png") def initialScript(self): """ Public method to get the 'initial' JavaScript script. @return initial JavaScript script (string) """ if self.__regenerateScript: self.__regenerateScript = False self.__initialScript = "" for page in self.__webPages: if page.broken: imgSource = "qrc:icons/brokenPage.png" else: imgSource = self.__imageFileName(page.url) if not os.path.exists(imgSource): self.loadThumbnail(page.url) imgSource = "qrc:icons/loading.gif" if not page.url: imgSource = "" else: imgSource = QUrl.fromLocalFile(imgSource).toString() self.__initialScript += \ "addBox('{0}', '{1}', '{2}');\n".format( page.url, Utilities.html_uencode(page.title), imgSource) return self.__initialScript def getFileName(self): """ Public method to get the file name of the user agents file. @return name of the user agents file (string) """ return os.path.join( Utilities.getConfigDir(), "browser", "speedDial.xml") def __initialize(self): """ Private method to initialize the speed dial. """ self.__thumbnailsDirectory = os.path.join( Utilities.getConfigDir(), "browser", "thumbnails") # Create directory if it does not exist yet if not os.path.exists(self.__thumbnailsDirectory): os.makedirs(self.__thumbnailsDirectory) self.__load() def reload(self): """ Public method to reload the speed dial data. """ self.__load() def __load(self): """ Private method to load the speed dial configuration. """ allPages, pagesPerRow, speedDialSize = [], 0, 0 speedDialFile = self.getFileName() if os.path.exists(speedDialFile): from .SpeedDialReader import SpeedDialReader reader = SpeedDialReader() allPages, pagesPerRow, speedDialSize = reader.read(speedDialFile) self.__pagesPerRow = pagesPerRow if pagesPerRow else 4 self.__speedDialSize = speedDialSize if speedDialSize else 231 if allPages: self.__webPages = allPages self.pagesChanged.emit() else: allPages = \ 'url:"http://eric-ide.python-projects.org/"|'\ 'title:"Eric Web Site";'\ 'url:"http://www.riverbankcomputing.com/"|'\ 'title:"PyQt Web Site";'\ 'url:"http://www.qt.io/"|title:"Qt Web Site";'\ 'url:"http://blog.qt.digia.com/"|title:"Qt Blog";'\ 'url:"http://www.python.org"|title:"Python Language Website";'\ 'url:"http://www.google.com"|title:"Google";' self.changed(allPages) def save(self): """ Public method to save the speed dial configuration. """ from .SpeedDialWriter import SpeedDialWriter speedDialFile = self.getFileName() writer = SpeedDialWriter() if not writer.write(speedDialFile, self.__webPages, self.__pagesPerRow, self.__speedDialSize): E5MessageBox.critical( None, self.tr("Saving Speed Dial data"), self.tr( """<p>Speed Dial data could not be saved to""" """ <b>{0}</b></p>""").format(speedDialFile)) else: self.speedDialSaved.emit() def close(self): """ Public method to close the user agents manager. """ self.__saveTimer.saveIfNeccessary() def pageForUrl(self, url): """ Public method to get the page for the given URL. @param url URL to be searched for (QUrl) @return page for the URL (Page) """ urlString = url.toString() for page in self.__webPages: if page.url == urlString: return page from .Page import Page return Page() def urlForShortcut(self, key): """ Public method to get the URL for the given shortcut key. @param key shortcut key (integer) @return URL for the key (QUrl) """ if key < 0 or len(self.__webPages) <= key: return QUrl() return QUrl.fromEncoded(self.__webPages[key].url.encode("utf-8")) @pyqtSlot(str) def changed(self, allPages): """ Public slot to react on changed pages. @param allPages string giving all pages (string) """ if not allPages: return entries = allPages.split('";') self.__webPages = [] from .Page import Page for entry in entries: if not entry: continue tmp = entry.split('"|') if len(tmp) == 2: broken = False elif len(tmp) == 3: broken = "brokenPage" in tmp[2][5:] else: continue page = Page(tmp[0][5:], tmp[1][7:], broken) self.__webPages.append(page) self.pagesChanged.emit() @pyqtSlot(str) @pyqtSlot(str, bool) def loadThumbnail(self, url, loadTitle=False): """ Public slot to load a thumbnail of the given URL. @param url URL of the thumbnail (string) @param loadTitle flag indicating to get the title for the thumbnail from the site (boolean) """ if not url: return from .PageThumbnailer import PageThumbnailer thumbnailer = PageThumbnailer(self) thumbnailer.setUrl(QUrl.fromEncoded(url.encode("utf-8"))) thumbnailer.setLoadTitle(loadTitle) thumbnailer.thumbnailCreated.connect(self.__thumbnailCreated) self.__thumbnailers.append(thumbnailer) thumbnailer.start() @pyqtSlot(str) def removeImageForUrl(self, url): """ Public slot to remove the image for a URL. @param url URL to remove the image for (string) """ fileName = self.__imageFileName(url) if os.path.exists(fileName): os.remove(fileName) @pyqtSlot(str, result=str) def urlFromUserInput(self, url): """ Public slot to get the URL from user input. @param url URL entered by the user (string) @return sanitized URL (string) """ return QUrl.fromUserInput(url).toString() @pyqtSlot(str, result=str) def unescapeTitle(self, title): """ Public slot to unescape the titel string. @param title escaped title (string) @return un-escaped title (string) """ return Utilities.html_udecode(title) @pyqtSlot(int) def setPagesInRow(self, count): """ Public slot to set the number of pages per row. @param count number of pages per row (integer) """ self.__pagesPerRow = count self.__saveTimer.changeOccurred() def pagesInRow(self): """ Public method to get the number of dials per row. @return number of dials per row (integer) """ return self.__pagesPerRow @pyqtSlot(int) def setSdSize(self, size): """ Public slot to set the size of the speed dial. @param size size of the speed dial (integer) """ self.__speedDialSize = size self.__saveTimer.changeOccurred() def sdSize(self): """ Public method to get the speed dial size. @return speed dial size (integer) """ return self.__speedDialSize def __thumbnailCreated(self, image): """ Private slot to handle the creation of a thumbnail image. @param image thumbnail image (QPixmap) """ from .PageThumbnailer import PageThumbnailer thumbnailer = self.sender() if not isinstance(thumbnailer, PageThumbnailer) or \ thumbnailer not in self.__thumbnailers: return loadTitle = thumbnailer.loadTitle() title = thumbnailer.title() url = thumbnailer.url().toString() fileName = self.__imageFileName(url) if image.isNull(): fileName = "qrc:icons/brokenPage.png" title = self.tr("Unable to load") loadTitle = True page = self.pageForUrl(thumbnailer.url()) page.broken = True else: if not image.save(fileName): qWarning( "SpeedDial.__thumbnailCreated: Cannot save thumbnail" " to {0}".format(fileName)) fileName = QUrl.fromLocalFile(fileName).toString() self.__regenerateScript = True for frame in self.__cleanFrames(): frame.evaluateJavaScript("setImageToUrl('{0}', '{1}');".format( url, fileName)) if loadTitle: frame.evaluateJavaScript("setTitleToUrl('{0}', '{1}');".format( url, Utilities.html_uencode(title))) thumbnailer.deleteLater() self.__thumbnailers.remove(thumbnailer) def __cleanFrames(self): """ Private method to clean all frames. @return list of speed dial frames (list of QWebFrame) """ frames = [] for frame in self.__webFrames[:]: if frame.url().toString() == "eric:speeddial": frames.append(frame) else: self.__webFrames.remove(frame) return frames def __pagesChanged(self): """ Private slot to react on a change of the pages configuration. """ # update all speed dial pages self.__regenerateScript = True for frame in self.__cleanFrames(): frame.page().triggerAction(QWebPage.Reload)
class HistoryManager(QWebHistoryInterface): """ Class implementing the history manager. @signal historyCleared() emitted after the history has been cleared @signal historyReset() emitted after the history has been reset @signal entryAdded(HistoryEntry) emitted after a history entry has been added @signal entryRemoved(HistoryEntry) emitted after a history entry has been removed @signal entryUpdated(int) emitted after a history entry has been updated @signal historySaved() emitted after the history was saved """ historyCleared = pyqtSignal() historyReset = pyqtSignal() entryAdded = pyqtSignal(HistoryEntry) entryRemoved = pyqtSignal(HistoryEntry) entryUpdated = pyqtSignal(int) historySaved = pyqtSignal() def __init__(self, parent=None): """ Constructor @param parent reference to the parent object (QObject) """ super(HistoryManager, self).__init__(parent) self.__saveTimer = AutoSaver(self, self.save) self.__daysToExpire = Preferences.getHelp("HistoryLimit") self.__history = [] self.__lastSavedUrl = "" self.__expiredTimer = QTimer(self) self.__expiredTimer.setSingleShot(True) self.__expiredTimer.timeout.connect(self.__checkForExpired) self.__frequencyTimer = QTimer(self) self.__frequencyTimer.setSingleShot(True) self.__frequencyTimer.timeout.connect(self.__refreshFrequencies) self.entryAdded.connect(self.__saveTimer.changeOccurred) self.entryRemoved.connect(self.__saveTimer.changeOccurred) self.__load() from .HistoryModel import HistoryModel from .HistoryFilterModel import HistoryFilterModel from .HistoryTreeModel import HistoryTreeModel self.__historyModel = HistoryModel(self, self) self.__historyFilterModel = \ HistoryFilterModel(self.__historyModel, self) self.__historyTreeModel = \ HistoryTreeModel(self.__historyFilterModel, self) super(HistoryManager, self).setDefaultInterface(self) self.__startFrequencyTimer() def close(self): """ Public method to close the history manager. """ # remove history items on application exit if self.__daysToExpire == -2: self.clear() self.__saveTimer.saveIfNeccessary() def history(self): """ Public method to return the history. @return reference to the list of history entries (list of HistoryEntry) """ return self.__history[:] def setHistory(self, history, loadedAndSorted=False): """ Public method to set a new history. @param history reference to the list of history entries to be set (list of HistoryEntry) @param loadedAndSorted flag indicating that the list is sorted (boolean) """ self.__history = history[:] if not loadedAndSorted: self.__history.sort() self.__checkForExpired() if loadedAndSorted: try: self.__lastSavedUrl = self.__history[0].url except IndexError: self.__lastSavedUrl = "" else: self.__lastSavedUrl = "" self.__saveTimer.changeOccurred() self.historyReset.emit() def historyContains(self, url): """ Public method to check the history for an entry. @param url URL to check for (string) @return flag indicating success (boolean) """ return self.__historyFilterModel.historyContains(url) def _addHistoryEntry(self, itm): """ Protected method to add a history item. @param itm reference to the history item to add (HistoryEntry) """ globalSettings = QWebSettings.globalSettings() if globalSettings.testAttribute(QWebSettings.PrivateBrowsingEnabled): return self.__history.insert(0, itm) self.entryAdded.emit(itm) if len(self.__history) == 1: self.__checkForExpired() def _removeHistoryEntry(self, itm): """ Protected method to remove a history item. @param itm reference to the history item to remove (HistoryEntry) """ self.__lastSavedUrl = "" self.__history.remove(itm) self.entryRemoved.emit(itm) def addHistoryEntry(self, url): """ Public method to add a history entry. @param url URL to be added (string) """ cleanurl = QUrl(url) if cleanurl.scheme() not in ["eric", "about"]: if cleanurl.password(): # don't save the password in the history cleanurl.setPassword("") if cleanurl.host(): cleanurl.setHost(cleanurl.host().lower()) itm = HistoryEntry(cleanurl.toString(), QDateTime.currentDateTime()) self._addHistoryEntry(itm) def updateHistoryEntry(self, url, title): """ Public method to update a history entry. @param url URL of the entry to update (string) @param title title of the entry to update (string) """ cleanurl = QUrl(url) if cleanurl.scheme() not in ["eric", "about"]: for index in range(len(self.__history)): if url == self.__history[index].url: self.__history[index].title = title self.__saveTimer.changeOccurred() if not self.__lastSavedUrl: self.__lastSavedUrl = self.__history[index].url self.entryUpdated.emit(index) break def removeHistoryEntry(self, url, title=""): """ Public method to remove a history entry. @param url URL of the entry to remove (QUrl) @param title title of the entry to remove (string) """ for index in range(len(self.__history)): if url == QUrl(self.__history[index].url) and \ (not title or title == self.__history[index].title): self._removeHistoryEntry(self.__history[index]) break def historyModel(self): """ Public method to get a reference to the history model. @return reference to the history model (HistoryModel) """ return self.__historyModel def historyFilterModel(self): """ Public method to get a reference to the history filter model. @return reference to the history filter model (HistoryFilterModel) """ return self.__historyFilterModel def historyTreeModel(self): """ Public method to get a reference to the history tree model. @return reference to the history tree model (HistoryTreeModel) """ return self.__historyTreeModel def __checkForExpired(self): """ Private slot to check entries for expiration. """ if self.__daysToExpire < 0 or len(self.__history) == 0: return now = QDateTime.currentDateTime() nextTimeout = 0 while self.__history: checkForExpired = QDateTime(self.__history[-1].dateTime) checkForExpired.setDate(checkForExpired.date().addDays( self.__daysToExpire)) if now.daysTo(checkForExpired) > 7: nextTimeout = 7 * 86400 else: nextTimeout = now.secsTo(checkForExpired) if nextTimeout > 0: break itm = self.__history.pop(-1) self.__lastSavedUrl = "" self.entryRemoved.emit(itm) self.__saveTimer.saveIfNeccessary() if nextTimeout > 0: self.__expiredTimer.start(nextTimeout * 1000) def daysToExpire(self): """ Public method to get the days for entry expiration. @return days for entry expiration (integer) """ return self.__daysToExpire def setDaysToExpire(self, limit): """ Public method to set the days for entry expiration. @param limit days for entry expiration (integer) """ if self.__daysToExpire == limit: return self.__daysToExpire = limit self.__checkForExpired() self.__saveTimer.changeOccurred() def preferencesChanged(self): """ Public method to indicate a change of preferences. """ self.setDaysToExpire(Preferences.getHelp("HistoryLimit")) @pyqtSlot() def clear(self, period=0): """ Public slot to clear the complete history. @param period history period in milliseconds to be cleared (integer) """ if period == 0: self.__history = [] self.historyReset.emit() else: breakMS = QDateTime.currentMSecsSinceEpoch() - period while self.__history and \ (QDateTime(self.__history[0].dateTime).toMSecsSinceEpoch() > breakMS): itm = self.__history.pop(0) self.entryRemoved.emit(itm) self.__lastSavedUrl = "" self.__saveTimer.changeOccurred() self.__saveTimer.saveIfNeccessary() self.historyCleared.emit() def getFileName(self): """ Public method to get the file name of the history file. @return name of the history file (string) """ return os.path.join(Utilities.getConfigDir(), "browser", "history") def reload(self): """ Public method to reload the history. """ self.__load() def __load(self): """ Private method to load the saved history entries from disk. """ historyFile = QFile(self.getFileName()) if not historyFile.exists(): return if not historyFile.open(QIODevice.ReadOnly): E5MessageBox.warning( None, self.tr("Loading History"), self.tr("""<p>Unable to open history file <b>{0}</b>.<br/>""" """Reason: {1}</p>""").format( historyFile.fileName, historyFile.errorString())) return history = [] # double check, that the history file is sorted as it is read needToSort = False lastInsertedItem = HistoryEntry() data = QByteArray(historyFile.readAll()) stream = QDataStream(data, QIODevice.ReadOnly) stream.setVersion(QDataStream.Qt_4_6) while not stream.atEnd(): ver = stream.readUInt32() if ver != HISTORY_VERSION: continue itm = HistoryEntry() itm.url = Utilities.readStringFromStream(stream) stream >> itm.dateTime itm.title = Utilities.readStringFromStream(stream) if not itm.dateTime.isValid(): continue if itm == lastInsertedItem: if not lastInsertedItem.title and len(history) > 0: history[0].title = itm.title continue if not needToSort and history and lastInsertedItem < itm: needToSort = True history.insert(0, itm) lastInsertedItem = itm historyFile.close() if needToSort: history.sort() self.setHistory(history, True) # if the history had to be sorted, rewrite the history sorted if needToSort: self.__lastSavedUrl = "" self.__saveTimer.changeOccurred() def save(self): """ Public slot to save the history entries to disk. """ historyFile = QFile(self.getFileName()) if not historyFile.exists(): self.__lastSavedUrl = "" saveAll = self.__lastSavedUrl == "" first = len(self.__history) - 1 if not saveAll: # find the first one to save for index in range(len(self.__history)): if self.__history[index].url == self.__lastSavedUrl: first = index - 1 break if first == len(self.__history) - 1: saveAll = True if saveAll: # use a temporary file when saving everything f = QTemporaryFile() f.setAutoRemove(False) opened = f.open() else: f = historyFile opened = f.open(QIODevice.Append) if not opened: E5MessageBox.warning( None, self.tr("Saving History"), self.tr("""<p>Unable to open history file <b>{0}</b>.<br/>""" """Reason: {1}</p>""").format(f.fileName(), f.errorString())) return for index in range(first, -1, -1): data = QByteArray() stream = QDataStream(data, QIODevice.WriteOnly) stream.setVersion(QDataStream.Qt_4_6) itm = self.__history[index] stream.writeUInt32(HISTORY_VERSION) stream.writeString(itm.url.encode("utf-8")) stream << itm.dateTime stream.writeString(itm.title.encode('utf-8')) f.write(data) f.close() if saveAll: if historyFile.exists() and not historyFile.remove(): E5MessageBox.warning( None, self.tr("Saving History"), self.tr( """<p>Error removing old history file <b>{0}</b>.""" """<br/>Reason: {1}</p>""").format( historyFile.fileName(), historyFile.errorString())) if not f.copy(historyFile.fileName()): E5MessageBox.warning( None, self.tr("Saving History"), self.tr( """<p>Error moving new history file over old one """ """(<b>{0}</b>).<br/>Reason: {1}</p>""").format( historyFile.fileName(), f.errorString())) self.historySaved.emit() try: self.__lastSavedUrl = self.__history[0].url except IndexError: self.__lastSavedUrl = "" def __refreshFrequencies(self): """ Private slot to recalculate the refresh frequencies. """ self.__historyFilterModel.recalculateFrequencies() self.__startFrequencyTimer() def __startFrequencyTimer(self): """ Private method to start the timer to recalculate the frequencies. """ tomorrow = QDateTime(QDate.currentDate().addDays(1), QTime(3, 0)) self.__frequencyTimer.start( QDateTime.currentDateTime().secsTo(tomorrow) * 1000)
class UserAgentManager(QObject): """ Class implementing a user agent manager. @signal changed() emitted to indicate a change @signal userAgentSettingsSaved() emitted after the user agent settings were saved """ changed = pyqtSignal() userAgentSettingsSaved = pyqtSignal() def __init__(self, parent=None): """ Constructor @param parent reference to the parent object (QObject) """ super(UserAgentManager, self).__init__(parent) self.__agents = {} # dictionary with agent strings indexed by host name self.__loaded = False self.__saveTimer = AutoSaver(self, self.save) self.changed.connect(self.__saveTimer.changeOccurred) def getFileName(self): """ Public method to get the file name of the user agents file. @return name of the user agents file (string) """ return os.path.join( Utilities.getConfigDir(), "browser", "userAgentSettings.xml") def save(self): """ Public slot to save the user agent entries to disk. """ if not self.__loaded: return from .UserAgentWriter import UserAgentWriter agentFile = self.getFileName() writer = UserAgentWriter() if not writer.write(agentFile, self.__agents): E5MessageBox.critical( None, self.tr("Saving user agent data"), self.tr( """<p>User agent data could not be saved to""" """ <b>{0}</b></p>""").format(agentFile)) else: self.userAgentSettingsSaved.emit() def __load(self): """ Private method to load the saved user agent settings. """ agentFile = self.getFileName() if not os.path.exists(agentFile): self.__loadNonXml(os.path.splitext(agentFile)[0]) else: from .UserAgentReader import UserAgentReader reader = UserAgentReader() self.__agents = reader.read(agentFile) if reader.error() != QXmlStreamReader.NoError: E5MessageBox.warning( None, self.tr("Loading user agent data"), self.tr("""Error when loading user agent data on""" """ line {0}, column {1}:\n{2}""") .format(reader.lineNumber(), reader.columnNumber(), reader.errorString())) self.__loaded = True def __loadNonXml(self, agentFile): """ Private method to load non-XML user agent files. This method is to convert from the old, non-XML format to the new XML based format. @param agentFile name of the non-XML user agent file (string) """ if os.path.exists(agentFile): try: f = open(agentFile, "r", encoding="utf-8") lines = f.read() f.close() except IOError as err: E5MessageBox.critical( None, self.tr("Loading user agent data"), self.tr("""<p>User agent data could not be loaded """ """from <b>{0}</b></p>""" """<p>Reason: {1}</p>""") .format(agentFile, str(err))) return for line in lines.splitlines(): if not line or \ line.startswith("#") or \ "@@" not in line: continue host, agent = line.split("@@", 1) self.__agents[host] = agent os.remove(agentFile) self.__loaded = True # this does the conversion self.save() def reload(self): """ Public method to reload the user agent settings. """ if not self.__loaded: return self.__agents = {} self.__load() def close(self): """ Public method to close the user agents manager. """ self.__saveTimer.saveIfNeccessary() def removeUserAgent(self, host): """ Public method to remove a user agent entry. @param host host name (string) """ if host in self.__agents: del self.__agents[host] self.changed.emit() def allHostNames(self): """ Public method to get a list of all host names we a user agent setting for. @return sorted list of all host names (list of strings) """ if not self.__loaded: self.__load() return sorted(self.__agents.keys()) def hostsCount(self): """ Public method to get the number of available user agent settings. @return number of user agent settings (integer) """ if not self.__loaded: self.__load() return len(self.__agents) def userAgent(self, host): """ Public method to get the user agent setting for a host. @param host host name (string) @return user agent string (string) """ if not self.__loaded: self.__load() if host not in self.__agents: return "" return self.__agents[host] def setUserAgent(self, host, agent): """ Public method to set the user agent string for a host. @param host host name (string) @param agent user agent string (string) """ if host != "" and agent != "": self.__agents[host] = agent self.changed.emit() def userAgentForUrl(self, url): """ Public method to determine the user agent for the given URL. @param url URL to determine user agent for (QUrl) @return user agent string (string) """ if url.isValid(): host = url.host() return self.userAgent(host) return "" def setUserAgentForUrl(self, url, agent): """ Public method to set the user agent string for an URL. @param url URL to register user agent setting for (QUrl) @param agent new current user agent string (string) """ if url.isValid(): host = url.host() self.setUserAgent(host, agent)
class TaskViewer(QTreeWidget): """ Class implementing the task viewer. @signal displayFile(str, int) emitted to go to a file task """ displayFile = pyqtSignal(str, int) def __init__(self, parent, project): """ Constructor @param parent the parent (QWidget) @param project reference to the project object """ super(TaskViewer, self).__init__(parent) self.setRootIsDecorated(False) self.setItemsExpandable(False) self.setSortingEnabled(True) self.__headerItem = QTreeWidgetItem([ "", "", self.tr("Summary"), self.tr("Filename"), self.tr("Line"), "" ]) self.__headerItem.setIcon(0, UI.PixmapCache.getIcon("taskCompleted.png")) self.__headerItem.setIcon(1, UI.PixmapCache.getIcon("taskPriority.png")) self.setHeaderItem(self.__headerItem) self.header().setSortIndicator(2, Qt.AscendingOrder) self.__resizeColumns() self.tasks = [] self.copyTask = None self.projectOpen = False self.project = project self.projectTasksScanFilter = "" from .TaskFilter import TaskFilter self.taskFilter = TaskFilter() self.taskFilter.setActive(False) self.__projectTasksSaveTimer = AutoSaver(self, self.saveProjectTasks) self.__projectTasksMenu = QMenu(self.tr("P&roject Tasks"), self) self.__projectTasksMenu.addAction(self.tr("&Regenerate project tasks"), self.__regenerateProjectTasks) self.__projectTasksMenu.addSeparator() self.__projectTasksMenu.addAction( self.tr("&Configure scan options"), self.__configureProjectTasksScanOptions) self.__menu = QMenu(self) self.__menu.addAction(self.tr("&New Task..."), self.__newTask) self.__menu.addSeparator() self.projectTasksMenuItem = self.__menu.addMenu( self.__projectTasksMenu) self.__menu.addSeparator() self.gotoItem = self.__menu.addAction(self.tr("&Go To"), self.__goToTask) self.__menu.addSeparator() self.copyItem = self.__menu.addAction(self.tr("&Copy"), self.__copyTask) self.pasteItem = self.__menu.addAction(self.tr("&Paste"), self.__pasteTask) self.deleteItem = self.__menu.addAction(self.tr("&Delete"), self.__deleteTask) self.__menu.addSeparator() self.markCompletedItem = self.__menu.addAction( self.tr("&Mark Completed"), self.__markCompleted) self.__menu.addAction(self.tr("Delete Completed &Tasks"), self.__deleteCompleted) self.__menu.addSeparator() self.__menu.addAction(self.tr("P&roperties..."), self.__editTaskProperties) self.__menu.addSeparator() self.__menuFilteredAct = self.__menu.addAction( self.tr("&Filtered display")) self.__menuFilteredAct.setCheckable(True) self.__menuFilteredAct.setChecked(False) self.__menuFilteredAct.triggered[bool].connect(self.__activateFilter) self.__menu.addAction(self.tr("Filter c&onfiguration..."), self.__configureFilter) self.__menu.addSeparator() self.__menu.addAction(self.tr("Resi&ze columns"), self.__resizeColumns) self.__menu.addSeparator() self.__menu.addAction(self.tr("Configure..."), self.__configure) self.__backMenu = QMenu(self) self.__backMenu.addAction(self.tr("&New Task..."), self.__newTask) self.__backMenu.addSeparator() self.backProjectTasksMenuItem = self.__backMenu.addMenu( self.__projectTasksMenu) self.__backMenu.addSeparator() self.backPasteItem = self.__backMenu.addAction(self.tr("&Paste"), self.__pasteTask) self.__backMenu.addSeparator() self.__backMenu.addAction(self.tr("Delete Completed &Tasks"), self.__deleteCompleted) self.__backMenu.addSeparator() self.__backMenuFilteredAct = self.__backMenu.addAction( self.tr("&Filtered display")) self.__backMenuFilteredAct.setCheckable(True) self.__backMenuFilteredAct.setChecked(False) self.__backMenuFilteredAct.triggered[bool].connect( self.__activateFilter) self.__backMenu.addAction(self.tr("Filter c&onfiguration..."), self.__configureFilter) self.__backMenu.addSeparator() self.__backMenu.addAction(self.tr("Resi&ze columns"), self.__resizeColumns) self.__backMenu.addSeparator() self.__backMenu.addAction(self.tr("Configure..."), self.__configure) self.__activating = False self.setContextMenuPolicy(Qt.CustomContextMenu) self.customContextMenuRequested.connect(self.__showContextMenu) self.itemActivated.connect(self.__taskItemActivated) self.setWindowIcon(UI.PixmapCache.getIcon("eric.png")) def __resort(self): """ Private method to resort the tree. """ self.sortItems(self.sortColumn(), self.header().sortIndicatorOrder()) def __resizeColumns(self): """ Private method to resize the list columns. """ self.header().resizeSections(QHeaderView.ResizeToContents) self.header().setStretchLastSection(True) def __refreshDisplay(self): """ Private method to refresh the display. """ for task in self.tasks: task.setHidden(not self.taskFilter.showTask(task)) self.__resort() self.__resizeColumns() def __taskItemActivated(self, itm, col): """ Private slot to handle the activation of an item. @param itm reference to the activated item (QTreeWidgetItem) @param col column the item was activated in (integer) """ if not self.__activating: self.__activating = True fn = itm.getFilename() if fn: self.displayFile.emit(fn, itm.getLineno()) else: self.__editTaskProperties() self.__activating = False def __showContextMenu(self, coord): """ Private slot to show the context menu of the list. @param coord the position of the mouse pointer (QPoint) """ itm = self.itemAt(coord) coord = self.mapToGlobal(coord) if itm is None: self.backProjectTasksMenuItem.setEnabled(self.projectOpen) if self.copyTask: self.backPasteItem.setEnabled(True) else: self.backPasteItem.setEnabled(False) self.__backMenu.popup(coord) else: self.projectTasksMenuItem.setEnabled(self.projectOpen) if itm.getFilename(): self.gotoItem.setEnabled(True) self.deleteItem.setEnabled(True) self.markCompletedItem.setEnabled(False) self.copyItem.setEnabled(False) else: self.gotoItem.setEnabled(False) self.deleteItem.setEnabled(True) self.markCompletedItem.setEnabled(True) self.copyItem.setEnabled(True) if self.copyTask: self.pasteItem.setEnabled(True) else: self.pasteItem.setEnabled(False) self.__menu.popup(coord) def setProjectOpen(self, o=False): """ Public slot to set the project status. @param o flag indicating the project status """ self.projectOpen = o def addTask(self, summary, priority=1, filename="", lineno=0, completed=False, _time=0, isProjectTask=False, taskType=Task.TypeTodo, description=""): """ Public slot to add a task. @param summary summary text of the task (string) @param priority priority of the task (0=high, 1=normal, 2=low) @param filename filename containing the task (string) @param lineno line number containing the task (integer) @param completed flag indicating completion status (boolean) @param _time creation time of the task (float, if 0 use current time) @param isProjectTask flag indicating a task related to the current project (boolean) @param taskType type of the task (one of Task.TypeFixme, Task.TypeTodo, Task.TypeWarning, Task.TypeNote) @param description explanatory text of the task (string) """ task = Task(summary, priority, filename, lineno, completed, _time, isProjectTask, taskType, self.project, description) self.tasks.append(task) self.addTopLevelItem(task) task.setHidden(not self.taskFilter.showTask(task)) self.__resort() self.__resizeColumns() if isProjectTask: self.__projectTasksSaveTimer.changeOccurred() def addFileTask(self, summary, filename, lineno, taskType=Task.TypeTodo, description=""): """ Public slot to add a file related task. @param summary summary text of the task (string) @param filename filename containing the task (string) @param lineno line number containing the task (integer) @param taskType type of the task (one of Task.TypeFixme, Task.TypeTodo, Task.TypeWarning, Task.TypeNote) @param description explanatory text of the task (string) """ self.addTask( summary, filename=filename, lineno=lineno, isProjectTask=(self.project and self.project.isProjectSource(filename)), taskType=taskType, description=description) def getProjectTasks(self): """ Public method to retrieve all project related tasks. @return copy of tasks (list of Task) """ tasks = [task for task in self.tasks if task.isProjectTask()] return tasks[:] def getGlobalTasks(self): """ Public method to retrieve all non project related tasks. @return copy of tasks (list of Task) """ tasks = [task for task in self.tasks if not task.isProjectTask()] return tasks[:] def clearTasks(self): """ Public slot to clear all tasks from display. """ self.tasks = [] self.clear() def clearProjectTasks(self, fileOnly=False): """ Public slot to clear project related tasks. @keyparam fileOnly flag indicating to clear only file related project tasks (boolean) """ for task in self.tasks[:]: if (fileOnly and task.isProjectFileTask()) or \ (not fileOnly and task.isProjectTask()): if self.copyTask == task: self.copyTask = None index = self.indexOfTopLevelItem(task) self.takeTopLevelItem(index) self.tasks.remove(task) del task def clearFileTasks(self, filename, conditionally=False): """ Public slot to clear all tasks related to a file. @param filename name of the file (string) @param conditionally flag indicating to clear the tasks of the file checking some conditions (boolean) """ if conditionally: if self.project and self.project.isProjectSource(filename): # project related tasks will not be cleared return if not Preferences.getTasks("ClearOnFileClose"): return for task in self.tasks[:]: if task.getFilename() == filename: if self.copyTask == task: self.copyTask = None index = self.indexOfTopLevelItem(task) self.takeTopLevelItem(index) self.tasks.remove(task) if task.isProjectTask: self.__projectTasksSaveTimer.changeOccurred() del task def __editTaskProperties(self): """ Private slot to handle the "Properties" context menu entry. """ from .TaskPropertiesDialog import TaskPropertiesDialog task = self.currentItem() dlg = TaskPropertiesDialog(task, self, self.projectOpen) ro = task.getFilename() != "" if ro: dlg.setReadOnly() if dlg.exec_() == QDialog.Accepted and not ro: data = dlg.getData() task.setSummary(data[0]) task.setPriority(data[1]) task.setCompleted(data[2]) task.setProjectTask(data[3]) task.setDescription(data[4]) self.__projectTasksSaveTimer.changeOccurred() def __newTask(self): """ Private slot to handle the "New Task" context menu entry. """ from .TaskPropertiesDialog import TaskPropertiesDialog dlg = TaskPropertiesDialog(None, self, self.projectOpen) if dlg.exec_() == QDialog.Accepted: data = dlg.getData() self.addTask(data[0], data[1], completed=data[2], isProjectTask=data[3], description=data[4]) def __markCompleted(self): """ Private slot to handle the "Mark Completed" context menu entry. """ task = self.currentItem() task.setCompleted(True) def __deleteCompleted(self): """ Private slot to handle the "Delete Completed Tasks" context menu entry. """ for task in self.tasks[:]: if task.isCompleted(): if self.copyTask == task: self.copyTask = None index = self.indexOfTopLevelItem(task) self.takeTopLevelItem(index) self.tasks.remove(task) if task.isProjectTask: self.__projectTasksSaveTimer.changeOccurred() del task ci = self.currentItem() if ci: ind = self.indexFromItem(ci, self.currentColumn()) self.scrollTo(ind, QAbstractItemView.PositionAtCenter) def __copyTask(self): """ Private slot to handle the "Copy" context menu entry. """ task = self.currentItem() self.copyTask = task def __pasteTask(self): """ Private slot to handle the "Paste" context menu entry. """ if self.copyTask: self.addTask(self.copyTask.summary, priority=self.copyTask.priority, completed=self.copyTask.completed, description=self.copyTask.description, isProjectTask=self.copyTask._isProjectTask) def __deleteTask(self): """ Private slot to handle the "Delete Task" context menu entry. """ task = self.currentItem() if self.copyTask == task: self.copyTask = None index = self.indexOfTopLevelItem(task) self.takeTopLevelItem(index) self.tasks.remove(task) if task.isProjectTask: self.__projectTasksSaveTimer.changeOccurred() del task ci = self.currentItem() if ci: ind = self.indexFromItem(ci, self.currentColumn()) self.scrollTo(ind, QAbstractItemView.PositionAtCenter) def __goToTask(self): """ Private slot to handle the "Go To" context menu entry. """ task = self.currentItem() self.displayFile.emit(task.getFilename(), task.getLineno()) def handlePreferencesChanged(self): """ Public slot to react to changes of the preferences. """ for task in self.tasks: task.colorizeTask() def __activateFilter(self, on): """ Private slot to handle the "Filtered display" context menu entry. @param on flag indicating the filter state (boolean) """ if on and not self.taskFilter.hasActiveFilter(): res = E5MessageBox.yesNo( self, self.tr("Activate task filter"), self.tr("""The task filter doesn't have any active filters.""" """ Do you want to configure the filter settings?"""), yesDefault=True) if not res: on = False else: self.__configureFilter() on = self.taskFilter.hasActiveFilter() self.taskFilter.setActive(on) self.__menuFilteredAct.setChecked(on) self.__backMenuFilteredAct.setChecked(on) self.__refreshDisplay() def __configureFilter(self): """ Private slot to handle the "Configure filter" context menu entry. """ from .TaskFilterConfigDialog import TaskFilterConfigDialog dlg = TaskFilterConfigDialog(self.taskFilter) if dlg.exec_() == QDialog.Accepted: dlg.configureTaskFilter(self.taskFilter) self.__refreshDisplay() def __configureProjectTasksScanOptions(self): """ Private slot to configure scan options for project tasks. """ filter, ok = QInputDialog.getText( self, self.tr("Scan Filter Patterns"), self.tr("Enter filename patterns of files" " to be excluded separated by a comma:"), QLineEdit.Normal, self.projectTasksScanFilter) if ok: self.projectTasksScanFilter = filter def __regenerateProjectTasks(self): """ Private slot to handle the "Regenerated project tasks" context menu entry. """ markers = { Task.TypeWarning: Preferences.getTasks("TasksWarningMarkers").split(), Task.TypeNote: Preferences.getTasks("TasksNoteMarkers").split(), Task.TypeTodo: Preferences.getTasks("TasksTodoMarkers").split(), Task.TypeFixme: Preferences.getTasks("TasksFixmeMarkers").split(), } files = self.project.pdata["SOURCES"] # apply file filter filterList = [ f.strip() for f in self.projectTasksScanFilter.split(",") if f.strip() ] if filterList: for filter in filterList: files = [f for f in files if not fnmatch.fnmatch(f, filter)] # remove all project tasks self.clearProjectTasks(fileOnly=True) # now process them progress = E5ProgressDialog(self.tr("Extracting project tasks..."), self.tr("Abort"), 0, len(files), self.tr("%v/%m Files")) progress.setMinimumDuration(0) progress.setWindowTitle(self.tr("Tasks")) count = 0 for file in files: progress.setLabelText( self.tr("Extracting project tasks...\n{0}").format(file)) progress.setValue(count) QApplication.processEvents() if progress.wasCanceled(): break fn = os.path.join(self.project.ppath, file) # read the file and split it into textlines try: text, encoding = Utilities.readEncodedFile(fn) lines = text.splitlines() except (UnicodeError, IOError): count += 1 progress.setValue(count) continue # now search tasks and record them lineIndex = 0 for line in lines: lineIndex += 1 shouldBreak = False for taskType, taskMarkers in markers.items(): for taskMarker in taskMarkers: index = line.find(taskMarker) if index > -1: task = line[index:] self.addFileTask(task, fn, lineIndex, taskType) shouldBreak = True break if shouldBreak: break count += 1 progress.setValue(len(files)) def __configure(self): """ Private method to open the configuration dialog. """ e5App().getObject("UserInterface").showPreferences("tasksPage") def saveProjectTasks(self): """ Public method to write the project tasks. """ if self.projectOpen and Preferences.getTasks("TasksProjectAutoSave"): self.project.writeTasks()
class OpenSearchManager(QObject): """ Class implementing a manager for open search engines. @signal changed() emitted to indicate a change @signal currentEngineChanged() emitted to indicate a change of the current search engine """ changed = pyqtSignal() currentEngineChanged = pyqtSignal() def __init__(self, parent=None): """ Constructor @param parent reference to the parent object (QObject) """ if parent is None: parent = e5App() super(OpenSearchManager, self).__init__(parent) self.__replies = [] self.__engines = {} self.__keywords = {} self.__current = "" self.__loading = False self.__saveTimer = AutoSaver(self, self.save) self.changed.connect(self.__saveTimer.changeOccurred) self.load() def close(self): """ Public method to close the open search engines manager. """ self.__saveTimer.saveIfNeccessary() def currentEngineName(self): """ Public method to get the name of the current search engine. @return name of the current search engine (string) """ return self.__current def setCurrentEngineName(self, name): """ Public method to set the current engine by name. @param name name of the new current engine (string) """ if name not in self.__engines: return self.__current = name self.currentEngineChanged.emit() self.changed.emit() def currentEngine(self): """ Public method to get a reference to the current engine. @return reference to the current engine (OpenSearchEngine) """ if not self.__current or self.__current not in self.__engines: return None return self.__engines[self.__current] def setCurrentEngine(self, engine): """ Public method to set the current engine. @param engine reference to the new current engine (OpenSearchEngine) """ if engine is None: return for engineName in self.__engines: if self.__engines[engineName] == engine: self.setCurrentEngineName(engineName) break def engine(self, name): """ Public method to get a reference to the named engine. @param name name of the engine (string) @return reference to the engine (OpenSearchEngine) """ if name not in self.__engines: return None return self.__engines[name] def engineExists(self, name): """ Public method to check, if an engine exists. @param name name of the engine (string) @return flag indicating an existing engine (boolean) """ return name in self.__engines def allEnginesNames(self): """ Public method to get a list of all engine names. @return sorted list of all engine names (list of strings) """ return sorted(self.__engines.keys()) def enginesCount(self): """ Public method to get the number of available engines. @return number of engines (integer) """ return len(self.__engines) def addEngine(self, engine): """ Public method to add a new search engine. @param engine URL of the engine definition file (QUrl) or name of a file containing the engine definition (string) or reference to an engine object (OpenSearchEngine) @return flag indicating success (boolean) """ from .OpenSearchEngine import OpenSearchEngine if isinstance(engine, QUrl): return self.__addEngineByUrl(engine) elif isinstance(engine, OpenSearchEngine): return self.__addEngineByEngine(engine) else: return self.__addEngineByFile(engine) def __addEngineByUrl(self, url): """ Private method to add a new search engine given its URL. @param url URL of the engine definition file (QUrl) @return flag indicating success (boolean) """ if not url.isValid(): return from Helpviewer.HelpWindow import HelpWindow reply = HelpWindow.networkAccessManager().get(QNetworkRequest(url)) reply.finished.connect(self.__engineFromUrlAvailable) reply.setParent(self) self.__replies.append(reply) return True def __addEngineByFile(self, filename): """ Private method to add a new search engine given a filename. @param filename name of a file containing the engine definition (string) @return flag indicating success (boolean) """ file_ = QFile(filename) if not file_.open(QIODevice.ReadOnly): return False from .OpenSearchReader import OpenSearchReader reader = OpenSearchReader() engine = reader.read(file_) if not self.__addEngineByEngine(engine): return False return True def __addEngineByEngine(self, engine): """ Private method to add a new search engine given a reference to an engine. @param engine reference to an engine object (OpenSearchEngine) @return flag indicating success (boolean) """ if engine is None: return False if not engine.isValid(): return False if engine.name() in self.__engines: return False engine.setParent(self) self.__engines[engine.name()] = engine self.changed.emit() return True def removeEngine(self, name): """ Public method to remove an engine. @param name name of the engine (string) """ if len(self.__engines) <= 1: return if name not in self.__engines: return engine = self.__engines[name] for keyword in [ k for k in self.__keywords if self.__keywords[k] == engine ]: del self.__keywords[keyword] del self.__engines[name] file_ = QDir(self.enginesDirectory()).filePath( self.generateEngineFileName(name)) QFile.remove(file_) if name == self.__current: self.setCurrentEngineName(list(self.__engines.keys())[0]) self.changed.emit() def generateEngineFileName(self, engineName): """ Public method to generate a valid engine file name. @param engineName name of the engine (string) @return valid engine file name (string) """ fileName = "" # strip special characters for c in engineName: if c.isspace(): fileName += '_' continue if c.isalnum(): fileName += c fileName += ".xml" return fileName def saveDirectory(self, dirName): """ Public method to save the search engine definitions to files. @param dirName name of the directory to write the files to (string) """ dir = QDir() if not dir.mkpath(dirName): return dir.setPath(dirName) from .OpenSearchWriter import OpenSearchWriter writer = OpenSearchWriter() for engine in list(self.__engines.values()): name = self.generateEngineFileName(engine.name()) fileName = dir.filePath(name) file = QFile(fileName) if not file.open(QIODevice.WriteOnly): continue writer.write(file, engine) def save(self): """ Public method to save the search engines configuration. """ if self.__loading: return self.saveDirectory(self.enginesDirectory()) Preferences.setHelp("WebSearchEngine", self.__current) keywords = [] for k in self.__keywords: if self.__keywords[k]: keywords.append((k, self.__keywords[k].name())) Preferences.setHelp("WebSearchKeywords", keywords) def loadDirectory(self, dirName): """ Public method to load the search engine definitions from files. @param dirName name of the directory to load the files from (string) @return flag indicating success (boolean) """ if not QFile.exists(dirName): return False success = False dir = QDir(dirName) for name in dir.entryList(["*.xml"]): fileName = dir.filePath(name) if self.__addEngineByFile(fileName): success = True return success def load(self): """ Public method to load the search engines configuration. """ self.__loading = True self.__current = Preferences.getHelp("WebSearchEngine") keywords = Preferences.getHelp("WebSearchKeywords") if not self.loadDirectory(self.enginesDirectory()): self.restoreDefaults() for keyword, engineName in keywords: self.__keywords[keyword] = self.engine(engineName) if self.__current not in self.__engines and \ len(self.__engines) > 0: self.__current = list(self.__engines.keys())[0] self.__loading = False self.currentEngineChanged.emit() def restoreDefaults(self): """ Public method to restore the default search engines. """ from .OpenSearchReader import OpenSearchReader from .DefaultSearchEngines import DefaultSearchEngines_rc # __IGNORE_WARNING__ defaultEngineFiles = [ "YouTube.xml", "Amazoncom.xml", "Bing.xml", "DeEn_Beolingus.xml", "Facebook.xml", "Google_Im_Feeling_Lucky.xml", "Google.xml", "LEO_DeuEng.xml", "LinuxMagazin.xml", "Reddit.xml", "Wikia_en.xml", "Wikia.xml", "Wikipedia.xml", "Wiktionary.xml", "Yahoo.xml" ] # Keep this list in sync with the contents of the resource file. reader = OpenSearchReader() for engineFileName in defaultEngineFiles: engineFile = QFile(":/" + engineFileName) if not engineFile.open(QIODevice.ReadOnly): continue engine = reader.read(engineFile) self.__addEngineByEngine(engine) def enginesDirectory(self): """ Public method to determine the directory containing the search engine descriptions. @return directory name (string) """ return os.path.join(Utilities.getConfigDir(), "browser", "searchengines") def __confirmAddition(self, engine): """ Private method to confirm the addition of a new search engine. @param engine reference to the engine to be added (OpenSearchEngine) @return flag indicating the engine shall be added (boolean) """ if engine is None or not engine.isValid(): return False host = QUrl(engine.searchUrlTemplate()).host() res = E5MessageBox.yesNo( None, "", self.tr("""<p>Do you want to add the following engine to your""" """ list of search engines?<br/><br/>Name: {0}<br/>""" """Searches on: {1}</p>""").format(engine.name(), host)) return res def __engineFromUrlAvailable(self): """ Private slot to add a search engine from the net. """ reply = self.sender() if reply is None: return if reply.error() != QNetworkReply.NoError: reply.close() if reply in self.__replies: self.__replies.remove(reply) return from .OpenSearchReader import OpenSearchReader reader = OpenSearchReader() engine = reader.read(reply) reply.close() if reply in self.__replies: self.__replies.remove(reply) if not engine.isValid(): return if self.engineExists(engine.name()): return if not self.__confirmAddition(engine): return if not self.__addEngineByEngine(engine): return def convertKeywordSearchToUrl(self, keywordSearch): """ Public method to get the search URL for a keyword search. @param keywordSearch search string for keyword search (string) @return search URL (QUrl) """ try: keyword, term = keywordSearch.split(" ", 1) except ValueError: return QUrl() if not term: return QUrl() engine = self.engineForKeyword(keyword) if engine: return engine.searchUrl(term) return QUrl() def engineForKeyword(self, keyword): """ Public method to get the engine for a keyword. @param keyword keyword to get engine for (string) @return reference to the search engine object (OpenSearchEngine) """ if keyword and keyword in self.__keywords: return self.__keywords[keyword] return None def setEngineForKeyword(self, keyword, engine): """ Public method to set the engine for a keyword. @param keyword keyword to get engine for (string) @param engine reference to the search engine object (OpenSearchEngine) or None to remove the keyword """ if not keyword: return if engine is None: try: del self.__keywords[keyword] except KeyError: pass else: self.__keywords[keyword] = engine self.changed.emit() def keywordsForEngine(self, engine): """ Public method to get the keywords for a given engine. @param engine reference to the search engine object (OpenSearchEngine) @return list of keywords (list of strings) """ return [k for k in self.__keywords if self.__keywords[k] == engine] def setKeywordsForEngine(self, engine, keywords): """ Public method to set the keywords for an engine. @param engine reference to the search engine object (OpenSearchEngine) @param keywords list of keywords (list of strings) """ if engine is None: return for keyword in self.keywordsForEngine(engine): del self.__keywords[keyword] for keyword in keywords: if not keyword: continue self.__keywords[keyword] = engine self.changed.emit() def enginesChanged(self): """ Public slot to tell the search engine manager, that something has changed. """ self.changed.emit()
def __init__(self, parent, project): """ Constructor @param parent the parent (QWidget) @param project reference to the project object """ super(TaskViewer, self).__init__(parent) self.setRootIsDecorated(False) self.setItemsExpandable(False) self.setSortingEnabled(True) self.__headerItem = QTreeWidgetItem([ "", "", self.tr("Summary"), self.tr("Filename"), self.tr("Line"), "" ]) self.__headerItem.setIcon(0, UI.PixmapCache.getIcon("taskCompleted.png")) self.__headerItem.setIcon(1, UI.PixmapCache.getIcon("taskPriority.png")) self.setHeaderItem(self.__headerItem) self.header().setSortIndicator(2, Qt.AscendingOrder) self.__resizeColumns() self.tasks = [] self.copyTask = None self.projectOpen = False self.project = project self.projectTasksScanFilter = "" from .TaskFilter import TaskFilter self.taskFilter = TaskFilter() self.taskFilter.setActive(False) self.__projectTasksSaveTimer = AutoSaver(self, self.saveProjectTasks) self.__projectTasksMenu = QMenu(self.tr("P&roject Tasks"), self) self.__projectTasksMenu.addAction(self.tr("&Regenerate project tasks"), self.__regenerateProjectTasks) self.__projectTasksMenu.addSeparator() self.__projectTasksMenu.addAction( self.tr("&Configure scan options"), self.__configureProjectTasksScanOptions) self.__menu = QMenu(self) self.__menu.addAction(self.tr("&New Task..."), self.__newTask) self.__menu.addSeparator() self.projectTasksMenuItem = self.__menu.addMenu( self.__projectTasksMenu) self.__menu.addSeparator() self.gotoItem = self.__menu.addAction(self.tr("&Go To"), self.__goToTask) self.__menu.addSeparator() self.copyItem = self.__menu.addAction(self.tr("&Copy"), self.__copyTask) self.pasteItem = self.__menu.addAction(self.tr("&Paste"), self.__pasteTask) self.deleteItem = self.__menu.addAction(self.tr("&Delete"), self.__deleteTask) self.__menu.addSeparator() self.markCompletedItem = self.__menu.addAction( self.tr("&Mark Completed"), self.__markCompleted) self.__menu.addAction(self.tr("Delete Completed &Tasks"), self.__deleteCompleted) self.__menu.addSeparator() self.__menu.addAction(self.tr("P&roperties..."), self.__editTaskProperties) self.__menu.addSeparator() self.__menuFilteredAct = self.__menu.addAction( self.tr("&Filtered display")) self.__menuFilteredAct.setCheckable(True) self.__menuFilteredAct.setChecked(False) self.__menuFilteredAct.triggered[bool].connect(self.__activateFilter) self.__menu.addAction(self.tr("Filter c&onfiguration..."), self.__configureFilter) self.__menu.addSeparator() self.__menu.addAction(self.tr("Resi&ze columns"), self.__resizeColumns) self.__menu.addSeparator() self.__menu.addAction(self.tr("Configure..."), self.__configure) self.__backMenu = QMenu(self) self.__backMenu.addAction(self.tr("&New Task..."), self.__newTask) self.__backMenu.addSeparator() self.backProjectTasksMenuItem = self.__backMenu.addMenu( self.__projectTasksMenu) self.__backMenu.addSeparator() self.backPasteItem = self.__backMenu.addAction(self.tr("&Paste"), self.__pasteTask) self.__backMenu.addSeparator() self.__backMenu.addAction(self.tr("Delete Completed &Tasks"), self.__deleteCompleted) self.__backMenu.addSeparator() self.__backMenuFilteredAct = self.__backMenu.addAction( self.tr("&Filtered display")) self.__backMenuFilteredAct.setCheckable(True) self.__backMenuFilteredAct.setChecked(False) self.__backMenuFilteredAct.triggered[bool].connect( self.__activateFilter) self.__backMenu.addAction(self.tr("Filter c&onfiguration..."), self.__configureFilter) self.__backMenu.addSeparator() self.__backMenu.addAction(self.tr("Resi&ze columns"), self.__resizeColumns) self.__backMenu.addSeparator() self.__backMenu.addAction(self.tr("Configure..."), self.__configure) self.__activating = False self.setContextMenuPolicy(Qt.CustomContextMenu) self.customContextMenuRequested.connect(self.__showContextMenu) self.itemActivated.connect(self.__taskItemActivated) self.setWindowIcon(UI.PixmapCache.getIcon("eric.png"))
class SpeedDial(QObject): """ Class implementing the speed dial. @signal pagesChanged() emitted after the list of pages changed @signal speedDialSaved() emitted after the speed dial data was saved """ pagesChanged = pyqtSignal() speedDialSaved = pyqtSignal() def __init__(self, parent=None): """ Constructor @param parent reference to the parent object (QObject) """ super(SpeedDial, self).__init__(parent) self.__regenerateScript = True self.__webPages = [] self.__webFrames = [] self.__initialScript = "" self.__thumbnailsDirectory = "" self.__thumbnailers = [] self.__initialize() self.pagesChanged.connect(self.__pagesChanged) self.__saveTimer = AutoSaver(self, self.save) self.pagesChanged.connect(self.__saveTimer.changeOccurred) def addWebFrame(self, frame): """ Public method to add a web frame. @param frame reference to the frame to be added (QWebFrame) """ if frame not in self.__webFrames: self.__webFrames.append(frame) def addPage(self, url, title): """ Public method to add a page for the given data. @param url URL of the page (QUrl) @param title title of the page (string) """ if url.isEmpty(): return from .Page import Page page = Page(url.toString(), title) self.__webPages.append(page) self.pagesChanged.emit() def removePage(self, url): """ Public method to remove a page. @param url URL of the page (QUrl) """ page = self.pageForUrl(url) if not page.url: return self.removeImageForUrl(page.url) self.__webPages.remove(page) self.pagesChanged.emit() def __imageFileName(self, url): """ Private method to generate the image file name for a URL. @param url URL to generate the file name from (string) @return name of the image file (string) """ return os.path.join( self.__thumbnailsDirectory, str(QCryptographicHash.hash(QByteArray(url.encode("utf-8")), QCryptographicHash.Md5).toHex(), encoding="utf-8") + ".png") def initialScript(self): """ Public method to get the 'initial' JavaScript script. @return initial JavaScript script (string) """ if self.__regenerateScript: self.__regenerateScript = False self.__initialScript = "" for page in self.__webPages: if page.broken: imgSource = "qrc:icons/brokenPage.png" else: imgSource = self.__imageFileName(page.url) if not os.path.exists(imgSource): self.loadThumbnail(page.url) imgSource = "qrc:icons/loading.gif" if not page.url: imgSource = "" else: imgSource = QUrl.fromLocalFile(imgSource).toString() self.__initialScript += \ "addBox('{0}', '{1}', '{2}');\n".format( page.url, Utilities.html_uencode(page.title), imgSource) return self.__initialScript def getFileName(self): """ Public method to get the file name of the user agents file. @return name of the user agents file (string) """ return os.path.join(Utilities.getConfigDir(), "browser", "speedDial.xml") def __initialize(self): """ Private method to initialize the speed dial. """ self.__thumbnailsDirectory = os.path.join(Utilities.getConfigDir(), "browser", "thumbnails") # Create directory if it does not exist yet if not os.path.exists(self.__thumbnailsDirectory): os.makedirs(self.__thumbnailsDirectory) self.__load() def reload(self): """ Public method to reload the speed dial data. """ self.__load() def __load(self): """ Private method to load the speed dial configuration. """ allPages, pagesPerRow, speedDialSize = [], 0, 0 speedDialFile = self.getFileName() if os.path.exists(speedDialFile): from .SpeedDialReader import SpeedDialReader reader = SpeedDialReader() allPages, pagesPerRow, speedDialSize = reader.read(speedDialFile) self.__pagesPerRow = pagesPerRow if pagesPerRow else 4 self.__speedDialSize = speedDialSize if speedDialSize else 231 if allPages: self.__webPages = allPages self.pagesChanged.emit() else: allPages = \ 'url:"http://eric-ide.python-projects.org/"|'\ 'title:"Eric Web Site";'\ 'url:"http://www.riverbankcomputing.com/"|'\ 'title:"PyQt Web Site";'\ 'url:"http://www.qt.io/"|title:"Qt Web Site";'\ 'url:"http://blog.qt.digia.com/"|title:"Qt Blog";'\ 'url:"http://www.python.org"|title:"Python Language Website";'\ 'url:"http://www.google.com"|title:"Google";' self.changed(allPages) def save(self): """ Public method to save the speed dial configuration. """ from .SpeedDialWriter import SpeedDialWriter speedDialFile = self.getFileName() writer = SpeedDialWriter() if not writer.write(speedDialFile, self.__webPages, self.__pagesPerRow, self.__speedDialSize): E5MessageBox.critical( None, self.tr("Saving Speed Dial data"), self.tr("""<p>Speed Dial data could not be saved to""" """ <b>{0}</b></p>""").format(speedDialFile)) else: self.speedDialSaved.emit() def close(self): """ Public method to close the user agents manager. """ self.__saveTimer.saveIfNeccessary() def pageForUrl(self, url): """ Public method to get the page for the given URL. @param url URL to be searched for (QUrl) @return page for the URL (Page) """ urlString = url.toString() for page in self.__webPages: if page.url == urlString: return page from .Page import Page return Page() def urlForShortcut(self, key): """ Public method to get the URL for the given shortcut key. @param key shortcut key (integer) @return URL for the key (QUrl) """ if key < 0 or len(self.__webPages) <= key: return QUrl() return QUrl.fromEncoded(self.__webPages[key].url.encode("utf-8")) @pyqtSlot(str) def changed(self, allPages): """ Public slot to react on changed pages. @param allPages string giving all pages (string) """ if not allPages: return entries = allPages.split('";') self.__webPages = [] from .Page import Page for entry in entries: if not entry: continue tmp = entry.split('"|') if len(tmp) == 2: broken = False elif len(tmp) == 3: broken = "brokenPage" in tmp[2][5:] else: continue page = Page(tmp[0][5:], tmp[1][7:], broken) self.__webPages.append(page) self.pagesChanged.emit() @pyqtSlot(str) @pyqtSlot(str, bool) def loadThumbnail(self, url, loadTitle=False): """ Public slot to load a thumbnail of the given URL. @param url URL of the thumbnail (string) @param loadTitle flag indicating to get the title for the thumbnail from the site (boolean) """ if not url: return from .PageThumbnailer import PageThumbnailer thumbnailer = PageThumbnailer(self) thumbnailer.setUrl(QUrl.fromEncoded(url.encode("utf-8"))) thumbnailer.setLoadTitle(loadTitle) thumbnailer.thumbnailCreated.connect(self.__thumbnailCreated) self.__thumbnailers.append(thumbnailer) thumbnailer.start() @pyqtSlot(str) def removeImageForUrl(self, url): """ Public slot to remove the image for a URL. @param url URL to remove the image for (string) """ fileName = self.__imageFileName(url) if os.path.exists(fileName): os.remove(fileName) @pyqtSlot(str, result=str) def urlFromUserInput(self, url): """ Public slot to get the URL from user input. @param url URL entered by the user (string) @return sanitized URL (string) """ return QUrl.fromUserInput(url).toString() @pyqtSlot(str, result=str) def unescapeTitle(self, title): """ Public slot to unescape the titel string. @param title escaped title (string) @return un-escaped title (string) """ return Utilities.html_udecode(title) @pyqtSlot(int) def setPagesInRow(self, count): """ Public slot to set the number of pages per row. @param count number of pages per row (integer) """ self.__pagesPerRow = count self.__saveTimer.changeOccurred() def pagesInRow(self): """ Public method to get the number of dials per row. @return number of dials per row (integer) """ return self.__pagesPerRow @pyqtSlot(int) def setSdSize(self, size): """ Public slot to set the size of the speed dial. @param size size of the speed dial (integer) """ self.__speedDialSize = size self.__saveTimer.changeOccurred() def sdSize(self): """ Public method to get the speed dial size. @return speed dial size (integer) """ return self.__speedDialSize def __thumbnailCreated(self, image): """ Private slot to handle the creation of a thumbnail image. @param image thumbnail image (QPixmap) """ from .PageThumbnailer import PageThumbnailer thumbnailer = self.sender() if not isinstance(thumbnailer, PageThumbnailer) or \ thumbnailer not in self.__thumbnailers: return loadTitle = thumbnailer.loadTitle() title = thumbnailer.title() url = thumbnailer.url().toString() fileName = self.__imageFileName(url) if image.isNull(): fileName = "qrc:icons/brokenPage.png" title = self.tr("Unable to load") loadTitle = True page = self.pageForUrl(thumbnailer.url()) page.broken = True else: if not image.save(fileName): qWarning("SpeedDial.__thumbnailCreated: Cannot save thumbnail" " to {0}".format(fileName)) fileName = QUrl.fromLocalFile(fileName).toString() self.__regenerateScript = True for frame in self.__cleanFrames(): frame.evaluateJavaScript("setImageToUrl('{0}', '{1}');".format( url, fileName)) if loadTitle: frame.evaluateJavaScript("setTitleToUrl('{0}', '{1}');".format( url, Utilities.html_uencode(title))) thumbnailer.deleteLater() self.__thumbnailers.remove(thumbnailer) def __cleanFrames(self): """ Private method to clean all frames. @return list of speed dial frames (list of QWebFrame) """ frames = [] for frame in self.__webFrames[:]: if frame.url().toString() == "eric:speeddial": frames.append(frame) else: self.__webFrames.remove(frame) return frames def __pagesChanged(self): """ Private slot to react on a change of the pages configuration. """ # update all speed dial pages self.__regenerateScript = True for frame in self.__cleanFrames(): frame.page().triggerAction(QWebPage.Reload)
class OpenSearchManager(QObject): """ Class implementing a manager for open search engines. @signal changed() emitted to indicate a change @signal currentEngineChanged() emitted to indicate a change of the current search engine """ changed = pyqtSignal() currentEngineChanged = pyqtSignal() def __init__(self, parent=None): """ Constructor @param parent reference to the parent object (QObject) """ if parent is None: parent = e5App() super(OpenSearchManager, self).__init__(parent) self.__replies = [] self.__engines = {} self.__keywords = {} self.__current = "" self.__loading = False self.__saveTimer = AutoSaver(self, self.save) self.changed.connect(self.__saveTimer.changeOccurred) self.load() def close(self): """ Public method to close the open search engines manager. """ self.__saveTimer.saveIfNeccessary() def currentEngineName(self): """ Public method to get the name of the current search engine. @return name of the current search engine (string) """ return self.__current def setCurrentEngineName(self, name): """ Public method to set the current engine by name. @param name name of the new current engine (string) """ if name not in self.__engines: return self.__current = name self.currentEngineChanged.emit() self.changed.emit() def currentEngine(self): """ Public method to get a reference to the current engine. @return reference to the current engine (OpenSearchEngine) """ if not self.__current or self.__current not in self.__engines: return None return self.__engines[self.__current] def setCurrentEngine(self, engine): """ Public method to set the current engine. @param engine reference to the new current engine (OpenSearchEngine) """ if engine is None: return for engineName in self.__engines: if self.__engines[engineName] == engine: self.setCurrentEngineName(engineName) break def engine(self, name): """ Public method to get a reference to the named engine. @param name name of the engine (string) @return reference to the engine (OpenSearchEngine) """ if name not in self.__engines: return None return self.__engines[name] def engineExists(self, name): """ Public method to check, if an engine exists. @param name name of the engine (string) @return flag indicating an existing engine (boolean) """ return name in self.__engines def allEnginesNames(self): """ Public method to get a list of all engine names. @return sorted list of all engine names (list of strings) """ return sorted(self.__engines.keys()) def enginesCount(self): """ Public method to get the number of available engines. @return number of engines (integer) """ return len(self.__engines) def addEngine(self, engine): """ Public method to add a new search engine. @param engine URL of the engine definition file (QUrl) or name of a file containing the engine definition (string) or reference to an engine object (OpenSearchEngine) @return flag indicating success (boolean) """ from .OpenSearchEngine import OpenSearchEngine if isinstance(engine, QUrl): return self.__addEngineByUrl(engine) elif isinstance(engine, OpenSearchEngine): return self.__addEngineByEngine(engine) else: return self.__addEngineByFile(engine) def __addEngineByUrl(self, url): """ Private method to add a new search engine given its URL. @param url URL of the engine definition file (QUrl) @return flag indicating success (boolean) """ if not url.isValid(): return from Helpviewer.HelpWindow import HelpWindow reply = HelpWindow.networkAccessManager().get(QNetworkRequest(url)) reply.finished.connect(self.__engineFromUrlAvailable) reply.setParent(self) self.__replies.append(reply) return True def __addEngineByFile(self, filename): """ Private method to add a new search engine given a filename. @param filename name of a file containing the engine definition (string) @return flag indicating success (boolean) """ file_ = QFile(filename) if not file_.open(QIODevice.ReadOnly): return False from .OpenSearchReader import OpenSearchReader reader = OpenSearchReader() engine = reader.read(file_) if not self.__addEngineByEngine(engine): return False return True def __addEngineByEngine(self, engine): """ Private method to add a new search engine given a reference to an engine. @param engine reference to an engine object (OpenSearchEngine) @return flag indicating success (boolean) """ if engine is None: return False if not engine.isValid(): return False if engine.name() in self.__engines: return False engine.setParent(self) self.__engines[engine.name()] = engine self.changed.emit() return True def removeEngine(self, name): """ Public method to remove an engine. @param name name of the engine (string) """ if len(self.__engines) <= 1: return if name not in self.__engines: return engine = self.__engines[name] for keyword in [k for k in self.__keywords if self.__keywords[k] == engine]: del self.__keywords[keyword] del self.__engines[name] file_ = QDir(self.enginesDirectory()).filePath( self.generateEngineFileName(name)) QFile.remove(file_) if name == self.__current: self.setCurrentEngineName(list(self.__engines.keys())[0]) self.changed.emit() def generateEngineFileName(self, engineName): """ Public method to generate a valid engine file name. @param engineName name of the engine (string) @return valid engine file name (string) """ fileName = "" # strip special characters for c in engineName: if c.isspace(): fileName += '_' continue if c.isalnum(): fileName += c fileName += ".xml" return fileName def saveDirectory(self, dirName): """ Public method to save the search engine definitions to files. @param dirName name of the directory to write the files to (string) """ dir = QDir() if not dir.mkpath(dirName): return dir.setPath(dirName) from .OpenSearchWriter import OpenSearchWriter writer = OpenSearchWriter() for engine in list(self.__engines.values()): name = self.generateEngineFileName(engine.name()) fileName = dir.filePath(name) file = QFile(fileName) if not file.open(QIODevice.WriteOnly): continue writer.write(file, engine) def save(self): """ Public method to save the search engines configuration. """ if self.__loading: return self.saveDirectory(self.enginesDirectory()) Preferences.setHelp("WebSearchEngine", self.__current) keywords = [] for k in self.__keywords: if self.__keywords[k]: keywords.append((k, self.__keywords[k].name())) Preferences.setHelp("WebSearchKeywords", keywords) def loadDirectory(self, dirName): """ Public method to load the search engine definitions from files. @param dirName name of the directory to load the files from (string) @return flag indicating success (boolean) """ if not QFile.exists(dirName): return False success = False dir = QDir(dirName) for name in dir.entryList(["*.xml"]): fileName = dir.filePath(name) if self.__addEngineByFile(fileName): success = True return success def load(self): """ Public method to load the search engines configuration. """ self.__loading = True self.__current = Preferences.getHelp("WebSearchEngine") keywords = Preferences.getHelp("WebSearchKeywords") if not self.loadDirectory(self.enginesDirectory()): self.restoreDefaults() for keyword, engineName in keywords: self.__keywords[keyword] = self.engine(engineName) if self.__current not in self.__engines and \ len(self.__engines) > 0: self.__current = list(self.__engines.keys())[0] self.__loading = False self.currentEngineChanged.emit() def restoreDefaults(self): """ Public method to restore the default search engines. """ from .OpenSearchReader import OpenSearchReader from .DefaultSearchEngines import DefaultSearchEngines_rc # __IGNORE_WARNING__ defaultEngineFiles = ["YouTube.xml", "Amazoncom.xml", "Bing.xml", "DeEn_Beolingus.xml", "Facebook.xml", "Google_Im_Feeling_Lucky.xml", "Google.xml", "LEO_DeuEng.xml", "LinuxMagazin.xml", "Reddit.xml", "Wikia_en.xml", "Wikia.xml", "Wikipedia.xml", "Wiktionary.xml", "Yahoo.xml"] # Keep this list in sync with the contents of the resource file. reader = OpenSearchReader() for engineFileName in defaultEngineFiles: engineFile = QFile(":/" + engineFileName) if not engineFile.open(QIODevice.ReadOnly): continue engine = reader.read(engineFile) self.__addEngineByEngine(engine) def enginesDirectory(self): """ Public method to determine the directory containing the search engine descriptions. @return directory name (string) """ return os.path.join( Utilities.getConfigDir(), "browser", "searchengines") def __confirmAddition(self, engine): """ Private method to confirm the addition of a new search engine. @param engine reference to the engine to be added (OpenSearchEngine) @return flag indicating the engine shall be added (boolean) """ if engine is None or not engine.isValid(): return False host = QUrl(engine.searchUrlTemplate()).host() res = E5MessageBox.yesNo( None, "", self.tr( """<p>Do you want to add the following engine to your""" """ list of search engines?<br/><br/>Name: {0}<br/>""" """Searches on: {1}</p>""").format(engine.name(), host)) return res def __engineFromUrlAvailable(self): """ Private slot to add a search engine from the net. """ reply = self.sender() if reply is None: return if reply.error() != QNetworkReply.NoError: reply.close() if reply in self.__replies: self.__replies.remove(reply) return from .OpenSearchReader import OpenSearchReader reader = OpenSearchReader() engine = reader.read(reply) reply.close() if reply in self.__replies: self.__replies.remove(reply) if not engine.isValid(): return if self.engineExists(engine.name()): return if not self.__confirmAddition(engine): return if not self.__addEngineByEngine(engine): return def convertKeywordSearchToUrl(self, keywordSearch): """ Public method to get the search URL for a keyword search. @param keywordSearch search string for keyword search (string) @return search URL (QUrl) """ try: keyword, term = keywordSearch.split(" ", 1) except ValueError: return QUrl() if not term: return QUrl() engine = self.engineForKeyword(keyword) if engine: return engine.searchUrl(term) return QUrl() def engineForKeyword(self, keyword): """ Public method to get the engine for a keyword. @param keyword keyword to get engine for (string) @return reference to the search engine object (OpenSearchEngine) """ if keyword and keyword in self.__keywords: return self.__keywords[keyword] return None def setEngineForKeyword(self, keyword, engine): """ Public method to set the engine for a keyword. @param keyword keyword to get engine for (string) @param engine reference to the search engine object (OpenSearchEngine) or None to remove the keyword """ if not keyword: return if engine is None: try: del self.__keywords[keyword] except KeyError: pass else: self.__keywords[keyword] = engine self.changed.emit() def keywordsForEngine(self, engine): """ Public method to get the keywords for a given engine. @param engine reference to the search engine object (OpenSearchEngine) @return list of keywords (list of strings) """ return [k for k in self.__keywords if self.__keywords[k] == engine] def setKeywordsForEngine(self, engine, keywords): """ Public method to set the keywords for an engine. @param engine reference to the search engine object (OpenSearchEngine) @param keywords list of keywords (list of strings) """ if engine is None: return for keyword in self.keywordsForEngine(engine): del self.__keywords[keyword] for keyword in keywords: if not keyword: continue self.__keywords[keyword] = engine self.changed.emit() def enginesChanged(self): """ Public slot to tell the search engine manager, that something has changed. """ self.changed.emit()
class PasswordManager(QObject): """ Class implementing the password manager. @signal changed() emitted to indicate a change """ SEPARATOR = "====================" FORMS = "=====FORMS=====" NEVER = "=====NEVER=====" def __init__(self, parent = None): """ Constructor @param parent reference to the parent object (QObject) """ QObject.__init__(self, parent) self.__logins = {} self.__loginForms = {} self.__never = [] self.__loaded = False self.__saveTimer = AutoSaver(self, self.save) self.connect(self, SIGNAL("changed()"), self.__saveTimer.changeOccurred) def clear(self): """ Public slot to clear the saved passwords. """ if not self.__loaded: self.__load() self.__logins = {} self.__loginForms = {} self.__never = [] self.__saveTimer.changeOccurred() self.__saveTimer.saveIfNeccessary() self.emit(SIGNAL("changed()")) def getLogin(self, url, realm): """ Public method to get the login credentials. @param url URL to get the credentials for (QUrl) @param realm realm to get the credentials for (string or QString) @return tuple containing the user name (string) and password (string) """ if not self.__loaded: self.__load() key = self.__createKey(url, realm) try: return self.__logins[key][0], Utilities.pwDecode(self.__logins[key][1]) except KeyError: return "", "" def setLogin(self, url, realm, username, password): """ Public method to set the login credentials. @param url URL to set the credentials for (QUrl) @param realm realm to set the credentials for (string or QString) @param username username for the login (string or QString) @param password password for the login (string or QString) """ if not self.__loaded: self.__load() key = self.__createKey(url, realm) self.__logins[key] = (unicode(username), Utilities.pwEncode(password)) self.emit(SIGNAL("changed()")) def __createKey(self, url, realm): """ Private method to create the key string for the login credentials. @param url URL to get the credentials for (QUrl) @param realm realm to get the credentials for (string or QString) @return key string (string) """ realm = unicode(realm) if realm: key = "%s://%s (%s)" % (url.scheme(), url.authority(), realm) else: key = "%s://%s" % (url.scheme(), url.authority()) return key def save(self): """ Public slot to save the login entries to disk. """ if not self.__loaded: return loginFile = os.path.join(Utilities.getConfigDir(), "browser", "logins") try: f = open(loginFile, "w") for key, login in self.__logins.items(): f.write("%s\n" % key) f.write("%s\n" % login[0]) f.write("%s\n" % login[1]) f.write("%s\n" % self.SEPARATOR) if self.__loginForms: f.write("%s\n" % self.FORMS) for key, form in self.__loginForms.items(): f.write("%s\n" % key) form.save(f) f.write("%s\n" % self.SEPARATOR) if self.__never: f.write("%s\n" % self.NEVER) for key in self.__never: f.write("%s\n") % key f.close() except IOError, err: KQMessageBox.critical(None, self.trUtf8("Saving login data"), self.trUtf8("""<p>Login data could not be saved to <b>%1</b></p>""" """<p>Reason: %2</p>""").arg(loginFile).arg(str(err))) return
class AdBlockManager(QObject): """ Class implementing the AdBlock manager. @signal rulesChanged() emitted after some rule has changed """ rulesChanged = pyqtSignal() requiredSubscriptionLoaded = pyqtSignal(AdBlockSubscription) def __init__(self, parent=None): """ Constructor @param parent reference to the parent object (QObject) """ super(AdBlockManager, self).__init__(parent) self.__loaded = False self.__subscriptionsLoaded = False self.__enabled = False self.__adBlockDialog = None self.__adBlockExceptionsDialog = None self.__adBlockNetwork = None self.__adBlockPage = None self.__subscriptions = [] self.__exceptedHosts = Preferences.getHelp("AdBlockExceptions") self.__saveTimer = AutoSaver(self, self.save) self.__defaultSubscriptionUrlString = \ "abp:subscribe?location=" \ "https://easylist-downloads.adblockplus.org/easylist.txt&"\ "title=EasyList" self.__customSubscriptionUrlString = \ bytes(self.__customSubscriptionUrl().toEncoded()).decode() self.rulesChanged.connect(self.__saveTimer.changeOccurred) def close(self): """ Public method to close the open search engines manager. """ self.__adBlockDialog and self.__adBlockDialog.close() self.__adBlockExceptionsDialog and \ self.__adBlockExceptionsDialog.close() self.__saveTimer.saveIfNeccessary() def isEnabled(self): """ Public method to check, if blocking ads is enabled. @return flag indicating the enabled state (boolean) """ if not self.__loaded: self.load() return self.__enabled def setEnabled(self, enabled): """ Public slot to set the enabled state. @param enabled flag indicating the enabled state (boolean) """ if self.isEnabled() == enabled: return import Helpviewer.HelpWindow self.__enabled = enabled for mainWindow in Helpviewer.HelpWindow.HelpWindow.mainWindows(): mainWindow.adBlockIcon().setEnabled(enabled) if enabled: self.__loadSubscriptions() self.rulesChanged.emit() def network(self): """ Public method to get a reference to the network block object. @return reference to the network block object (AdBlockNetwork) """ if self.__adBlockNetwork is None: from .AdBlockNetwork import AdBlockNetwork self.__adBlockNetwork = AdBlockNetwork(self) return self.__adBlockNetwork def page(self): """ Public method to get a reference to the page block object. @return reference to the page block object (AdBlockPage) """ if self.__adBlockPage is None: from .AdBlockPage import AdBlockPage self.__adBlockPage = AdBlockPage(self) return self.__adBlockPage def __customSubscriptionLocation(self): """ Private method to generate the path for custom subscriptions. @return URL for custom subscriptions (QUrl) """ dataDir = os.path.join(Utilities.getConfigDir(), "browser", "subscriptions") if not os.path.exists(dataDir): os.makedirs(dataDir) fileName = os.path.join(dataDir, "adblock_subscription_custom") return QUrl.fromLocalFile(fileName) def __customSubscriptionUrl(self): """ Private method to generate the URL for custom subscriptions. @return URL for custom subscriptions (QUrl) """ location = self.__customSubscriptionLocation() encodedUrl = bytes(location.toEncoded()).decode() url = QUrl("abp:subscribe?location={0}&title={1}".format( encodedUrl, self.tr("Custom Rules"))) return url def customRules(self): """ Public method to get a subscription for custom rules. @return subscription object for custom rules (AdBlockSubscription) """ location = self.__customSubscriptionLocation() for subscription in self.__subscriptions: if subscription.location() == location: return subscription url = self.__customSubscriptionUrl() customAdBlockSubscription = AdBlockSubscription(url, True, self) self.addSubscription(customAdBlockSubscription) return customAdBlockSubscription def subscriptions(self): """ Public method to get all subscriptions. @return list of subscriptions (list of AdBlockSubscription) """ if not self.__loaded: self.load() return self.__subscriptions[:] def subscription(self, location): """ Public method to get a subscription based on its location. @param location location of the subscription to search for (string) @return subscription or None (AdBlockSubscription) """ if location != "": for subscription in self.__subscriptions: if subscription.location().toString() == location: return subscription return None def updateAllSubscriptions(self): """ Public method to update all subscriptions. """ for subscription in self.__subscriptions: subscription.updateNow() def removeSubscription(self, subscription, emitSignal=True): """ Public method to remove an AdBlock subscription. @param subscription AdBlock subscription to be removed (AdBlockSubscription) @param emitSignal flag indicating to send a signal (boolean) """ if subscription is None: return if subscription.url().toString().startswith( (self.__defaultSubscriptionUrlString, self.__customSubscriptionUrlString)): return try: self.__subscriptions.remove(subscription) rulesFileName = subscription.rulesFileName() QFile.remove(rulesFileName) requiresSubscriptions = self.getRequiresSubscriptions(subscription) for requiresSubscription in requiresSubscriptions: self.removeSubscription(requiresSubscription, False) if emitSignal: self.rulesChanged.emit() except ValueError: pass def addSubscription(self, subscription): """ Public method to add an AdBlock subscription. @param subscription AdBlock subscription to be added (AdBlockSubscription) """ if subscription is None: return self.__subscriptions.insert(-1, subscription) subscription.rulesChanged.connect(self.rulesChanged) subscription.changed.connect(self.rulesChanged) subscription.enabledChanged.connect(self.rulesChanged) self.rulesChanged.emit() def save(self): """ Public method to save the AdBlock subscriptions. """ if not self.__loaded: return Preferences.setHelp("AdBlockEnabled", self.__enabled) if self.__subscriptionsLoaded: subscriptions = [] requiresSubscriptions = [] # intermediate store for subscription requiring others for subscription in self.__subscriptions: if subscription is None: continue urlString = bytes(subscription.url().toEncoded()).decode() if "requiresLocation" in urlString: requiresSubscriptions.append(urlString) else: subscriptions.append(urlString) subscription.saveRules() for subscription in requiresSubscriptions: subscriptions.insert(-1, subscription) # custom should be last Preferences.setHelp("AdBlockSubscriptions", subscriptions) def load(self): """ Public method to load the AdBlock subscriptions. """ if self.__loaded: return self.__loaded = True self.__enabled = Preferences.getHelp("AdBlockEnabled") if self.__enabled: self.__loadSubscriptions() def __loadSubscriptions(self): """ Private method to load the set of subscriptions. """ if self.__subscriptionsLoaded: return subscriptions = Preferences.getHelp("AdBlockSubscriptions") if subscriptions: for subscription in subscriptions: if subscription.startswith( self.__defaultSubscriptionUrlString): break else: subscriptions.insert(0, self.__defaultSubscriptionUrlString) for subscription in subscriptions: if subscription.startswith(self.__customSubscriptionUrlString): break else: subscriptions.append(self.__customSubscriptionUrlString) else: subscriptions = [self.__defaultSubscriptionUrlString, self.__customSubscriptionUrlString] for subscription in subscriptions: url = QUrl.fromEncoded(subscription.encode("utf-8")) adBlockSubscription = AdBlockSubscription( url, subscription.startswith(self.__customSubscriptionUrlString), self, subscription.startswith(self.__defaultSubscriptionUrlString)) adBlockSubscription.rulesChanged.connect(self.rulesChanged) adBlockSubscription.changed.connect(self.rulesChanged) adBlockSubscription.enabledChanged.connect(self.rulesChanged) self.__subscriptions.append(adBlockSubscription) self.__subscriptionsLoaded = True def loadRequiredSubscription(self, location, title): """ Public method to load a subscription required by another one. @param location location of the required subscription (string) @param title title of the required subscription (string) """ # Step 1: check, if the subscription is in the list of subscriptions urlString = "abp:subscribe?location={0}&title={1}".format( location, title) for subscription in self.__subscriptions: if subscription.url().toString().startswith(urlString): # We found it! return # Step 2: if it is not, get it url = QUrl.fromEncoded(urlString.encode("utf-8")) adBlockSubscription = AdBlockSubscription(url, False, self) self.addSubscription(adBlockSubscription) self.requiredSubscriptionLoaded.emit(adBlockSubscription) def getRequiresSubscriptions(self, subscription): """ Public method to get a list of subscriptions, that require the given one. @param subscription subscription to check for (AdBlockSubscription) @return list of subscription requiring the given one (list of AdBlockSubscription) """ subscriptions = [] location = subscription.location().toString() for subscription in self.__subscriptions: if subscription.requiresLocation() == location: subscriptions.append(subscription) return subscriptions def showDialog(self): """ Public slot to show the AdBlock subscription management dialog. @return reference to the dialog (AdBlockDialog) """ if self.__adBlockDialog is None: from .AdBlockDialog import AdBlockDialog self.__adBlockDialog = AdBlockDialog() self.__adBlockDialog.show() return self.__adBlockDialog def showRule(self): """ Public slot to show an AdBlock rule. """ act = self.sender() if act is not None: rule = act.data() if rule: self.showDialog().showRule(rule) def elementHidingRules(self): """ Public method to get the element hiding rules. @return element hiding rules (string) """ if not self.__enabled: return "" rules = "" for subscription in self.__subscriptions: rules += subscription.elementHidingRules() if rules: # remove last ", rules = rules[:-1] return rules def elementHidingRulesForDomain(self, url): """ Public method to get the element hiding rules for a domain. @param url URL to get hiding rules for (QUrl) @return element hiding rules (string) """ if not self.__enabled: return "" rules = "" for subscription in self.__subscriptions: if subscription.elemHideDisabledForUrl(url): return "" rules += subscription.elementHidingRulesForDomain(url.host()) if rules: # remove last ", rules = rules[:-1] return rules def exceptions(self): """ Public method to get a list of excepted hosts. @return list of excepted hosts (list of string) """ return self.__exceptedHosts def setExceptions(self, hosts): """ Public method to set the list of excepted hosts. @param hosts list of excepted hosts (list of string) """ self.__exceptedHosts = hosts[:] Preferences.setHelp("AdBlockExceptions", self.__exceptedHosts) def addException(self, host): """ Public method to add an exception. @param host to be excepted (string) """ if host and host not in self.__exceptedHosts: self.__exceptedHosts.append(host) Preferences.setHelp("AdBlockExceptions", self.__exceptedHosts) def removeException(self, host): """ Public method to remove an exception. @param host to be removed from the list of exceptions (string) """ if host in self.__exceptedHosts: self.__exceptedHosts.remove(host) Preferences.setHelp("AdBlockExceptions", self.__exceptedHosts) def isHostExcepted(self, host): """ Public slot to check, if a host is excepted. @param host host to check (string) @return flag indicating an exception (boolean) """ return host in self.__exceptedHosts def showExceptionsDialog(self): """ Public method to show the AdBlock Exceptions dialog. @return reference to the exceptions dialog (AdBlockExceptionsDialog) """ if self.__adBlockExceptionsDialog is None: from .AdBlockExceptionsDialog import AdBlockExceptionsDialog self.__adBlockExceptionsDialog = AdBlockExceptionsDialog() self.__adBlockExceptionsDialog.load(self.__exceptedHosts) self.__adBlockExceptionsDialog.show() return self.__adBlockExceptionsDialog
class BookmarksManager(QObject): """ Class implementing the bookmarks manager. @signal entryAdded emitted after a bookmark node has been added @signal entryRemoved emitted after a bookmark node has been removed @signal entryChanged emitted after a bookmark node has been changed """ def __init__(self, parent = None): """ Constructor @param parent reference to the parent object (QObject) """ QObject.__init__(self, parent) self.__loaded = False self.__saveTimer = AutoSaver(self, self.save) self.__bookmarkRootNode = None self.__toolbar = None self.__menu = None self.__bookmarksModel = None self.__commands = QUndoStack() self.connect(self, SIGNAL("entryAdded"), self.__saveTimer.changeOccurred) self.connect(self, SIGNAL("entryRemoved"), self.__saveTimer.changeOccurred) self.connect(self, SIGNAL("entryChanged"), self.__saveTimer.changeOccurred) def close(self): """ Public method to close the bookmark manager. """ self.__saveTimer.saveIfNeccessary() def undoRedoStack(self): """ Public method to get a reference to the undo stack. @return reference to the undo stack (QUndoStack) """ return self.__commands def changeExpanded(self): """ Public method to handle a change of the expanded state. """ self.__saveTimer.changeOccurred() def load(self): """ Public method to load the bookmarks. """ if self.__loaded: return self.__loaded = True bookmarkFile = os.path.join(Utilities.getConfigDir(), "browser", "bookmarks.xbel") if not QFile.exists(bookmarkFile): ba = QByteArray(DefaultBookmarks) bookmarkFile = QBuffer(ba) bookmarkFile.open(QIODevice.ReadOnly) reader = XbelReader() self.__bookmarkRootNode = reader.read(bookmarkFile) if reader.error() != QXmlStreamReader.NoError: KQMessageBox.warning(None, self.trUtf8("Loading Bookmarks"), self.trUtf8("""Error when loading bookmarks on line %1, column %2:\n""" """%3""")\ .arg(reader.lineNumber())\ .arg(reader.columnNumber())\ .arg(reader.errorString())) others = [] for index in range(len(self.__bookmarkRootNode.children()) - 1, -1, -1): node = self.__bookmarkRootNode.children()[index] if node.type() == BookmarkNode.Folder: if (node.title == self.trUtf8("Toolbar Bookmarks") or \ node.title == BOOKMARKBAR) and \ self.__toolbar is None: node.title = self.trUtf8(BOOKMARKBAR) self.__toolbar = node if (node.title == self.trUtf8("Menu") or \ node.title == BOOKMARKMENU) and \ self.__menu is None: node.title = self.trUtf8(BOOKMARKMENU) self.__menu = node else: others.append(node) self.__bookmarkRootNode.remove(node) if len(self.__bookmarkRootNode.children()) > 0: raise RuntimeError("Error loading bookmarks.") if self.__toolbar is None: self.__toolbar = BookmarkNode(BookmarkNode.Folder, self.__bookmarkRootNode) self.__toolbar.title = self.trUtf8(BOOKMARKBAR) else: self.__bookmarkRootNode.add(self.__toolbar) if self.__menu is None: self.__menu = BookmarkNode(BookmarkNode.Folder, self.__bookmarkRootNode) self.__menu.title = self.trUtf8(BOOKMARKMENU) else: self.__bookmarkRootNode.add(self.__menu) for node in others: self.__menu.add(node) self.__convertFromOldBookmarks() def save(self): """ Public method to save the bookmarks. """ if not self.__loaded: return writer = XbelWriter() bookmarkFile = os.path.join(Utilities.getConfigDir(), "browser", "bookmarks.xbel") # save root folder titles in English (i.e. not localized) self.__menu.title = BOOKMARKMENU self.__toolbar.title = BOOKMARKBAR if not writer.write(bookmarkFile, self.__bookmarkRootNode): KQMessageBox.warning(None, self.trUtf8("Saving Bookmarks"), self.trUtf8("""Error saving bookmarks to <b>%1</b>.""").arg(bookmarkFile)) # restore localized titles self.__menu.title = self.trUtf8(BOOKMARKMENU) self.__toolbar.title = self.trUtf8(BOOKMARKBAR) def addBookmark(self, parent, node, row = -1): """ Public method to add a bookmark. @param parent reference to the node to add to (BookmarkNode) @param node reference to the node to add (BookmarkNode) @param row row number (integer) """ if not self.__loaded: return command = InsertBookmarksCommand(self, parent, node, row) self.__commands.push(command) def removeBookmark(self, node): """ Public method to remove a bookmark. @param node reference to the node to be removed (BookmarkNode) """ if not self.__loaded: return parent = node.parent() row = parent.children().index(node) command = RemoveBookmarksCommand(self, parent, row) self.__commands.push(command) def setTitle(self, node, newTitle): """ Public method to set the title of a bookmark. @param node reference to the node to be changed (BookmarkNode) @param newTitle title to be set (QString) """ if not self.__loaded: return command = ChangeBookmarkCommand(self, node, newTitle, True) self.__commands.push(command) def setUrl(self, node, newUrl): """ Public method to set the URL of a bookmark. @param node reference to the node to be changed (BookmarkNode) @param newUrl URL to be set (QString) """ if not self.__loaded: return command = ChangeBookmarkCommand(self, node, newUrl, False) self.__commands.push(command) def bookmarks(self): """ Public method to get a reference to the root bookmark node. @return reference to the root bookmark node (BookmarkNode) """ if not self.__loaded: self.load() return self.__bookmarkRootNode def menu(self): """ Public method to get a reference to the bookmarks menu node. @return reference to the bookmarks menu node (BookmarkNode) """ if not self.__loaded: self.load() return self.__menu def toolbar(self): """ Public method to get a reference to the bookmarks toolbar node. @return reference to the bookmarks toolbar node (BookmarkNode) """ if not self.__loaded: self.load() return self.__toolbar def bookmarksModel(self): """ Public method to get a reference to the bookmarks model. @return reference to the bookmarks model (BookmarksModel) """ if self.__bookmarksModel is None: self.__bookmarksModel = BookmarksModel(self, self) return self.__bookmarksModel def importBookmarks(self): """ Public method to import bookmarks. """ supportedFormats = QStringList() \ << self.trUtf8("XBEL bookmarks").append(" (*.xbel *.xml)") \ << self.trUtf8("HTML Netscape bookmarks").append(" (*.html *.htm)") fileName = KQFileDialog.getOpenFileName(\ None, self.trUtf8("Import Bookmarks"), QString(), supportedFormats.join(";;"), None) if fileName.isEmpty(): return reader = XbelReader() importRootNode = None if fileName.endsWith(".html"): inFile = QFile(fileName) inFile.open(QIODevice.ReadOnly) if inFile.openMode == QIODevice.NotOpen: KQMessageBox.warning(None, self.trUtf8("Import Bookmarks"), self.trUtf8("""Error opening bookmarks file <b>%1</b>.""")\ .arg(fileName)) return webpage = QWebPage() webpage.mainFrame().setHtml(QString(inFile.readAll())) result = webpage.mainFrame().evaluateJavaScript(extract_js).toByteArray() buffer_ = QBuffer(result) buffer_.open(QIODevice.ReadOnly) importRootNode = reader.read(buffer_) else: importRootNode = reader.read(fileName) if reader.error() != QXmlStreamReader.NoError: KQMessageBox.warning(None, self.trUtf8("Import Bookmarks"), self.trUtf8("""Error when importing bookmarks on line %1, column %2:\n""" """%3""")\ .arg(reader.lineNumber())\ .arg(reader.columnNumber())\ .arg(reader.errorString())) return importRootNode.setType(BookmarkNode.Folder) importRootNode.title = self.trUtf8("Imported %1")\ .arg(QDate.currentDate().toString(Qt.SystemLocaleShortDate)) self.addBookmark(self.menu(), importRootNode) def exportBookmarks(self): """ Public method to export the bookmarks. """ fileName = KQFileDialog.getSaveFileName(\ None, self.trUtf8("Export Bookmarks"), "eric4_bookmarks.xbel", self.trUtf8("XBEL bookmarks").append(" (*.xbel *.xml)")) if fileName.isEmpty(): return writer = XbelWriter() if not writer.write(fileName, self.__bookmarkRootNode): KQMessageBox.critical(None, self.trUtf8("Exporting Bookmarks"), self.trUtf8("""Error exporting bookmarks to <b>%1</b>.""")\ .arg(fileName)) def __convertFromOldBookmarks(self): """ Private method to convert the old bookmarks into the new ones. """ bmNames = Preferences.Prefs.settings.value('Bookmarks/Names') bmFiles = Preferences.Prefs.settings.value('Bookmarks/Files') if bmNames.isValid() and bmFiles.isValid(): bmNames = bmNames.toStringList() bmFiles = bmFiles.toStringList() if len(bmNames) == len(bmFiles): convertedRootNode = BookmarkNode(BookmarkNode.Folder) convertedRootNode.title = self.trUtf8("Converted %1")\ .arg(QDate.currentDate().toString(Qt.SystemLocaleShortDate)) for i in range(len(bmNames)): node = BookmarkNode(BookmarkNode.Bookmark, convertedRootNode) node.title = bmNames[i] url = QUrl(bmFiles[i]) if url.scheme().isEmpty(): url.setScheme("file") node.url = url.toString() self.addBookmark(self.menu(), convertedRootNode) Preferences.Prefs.settings.remove('Bookmarks')
class BookmarksManager(QObject): """ Class implementing the bookmarks manager. @signal entryAdded(BookmarkNode) emitted after a bookmark node has been added @signal entryRemoved(BookmarkNode, int, BookmarkNode) emitted after a bookmark node has been removed @signal entryChanged(BookmarkNode) emitted after a bookmark node has been changed @signal bookmarksSaved() emitted after the bookmarks were saved @signal bookmarksReloaded() emitted after the bookmarks were reloaded """ entryAdded = pyqtSignal(BookmarkNode) entryRemoved = pyqtSignal(BookmarkNode, int, BookmarkNode) entryChanged = pyqtSignal(BookmarkNode) bookmarksSaved = pyqtSignal() bookmarksReloaded = pyqtSignal() def __init__(self, parent=None): """ Constructor @param parent reference to the parent object (QObject) """ super(BookmarksManager, self).__init__(parent) self.__saveTimer = AutoSaver(self, self.save) self.entryAdded.connect(self.__saveTimer.changeOccurred) self.entryRemoved.connect(self.__saveTimer.changeOccurred) self.entryChanged.connect(self.__saveTimer.changeOccurred) self.__initialize() def __initialize(self): """ Private method to initialize some data. """ self.__loaded = False self.__bookmarkRootNode = None self.__toolbar = None self.__menu = None self.__bookmarksModel = None self.__commands = QUndoStack() @classmethod def getFileName(cls): """ Class method to get the file name of the bookmark file. @return name of the bookmark file (string) """ return os.path.join(Utilities.getConfigDir(), "web_browser", "bookmarks.xbel") def close(self): """ Public method to close the bookmark manager. """ self.__saveTimer.saveIfNeccessary() def undoRedoStack(self): """ Public method to get a reference to the undo stack. @return reference to the undo stack (QUndoStack) """ return self.__commands def changeExpanded(self): """ Public method to handle a change of the expanded state. """ self.__saveTimer.changeOccurred() def reload(self): """ Public method used to initiate a reloading of the bookmarks. """ self.__initialize() self.load() self.bookmarksReloaded.emit() def load(self): """ Public method to load the bookmarks. @exception RuntimeError raised to indicate an error loading the bookmarks """ if self.__loaded: return self.__loaded = True bookmarkFile = self.getFileName() if not QFile.exists(bookmarkFile): from . import DefaultBookmarks_rc # __IGNORE_WARNING__ bookmarkFile = QFile(":/DefaultBookmarks.xbel") bookmarkFile.open(QIODevice.ReadOnly) from .XbelReader import XbelReader reader = XbelReader() self.__bookmarkRootNode = reader.read(bookmarkFile) if reader.error() != QXmlStreamReader.NoError: E5MessageBox.warning( None, self.tr("Loading Bookmarks"), self.tr( """Error when loading bookmarks on line {0},""" """ column {1}:\n {2}""") .format(reader.lineNumber(), reader.columnNumber(), reader.errorString())) others = [] for index in range( len(self.__bookmarkRootNode.children()) - 1, -1, -1): node = self.__bookmarkRootNode.children()[index] if node.type() == BookmarkNode.Folder: if ( (node.title == self.tr("Toolbar Bookmarks") or node.title == BOOKMARKBAR) and self.__toolbar is None ): node.title = self.tr(BOOKMARKBAR) self.__toolbar = node if ( (node.title == self.tr("Menu") or node.title == BOOKMARKMENU) and self.__menu is None ): node.title = self.tr(BOOKMARKMENU) self.__menu = node else: others.append(node) self.__bookmarkRootNode.remove(node) if len(self.__bookmarkRootNode.children()) > 0: raise RuntimeError("Error loading bookmarks.") if self.__toolbar is None: self.__toolbar = BookmarkNode(BookmarkNode.Folder, self.__bookmarkRootNode) self.__toolbar.title = self.tr(BOOKMARKBAR) else: self.__bookmarkRootNode.add(self.__toolbar) if self.__menu is None: self.__menu = BookmarkNode(BookmarkNode.Folder, self.__bookmarkRootNode) self.__menu.title = self.tr(BOOKMARKMENU) else: self.__bookmarkRootNode.add(self.__menu) for node in others: self.__menu.add(node) def save(self): """ Public method to save the bookmarks. """ if not self.__loaded: return from .XbelWriter import XbelWriter writer = XbelWriter() bookmarkFile = self.getFileName() # save root folder titles in English (i.e. not localized) self.__menu.title = BOOKMARKMENU self.__toolbar.title = BOOKMARKBAR if not writer.write(bookmarkFile, self.__bookmarkRootNode): E5MessageBox.warning( None, self.tr("Saving Bookmarks"), self.tr("""Error saving bookmarks to <b>{0}</b>.""") .format(bookmarkFile)) # restore localized titles self.__menu.title = self.tr(BOOKMARKMENU) self.__toolbar.title = self.tr(BOOKMARKBAR) self.bookmarksSaved.emit() def addBookmark(self, parent, node, row=-1): """ Public method to add a bookmark. @param parent reference to the node to add to (BookmarkNode) @param node reference to the node to add (BookmarkNode) @param row row number (integer) """ if not self.__loaded: return self.setTimestamp(node, BookmarkNode.TsAdded, QDateTime.currentDateTime()) command = InsertBookmarksCommand(self, parent, node, row) self.__commands.push(command) def removeBookmark(self, node): """ Public method to remove a bookmark. @param node reference to the node to be removed (BookmarkNode) """ if not self.__loaded: return parent = node.parent() row = parent.children().index(node) command = RemoveBookmarksCommand(self, parent, row) self.__commands.push(command) def setTitle(self, node, newTitle): """ Public method to set the title of a bookmark. @param node reference to the node to be changed (BookmarkNode) @param newTitle title to be set (string) """ if not self.__loaded: return command = ChangeBookmarkCommand(self, node, newTitle, True) self.__commands.push(command) def setUrl(self, node, newUrl): """ Public method to set the URL of a bookmark. @param node reference to the node to be changed (BookmarkNode) @param newUrl URL to be set (string) """ if not self.__loaded: return command = ChangeBookmarkCommand(self, node, newUrl, False) self.__commands.push(command) def setNodeChanged(self, node): """ Public method to signal changes of bookmarks other than title, URL or timestamp. @param node reference to the bookmark (BookmarkNode) """ self.__saveTimer.changeOccurred() def setTimestamp(self, node, timestampType, timestamp): """ Public method to set the URL of a bookmark. @param node reference to the node to be changed (BookmarkNode) @param timestampType type of the timestamp to set (BookmarkNode.TsAdded, BookmarkNode.TsModified, BookmarkNode.TsVisited) @param timestamp timestamp to set (QDateTime) """ if not self.__loaded: return assert timestampType in [BookmarkNode.TsAdded, BookmarkNode.TsModified, BookmarkNode.TsVisited] if timestampType == BookmarkNode.TsAdded: node.added = timestamp elif timestampType == BookmarkNode.TsModified: node.modified = timestamp elif timestampType == BookmarkNode.TsVisited: node.visited = timestamp self.__saveTimer.changeOccurred() def incVisitCount(self, node): """ Public method to increment the visit count of a bookmark. @param node reference to the node to be changed (BookmarkNode) """ if not self.__loaded: return if node: node.visitCount += 1 self.__saveTimer.changeOccurred() def setVisitCount(self, node, count): """ Public method to set the visit count of a bookmark. @param node reference to the node to be changed (BookmarkNode) @param count visit count to be set (int or str) """ try: node.visitCount = int(count) self.__saveTimer.changeOccurred() except ValueError: # ignore invalid values pass def bookmarks(self): """ Public method to get a reference to the root bookmark node. @return reference to the root bookmark node (BookmarkNode) """ if not self.__loaded: self.load() return self.__bookmarkRootNode def menu(self): """ Public method to get a reference to the bookmarks menu node. @return reference to the bookmarks menu node (BookmarkNode) """ if not self.__loaded: self.load() return self.__menu def toolbar(self): """ Public method to get a reference to the bookmarks toolbar node. @return reference to the bookmarks toolbar node (BookmarkNode) """ if not self.__loaded: self.load() return self.__toolbar def bookmarksModel(self): """ Public method to get a reference to the bookmarks model. @return reference to the bookmarks model (BookmarksModel) """ if self.__bookmarksModel is None: from .BookmarksModel import BookmarksModel self.__bookmarksModel = BookmarksModel(self, self) return self.__bookmarksModel def importBookmarks(self): """ Public method to import bookmarks. """ from .BookmarksImportDialog import BookmarksImportDialog dlg = BookmarksImportDialog() if dlg.exec_() == QDialog.Accepted: importRootNode = dlg.getImportedBookmarks() if importRootNode is not None: self.addBookmark(self.menu(), importRootNode) def exportBookmarks(self): """ Public method to export the bookmarks. """ fileName, selectedFilter = E5FileDialog.getSaveFileNameAndFilter( None, self.tr("Export Bookmarks"), "eric6_bookmarks.xbel", self.tr("XBEL bookmarks (*.xbel);;" "XBEL bookmarks (*.xml);;" "HTML Bookmarks (*.html)")) if not fileName: return ext = QFileInfo(fileName).suffix() if not ext: ex = selectedFilter.split("(*")[1].split(")")[0] if ex: fileName += ex ext = QFileInfo(fileName).suffix() if ext == "html": from .NsHtmlWriter import NsHtmlWriter writer = NsHtmlWriter() else: from .XbelWriter import XbelWriter writer = XbelWriter() if not writer.write(fileName, self.__bookmarkRootNode): E5MessageBox.critical( None, self.tr("Exporting Bookmarks"), self.tr("""Error exporting bookmarks to <b>{0}</b>.""") .format(fileName)) def faviconChanged(self, url): """ Public slot to update the icon image for an URL. @param url URL of the icon to update (QUrl or string) """ if isinstance(url, QUrl): url = url.toString() nodes = self.bookmarksForUrl(url) for node in nodes: self.bookmarksModel().entryChanged(node) def bookmarkForUrl(self, url, start=StartRoot): """ Public method to get a bookmark node for a given URL. @param url URL of the bookmark to search for (QUrl or string) @keyparam start indicator for the start of the search (StartRoot, StartMenu, StartToolBar) @return bookmark node for the given url (BookmarkNode) """ if start == StartMenu: startNode = self.__menu elif start == StartToolBar: startNode = self.__toolbar else: startNode = self.__bookmarkRootNode if startNode is None: return None if isinstance(url, QUrl): url = url.toString() return self.__searchBookmark(url, startNode) def __searchBookmark(self, url, startNode): """ Private method get a bookmark node for a given URL. @param url URL of the bookmark to search for (string) @param startNode reference to the node to start searching (BookmarkNode) @return bookmark node for the given url (BookmarkNode) """ bm = None for node in startNode.children(): if node.type() == BookmarkNode.Folder: bm = self.__searchBookmark(url, node) elif node.type() == BookmarkNode.Bookmark: if node.url == url: bm = node if bm is not None: return bm return None def bookmarksForUrl(self, url, start=StartRoot): """ Public method to get a list of bookmark nodes for a given URL. @param url URL of the bookmarks to search for (QUrl or string) @keyparam start indicator for the start of the search (StartRoot, StartMenu, StartToolBar) @return list of bookmark nodes for the given url (list of BookmarkNode) """ if start == StartMenu: startNode = self.__menu elif start == StartToolBar: startNode = self.__toolbar else: startNode = self.__bookmarkRootNode if startNode is None: return [] if isinstance(url, QUrl): url = url.toString() return self.__searchBookmarks(url, startNode) def __searchBookmarks(self, url, startNode): """ Private method get a list of bookmark nodes for a given URL. @param url URL of the bookmarks to search for (string) @param startNode reference to the node to start searching (BookmarkNode) @return list of bookmark nodes for the given url (list of BookmarkNode) """ bm = [] for node in startNode.children(): if node.type() == BookmarkNode.Folder: bm.extend(self.__searchBookmarks(url, node)) elif node.type() == BookmarkNode.Bookmark: if node.url == url: bm.append(node) return bm
def __init__(self, parent, project): """ Constructor @param parent the parent (QWidget) @param project reference to the project object """ super(TaskViewer, self).__init__(parent) self.setSortingEnabled(True) self.setExpandsOnDoubleClick(False) self.__headerItem = QTreeWidgetItem( ["", "", self.tr("Summary"), self.tr("Filename"), self.tr("Line"), ""]) self.__headerItem.setIcon( 0, UI.PixmapCache.getIcon("taskCompleted.png")) self.__headerItem.setIcon( 1, UI.PixmapCache.getIcon("taskPriority.png")) self.setHeaderItem(self.__headerItem) self.header().setSortIndicator(2, Qt.AscendingOrder) self.__resizeColumns() self.tasks = [] self.copyTask = None self.projectOpen = False self.project = project self.projectTasksScanFilter = "" from .TaskFilter import TaskFilter self.taskFilter = TaskFilter() self.taskFilter.setActive(False) self.__projectTasksSaveTimer = AutoSaver(self, self.saveProjectTasks) self.__projectTasksMenu = QMenu( self.tr("P&roject Tasks"), self) self.__projectTasksMenu.addAction( self.tr("&Regenerate project tasks"), self.__regenerateProjectTasks) self.__projectTasksMenu.addSeparator() self.__projectTasksMenu.addAction( self.tr("&Configure scan options"), self.__configureProjectTasksScanOptions) self.__menu = QMenu(self) self.__menu.addAction(self.tr("&New Task..."), self.__newTask) self.subtaskItem = self.__menu.addAction( self.tr("New &Sub-Task..."), self.__newSubTask) self.__menu.addSeparator() self.projectTasksMenuItem = self.__menu.addMenu( self.__projectTasksMenu) self.__menu.addSeparator() self.gotoItem = self.__menu.addAction( self.tr("&Go To"), self.__goToTask) self.__menu.addSeparator() self.copyItem = self.__menu.addAction( self.tr("&Copy"), self.__copyTask) self.pasteItem = self.__menu.addAction( self.tr("&Paste"), self.__pasteTask) self.pasteMainItem = self.__menu.addAction( self.tr("Paste as &Main Task"), self.__pasteMainTask) self.deleteItem = self.__menu.addAction( self.tr("&Delete"), self.__deleteTask) self.__menu.addSeparator() self.markCompletedItem = self.__menu.addAction( self.tr("&Mark Completed"), self.__markCompleted) self.__menu.addAction( self.tr("Delete Completed &Tasks"), self.__deleteCompleted) self.__menu.addSeparator() self.__menu.addAction( self.tr("P&roperties..."), self.__editTaskProperties) self.__menu.addSeparator() self.__menuFilteredAct = self.__menu.addAction( self.tr("&Filtered display")) self.__menuFilteredAct.setCheckable(True) self.__menuFilteredAct.setChecked(False) self.__menuFilteredAct.triggered[bool].connect(self.__activateFilter) self.__menu.addAction( self.tr("Filter c&onfiguration..."), self.__configureFilter) self.__menu.addSeparator() self.__menu.addAction( self.tr("Resi&ze columns"), self.__resizeColumns) self.__menu.addSeparator() self.__menu.addAction(self.tr("Configure..."), self.__configure) self.__backMenu = QMenu(self) self.__backMenu.addAction(self.tr("&New Task..."), self.__newTask) self.__backMenu.addSeparator() self.backProjectTasksMenuItem = self.__backMenu.addMenu( self.__projectTasksMenu) self.__backMenu.addSeparator() self.backPasteItem = self.__backMenu.addAction( self.tr("&Paste"), self.__pasteTask) self.backPasteMainItem = self.__menu.addAction( self.tr("Paste as &Main Task"), self.__pasteMainTask) self.__backMenu.addSeparator() self.__backMenu.addAction( self.tr("Delete Completed &Tasks"), self.__deleteCompleted) self.__backMenu.addSeparator() self.__backMenuFilteredAct = self.__backMenu.addAction( self.tr("&Filtered display")) self.__backMenuFilteredAct.setCheckable(True) self.__backMenuFilteredAct.setChecked(False) self.__backMenuFilteredAct.triggered[bool].connect( self.__activateFilter) self.__backMenu.addAction( self.tr("Filter c&onfiguration..."), self.__configureFilter) self.__backMenu.addSeparator() self.__backMenu.addAction( self.tr("Resi&ze columns"), self.__resizeColumns) self.__backMenu.addSeparator() self.__backMenu.addAction( self.tr("Configure..."), self.__configure) self.__activating = False self.setContextMenuPolicy(Qt.CustomContextMenu) self.customContextMenuRequested.connect(self.__showContextMenu) self.itemActivated.connect(self.__taskItemActivated) self.setWindowIcon(UI.PixmapCache.getIcon("eric.png")) self.__generateTopLevelItems()
class DownloadManager(QDialog, Ui_DownloadManager): """ Class implementing the download manager. """ RemoveNever = 0 RemoveExit = 1 RemoveSuccessFullDownload = 2 def __init__(self, parent=None): """ Constructor @param parent reference to the parent widget (QWidget) """ super(DownloadManager, self).__init__(parent) self.setupUi(self) self.__saveTimer = AutoSaver(self, self.save) self.__model = DownloadModel(self) self.__manager = Helpviewer.HelpWindow.HelpWindow\ .networkAccessManager() self.__iconProvider = None self.__downloads = [] self.__downloadDirectory = "" self.__loaded = False self.setDownloadDirectory(Preferences.getUI("DownloadPath")) self.downloadsView.setShowGrid(False) self.downloadsView.verticalHeader().hide() self.downloadsView.horizontalHeader().hide() self.downloadsView.setAlternatingRowColors(True) self.downloadsView.horizontalHeader().setStretchLastSection(True) self.downloadsView.setModel(self.__model) self.downloadsView.setContextMenuPolicy(Qt.CustomContextMenu) self.downloadsView.customContextMenuRequested.connect( self.__customContextMenuRequested) self.__load() def __customContextMenuRequested(self, pos): """ Private slot to handle the context menu request for the bookmarks tree. @param pos position the context menu was requested (QPoint) """ menu = QMenu() selectedRowsCount = len( self.downloadsView.selectionModel().selectedRows()) if selectedRowsCount == 1: row = self.downloadsView.selectionModel().selectedRows()[0].row() itm = self.__downloads[row] if itm.downloadCanceled(): menu.addAction( UI.PixmapCache.getIcon("restart.png"), self.tr("Retry"), self.__contextMenuRetry) else: if itm.downloadedSuccessfully(): menu.addAction( UI.PixmapCache.getIcon("open.png"), self.tr("Open"), self.__contextMenuOpen) elif itm.downloading(): menu.addAction( UI.PixmapCache.getIcon("stopLoading.png"), self.tr("Cancel"), self.__contextMenuCancel) menu.addSeparator() menu.addAction( self.tr("Open Containing Folder"), self.__contextMenuOpenFolder) menu.addSeparator() menu.addAction( self.tr("Go to Download Page"), self.__contextMenuGotoPage) menu.addAction( self.tr("Copy Download Link"), self.__contextMenuCopyLink) menu.addSeparator() menu.addAction(self.tr("Select All"), self.__contextMenuSelectAll) if selectedRowsCount > 1 or \ (selectedRowsCount == 1 and not self.__downloads[ self.downloadsView.selectionModel().selectedRows()[0].row()] .downloading()): menu.addSeparator() menu.addAction( self.tr("Remove From List"), self.__contextMenuRemoveSelected) menu.exec_(QCursor.pos()) def shutdown(self): """ Public method to stop the download manager. """ self.__saveTimer.changeOccurred() self.__saveTimer.saveIfNeccessary() self.close() def activeDownloads(self): """ Public method to get the number of active downloads. @return number of active downloads (integer) """ count = 0 for download in self.__downloads: if download.downloading(): count += 1 return count def allowQuit(self): """ Public method to check, if it is ok to quit. @return flag indicating allowance to quit (boolean) """ if self.activeDownloads() > 0: res = E5MessageBox.yesNo( self, self.tr(""), self.tr("""There are %n downloads in progress.\n""" """Do you want to quit anyway?""", "", self.activeDownloads()), icon=E5MessageBox.Warning) if not res: self.show() return False return True def download(self, requestOrUrl, requestFileName=False, mainWindow=None): """ Public method to download a file. @param requestOrUrl reference to a request object (QNetworkRequest) or a URL to be downloaded (QUrl) @keyparam requestFileName flag indicating to ask for the download file name (boolean) @keyparam mainWindow reference to the main window (HelpWindow) """ request = QNetworkRequest(requestOrUrl) if request.url().isEmpty(): return self.handleUnsupportedContent( self.__manager.get(request), requestFileName=requestFileName, download=True, mainWindow=mainWindow) def handleUnsupportedContent(self, reply, requestFileName=False, webPage=None, download=False, mainWindow=None): """ Public method to handle unsupported content by downloading the referenced resource. @param reply reference to the reply object (QNetworkReply) @keyparam requestFileName indicating to ask for a filename (boolean) @keyparam webPage reference to the web page (HelpWebPage) @keyparam download flag indicating a download request (boolean) @keyparam mainWindow reference to the main window (HelpWindow) """ if reply is None or reply.url().isEmpty(): return size = reply.header(QNetworkRequest.ContentLengthHeader) if size == 0: return from .DownloadItem import DownloadItem itm = DownloadItem( reply=reply, requestFilename=requestFileName, webPage=webPage, download=download, parent=self, mainWindow=mainWindow) self.__addItem(itm) if itm.canceledFileSelect(): return if not self.isVisible(): self.show() self.activateWindow() self.raise_() def __addItem(self, itm): """ Private method to add a download to the list of downloads. @param itm reference to the download item (DownloadItem) """ itm.statusChanged.connect(self.__updateRow) itm.downloadFinished.connect(self.__finished) row = len(self.__downloads) self.__model.beginInsertRows(QModelIndex(), row, row) self.__downloads.append(itm) self.__model.endInsertRows() self.downloadsView.setIndexWidget(self.__model.index(row, 0), itm) icon = self.style().standardIcon(QStyle.SP_FileIcon) itm.setIcon(icon) self.downloadsView.setRowHeight(row, itm.sizeHint().height() * 1.5) # just in case the download finished before the constructor returned self.__updateRow(itm) self.changeOccurred() self.__updateActiveItemCount() def __updateRow(self, itm=None): """ Private slot to update a download item. @param itm reference to the download item (DownloadItem) """ if itm is None: itm = self.sender() if itm not in self.__downloads: return row = self.__downloads.index(itm) if self.__iconProvider is None: self.__iconProvider = QFileIconProvider() icon = self.__iconProvider.icon(QFileInfo(itm.fileName())) if icon.isNull(): icon = self.style().standardIcon(QStyle.SP_FileIcon) itm.setIcon(icon) oldHeight = self.downloadsView.rowHeight(row) self.downloadsView.setRowHeight( row, max(oldHeight, itm.minimumSizeHint().height() * 1.5)) remove = False globalSettings = QWebSettings.globalSettings() if not itm.downloading() and \ globalSettings.testAttribute(QWebSettings.PrivateBrowsingEnabled): remove = True if itm.downloadedSuccessfully() and \ self.removePolicy() == DownloadManager.RemoveSuccessFullDownload: remove = True if remove: self.__model.removeRow(row) self.cleanupButton.setEnabled( (len(self.__downloads) - self.activeDownloads()) > 0) # record the change self.changeOccurred() def removePolicy(self): """ Public method to get the remove policy. @return remove policy (integer) """ return Preferences.getHelp("DownloadManagerRemovePolicy") def setRemovePolicy(self, policy): """ Public method to set the remove policy. @param policy policy to be set (DownloadManager.RemoveExit, DownloadManager.RemoveNever, DownloadManager.RemoveSuccessFullDownload) """ assert policy in (DownloadManager.RemoveExit, DownloadManager.RemoveNever, DownloadManager.RemoveSuccessFullDownload) if policy == self.removePolicy(): return Preferences.setHelp("DownloadManagerRemovePolicy", self.policy) def save(self): """ Public method to save the download settings. """ if not self.__loaded: return Preferences.setHelp("DownloadManagerSize", self.size()) Preferences.setHelp("DownloadManagerPosition", self.pos()) if self.removePolicy() == DownloadManager.RemoveExit: return downloads = [] for download in self.__downloads: downloads.append(download.getData()) Preferences.setHelp("DownloadManagerDownloads", downloads) def __load(self): """ Private method to load the download settings. """ if self.__loaded: return size = Preferences.getHelp("DownloadManagerSize") if size.isValid(): self.resize(size) pos = Preferences.getHelp("DownloadManagerPosition") self.move(pos) downloads = Preferences.getHelp("DownloadManagerDownloads") for download in downloads: if not download[0].isEmpty() and \ download[1] != "": from .DownloadItem import DownloadItem itm = DownloadItem(parent=self) itm.setData(download) self.__addItem(itm) self.cleanupButton.setEnabled( (len(self.__downloads) - self.activeDownloads()) > 0) self.__loaded = True self.__updateActiveItemCount() def cleanup(self): """ Public slot to cleanup the downloads. """ self.on_cleanupButton_clicked() @pyqtSlot() def on_cleanupButton_clicked(self): """ Private slot cleanup the downloads. """ if len(self.__downloads) == 0: return self.__model.removeRows(0, len(self.__downloads)) if len(self.__downloads) == 0 and \ self.__iconProvider is not None: self.__iconProvider = None self.changeOccurred() self.__updateActiveItemCount() def __updateItemCount(self): """ Private method to update the count label. """ count = len(self.__downloads) self.countLabel.setText(self.tr("%n Download(s)", "", count)) def __updateActiveItemCount(self): """ Private method to update the window title. """ count = self.activeDownloads() if count > 0: self.setWindowTitle( self.tr("Downloading %n file(s)", "", count)) else: self.setWindowTitle(self.tr("Downloads")) def __finished(self): """ Private slot to handle a finished download. """ self.__updateActiveItemCount() if self.isVisible(): QApplication.alert(self) def setDownloadDirectory(self, directory): """ Public method to set the current download directory. @param directory current download directory (string) """ self.__downloadDirectory = directory if self.__downloadDirectory != "": self.__downloadDirectory += "/" def downloadDirectory(self): """ Public method to get the current download directory. @return current download directory (string) """ return self.__downloadDirectory def count(self): """ Public method to get the number of downloads. @return number of downloads (integer) """ return len(self.__downloads) def downloads(self): """ Public method to get a reference to the downloads. @return reference to the downloads (list of DownloadItem) """ return self.__downloads def changeOccurred(self): """ Public method to signal a change. """ self.__saveTimer.changeOccurred() self.__updateItemCount() ########################################################################### ## Context menu related methods below ########################################################################### def __currentItem(self): """ Private method to get a reference to the current item. @return reference to the current item (DownloadItem) """ index = self.downloadsView.currentIndex() if index and index.isValid(): row = index.row() return self.__downloads[row] return None def __contextMenuRetry(self): """ Private method to retry of the download. """ itm = self.__currentItem() if itm is not None: itm.retry() def __contextMenuOpen(self): """ Private method to open the downloaded file. """ itm = self.__currentItem() if itm is not None: itm.openFile() def __contextMenuOpenFolder(self): """ Private method to open the folder containing the downloaded file. """ itm = self.__currentItem() if itm is not None: itm.openFolder() def __contextMenuCancel(self): """ Private method to cancel the current download. """ itm = self.__currentItem() if itm is not None: itm.cancelDownload() def __contextMenuGotoPage(self): """ Private method to open the download page. """ itm = self.__currentItem() if itm is not None: url = itm.getPageUrl() Helpviewer.HelpWindow.HelpWindow.mainWindow().openUrl(url, "") def __contextMenuCopyLink(self): """ Private method to copy the download link to the clipboard. """ itm = self.__currentItem() if itm is not None: url = itm.getPageUrl().toString() QApplication.clipboard().setText(url) def __contextMenuSelectAll(self): """ Private method to select all downloads. """ self.downloadsView.selectAll() def __contextMenuRemoveSelected(self): """ Private method to remove the selected downloads from the list. """ self.downloadsView.removeSelected()
class TaskViewer(QTreeWidget): """ Class implementing the task viewer. @signal displayFile(str, int) emitted to go to a file task """ displayFile = pyqtSignal(str, int) def __init__(self, parent, project): """ Constructor @param parent the parent (QWidget) @param project reference to the project object """ super(TaskViewer, self).__init__(parent) self.setSortingEnabled(True) self.setExpandsOnDoubleClick(False) self.__headerItem = QTreeWidgetItem( ["", "", self.tr("Summary"), self.tr("Filename"), self.tr("Line"), ""]) self.__headerItem.setIcon( 0, UI.PixmapCache.getIcon("taskCompleted.png")) self.__headerItem.setIcon( 1, UI.PixmapCache.getIcon("taskPriority.png")) self.setHeaderItem(self.__headerItem) self.header().setSortIndicator(2, Qt.AscendingOrder) self.__resizeColumns() self.tasks = [] self.copyTask = None self.projectOpen = False self.project = project self.projectTasksScanFilter = "" from .TaskFilter import TaskFilter self.taskFilter = TaskFilter() self.taskFilter.setActive(False) self.__projectTasksSaveTimer = AutoSaver(self, self.saveProjectTasks) self.__projectTasksMenu = QMenu( self.tr("P&roject Tasks"), self) self.__projectTasksMenu.addAction( self.tr("&Regenerate project tasks"), self.__regenerateProjectTasks) self.__projectTasksMenu.addSeparator() self.__projectTasksMenu.addAction( self.tr("&Configure scan options"), self.__configureProjectTasksScanOptions) self.__menu = QMenu(self) self.__menu.addAction(self.tr("&New Task..."), self.__newTask) self.subtaskItem = self.__menu.addAction( self.tr("New &Sub-Task..."), self.__newSubTask) self.__menu.addSeparator() self.projectTasksMenuItem = self.__menu.addMenu( self.__projectTasksMenu) self.__menu.addSeparator() self.gotoItem = self.__menu.addAction( self.tr("&Go To"), self.__goToTask) self.__menu.addSeparator() self.copyItem = self.__menu.addAction( self.tr("&Copy"), self.__copyTask) self.pasteItem = self.__menu.addAction( self.tr("&Paste"), self.__pasteTask) self.pasteMainItem = self.__menu.addAction( self.tr("Paste as &Main Task"), self.__pasteMainTask) self.deleteItem = self.__menu.addAction( self.tr("&Delete"), self.__deleteTask) self.__menu.addSeparator() self.markCompletedItem = self.__menu.addAction( self.tr("&Mark Completed"), self.__markCompleted) self.__menu.addAction( self.tr("Delete Completed &Tasks"), self.__deleteCompleted) self.__menu.addSeparator() self.__menu.addAction( self.tr("P&roperties..."), self.__editTaskProperties) self.__menu.addSeparator() self.__menuFilteredAct = self.__menu.addAction( self.tr("&Filtered display")) self.__menuFilteredAct.setCheckable(True) self.__menuFilteredAct.setChecked(False) self.__menuFilteredAct.triggered[bool].connect(self.__activateFilter) self.__menu.addAction( self.tr("Filter c&onfiguration..."), self.__configureFilter) self.__menu.addSeparator() self.__menu.addAction( self.tr("Resi&ze columns"), self.__resizeColumns) self.__menu.addSeparator() self.__menu.addAction(self.tr("Configure..."), self.__configure) self.__backMenu = QMenu(self) self.__backMenu.addAction(self.tr("&New Task..."), self.__newTask) self.__backMenu.addSeparator() self.backProjectTasksMenuItem = self.__backMenu.addMenu( self.__projectTasksMenu) self.__backMenu.addSeparator() self.backPasteItem = self.__backMenu.addAction( self.tr("&Paste"), self.__pasteTask) self.backPasteMainItem = self.__menu.addAction( self.tr("Paste as &Main Task"), self.__pasteMainTask) self.__backMenu.addSeparator() self.__backMenu.addAction( self.tr("Delete Completed &Tasks"), self.__deleteCompleted) self.__backMenu.addSeparator() self.__backMenuFilteredAct = self.__backMenu.addAction( self.tr("&Filtered display")) self.__backMenuFilteredAct.setCheckable(True) self.__backMenuFilteredAct.setChecked(False) self.__backMenuFilteredAct.triggered[bool].connect( self.__activateFilter) self.__backMenu.addAction( self.tr("Filter c&onfiguration..."), self.__configureFilter) self.__backMenu.addSeparator() self.__backMenu.addAction( self.tr("Resi&ze columns"), self.__resizeColumns) self.__backMenu.addSeparator() self.__backMenu.addAction( self.tr("Configure..."), self.__configure) self.__activating = False self.setContextMenuPolicy(Qt.CustomContextMenu) self.customContextMenuRequested.connect(self.__showContextMenu) self.itemActivated.connect(self.__taskItemActivated) self.setWindowIcon(UI.PixmapCache.getIcon("eric.png")) self.__generateTopLevelItems() def __generateTopLevelItems(self): """ Private method to generate the 'Extracted Tasks' item. """ self.__extractedItem = QTreeWidgetItem(self, [self.tr("Extracted Tasks")]) self.__manualItem = QTreeWidgetItem(self, [self.tr("Manual Tasks")]) for itm in [self.__extractedItem, self.__manualItem]: itm.setFirstColumnSpanned(True) itm.setExpanded(True) itm.setHidden(True) font = itm.font(0) font.setUnderline(True) itm.setFont(0, font) def __checkTopLevelItems(self): """ Private slot to check the 'Extracted Tasks' item for children. """ for itm in [self.__extractedItem, self.__manualItem]: visibleCount = itm.childCount() for index in range(itm.childCount()): if itm.child(index).isHidden(): visibleCount -= 1 itm.setHidden(visibleCount == 0) def __resort(self): """ Private method to resort the tree. """ self.sortItems(self.sortColumn(), self.header().sortIndicatorOrder()) def __resizeColumns(self): """ Private method to resize the list columns. """ self.header().resizeSections(QHeaderView.ResizeToContents) self.header().setStretchLastSection(True) def findParentTask(self, parentUid): """ Public method to find a parent task by its ID. @param parentUid uid of the parent task (string) @return reference to the task (Task) """ if not parentUid: return None parentTask = None for task in self.tasks: if task.getUuid() == parentUid: parentTask = task break return parentTask def __refreshDisplay(self): """ Private method to refresh the display. """ for task in self.tasks: task.setHidden(not self.taskFilter.showTask(task)) self.__checkTopLevelItems() self.__resort() self.__resizeColumns() def __taskItemActivated(self, itm, col): """ Private slot to handle the activation of an item. @param itm reference to the activated item (QTreeWidgetItem) @param col column the item was activated in (integer) """ if not self.__activating and \ itm is not self.__extractedItem and \ itm is not self.__manualItem: self.__activating = True fn = itm.getFilename() if fn: self.displayFile.emit(fn, itm.getLineno()) else: self.__editTaskProperties() self.__activating = False def __showContextMenu(self, coord): """ Private slot to show the context menu of the list. @param coord the position of the mouse pointer (QPoint) """ itm = self.itemAt(coord) coord = self.mapToGlobal(coord) if itm is None or \ itm is self.__extractedItem or \ itm is self.__manualItem: self.backProjectTasksMenuItem.setEnabled(self.projectOpen) if self.copyTask: self.backPasteItem.setEnabled(True) self.backPasteMainItem.setEnabled(True) else: self.backPasteItem.setEnabled(False) self.backPasteMainItem.setEnabled(False) self.__backMenu.popup(coord) else: self.projectTasksMenuItem.setEnabled(self.projectOpen) if itm.getFilename(): self.gotoItem.setEnabled(True) self.deleteItem.setEnabled(True) self.markCompletedItem.setEnabled(False) self.copyItem.setEnabled(False) self.subtaskItem.setEnabled(False) else: self.gotoItem.setEnabled(False) self.deleteItem.setEnabled(True) self.markCompletedItem.setEnabled(True) self.copyItem.setEnabled(True) self.subtaskItem.setEnabled(True) if self.copyTask: self.pasteItem.setEnabled(True) self.pasteMainItem.setEnabled(True) else: self.pasteItem.setEnabled(False) self.pasteMainItem.setEnabled(False) self.__menu.popup(coord) def setProjectOpen(self, o=False): """ Public slot to set the project status. @param o flag indicating the project status """ self.projectOpen = o def addTask(self, summary, priority=1, filename="", lineno=0, completed=False, _time=0, isProjectTask=False, taskType=Task.TypeTodo, description="", uid="", parentTask=None): """ Public slot to add a task. @param summary summary text of the task (string) @param priority priority of the task (0=high, 1=normal, 2=low) @param filename filename containing the task (string) @param lineno line number containing the task (integer) @param completed flag indicating completion status (boolean) @param _time creation time of the task (float, if 0 use current time) @param isProjectTask flag indicating a task related to the current project (boolean) @param taskType type of the task (one of Task.TypeFixme, Task.TypeTodo, Task.TypeWarning, Task.TypeNote) @param description explanatory text of the task (string) @param uid unique id of the task (string) @param parentTask reference to the parent task item (Task) @return reference to the task item (Task) """ if parentTask: parentUid = parentTask.getUuid() else: parentUid = "" task = Task(summary, priority, filename, lineno, completed, _time, isProjectTask, taskType, self.project, description, uid, parentUid) self.tasks.append(task) if parentTask: parentTask.addChild(task) parentTask.setExpanded(True) elif filename: self.__extractedItem.addChild(task) else: self.__manualItem.addChild(task) task.setHidden(not self.taskFilter.showTask(task)) self.__checkTopLevelItems() self.__resort() self.__resizeColumns() if isProjectTask: self.__projectTasksSaveTimer.changeOccurred() return task def addFileTask(self, summary, filename, lineno, taskType=Task.TypeTodo, description=""): """ Public slot to add a file related task. @param summary summary text of the task (string) @param filename filename containing the task (string) @param lineno line number containing the task (integer) @param taskType type of the task (one of Task.TypeFixme, Task.TypeTodo, Task.TypeWarning, Task.TypeNote) @param description explanatory text of the task (string) """ self.addTask(summary, filename=filename, lineno=lineno, isProjectTask=( self.project and self.project.isProjectSource(filename)), taskType=taskType, description=description) def getProjectTasks(self): """ Public method to retrieve all project related tasks. @return copy of tasks (list of Task) """ tasks = [task for task in self.tasks if task.isProjectTask()] return tasks[:] def getGlobalTasks(self): """ Public method to retrieve all non project related tasks. @return copy of tasks (list of Task) """ tasks = [task for task in self.tasks if not task.isProjectTask()] return tasks[:] def clearTasks(self): """ Public slot to clear all tasks from display. """ self.tasks = [] self.clear() self.__generateTopLevelItems() def clearProjectTasks(self, fileOnly=False): """ Public slot to clear project related tasks. @keyparam fileOnly flag indicating to clear only file related project tasks (boolean) """ for task in reversed(self.tasks[:]): if (fileOnly and task.isProjectFileTask()) or \ (not fileOnly and task.isProjectTask()): if self.copyTask == task: self.copyTask = None parent = task.parent() parent.removeChild(task) self.tasks.remove(task) del task self.__checkTopLevelItems() self.__resort() self.__resizeColumns() def clearFileTasks(self, filename, conditionally=False): """ Public slot to clear all tasks related to a file. @param filename name of the file (string) @param conditionally flag indicating to clear the tasks of the file checking some conditions (boolean) """ if conditionally: if self.project and self.project.isProjectSource(filename): # project related tasks will not be cleared return if not Preferences.getTasks("ClearOnFileClose"): return for task in self.tasks[:]: if task.getFilename() == filename: if self.copyTask == task: self.copyTask = None self.__extractedItem.removeChild(task) self.tasks.remove(task) if task.isProjectTask: self.__projectTasksSaveTimer.changeOccurred() del task self.__checkTopLevelItems() self.__resort() self.__resizeColumns() def __editTaskProperties(self): """ Private slot to handle the "Properties" context menu entry. """ from .TaskPropertiesDialog import TaskPropertiesDialog task = self.currentItem() dlg = TaskPropertiesDialog(task, self, self.projectOpen) ro = task.getFilename() != "" if ro: dlg.setReadOnly() if dlg.exec_() == QDialog.Accepted and not ro: summary, priority, completed, isProjectTask, description = \ dlg.getData() task.setSummary(summary) task.setPriority(priority) task.setCompleted(completed) task.setProjectTask(isProjectTask) task.setDescription(description) self.__projectTasksSaveTimer.changeOccurred() def __newTask(self): """ Private slot to handle the "New Task" context menu entry. """ from .TaskPropertiesDialog import TaskPropertiesDialog dlg = TaskPropertiesDialog(None, self, self.projectOpen) if dlg.exec_() == QDialog.Accepted: summary, priority, completed, isProjectTask, description = \ dlg.getData() self.addTask(summary, priority, completed=completed, isProjectTask=isProjectTask, description=description) def __newSubTask(self): """ Private slot to handle the "New Sub-Task" context menu entry. """ parentTask = self.currentItem() projectTask = parentTask.isProjectTask() from .TaskPropertiesDialog import TaskPropertiesDialog dlg = TaskPropertiesDialog(None, self, self.projectOpen) dlg.setSubTaskMode(projectTask) if dlg.exec_() == QDialog.Accepted: summary, priority, completed, isProjectTask, description = \ dlg.getData() self.addTask(summary, priority, completed=completed, isProjectTask=isProjectTask, description=description, parentTask=parentTask) def __markCompleted(self): """ Private slot to handle the "Mark Completed" context menu entry. """ task = self.currentItem() task.setCompleted(True) def __deleteCompleted(self): """ Private slot to handle the "Delete Completed Tasks" context menu entry. """ for task in reversed(self.tasks[:]): if task.isCompleted(): if self.copyTask == task: self.copyTask = None parent = task.parent() parent.removeChild(task) self.tasks.remove(task) if task.isProjectTask: self.__projectTasksSaveTimer.changeOccurred() del task self.__checkTopLevelItems() self.__resort() self.__resizeColumns() ci = self.currentItem() if ci: ind = self.indexFromItem(ci, self.currentColumn()) self.scrollTo(ind, QAbstractItemView.PositionAtCenter) def __copyTask(self): """ Private slot to handle the "Copy" context menu entry. """ task = self.currentItem() self.copyTask = task def __pasteTask(self): """ Private slot to handle the "Paste" context menu entry. """ if self.copyTask: parent = self.copyTask.parent() if not isinstance(parent, Task): parent = None self.addTask(self.copyTask.summary, priority=self.copyTask.priority, completed=self.copyTask.completed, description=self.copyTask.description, isProjectTask=self.copyTask._isProjectTask, parentTask=parent) def __pasteMainTask(self): """ Private slot to handle the "Paste as Main Task" context menu entry. """ if self.copyTask: self.addTask(self.copyTask.summary, priority=self.copyTask.priority, completed=self.copyTask.completed, description=self.copyTask.description, isProjectTask=self.copyTask._isProjectTask) def __deleteSubTasks(self, task): """ Private method to delete all sub-tasks. @param task task to delete sub-tasks of (Task) """ for subtask in task.takeChildren(): if self.copyTask == subtask: self.copyTask = None if subtask.childCount() > 0: self.__deleteSubTasks(subtask) self.tasks.remove(subtask) def __deleteTask(self): """ Private slot to handle the "Delete Task" context menu entry. """ task = self.currentItem() if self.copyTask == task: self.copyTask = None if task.childCount() > 0: self.__deleteSubTasks(task) parent = task.parent() parent.removeChild(task) self.tasks.remove(task) if task.isProjectTask: self.__projectTasksSaveTimer.changeOccurred() del task self.__checkTopLevelItems() self.__resort() self.__resizeColumns() ci = self.currentItem() if ci: ind = self.indexFromItem(ci, self.currentColumn()) self.scrollTo(ind, QAbstractItemView.PositionAtCenter) def __goToTask(self): """ Private slot to handle the "Go To" context menu entry. """ task = self.currentItem() self.displayFile.emit(task.getFilename(), task.getLineno()) def handlePreferencesChanged(self): """ Public slot to react to changes of the preferences. """ for task in self.tasks: task.colorizeTask() def __activateFilter(self, on): """ Private slot to handle the "Filtered display" context menu entry. @param on flag indicating the filter state (boolean) """ if on and not self.taskFilter.hasActiveFilter(): res = E5MessageBox.yesNo( self, self.tr("Activate task filter"), self.tr( """The task filter doesn't have any active filters.""" """ Do you want to configure the filter settings?"""), yesDefault=True) if not res: on = False else: self.__configureFilter() on = self.taskFilter.hasActiveFilter() self.taskFilter.setActive(on) self.__menuFilteredAct.setChecked(on) self.__backMenuFilteredAct.setChecked(on) self.__refreshDisplay() def __configureFilter(self): """ Private slot to handle the "Configure filter" context menu entry. """ from .TaskFilterConfigDialog import TaskFilterConfigDialog dlg = TaskFilterConfigDialog(self.taskFilter) if dlg.exec_() == QDialog.Accepted: dlg.configureTaskFilter(self.taskFilter) self.__refreshDisplay() def __configureProjectTasksScanOptions(self): """ Private slot to configure scan options for project tasks. """ filter, ok = QInputDialog.getText( self, self.tr("Scan Filter Patterns"), self.tr("Enter filename patterns of files" " to be excluded separated by a comma:"), QLineEdit.Normal, self.projectTasksScanFilter) if ok: self.projectTasksScanFilter = filter def __regenerateProjectTasks(self): """ Private slot to handle the "Regenerated project tasks" context menu entry. """ markers = { Task.TypeWarning: Preferences.getTasks("TasksWarningMarkers").split(), Task.TypeNote: Preferences.getTasks("TasksNoteMarkers").split(), Task.TypeTodo: Preferences.getTasks("TasksTodoMarkers").split(), Task.TypeFixme: Preferences.getTasks("TasksFixmeMarkers").split(), } files = self.project.pdata["SOURCES"] # apply file filter filterList = [f.strip() for f in self.projectTasksScanFilter.split(",") if f.strip()] if filterList: for filter in filterList: files = [f for f in files if not fnmatch.fnmatch(f, filter)] # remove all project tasks self.clearProjectTasks(fileOnly=True) # now process them progress = E5ProgressDialog( self.tr("Extracting project tasks..."), self.tr("Abort"), 0, len(files), self.tr("%v/%m Files")) progress.setMinimumDuration(0) progress.setWindowTitle(self.tr("Tasks")) count = 0 for file in files: progress.setLabelText( self.tr("Extracting project tasks...\n{0}").format(file)) progress.setValue(count) QApplication.processEvents() if progress.wasCanceled(): break fn = os.path.join(self.project.ppath, file) # read the file and split it into textlines try: text, encoding = Utilities.readEncodedFile(fn) lines = text.splitlines() except (UnicodeError, IOError): count += 1 progress.setValue(count) continue # now search tasks and record them lineIndex = 0 for line in lines: lineIndex += 1 shouldBreak = False for taskType, taskMarkers in markers.items(): for taskMarker in taskMarkers: index = line.find(taskMarker) if index > -1: task = line[index:] self.addFileTask(task, fn, lineIndex, taskType) shouldBreak = True break if shouldBreak: break count += 1 progress.setValue(len(files)) def __configure(self): """ Private method to open the configuration dialog. """ e5App().getObject("UserInterface").showPreferences("tasksPage") def saveProjectTasks(self): """ Public method to write the project tasks. """ if self.projectOpen and Preferences.getTasks("TasksProjectAutoSave"): self.project.writeTasks()
class CookieJar(QNetworkCookieJar): """ Class implementing a QNetworkCookieJar subclass with various accept policies. @signal cookiesChanged() emitted after the cookies have been changed """ JAR_VERSION = 23 AcceptAlways = 0 AcceptNever = 1 AcceptOnlyFromSitesNavigatedTo = 2 KeepUntilExpire = 0 KeepUntilExit = 1 KeepUntilTimeLimit = 2 Allow = 0 Block = 1 AllowForSession = 2 def __init__(self, parent = None): """ Constructor @param parent reference to the parent object (QObject) """ QNetworkCookieJar.__init__(self, parent) self.__loaded = False self.__acceptCookies = self.AcceptOnlyFromSitesNavigatedTo self.__saveTimer = AutoSaver(self, self.save) self.__cookiesFile = os.path.join(Utilities.getConfigDir(), "browser", "cookies.ini") def saveCookies(self, cookiesList): """ Public method to save the cookies. @param cookiesList list of cookies to be saved @return saved cookies as a byte array (QByteArray) """ data = QByteArray() stream = QDataStream(data, QIODevice.WriteOnly) stream.writeUInt16(self.JAR_VERSION) stream.writeUInt32(len(cookiesList)) for cookie in cookiesList: stream << cookie.toRawForm() return data def loadCookies(self, cookies): """ Public method to restore the saved cookies. @param cookies byte array containing the saved cookies (QByteArray) @return list of cookies """ if cookies.isEmpty(): return [] cookiesList = [] data = QByteArray(cookies) stream = QDataStream(data, QIODevice.ReadOnly) version = stream.readUInt16() if version != self.JAR_VERSION: return [] noCookies = stream.readUInt32() rawCookie = QByteArray() while not stream.atEnd(): stream >> rawCookie newCookies = QNetworkCookie.parseCookies(rawCookie) for newCookie in newCookies: cookiesList.append(newCookie) return cookiesList def close(self): """ Public slot to close the cookie jar. """ if self.__loaded and self.__keepCookies == self.KeepUntilExit: self.clear() self.__saveTimer.saveIfNeccessary() def clear(self): """ Public method to clear all cookies. """ if not self.__loaded: self.load() self.setAllCookies([]) self.__saveTimer.changeOccurred() self.emit(SIGNAL("cookiesChanged()")) def load(self): """ Public method to load the cookies. """ if self.__loaded: return cookieSettings = QSettings(self.__cookiesFile, QSettings.IniFormat) # load cookies cookies = cookieSettings.value("Cookies") if cookies.isValid(): cookiesList = self.loadCookies(cookies.toByteArray()) else: cookiesList = [] self.setAllCookies(cookiesList) # load exceptions self.__exceptionsBlock = \ cookieSettings.value("Exceptions/block").toStringList() self.__exceptionsAllow = \ cookieSettings.value("Exceptions/allow").toStringList() self.__exceptionsAllowForSession = \ cookieSettings.value("Exceptions/allowForSession").toStringList() self.__exceptionsBlock.sort() self.__exceptionsAllow.sort() self.__exceptionsAllowForSession.sort() self.__acceptCookies = Preferences.getHelp("AcceptCookies") self.__keepCookies = Preferences.getHelp("KeepCookiesUntil") if self.__keepCookies == self.KeepUntilExit: self.setAllCookies([]) self.__filterTrackingCookies = Preferences.getHelp("FilterTrackingCookies") self.__loaded = True self.emit(SIGNAL("cookiesChanged()")) def save(self): """ Public method to save the cookies. """ if not self.__loaded: return self.__purgeOldCookies() cookieSettings = QSettings(self.__cookiesFile, QSettings.IniFormat) cookiesList = self.allCookies() for index in range(len(cookiesList) -1, -1, -1): if cookiesList[index].isSessionCookie(): del cookiesList[index] cookies = self.saveCookies(cookiesList) cookieSettings.setValue("Cookies", QVariant(cookies)) cookieSettings.setValue("Exceptions/block", QVariant(self.__exceptionsBlock)) cookieSettings.setValue("Exceptions/allow", QVariant(self.__exceptionsAllow)) cookieSettings.setValue("Exceptions/allowForSession", QVariant(self.__exceptionsAllowForSession)) Preferences.setHelp("AcceptCookies", self.__acceptCookies) Preferences.setHelp("KeepCookiesUntil", self.__keepCookies) Preferences.setHelp("FilterTrackingCookies", int(self.__filterTrackingCookies)) def __purgeOldCookies(self): """ Private method to purge old cookies """ cookies = self.allCookies() if len(cookies) == 0: return oldCount = len(cookies) now = QDateTime.currentDateTime() for index in range(len(cookies) - 1, -1, -1): if not cookies[index].isSessionCookie() and \ cookies[index].expirationDate() < now: del cookies[index] if oldCount == len(cookies): return self.setAllCookies(cookies) self.emit(SIGNAL("cookiesChanged()")) def cookiesForUrl(self, url): """ Public method to get the cookies for a URL. @param url URL to get cookies for (QUrl) @return list of cookies (list of QNetworkCookie) """ if not self.__loaded: self.load() globalSettings = QWebSettings.globalSettings() if globalSettings.testAttribute(QWebSettings.PrivateBrowsingEnabled): return [] return QNetworkCookieJar.cookiesForUrl(self, url) def setCookiesFromUrl(self, cookieList, url): """ Public method to set cookies for a URL. @param cookieList list of cookies to set (list of QNetworkCookie) @param url url to set cookies for (QUrl) @return flag indicating cookies were set (boolean) """ if not self.__loaded: self.load() globalSettings = QWebSettings.globalSettings() if globalSettings.testAttribute(QWebSettings.PrivateBrowsingEnabled): return False host = url.host() eBlock = self.__isOnDomainList(self.__exceptionsBlock, host) eAllow = not eBlock and \ self.__isOnDomainList(self.__exceptionsAllow, host) eAllowSession = not eBlock and \ not eAllow and \ self.__isOnDomainList(self.__exceptionsAllowForSession, host) addedCookies = False acceptInitially = self.__acceptCookies != self.AcceptNever if (acceptInitially and not eBlock) or \ (not acceptInitially and (eAllow or eAllowSession)): # url domain == cookie domain soon = QDateTime.currentDateTime() soon = soon.addDays(90) for cookie in cookieList: lst = [] if not (self.__filterTrackingCookies and \ cookie.name().startsWith("__utm")): if eAllowSession: cookie.setExpirationDate(QDateTime()) if self.__keepCookies == self.KeepUntilTimeLimit and \ not cookie.isSessionCookie and \ cookie.expirationDate() > soon: cookie.setExpirationDate(soon) lst.append(cookie) if QNetworkCookieJar.setCookiesFromUrl(self, lst, url): addedCookies = True elif self.__acceptCookies == self.AcceptAlways: # force it in if wanted cookies = self.allCookies() for ocookie in cookies[:]: # does the cookie exist already? if cookie.name() == ocookie.name() and \ cookie.domain() == ocookie.domain() and \ cookie.path() == ocookie.path(): # found a match cookies.remove(ocookie) cookies.append(cookie) self.setAllCookies(cookies) addedCookies = True if addedCookies: self.__saveTimer.changeOccurred() self.emit(SIGNAL("cookiesChanged()")) return addedCookies def acceptPolicy(self): """ Public method to get the accept policy. @return current accept policy """ if not self.__loaded: self.load() return self.__acceptCookies def setAcceptPolicy(self, policy): """ Public method to set the accept policy. @param policy accept policy to be set """ if not self.__loaded: self.load() if policy > self.AcceptOnlyFromSitesNavigatedTo: return if policy == self.__acceptCookies: return self.__acceptCookies = policy self.__saveTimer.changeOccurred() def keepPolicy(self): """ Private method to get the keep policy. """ if not self.__loaded: self.load() return self.__keepCookies def setKeepPolicy(self, policy): """ Public method to set the keep policy. @param policy keep policy to be set """ if not self.__loaded: self.load() if policy > self.KeepUntilTimeLimit: return if policy == self.__keepCookies: return self.__keepCookies = policy self.__saveTimer.changeOccurred() def blockedCookies(self): """ Public method to return the blocked cookies. @return list of blocked cookies (QStringList) """ if not self.__loaded: self.load() return self.__exceptionsBlock def allowedCookies(self): """ Public method to return the allowed cookies. @return list of allowed cookies (QStringList) """ if not self.__loaded: self.load() return self.__exceptionsAllow def allowForSessionCookies(self): """ Public method to return the allowed session cookies. @return list of allowed session cookies (QStringList) """ if not self.__loaded: self.load() return self.__exceptionsAllowForSession def setBlockedCookies(self, list_): """ Public method to set the list of blocked cookies. @param list_ list of blocked cookies (QStringList) """ if not self.__loaded: self.load() self.__exceptionsBlock = QStringList(list_) self.__exceptionsBlock.sort() self.__applyRules() self.__saveTimer.changeOccurred() def setAllowedCookies(self, list_): """ Public method to set the list of allowed cookies. @param list_ list of allowed cookies (QStringList) """ if not self.__loaded: self.load() self.__exceptionsAllow = QStringList(list_) self.__exceptionsAllow.sort() self.__applyRules() self.__saveTimer.changeOccurred() def setAllowForSessionCookies(self, list_): """ Public method to set the list of allowed session cookies. @param list_ list of allowed session cookies (QStringList) """ if not self.__loaded: self.load() self.__exceptionsAllowForSession = QStringList(list_) self.__exceptionsAllowForSession.sort() self.__applyRules() self.__saveTimer.changeOccurred() def filterTrackingCookies(self): """ Public method to get the filter tracking cookies flag. @return filter tracking cookies flag (boolean) """ return self.__filterTrackingCookies def setFilterTrackingCookies(self, filterTrackingCookies): """ Public method to set the filter tracking cookies flag. @param filterTrackingCookies filter tracking cookies flag (boolean) """ self.__filterTrackingCookies = filterTrackingCookies def __isOnDomainList(self, rules, domain): """ Private method to check, if either the rule matches the domain exactly or the domain ends with ".rule". @param rules list of rules (QStringList) @param domain domain name to check (QString) @return flag indicating a match (boolean) """ domain = QString(domain) for rule in rules: if rule.startsWith("."): if domain.endsWith(rule): return True withoutDot = rule.right(rule.size() - 1) if domain == withoutDot: return True else: domainEnding = domain.right(rule.size() + 1) if not domainEnding.isEmpty() and \ domainEnding[0] == "." and \ domain.endsWith(rule): return True if rule == domain: return True return False def __applyRules(self): """ Private method to apply the cookie rules. """ cookiesList = self.allCookies() changed = False for index in range(len(cookiesList) - 1, -1, -1): cookie = cookiesList[index] if self.__isOnDomainList(self.__exceptionsBlock, cookie.domain()): del cookiesList[index] changed = True elif self.__isOnDomainList(self.__exceptionsAllowForSession, cookie.domain()): cookie.setExpirationDate(QDateTime()) changed = True if changed: self.setAllCookies(cookiesList) self.__saveTimer.changeOccurred() self.emit(SIGNAL("cookiesChanged()")) def cookies(self): """ Public method to get the cookies of the cookie jar. @return list of all cookies (list of QNetworkCookie) """ if not self.__loaded: self.load() return self.allCookies() def setCookies(self, cookies): """ Public method to set all cookies. @param cookies list of cookies to be set. """ if not self.__loaded: self.load() self.setAllCookies(cookies) self.__saveTimer.changeOccurred() self.emit(SIGNAL("cookiesChanged()"))
class DownloadManager(QDialog, Ui_DownloadManager): """ Class implementing the download manager. """ RemoveNever = 0 RemoveExit = 1 RemoveSuccessFullDownload = 2 def __init__(self, parent=None): """ Constructor @param parent reference to the parent widget (QWidget) """ super(DownloadManager, self).__init__(parent) self.setupUi(self) self.setWindowFlags(Qt.Window) self.__saveTimer = AutoSaver(self, self.save) self.__model = DownloadModel(self) self.__manager = Helpviewer.HelpWindow.HelpWindow\ .networkAccessManager() self.__iconProvider = None self.__downloads = [] self.__downloadDirectory = "" self.__loaded = False self.setDownloadDirectory(Preferences.getUI("DownloadPath")) self.downloadsView.setShowGrid(False) self.downloadsView.verticalHeader().hide() self.downloadsView.horizontalHeader().hide() self.downloadsView.setAlternatingRowColors(True) self.downloadsView.horizontalHeader().setStretchLastSection(True) self.downloadsView.setModel(self.__model) self.downloadsView.setContextMenuPolicy(Qt.CustomContextMenu) self.downloadsView.customContextMenuRequested.connect( self.__customContextMenuRequested) self.__load() def __customContextMenuRequested(self, pos): """ Private slot to handle the context menu request for the bookmarks tree. @param pos position the context menu was requested (QPoint) """ menu = QMenu() selectedRowsCount = len( self.downloadsView.selectionModel().selectedRows()) if selectedRowsCount == 1: row = self.downloadsView.selectionModel().selectedRows()[0].row() itm = self.__downloads[row] if itm.downloadCanceled(): menu.addAction(UI.PixmapCache.getIcon("restart.png"), self.tr("Retry"), self.__contextMenuRetry) else: if itm.downloadedSuccessfully(): menu.addAction(UI.PixmapCache.getIcon("open.png"), self.tr("Open"), self.__contextMenuOpen) elif itm.downloading(): menu.addAction(UI.PixmapCache.getIcon("stopLoading.png"), self.tr("Cancel"), self.__contextMenuCancel) menu.addSeparator() menu.addAction(self.tr("Open Containing Folder"), self.__contextMenuOpenFolder) menu.addSeparator() menu.addAction(self.tr("Go to Download Page"), self.__contextMenuGotoPage) menu.addAction(self.tr("Copy Download Link"), self.__contextMenuCopyLink) menu.addSeparator() menu.addAction(self.tr("Select All"), self.__contextMenuSelectAll) if selectedRowsCount > 1 or \ (selectedRowsCount == 1 and not self.__downloads[ self.downloadsView.selectionModel().selectedRows()[0].row()] .downloading()): menu.addSeparator() menu.addAction(self.tr("Remove From List"), self.__contextMenuRemoveSelected) menu.exec_(QCursor.pos()) def shutdown(self): """ Public method to stop the download manager. """ self.__saveTimer.changeOccurred() self.__saveTimer.saveIfNeccessary() self.close() def activeDownloads(self): """ Public method to get the number of active downloads. @return number of active downloads (integer) """ count = 0 for download in self.__downloads: if download.downloading(): count += 1 return count def allowQuit(self): """ Public method to check, if it is ok to quit. @return flag indicating allowance to quit (boolean) """ if self.activeDownloads() > 0: res = E5MessageBox.yesNo( self, self.tr(""), self.tr( """There are %n downloads in progress.\n""" """Do you want to quit anyway?""", "", self.activeDownloads()), icon=E5MessageBox.Warning) if not res: self.show() return False return True def download(self, requestOrUrl, requestFileName=False, mainWindow=None): """ Public method to download a file. @param requestOrUrl reference to a request object (QNetworkRequest) or a URL to be downloaded (QUrl) @keyparam requestFileName flag indicating to ask for the download file name (boolean) @keyparam mainWindow reference to the main window (HelpWindow) """ request = QNetworkRequest(requestOrUrl) if request.url().isEmpty(): return self.handleUnsupportedContent(self.__manager.get(request), requestFileName=requestFileName, download=True, mainWindow=mainWindow) def handleUnsupportedContent(self, reply, requestFileName=False, webPage=None, download=False, mainWindow=None): """ Public method to handle unsupported content by downloading the referenced resource. @param reply reference to the reply object (QNetworkReply) @keyparam requestFileName indicating to ask for a filename (boolean) @keyparam webPage reference to the web page (HelpWebPage) @keyparam download flag indicating a download request (boolean) @keyparam mainWindow reference to the main window (HelpWindow) """ if reply is None or reply.url().isEmpty(): return size = reply.header(QNetworkRequest.ContentLengthHeader) if size == 0: return from .DownloadItem import DownloadItem itm = DownloadItem(reply=reply, requestFilename=requestFileName, webPage=webPage, download=download, parent=self, mainWindow=mainWindow) self.__addItem(itm) if itm.canceledFileSelect(): return if not self.isVisible(): self.show() self.activateWindow() self.raise_() def __addItem(self, itm): """ Private method to add a download to the list of downloads. @param itm reference to the download item (DownloadItem) """ itm.statusChanged.connect(self.__updateRow) itm.downloadFinished.connect(self.__finished) row = len(self.__downloads) self.__model.beginInsertRows(QModelIndex(), row, row) self.__downloads.append(itm) self.__model.endInsertRows() self.downloadsView.setIndexWidget(self.__model.index(row, 0), itm) icon = self.style().standardIcon(QStyle.SP_FileIcon) itm.setIcon(icon) self.downloadsView.setRowHeight(row, itm.sizeHint().height() * 1.5) # just in case the download finished before the constructor returned self.__updateRow(itm) self.changeOccurred() self.__updateActiveItemCount() def __updateRow(self, itm=None): """ Private slot to update a download item. @param itm reference to the download item (DownloadItem) """ if itm is None: itm = self.sender() if itm not in self.__downloads: return row = self.__downloads.index(itm) if self.__iconProvider is None: self.__iconProvider = QFileIconProvider() icon = self.__iconProvider.icon(QFileInfo(itm.fileName())) if icon.isNull(): icon = self.style().standardIcon(QStyle.SP_FileIcon) itm.setIcon(icon) oldHeight = self.downloadsView.rowHeight(row) self.downloadsView.setRowHeight( row, max(oldHeight, itm.minimumSizeHint().height() * 1.5)) remove = False globalSettings = QWebSettings.globalSettings() if not itm.downloading() and \ globalSettings.testAttribute(QWebSettings.PrivateBrowsingEnabled): remove = True if itm.downloadedSuccessfully() and \ self.removePolicy() == DownloadManager.RemoveSuccessFullDownload: remove = True if remove: self.__model.removeRow(row) self.cleanupButton.setEnabled( (len(self.__downloads) - self.activeDownloads()) > 0) # record the change self.changeOccurred() def removePolicy(self): """ Public method to get the remove policy. @return remove policy (integer) """ return Preferences.getHelp("DownloadManagerRemovePolicy") def setRemovePolicy(self, policy): """ Public method to set the remove policy. @param policy policy to be set (DownloadManager.RemoveExit, DownloadManager.RemoveNever, DownloadManager.RemoveSuccessFullDownload) """ assert policy in (DownloadManager.RemoveExit, DownloadManager.RemoveNever, DownloadManager.RemoveSuccessFullDownload) if policy == self.removePolicy(): return Preferences.setHelp("DownloadManagerRemovePolicy", self.policy) def save(self): """ Public method to save the download settings. """ if not self.__loaded: return Preferences.setHelp("DownloadManagerSize", self.size()) Preferences.setHelp("DownloadManagerPosition", self.pos()) if self.removePolicy() == DownloadManager.RemoveExit: return downloads = [] for download in self.__downloads: downloads.append(download.getData()) Preferences.setHelp("DownloadManagerDownloads", downloads) def __load(self): """ Private method to load the download settings. """ if self.__loaded: return size = Preferences.getHelp("DownloadManagerSize") if size.isValid(): self.resize(size) pos = Preferences.getHelp("DownloadManagerPosition") self.move(pos) downloads = Preferences.getHelp("DownloadManagerDownloads") for download in downloads: if not download[0].isEmpty() and \ download[1] != "": from .DownloadItem import DownloadItem itm = DownloadItem(parent=self) itm.setData(download) self.__addItem(itm) self.cleanupButton.setEnabled( (len(self.__downloads) - self.activeDownloads()) > 0) self.__loaded = True self.__updateActiveItemCount() def cleanup(self): """ Public slot to cleanup the downloads. """ self.on_cleanupButton_clicked() @pyqtSlot() def on_cleanupButton_clicked(self): """ Private slot cleanup the downloads. """ if len(self.__downloads) == 0: return self.__model.removeRows(0, len(self.__downloads)) if len(self.__downloads) == 0 and \ self.__iconProvider is not None: self.__iconProvider = None self.changeOccurred() self.__updateActiveItemCount() def __updateItemCount(self): """ Private method to update the count label. """ count = len(self.__downloads) self.countLabel.setText(self.tr("%n Download(s)", "", count)) def __updateActiveItemCount(self): """ Private method to update the window title. """ count = self.activeDownloads() if count > 0: self.setWindowTitle(self.tr("Downloading %n file(s)", "", count)) else: self.setWindowTitle(self.tr("Downloads")) def __finished(self): """ Private slot to handle a finished download. """ self.__updateActiveItemCount() if self.isVisible(): QApplication.alert(self) def setDownloadDirectory(self, directory): """ Public method to set the current download directory. @param directory current download directory (string) """ self.__downloadDirectory = directory if self.__downloadDirectory != "": self.__downloadDirectory += "/" def downloadDirectory(self): """ Public method to get the current download directory. @return current download directory (string) """ return self.__downloadDirectory def count(self): """ Public method to get the number of downloads. @return number of downloads (integer) """ return len(self.__downloads) def downloads(self): """ Public method to get a reference to the downloads. @return reference to the downloads (list of DownloadItem) """ return self.__downloads def changeOccurred(self): """ Public method to signal a change. """ self.__saveTimer.changeOccurred() self.__updateItemCount() ########################################################################### ## Context menu related methods below ########################################################################### def __currentItem(self): """ Private method to get a reference to the current item. @return reference to the current item (DownloadItem) """ index = self.downloadsView.currentIndex() if index and index.isValid(): row = index.row() return self.__downloads[row] return None def __contextMenuRetry(self): """ Private method to retry of the download. """ itm = self.__currentItem() if itm is not None: itm.retry() def __contextMenuOpen(self): """ Private method to open the downloaded file. """ itm = self.__currentItem() if itm is not None: itm.openFile() def __contextMenuOpenFolder(self): """ Private method to open the folder containing the downloaded file. """ itm = self.__currentItem() if itm is not None: itm.openFolder() def __contextMenuCancel(self): """ Private method to cancel the current download. """ itm = self.__currentItem() if itm is not None: itm.cancelDownload() def __contextMenuGotoPage(self): """ Private method to open the download page. """ itm = self.__currentItem() if itm is not None: url = itm.getPageUrl() Helpviewer.HelpWindow.HelpWindow.mainWindow().openUrl(url, "") def __contextMenuCopyLink(self): """ Private method to copy the download link to the clipboard. """ itm = self.__currentItem() if itm is not None: url = itm.getPageUrl().toString() QApplication.clipboard().setText(url) def __contextMenuSelectAll(self): """ Private method to select all downloads. """ self.downloadsView.selectAll() def __contextMenuRemoveSelected(self): """ Private method to remove the selected downloads from the list. """ self.downloadsView.removeSelected()