Sun, 30 Jun 2013 19:55:48 +0200
Fixed a serious typo.
# -*- coding: utf-8 -*- # Copyright (c) 2009 - 2013 Detlev Offenbach <detlev@die-offenbachs.de> # """ Module implementing a manager for open search engines. """ import os from PyQt4.QtCore import pyqtSignal, QObject, QUrl, QFile, QDir, QIODevice, QByteArray, \ QBuffer from PyQt4.QtNetwork import QNetworkRequest, QNetworkReply from E5Gui.E5Application import e5App from E5Gui import E5MessageBox from Utilities.AutoSaver import AutoSaver import Utilities import Preferences 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().__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 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 .OpenSearchDefaultEngines import OpenSearchDefaultEngines from .OpenSearchReader import OpenSearchReader reader = OpenSearchReader() for engine in OpenSearchDefaultEngines: engineDescription = QByteArray(OpenSearchDefaultEngines[engine]) buffer_ = QBuffer(engineDescription) if not buffer_.open(QIODevice.ReadOnly): continue engine = reader.read(buffer_) 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.trUtf8("""<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()