diff -r 000000000000 -r de9c2efb9d02 PluginManager/PluginManager.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/PluginManager/PluginManager.py Mon Dec 28 16:03:33 2009 +0000 @@ -0,0 +1,957 @@ +# -*- coding: utf-8 -*- + +# Copyright (c) 2007 - 2009 Detlev Offenbach <detlev@die-offenbachs.de> +# + +""" +Module implementing the Plugin Manager. +""" + +import os +import sys +import imp + +from PyQt4.QtCore import * +from PyQt4.QtGui import QPixmap, QMessageBox + +from PluginExceptions import * + +import UI.PixmapCache + +import Utilities +import Preferences + +from eric4config import getConfig + +class PluginManager(QObject): + """ + Class implementing the Plugin Manager. + + @signal shutdown() emitted at shutdown of the IDE + @signal pluginAboutToBeActivated(modulName, pluginObject) emitted just before a + plugin is activated + @signal pluginActivated(modulName, pluginObject) emitted just after a plugin + was activated + @signal allPlugginsActivated() emitted at startup after all plugins have + been activated + @signal pluginAboutToBeDeactivated(modulName, pluginObject) emitted just before a + plugin is deactivated + @signal pluginDeactivated(modulName, pluginObject) emitted just after a plugin + was deactivated + """ + def __init__(self, parent = None, doLoadPlugins = True, develPlugin = None): + """ + Constructor + + The Plugin Manager deals with three different plugin directories. + The first is the one, that is part of eric4 (eric4/Plugins). The + second one is the global plugin directory called 'eric4plugins', + which is located inside the site-packages directory. The last one + is the user plugin directory located inside the .eric4 directory + of the users home directory. + + @param parent reference to the parent object (QObject) + @keyparam doLoadPlugins flag indicating, that plugins should + be loaded (boolean) + @keyparam develPlugin filename of a plugin to be loaded for + development (string) + """ + QObject.__init__(self, parent) + + self.__ui = parent + self.__develPluginFile = develPlugin + self.__develPluginName = None + + self.__inactivePluginsKey = "PluginManager/InactivePlugins" + + self.pluginDirs = { + "eric4" : os.path.join(getConfig('ericDir'), "Plugins"), + "global" : os.path.join(Utilities.getPythonModulesDirectory(), + "eric4plugins"), + "user" : os.path.join(Utilities.getConfigDir(), "eric4plugins"), + } + self.__priorityOrder = ["eric4", "global", "user"] + + self.__defaultDownloadDir = os.path.join(Utilities.getConfigDir(), "Downloads") + + self.__activePlugins = {} + self.__inactivePlugins = {} + self.__onDemandActivePlugins = {} + self.__onDemandInactivePlugins = {} + self.__activeModules = {} + self.__inactiveModules = {} + self.__onDemandActiveModules = {} + self.__onDemandInactiveModules = {} + self.__failedModules = {} + + self.__foundCoreModules = [] + self.__foundGlobalModules = [] + self.__foundUserModules = [] + + self.__modulesCount = 0 + + pdirsExist, msg = self.__pluginDirectoriesExist() + if not pdirsExist: + raise PluginPathError(msg) + + if doLoadPlugins: + if not self.__pluginModulesExist(): + raise PluginModulesError + + self.__insertPluginsPaths() + + self.__loadPlugins() + + self.__checkPluginsDownloadDirectory() + + def finalizeSetup(self): + """ + Public method to finalize the setup of the plugin manager. + """ + for module in self.__onDemandInactiveModules.values() + \ + self.__onDemandActiveModules.values(): + if hasattr(module, "moduleSetup"): + module.moduleSetup() + + def getPluginDir(self, key): + """ + Public method to get the path of a plugin directory. + + @return path of the requested plugin directory (string) + """ + if key not in ["global", "user"]: + return None + else: + try: + return self.pluginDirs[key] + except KeyError: + return None + + def __pluginDirectoriesExist(self): + """ + Private method to check, if the plugin folders exist. + + If the plugin folders don't exist, they are created (if possible). + + @return tuple of a flag indicating existence of any of the plugin + directories (boolean) and a message (string) + """ + if self.__develPluginFile: + path = Utilities.splitPath(self.__develPluginFile)[0] + fname = os.path.join(path, "__init__.py") + if not os.path.exists(fname): + try: + f = open(fname, "wb") + f.close() + except IOError: + return (False, + self.trUtf8("Could not create a package for {0}.")\ + .format(self.__develPluginFile)) + + if Preferences.getPluginManager("ActivateExternal"): + fname = os.path.join(self.pluginDirs["user"], "__init__.py") + if not os.path.exists(fname): + if not os.path.exists(self.pluginDirs["user"]): + os.mkdir(self.pluginDirs["user"], 0755) + try: + f = open(fname, "wb") + f.close() + except IOError: + del self.pluginDirs["user"] + + if not os.path.exists(self.pluginDirs["global"]) and \ + os.access(Utilities.getPythonModulesDirectory(), os.W_OK): + # create the global plugins directory + os.mkdir(self.pluginDirs["global"], 0755) + fname = os.path.join(self.pluginDirs["global"], "__init__.py") + f = open() + f.write('# -*- coding: utf-8 -*-' + os.linesep) + f.write(os.linesep) + f.write('"""' + os.linesep) + f.write('Package containing the global plugins.' + os.linesep) + f.write('"""' + os.linesep) + f.close() + if not os.path.exists(self.pluginDirs["global"]): + del self.pluginDirs["global"] + else: + del self.pluginDirs["user"] + del self.pluginDirs["global"] + + if not os.path.exists(self.pluginDirs["eric4"]): + return (False, + self.trUtf8("The internal plugin directory <b>{0}</b> does not exits.")\ + .format(self.pluginDirs["eric4"])) + + return (True, "") + + def __pluginModulesExist(self): + """ + Private method to check, if there are plugins available. + + @return flag indicating the availability of plugins (boolean) + """ + if self.__develPluginFile and not os.path.exists(self.__develPluginFile): + return False + + self.__foundCoreModules = self.getPluginModules(self.pluginDirs["eric4"]) + if "global" in self.pluginDirs: + self.__foundGlobalModules = \ + self.getPluginModules(self.pluginDirs["global"]) + if "user" in self.pluginDirs: + self.__foundUserModules = \ + self.getPluginModules(self.pluginDirs["user"]) + + return len(self.__foundCoreModules + self.__foundGlobalModules + \ + self.__foundUserModules) > 0 + + def getPluginModules(self, pluginPath): + """ + Public method to get a list of plugin modules. + + @param pluginPath name of the path to search (string) + @return list of plugin module names (list of string) + """ + pluginFiles = [f[:-3] for f in os.listdir(pluginPath) \ + if self.isValidPluginName(f)] + return pluginFiles[:] + + def isValidPluginName(self, pluginName): + """ + Public methode to check, if a file name is a valid plugin name. + + Plugin modules must start with "Plugin" and have the extension ".py". + + @param pluginName name of the file to be checked (string) + @return flag indicating a valid plugin name (boolean) + """ + return pluginName.startswith("Plugin") and pluginName.endswith(".py") + + def __insertPluginsPaths(self): + """ + Private method to insert the valid plugin paths intos the search path. + """ + for key in self.__priorityOrder: + if key in self.pluginDirs: + if not self.pluginDirs[key] in sys.path: + sys.path.insert(2, self.pluginDirs[key]) + UI.PixmapCache.addSearchPath(self.pluginDirs[key]) + + if self.__develPluginFile: + path = Utilities.splitPath(self.__develPluginFile)[0] + if not path in sys.path: + sys.path.insert(2, path) + UI.PixmapCache.addSearchPath(path) + + def __loadPlugins(self): + """ + Private method to load the plugins found. + """ + develPluginName = "" + if self.__develPluginFile: + develPluginPath, develPluginName = \ + Utilities.splitPath(self.__develPluginFile) + if self.isValidPluginName(develPluginName): + develPluginName = develPluginName[:-3] + + for pluginName in self.__foundCoreModules: + # global and user plugins have priority + if pluginName not in self.__foundGlobalModules and \ + pluginName not in self.__foundUserModules and \ + pluginName != develPluginName: + self.loadPlugin(pluginName, self.pluginDirs["eric4"]) + + for pluginName in self.__foundGlobalModules: + # user plugins have priority + if pluginName not in self.__foundUserModules and \ + pluginName != develPluginName: + self.loadPlugin(pluginName, self.pluginDirs["global"]) + + for pluginName in self.__foundUserModules: + if pluginName != develPluginName: + self.loadPlugin(pluginName, self.pluginDirs["user"]) + + if develPluginName: + self.loadPlugin(develPluginName, develPluginPath) + self.__develPluginName = develPluginName + + def loadPlugin(self, name, directory, reload_ = False): + """ + Public method to load a plugin module. + + Initially all modules are inactive. Modules that are requested on + demand are sorted out and are added to the on demand list. Some + basic validity checks are performed as well. Modules failing these + checks are added to the failed modules list. + + @param name name of the module to be loaded (string) + @param directory name of the plugin directory (string) + @param reload_ flag indicating to reload the module (boolean) + """ + try: + fname = "%s.py" % os.path.join(directory, name) + module = imp.load_source(name, fname) + if not hasattr(module, "autoactivate"): + module.error = \ + self.trUtf8("Module is missing the 'autoactivate' attribute.") + self.__failedModules[name] = module + raise PluginLoadError(name) + if getattr(module, "autoactivate"): + self.__inactiveModules[name] = module + else: + if not hasattr(module, "pluginType") or \ + not hasattr(module, "pluginTypename"): + module.error = \ + self.trUtf8("Module is missing the 'pluginType' " + "and/or 'pluginTypename' attributes.") + self.__failedModules[name] = module + raise PluginLoadError(name) + else: + self.__onDemandInactiveModules[name] = module + module.eric4PluginModuleName = name + module.eric4PluginModuleFilename = fname + self.__modulesCount += 1 + if reload_: + reload(module) + except PluginLoadError: + print "Error loading plugin module:", name + except StandardError, err: + module = imp.new_module(name) + module.error = \ + self.trUtf8("Module failed to load. Error: {0}").format(unicode(err)) + self.__failedModules[name] = module + print "Error loading plugin module:", name + print unicode(err) + + def unloadPlugin(self, name, directory): + """ + Public method to unload a plugin module. + + @param name name of the module to be unloaded (string) + @param directory name of the plugin directory (string) + @return flag indicating success (boolean) + """ + fname = "%s.py" % os.path.join(directory, name) + if name in self.__onDemandActiveModules and \ + self.__onDemandActiveModules[name].eric4PluginModuleFilename == fname: + # cannot unload an ondemand plugin, that is in use + return False + + if name in self.__activeModules and \ + self.__activeModules[name].eric4PluginModuleFilename == fname: + self.deactivatePlugin(name) + + if name in self.__inactiveModules and \ + self.__inactiveModules[name].eric4PluginModuleFilename == fname: + try: + del self.__inactivePlugins[name] + except KeyError: + pass + del self.__inactiveModules[name] + elif name in self.__onDemandInactiveModules and \ + self.__onDemandInactiveModules[name].eric4PluginModuleFilename == fname: + try: + del self.__onDemandInactivePlugins[name] + except KeyError: + pass + del self.__onDemandInactiveModules[name] + elif name in self.__failedModules: + del self.__failedModules[name] + + self.__modulesCount -= 1 + return True + + def removePluginFromSysModules(self, pluginName, package, internalPackages): + """ + Public method to remove a plugin and all related modules from sys.modules. + + @param pluginName name of the plugin module (string) + @param package name of the plugin package (string) + @param internalPackages list of intenal packages (list of string) + @return flag indicating the plugin module was found in sys.modules (boolean) + """ + packages = [package] + internalPackages + found = False + if not package: + package = "__None__" + for moduleName in sys.modules.keys()[:]: + if moduleName == pluginName or moduleName.split(".")[0] in packages: + found = True + del sys.modules[moduleName] + return found + + def initOnDemandPlugins(self): + """ + Public method to create plugin objects for all on demand plugins. + + Note: The plugins are not activated. + """ + names = sorted(self.__onDemandInactiveModules.keys()) + for name in names: + self.initOnDemandPlugin(name) + + def initOnDemandPlugin(self, name): + """ + Public method to create a plugin object for the named on demand plugin. + + Note: The plugin is not activated. + """ + try: + try: + module = self.__onDemandInactiveModules[name] + except KeyError: + return None + + if not self.__canActivatePlugin(module): + raise PluginActivationError(module.eric4PluginModuleName) + version = getattr(module, "version") + className = getattr(module, "className") + pluginClass = getattr(module, className) + pluginObject = None + if name not in self.__onDemandInactivePlugins: + pluginObject = pluginClass(self.__ui) + pluginObject.eric4PluginModule = module + pluginObject.eric4PluginName = className + pluginObject.eric4PluginVersion = version + self.__onDemandInactivePlugins[name] = pluginObject + except PluginActivationError: + return None + + def activatePlugins(self): + """ + Public method to activate all plugins having the "autoactivate" attribute + set to True. + """ + ial = Preferences.Prefs.settings.value(self.__inactivePluginsKey) + if ial.isValid(): + savedInactiveList = ial.toStringList() + else: + savedInactiveList = None + if self.__develPluginName is not None and \ + savedInactiveList is not None and \ + self.__develPluginName in savedInactiveList: + savedInactiveList.remove(self.__develPluginName) + names = sorted(self.__inactiveModules.keys()) + for name in names: + if savedInactiveList is None or name not in savedInactiveList: + self.activatePlugin(name) + self.emit(SIGNAL("allPlugginsActivated()")) + + def activatePlugin(self, name, onDemand = False): + """ + Public method to activate a plugin. + + @param name name of the module to be activated + @keyparam onDemand flag indicating activation of an + on demand plugin (boolean) + @return reference to the initialized plugin object + """ + try: + try: + if onDemand: + module = self.__onDemandInactiveModules[name] + else: + module = self.__inactiveModules[name] + except KeyError: + return None + + if not self.__canActivatePlugin(module): + raise PluginActivationError(module.eric4PluginModuleName) + version = getattr(module, "version") + className = getattr(module, "className") + pluginClass = getattr(module, className) + pluginObject = None + if onDemand and name in self.__onDemandInactivePlugins: + pluginObject = self.__onDemandInactivePlugins[name] + elif not onDemand and name in self.__inactivePlugins: + pluginObject = self.__inactivePlugins[name] + else: + pluginObject = pluginClass(self.__ui) + self.emit(SIGNAL("pluginAboutToBeActivated"), name, pluginObject) + try: + obj, ok = pluginObject.activate() + except TypeError: + module.error = self.trUtf8("Incompatible plugin activation method.") + obj = None + ok = True + except StandardError, err: + module.error = unicode(err) + obj = None + ok = False + if not ok: + return None + + self.emit(SIGNAL("pluginActivated"), name, pluginObject) + pluginObject.eric4PluginModule = module + pluginObject.eric4PluginName = className + pluginObject.eric4PluginVersion = version + + if onDemand: + self.__onDemandInactiveModules.pop(name) + try: + self.__onDemandInactivePlugins.pop(name) + except KeyError: + pass + self.__onDemandActivePlugins[name] = pluginObject + self.__onDemandActiveModules[name] = module + else: + self.__inactiveModules.pop(name) + try: + self.__inactivePlugins.pop(name) + except KeyError: + pass + self.__activePlugins[name] = pluginObject + self.__activeModules[name] = module + return obj + except PluginActivationError: + return None + + def __canActivatePlugin(self, module): + """ + Private method to check, if a plugin can be activated. + + @param module reference to the module to be activated + @return flag indicating, if the module satisfies all requirements + for being activated (boolean) + """ + try: + if not hasattr(module, "version"): + raise PluginModuleFormatError(module.eric4PluginModuleName, "version") + if not hasattr(module, "className"): + raise PluginModuleFormatError(module.eric4PluginModuleName, "className") + className = getattr(module, "className") + if not hasattr(module, className): + raise PluginModuleFormatError(module.eric4PluginModuleName, className) + pluginClass = getattr(module, className) + if not hasattr(pluginClass, "__init__"): + raise PluginClassFormatError(module.eric4PluginModuleName, + className, "__init__") + if not hasattr(pluginClass, "activate"): + raise PluginClassFormatError(module.eric4PluginModuleName, + className, "activate") + if not hasattr(pluginClass, "deactivate"): + raise PluginClassFormatError(module.eric4PluginModuleName, + className, "deactivate") + return True + except PluginModuleFormatError, e: + print repr(e) + return False + except PluginClassFormatError, e: + print repr(e) + return False + + def deactivatePlugin(self, name, onDemand = False): + """ + Public method to deactivate a plugin. + + @param name name of the module to be deactivated + @keyparam onDemand flag indicating deactivation of an + on demand plugin (boolean) + """ + try: + if onDemand: + module = self.__onDemandActiveModules[name] + else: + module = self.__activeModules[name] + except KeyError: + return + + if self.__canDeactivatePlugin(module): + pluginObject = None + if onDemand and name in self.__onDemandActivePlugins: + pluginObject = self.__onDemandActivePlugins[name] + elif not onDemand and name in self.__activePlugins: + pluginObject = self.__activePlugins[name] + if pluginObject: + self.emit(SIGNAL("pluginAboutToBeDeactivated"), name, pluginObject) + pluginObject.deactivate() + self.emit(SIGNAL("pluginDeactivated"), name, pluginObject) + + if onDemand: + self.__onDemandActiveModules.pop(name) + self.__onDemandActivePlugins.pop(name) + self.__onDemandInactivePlugins[name] = pluginObject + self.__onDemandInactiveModules[name] = module + else: + self.__activeModules.pop(name) + try: + self.__activePlugins.pop(name) + except KeyError: + pass + self.__inactivePlugins[name] = pluginObject + self.__inactiveModules[name] = module + + def __canDeactivatePlugin(self, module): + """ + Private method to check, if a plugin can be deactivated. + + @param module reference to the module to be deactivated + @return flag indicating, if the module satisfies all requirements + for being deactivated (boolean) + """ + return getattr(module, "deactivateable", True) + + def getPluginObject(self, type_, typename, maybeActive = False): + """ + Public method to activate an ondemand plugin given by type and typename. + + @param type_ type of the plugin to be activated (string) + @param typename name of the plugin within the type category (string) + @keyparam maybeActive flag indicating, that the plugin may be active + already (boolean) + @return reference to the initialized plugin object + """ + for name, module in self.__onDemandInactiveModules.items(): + if getattr(module, "pluginType") == type_ and \ + getattr(module, "pluginTypename") == typename: + return self.activatePlugin(name, onDemand = True) + + if maybeActive: + for name, module in self.__onDemandActiveModules.items(): + if getattr(module, "pluginType") == type_ and \ + getattr(module, "pluginTypename") == typename: + self.deactivatePlugin(name, onDemand = True) + return self.activatePlugin(name, onDemand = True) + + return None + + def getPluginInfos(self): + """ + Public method to get infos about all loaded plugins. + + @return list of tuples giving module name (string), plugin name (string), + version (string), autoactivate (boolean), active (boolean), + short description (string), error flag (boolean) + """ + infos = [] + + for name in self.__activeModules.keys(): + pname, shortDesc, error, version = \ + self.__getShortInfo(self.__activeModules[name]) + infos.append((name, pname, version, True, True, shortDesc, error)) + for name in self.__inactiveModules.keys(): + pname, shortDesc, error, version = \ + self.__getShortInfo(self.__inactiveModules[name]) + infos.append((name, pname, version, True, False, shortDesc, error)) + for name in self.__onDemandActiveModules.keys(): + pname, shortDesc, error, version = \ + self.__getShortInfo(self.__onDemandActiveModules[name]) + infos.append((name, pname, version, False, True, shortDesc, error)) + for name in self.__onDemandInactiveModules.keys(): + pname, shortDesc, error, version = \ + self.__getShortInfo(self.__onDemandInactiveModules[name]) + infos.append((name, pname, version, False, False, shortDesc, error)) + for name in self.__failedModules.keys(): + pname, shortDesc, error, version = \ + self.__getShortInfo(self.__failedModules[name]) + infos.append((name, pname, version, False, False, shortDesc, error)) + return infos + + def __getShortInfo(self, module): + """ + Private method to extract the short info from a module. + + @param module module to extract short info from + @return short info as a tuple giving plugin name (string), + short description (string), error flag (boolean) and + version (string) + """ + name = getattr(module, "name", "") + shortDesc = getattr(module, "shortDescription", "") + version = getattr(module, "version", "") + error = getattr(module, "error", "") != "" + return name, shortDesc, error, version + + def getPluginDetails(self, name): + """ + Public method to get detailed information about a plugin. + + @param name name of the module to get detailed infos about (string) + @return details of the plugin as a dictionary + """ + details = {} + + autoactivate = True + active = True + + if name in self.__activeModules: + module = self.__activeModules[name] + elif name in self.__inactiveModules: + module = self.__inactiveModules[name] + active = False + elif name in self.__onDemandActiveModules: + module = self.__onDemandActiveModules[name] + autoactivate = False + elif name in self.__onDemandInactiveModules: + module = self.__onDemandInactiveModules[name] + autoactivate = False + active = False + elif name in self.__failedModules: + module = self.__failedModules[name] + autoactivate = False + active = False + else: + # should not happen + return None + + details["moduleName"] = name + details["moduleFileName"] = getattr(module, "eric4PluginModuleFilename", "") + details["pluginName"] = getattr(module, "name", "") + details["version"] = getattr(module, "version", "") + details["author"] = getattr(module, "author", "") + details["description"] = getattr(module, "longDescription", "") + details["autoactivate"] = autoactivate + details["active"] = active + details["error"] = getattr(module, "error", "") + + return details + + def shutdown(self): + """ + Public method called to perform actions upon shutdown of the IDE. + """ + names = [] + for name in self.__inactiveModules.keys(): + names.append(name) + Preferences.Prefs.settings.setValue(self.__inactivePluginsKey, QVariant(names)) + + self.emit(SIGNAL("shutdown()")) + + def getPluginDisplayStrings(self, type_): + """ + Public method to get the display strings of all plugins of a specific type. + + @param type_ type of the plugins (string) + @return dictionary with name as key and display string as value + (dictionary of string) + """ + pluginDict = {} + + for name, module in \ + self.__onDemandActiveModules.items() + self.__onDemandInactiveModules.items(): + if getattr(module, "pluginType") == type_ and \ + getattr(module, "error", "") == "": + plugin_name = getattr(module, "pluginTypename") + if hasattr(module, "displayString"): + try: + disp = module.displayString() + except TypeError: + disp = getattr(module, "displayString") + if disp != "": + pluginDict[plugin_name] = disp + else: + pluginDict[plugin_name] = plugin_name + + return pluginDict + + def getPluginPreviewPixmap(self, type_, name): + """ + Public method to get a preview pixmap of a plugin of a specific type. + + @param type_ type of the plugin (string) + @param name name of the plugin type (string) + @return preview pixmap (QPixmap) + """ + for modname, module in \ + self.__onDemandActiveModules.items() + self.__onDemandInactiveModules.items(): + if getattr(module, "pluginType") == type_ and \ + getattr(module, "pluginTypename") == name: + if hasattr(module, "previewPix"): + return module.previewPix() + else: + return QPixmap() + + return QPixmap() + + def getPluginApiFiles(self, language): + """ + Public method to get the list of API files installed by a plugin. + + @param language language of the requested API files (string) + @return list of API filenames (list of string) + """ + apis = [] + + for module in self.__activeModules.values() + \ + self.__onDemandActiveModules.values(): + if hasattr(module, "apiFiles"): + apis.extend(module.apiFiles(language)) + + return apis + + def getPluginExeDisplayData(self): + """ + Public method to get data to display information about a plugins + external tool. + + @return list of dictionaries containing the data. Each dictionary must + either contain data for the determination or the data to be displayed.<br /> + A dictionary of the first form must have the following entries: + <ul> + <li>programEntry - indicator for this dictionary form (boolean), + always True</li> + <li>header - string to be diplayed as a header (string)</li> + <li>exe - the executable (string)</li> + <li>versionCommand - commandline parameter for the exe (string)</li> + <li>versionStartsWith - indicator for the output line containing + the version (string)</li> + <li>versionPosition - number of element containing the + version (integer)</li> + <li>version - version to be used as default (string)</li> + <li>versionCleanup - tuple of two integers giving string positions + start and stop for the version string (tuple of integers)</li> + </ul> + A dictionary of the second form must have the following entries: + <ul> + <li>programEntry - indicator for this dictionary form (boolean), + always False</li> + <li>header - string to be diplayed as a header (string)</li> + <li>text - entry text to be shown (string)</li> + <li>version - version text to be shown (string)</li> + </ul> + """ + infos = [] + + for module in self.__activeModules.values() + \ + self.__inactiveModules.values(): + if hasattr(module, "exeDisplayData"): + infos.append(module.exeDisplayData()) + for module in self.__onDemandActiveModules.values() + \ + self.__onDemandInactiveModules.values(): + if hasattr(module, "exeDisplayData"): + infos.append(module.exeDisplayData()) + + return infos + + def getPluginConfigData(self): + """ + Public method to get the config data of all active, non on-demand plugins + used by the configuration dialog. + + Plugins supporting this functionality must provide the plugin module + function 'getConfigData' returning a dictionary with unique keys + of lists with the following list contents: + <dl> + <dt>display string</dt> + <dd>string shown in the selection area of the configuration page. + This should be a localized string</dd> + <dt>pixmap name</dt> + <dd>filename of the pixmap to be shown next to the display string</dd> + <dt>page creation function</dt> + <dd>plugin module function to be called to create the configuration + page. The page must be subclasses from + Preferences.ConfigurationPages.ConfigurationPageBase and must + implement a method called 'save' to save the settings. A parent + entry will be created in the selection list, if this value is None.</dd> + <dt>parent key</dt> + <dd>dictionary key of the parent entry or None, if this defines a + toplevel entry.</dd> + <dt>reference to configuration page</dt> + <dd>This will be used by the configuration dialog and must always be None</dd> + </dl> + """ + configData = {} + for module in self.__activeModules.values() + \ + self.__onDemandActiveModules.values() + \ + self.__onDemandInactiveModules.values(): + if hasattr(module, 'getConfigData'): + configData.update(module.getConfigData()) + return configData + + def isPluginLoaded(self, pluginName): + """ + Public method to check, if a certain plugin is loaded. + + @param pluginName name of the plugin to check for (string or QString) + @return flag indicating, if the plugin is loaded (boolean) + """ + return pluginName in self.__activeModules or \ + pluginName in self.__inactiveModules or \ + pluginName in self.__onDemandActiveModules or \ + pluginName in self.__onDemandInactiveModules + + def isPluginActive(self, pluginName): + """ + Public method to check, if a certain plugin is active. + + @param pluginName name of the plugin to check for (string or QString) + @return flag indicating, if the plugin is active (boolean) + """ + return pluginName in self.__activeModules or \ + pluginName in self.__onDemandActiveModules + + ############################################################################ + ## Specialized plugin module handling methods below + ############################################################################ + + ############################################################################ + ## VCS related methods below + ############################################################################ + + def getVcsSystemIndicators(self): + """ + Public method to get the Vcs System indicators. + + Plugins supporting this functionality must support the module function + getVcsSystemIndicator returning a dictionary with indicator as key and + a tuple with the vcs name (string) and vcs display string (string). + + @return dictionary with indicator as key and a list of tuples as values. + Each tuple contains the vcs name (string) and vcs display string (string). + """ + vcsDict = {} + + for name, module in \ + self.__onDemandActiveModules.items() + self.__onDemandInactiveModules.items(): + if getattr(module, "pluginType") == "version_control": + if hasattr(module, "getVcsSystemIndicator"): + res = module.getVcsSystemIndicator() + for indicator, vcsData in res.items(): + if indicator in vcsDict: + vcsDict[indicator].append(vcsData) + else: + vcsDict[indicator] = [vcsData] + + return vcsDict + + def deactivateVcsPlugins(self): + """ + Public method to deactivated all activated VCS plugins. + """ + for name, module in self.__onDemandActiveModules.items(): + if getattr(module, "pluginType") == "version_control": + self.deactivatePlugin(name, True) + + def __checkPluginsDownloadDirectory(self): + """ + Private slot to check for the existence of the plugins download directory. + """ + downloadDir = Preferences.getPluginManager("DownloadPath") + if not downloadDir: + downloadDir = self.__defaultDownloadDir + + if not os.path.exists(downloadDir): + try: + os.mkdir(downloadDir, 0755) + except (OSError, IOError), err: + # try again with (possibly) new default + downloadDir = self.__defaultDownloadDir + if not os.path.exists(downloadDir): + try: + os.mkdir(downloadDir, 0755) + except (OSError, IOError), err: + QMessageBox.critical(self.__ui, + self.trUtf8("Plugin Manager Error"), + self.trUtf8("""<p>The plugin download directory <b>{0}</b> """ + """could not be created. Please configure it """ + """via the configuration dialog.</p>""" + """<p>Reason: {1}</p>""")\ + .format(downloadDir, unicode(err))) + downloadDir = "" + + Preferences.setPluginManager("DownloadPath", downloadDir) + + def preferencesChanged(self): + """ + Public slot to react to changes in configuration. + """ + self.__checkPluginsDownloadDirectory()