diff --git a/src/PluginUtils/_PluginWindows.py b/src/PluginUtils/_PluginWindows.py index f101195dd..cb172ae64 100644 --- a/src/PluginUtils/_PluginWindows.py +++ b/src/PluginUtils/_PluginWindows.py @@ -45,8 +45,10 @@ import gobject #------------------------------------------------------------------------- import ManagedWindow import Errors -from gen.plug import PluginManager +from gen.plug import PluginManager, PluginRegister, PTYPE_STR import _Tool as Tool +from QuestionDialog import InfoDialog +import config #------------------------------------------------------------------------- # @@ -55,6 +57,9 @@ import _Tool as Tool #------------------------------------------------------------------------- class PluginStatus(ManagedWindow.ManagedWindow): """Displays a dialog showing the status of loaded plugins""" + HIDDEN = '%s' % _('Hidden') + AVAILABLE = '%s'\ + % _('Visible') def __init__(self, uistate, track=[]): self.__uistate = uistate @@ -63,6 +68,7 @@ class PluginStatus(ManagedWindow.ManagedWindow): self.__class__) self.__pmgr = PluginManager.get_instance() + self.__preg = PluginRegister.get_instance() self.set_window(gtk.Dialog("", uistate.window, gtk.DIALOG_DESTROY_WITH_PARENT, (gtk.STOCK_CLOSE, gtk.RESPONSE_CLOSE)), @@ -70,6 +76,52 @@ class PluginStatus(ManagedWindow.ManagedWindow): self.window.set_size_request(600, 400) self.window.connect('response', self.close) + notebook = gtk.Notebook() + + #first page with all registered plugins + vbox_reg = gtk.VBox() + scrolled_window_reg = gtk.ScrolledWindow() + self.list_reg = gtk.TreeView() + # model: plugintype, hidden, pluginname, plugindescr, pluginid + self.model_reg = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_STRING, + gobject.TYPE_STRING, gobject.TYPE_STRING, gobject.TYPE_STRING) + self.selection_reg = self.list_reg.get_selection() + self.list_reg.set_model(self.model_reg) + self.list_reg.set_rules_hint(True) + self.list_reg.connect('button-press-event', self.button_press_reg) + col0_reg = gtk.TreeViewColumn(_('Type'), gtk.CellRendererText(), text=0) + col0_reg.set_sort_column_id(0) + self.list_reg.append_column(col0_reg) + self.list_reg.append_column( + gtk.TreeViewColumn(_('Hidden'), gtk.CellRendererText(), markup=1)) + col2_reg = gtk.TreeViewColumn(_('Name'), gtk.CellRendererText(), text=2) + col2_reg.set_sort_column_id(2) + self.list_reg.append_column(col2_reg) + self.list_reg.append_column( + gtk.TreeViewColumn(_('Description'), gtk.CellRendererText(), text=3)) + self.list_reg.set_search_column(2) + + scrolled_window_reg.add(self.list_reg) + vbox_reg.pack_start(scrolled_window_reg) + hbutbox = gtk.HButtonBox() + hbutbox.set_layout(gtk.BUTTONBOX_SPREAD) + self.__info_btn = gtk.Button(_("Info")) + hbutbox.add(self.__info_btn) + self.__info_btn.connect('clicked', self.__info) + self.__hide_btn = gtk.Button(_("Hide/Unhide")) + hbutbox.add(self.__hide_btn) + self.__hide_btn.connect('clicked', self.__hide) + if __debug__: + self.__load_btn = gtk.Button(_("Load")) + hbutbox.add(self.__load_btn) + self.__load_btn.connect('clicked', self.__load) + vbox_reg.pack_start(hbutbox, expand=False, padding=5) + + notebook.append_page(vbox_reg, + tab_label=gtk.Label(_('Registered plugins'))) + + + #second page with loaded plugins scrolled_window = gtk.ScrolledWindow() self.list = gtk.TreeView() self.model = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_STRING, @@ -91,7 +143,9 @@ class PluginStatus(ManagedWindow.ManagedWindow): self.list.set_search_column(1) scrolled_window.add(self.list) - self.window.vbox.add(scrolled_window) + notebook.append_page(scrolled_window, + tab_label=gtk.Label(_('Loaded plugins'))) + self.window.vbox.add(notebook) if __debug__: # Only show the "Reload" button when in debug mode @@ -100,11 +154,19 @@ class PluginStatus(ManagedWindow.ManagedWindow): self.window.action_area.add(self.__reload_btn) self.__reload_btn.connect('clicked', self.__reload) + #obtain hidden plugins from the pluginmanager + self.hidden = self.__pmgr.get_hidden_plugin_ids() + self.window.show_all() - self.__populate_list() + self.__populate_lists() - def __populate_list(self): - """ Build the list of plugins """ + def __populate_lists(self): + """ Build the lists of plugins """ + self.__populate_load_list() + self.__populate_reg_list() + + def __populate_load_list(self): + """ Build list of loaded plugins""" fail_list = self.__pmgr.get_fail_list() for i in fail_list: @@ -126,6 +188,28 @@ class PluginStatus(ManagedWindow.ManagedWindow): self.model.append(row=[ '%s' % _("OK"), i[0], descr, None]) + + def __populate_reg_list(self): + """ Build list of registered plugins""" + for (type, typestr) in PTYPE_STR.iteritems(): + for pdata in self.__preg.type_plugins(type): + # model: plugintype, hidden, pluginname, plugindescr, pluginid + hidden = pdata.id in self.hidden + if hidden: + hiddenstr = self.HIDDEN + else: + hiddenstr = self.AVAILABLE + self.model_reg.append(row=[ + typestr, hiddenstr, pdata.name, pdata.description, + pdata.id]) + + def __rebuild_load_list(self): + self.model.clear() + self.__populate_load_list() + + def __rebuild_reg_list(self): + self.model_reg.clear() + self.__populate_reg_list() def button_press(self, obj, event): """ Callback function from the user clicking on a line """ @@ -142,9 +226,76 @@ class PluginStatus(ManagedWindow.ManagedWindow): def __reload(self, obj): """ Callback function from the "Reload" button """ self.__pmgr.reload_plugins() - self.model.clear() - self.__populate_list() - + self.__rebuild_load_list() + self.__rebuild_reg_list() + + def button_press_reg(self, obj, event): + """ Callback function from the user clicking on a line in reg plugin + """ + if event.type == gtk.gdk._2BUTTON_PRESS and event.button == 1: + self.__info(None) + + def __info(self, obj): + """ Callback function from the "Info" button + """ + model, node = self.selection_reg.get_selected() + if not node: + return + id = model.get_value(node, 4) + typestr = model.get_value(node, 0) + pdata = self.__preg.get_plugin(id) + auth = ' - '.join(pdata.authors) + email = ' - '.join(pdata.authors_email) + if len(auth) > 60: + auth = auth[:60] + '...' + if len(email) > 60: + email = email[:60] + '...' + if pdata: + infotxt = """Plugin name: %(name)s [%(typestr)s] + +Description: %(descr)s +Authors: %(authors)s +Email: %(email)s +Filename: %(fname)s + """ % { + 'name': pdata.name, + 'typestr': typestr, + 'descr': pdata.description, + 'authors': auth, + 'email': email, + 'fname': pdata.fname + } + InfoDialog('Detailed Info', infotxt, parent=self.window) + + def __hide(self, obj): + """ Callback function from the "Hide" button + """ + model, node = self.selection_reg.get_selected() + if not node: + return + id = model.get_value(node, 4) + if id in self.hidden: + #unhide + self.hidden.remove(id) + model.set_value(node, 1, self.AVAILABLE) + self.__pmgr.unhide_plugin(id) + else: + #hide + self.hidden.add(id) + model.set_value(node, 1, self.HIDDEN) + self.__pmgr.hide_plugin(id) + + def __load(self, obj): + """ Callback function from the "Load" button + """ + model, node = self.selection_reg.get_selected() + if not node: + return + id = model.get_value(node, 4) + pdata = self.__preg.get_plugin(id) + self.__pmgr.load_plugin(pdata) + self.__rebuild_load_list() + #------------------------------------------------------------------------- # # Details for an individual plugin that failed diff --git a/src/config.py b/src/config.py index 775419ddf..61850efe1 100644 --- a/src/config.py +++ b/src/config.py @@ -643,6 +643,8 @@ register('researcher.researcher-phone', '') register('researcher.researcher-postal', '') register('researcher.researcher-state', '') +register('plugin.hiddenplugins', []) + #--------------------------------------------------------------- # # Upgrade Conversions go here. diff --git a/src/gen/plug/__init__.py b/src/gen/plug/__init__.py index aaf65fa58..685b0a61e 100644 --- a/src/gen/plug/__init__.py +++ b/src/gen/plug/__init__.py @@ -29,7 +29,7 @@ from _pluginreg import (PluginData, PluginRegister, REPORT, TOOL, TOOL_UTILS, CATEGORY_QR_MISC, CATEGORY_QR_PERSON, CATEGORY_QR_FAMILY, CATEGORY_QR_EVENT, CATEGORY_QR_SOURCE, CATEGORY_QR_PLACE, CATEGORY_QR_REPOSITORY, CATEGORY_QR_NOTE, - CATEGORY_QR_DATE ) + CATEGORY_QR_DATE, PTYPE_STR ) from _manager import PluginManager from _import import ImportPlugin from _export import ExportPlugin @@ -45,4 +45,4 @@ __all__ = [ "docbackend", "docgen", "menu", Plugin, PluginData, TOOL_UTILS, CATEGORY_QR_MISC, CATEGORY_QR_PERSON, CATEGORY_QR_FAMILY, CATEGORY_QR_EVENT, CATEGORY_QR_SOURCE, CATEGORY_QR_PLACE, CATEGORY_QR_REPOSITORY, CATEGORY_QR_NOTE, - CATEGORY_QR_DATE] + CATEGORY_QR_DATE, PTYPE_STR] diff --git a/src/gen/plug/_manager.py b/src/gen/plug/_manager.py index 838584460..bc1a4f112 100644 --- a/src/gen/plug/_manager.py +++ b/src/gen/plug/_manager.py @@ -47,6 +47,7 @@ from gettext import gettext as _ import gen.utils import Relationship from gen.plug import PluginRegister +import config #------------------------------------------------------------------------- # @@ -97,6 +98,21 @@ class PluginManager(gen.utils.Callback): self.__pgr = PluginRegister.get_instance() self.__registereddir_set = set() self.__loaded_plugins = {} + self.__hidden_plugins = set([]) + for id in config.get('plugin.hiddenplugins'): + self.__hidden_plugins.add(id) + self.__hidden_changed() + + def __hidden_changed(self, *args): + #if hidden changed, stored data must be emptied as it could contain + #something that now must be hidden + self.__import_plugins = [] + self.__export_plugins = [] + self.__docgen_plugins = [] + #objects that need to know if the plugins available changed, are + #listening to this signal to update themselves. If a plugin becomes + #(un)hidden, this should happen, so we emit. + self.emit('plugins-reloaded') def reg_plugins(self, direct): """ @@ -197,6 +213,33 @@ class PluginManager(gen.utils.Callback): self.emit('plugins-reloaded') + def get_hidden_plugin_ids(self): + """ + Returns copy of the set hidden plugin ids + """ + return self.__hidden_plugins.copy() + + def hide_plugin(self, id): + """ Hide plugin with given id. This will hide the plugin so queries do + not return it anymore, and write this change to the config. + Note that config will then emit a signal + """ + self.__hidden_plugins.add(id) + hideset = [x for x in self.__hidden_plugins] + config.set('plugin.hiddenplugins', hideset) + config.save() + self.__hidden_changed() + + def unhide_plugin(self, id): + """ Unhide plugin with given id. This will unhide the plugin so queries + return it again, and write this change to the config + """ + self.__hidden_plugins.remove(id) + hideset = [x for x in self.__hidden_plugins] + config.set('plugin.hiddenplugins', hideset) + config.save() + self.__hidden_changed() + def get_fail_list(self): """ Return the list of failed plugins. """ return self.__failmsg_list @@ -206,33 +249,38 @@ class PluginManager(gen.utils.Callback): return self.__success_list def get_reg_reports(self, gui=True): - """ Return list of registered reports + """ Return list of non hidden registered reports :Param gui: bool indicating if GUI reports or CLI reports must be returned """ - return self.__pgr.report_plugins(gui) + return [plg for plg in self.__pgr.report_plugins(gui) if plg.id not in + self.__hidden_plugins] def get_reg_tools(self, gui=True): - """ Return list of registered tools + """ Return list of non hidden registered tools :Param gui: bool indicating if GUI reports or CLI reports must be returned """ - return self.__pgr.tool_plugins(gui) + return [plg for plg in self.__pgr.tool_plugins(gui) if plg.id not in + self.__hidden_plugins] def get_reg_quick_reports(self): - """ Return list of registered quick reports + """ Return list of non hidden registered quick reports """ - return self.__pgr.quickreport_plugins() + return [plg for plg in self.__pgr.quickreport_plugins() if plg.id not in + self.__hidden_plugins] def get_reg_mapservices(self): - """ Return list of registered mapservices + """ Return list of non hidden registered mapservices """ - return self.__pgr.mapservice_plugins() + return [plg for plg in self.__pgr.mapservice_plugins() if plg.id not in + self.__hidden_plugins] def get_reg_bookitems(self): - """ Return list of reports registered as bookitem + """ Return list of non hidden reports registered as bookitem """ - return self.__pgr.bookitem_plugins() + return [plg for plg in self.__pgr.bookitem_plugins() if plg.id not in + self.__hidden_plugins] def get_external_opt_dict(self): """ Return the dictionary of external options. """ @@ -252,7 +300,8 @@ class PluginManager(gen.utils.Callback): ## only PluginData, loading from module when importfunction needed? if self.__import_plugins == []: #The module still needs to be imported - imps = self.__pgr.import_plugins() + imps = [pdata for pdata in self.__pgr.import_plugins() if pdata.id + not in self.__hidden_plugins] for pdata in imps: mod = self.load_plugin(pdata) if mod: @@ -274,7 +323,8 @@ class PluginManager(gen.utils.Callback): ## only PluginData, loading from module when export/options needed? if self.__export_plugins == []: #The modules still need to be imported - exps = self.__pgr.export_plugins() + exps = [pdata for pdata in self.__pgr.export_plugins() if pdata.id + not in self.__hidden_plugins] for pdata in exps: mod = self.load_plugin(pdata) if mod: @@ -299,7 +349,8 @@ class PluginManager(gen.utils.Callback): ## So, only do import when docgen.get_basedoc() is requested if self.__docgen_plugins == []: #The modules still need to be imported - dgdps = self.__pgr.docgen_plugins() + dgdps = [pdata for pdata in self.__pgr.docgen_plugins() if pdata.id + not in self.__hidden_plugins] for pdata in dgdps: mod = self.load_plugin(pdata) if mod: diff --git a/src/gen/plug/_pluginreg.py b/src/gen/plug/_pluginreg.py index a39d6823b..14e89ddf9 100644 --- a/src/gen/plug/_pluginreg.py +++ b/src/gen/plug/_pluginreg.py @@ -67,6 +67,19 @@ RELCALC = 9 GRAMPLET = 10 PTYPE = [ REPORT , QUICKREPORT, TOOL, IMPORT, EXPORT, DOCGEN, GENERAL, MAPSERVICE, VIEW, RELCALC, GRAMPLET] +PTYPE_STR = { + REPORT: _('Report') , + QUICKREPORT: _('Quickreport'), + TOOL: _('Tool'), + IMPORT: _('Importer'), + EXPORT: _('Exporter'), + DOCGEN: _('Doc creator'), + GENERAL: _('Plugin lib'), + MAPSERVICE: _('Map service'), + VIEW: _('GRAMPS View'), + RELCALC: _('Relationships'), + GRAMPLET: _('Gramplet'), + } #possible report categories CATEGORY_TEXT = 0 @@ -834,7 +847,14 @@ class PluginRegister(object): for ind in rmlist: del self.__plugindata[ind] - def __type_plugins(self, ptype): + def get_plugin(self, id): + """Return the PluginData for the plugin with id""" + for x in self.__plugindata: + if x.id == id: + return x + return None + + def type_plugins(self, ptype): """Return a list of PluginData that are of type ptype """ return [x for x in self.__plugindata if x.ptype == ptype] @@ -844,68 +864,68 @@ class PluginRegister(object): :param gui: bool, if True then gui plugin, otherwise cli plugin """ if gui: - return [x for x in self.__type_plugins(REPORT) if REPORT_MODE_GUI + return [x for x in self.type_plugins(REPORT) if REPORT_MODE_GUI in x.report_modes] else: - return [x for x in self.__type_plugins(REPORT) if REPORT_MODE_CLI + return [x for x in self.type_plugins(REPORT) if REPORT_MODE_CLI in x.report_modes] def tool_plugins(self, gui=True): """Return a list of PluginData that are of type TOOL """ if gui: - return [x for x in self.__type_plugins(TOOL) if TOOL_MODE_GUI + return [x for x in self.type_plugins(TOOL) if TOOL_MODE_GUI in x.tool_modes] else: - return [x for x in self.__type_plugins(TOOL) if TOOL_MODE_CLI + return [x for x in self.type_plugins(TOOL) if TOOL_MODE_CLI in x.tool_modes] def bookitem_plugins(self): """Return a list of REPORT PluginData that are can be used as bookitem """ - return [x for x in self.__type_plugins(REPORT) if REPORT_MODE_BKI + return [x for x in self.type_plugins(REPORT) if REPORT_MODE_BKI in x.report_modes] def quickreport_plugins(self): """Return a list of PluginData that are of type QUICKREPORT """ - return self.__type_plugins(QUICKREPORT) + return self.type_plugins(QUICKREPORT) def import_plugins(self): """Return a list of PluginData that are of type IMPORT """ - return self.__type_plugins(IMPORT) + return self.type_plugins(IMPORT) def export_plugins(self): """Return a list of PluginData that are of type EXPORT """ - return self.__type_plugins(EXPORT) + return self.type_plugins(EXPORT) def docgen_plugins(self): """Return a list of PluginData that are of type DOCGEN """ - return self.__type_plugins(DOCGEN) + return self.type_plugins(DOCGEN) def general_plugins(self): """Return a list of PluginData that are of type GENERAL """ - return self.__type_plugins(GENERAL) + return self.type_plugins(GENERAL) def mapservice_plugins(self): """Return a list of PluginData that are of type MAPSERVICE """ - return self.__type_plugins(MAPSERVICE) + return self.type_plugins(MAPSERVICE) def view_plugins(self): """Return a list of PluginData that are of type VIEW """ - return self.__type_plugins(RELCALC) + return self.type_plugins(VIEW) def relcalc_plugins(self): """Return a list of PluginData that are of type RELCALC """ - return self.__type_plugins(RELCALC) + return self.type_plugins(RELCALC) def filter_load_on_reg(self): """Return a list of PluginData that have load_on_reg == True diff --git a/src/gui/viewmanager.py b/src/gui/viewmanager.py index b595fe5be..3892ac709 100644 --- a/src/gui/viewmanager.py +++ b/src/gui/viewmanager.py @@ -223,6 +223,10 @@ class ViewManager(CLIManager): self.buttons = [] self.merge_ids = [] self._key = None + self.toolactions = None + self.tool_menu_ui_id = None + self.reportactions = None + self.report_menu_ui_id = None self.show_sidebar = config.get('interface.view') self.show_toolbar = config.get('interface.toolbar-on') @@ -1290,12 +1294,15 @@ class ViewManager(CLIManager): """ Builds a new tools menu """ + if self.toolactions: + self.uistate.uimanager.remove_action_group(self.toolactions) + self.uistate.uimanager.remove_ui(self.tool_menu_ui_id) self.toolactions = gtk.ActionGroup('ToolWindow') (uidef, actions) = self.build_plugin_menu( 'ToolsMenu', tool_menu_list, Tool.tool_categories, make_plugin_callback) self.toolactions.add_actions(actions) - self.uistate.uimanager.add_ui_from_string(uidef) + self.tool_menu_ui_id = self.uistate.uimanager.add_ui_from_string(uidef) self.uimanager.insert_action_group(self.toolactions, 1) self.uistate.uimanager.ensure_update() @@ -1303,12 +1310,15 @@ class ViewManager(CLIManager): """ Builds a new reports menu """ + if self.reportactions: + self.uistate.uimanager.remove_action_group(self.reportactions) + self.uistate.uimanager.remove_ui(self.report_menu_ui_id) self.reportactions = gtk.ActionGroup('ReportWindow') (uidef, actions) = self.build_plugin_menu( 'ReportsMenu', report_menu_list, ReportBase.standalone_categories, make_plugin_callback) self.reportactions.add_actions(actions) - self.uistate.uimanager.add_ui_from_string(uidef) + self.report_menu_ui_id = self.uistate.uimanager.add_ui_from_string(uidef) self.uimanager.insert_action_group(self.reportactions, 1) self.uistate.uimanager.ensure_update() diff --git a/src/plugins/webreport/WebCal.py b/src/plugins/webreport/WebCal.py index 5308300d4..56e353bcd 100644 --- a/src/plugins/webreport/WebCal.py +++ b/src/plugins/webreport/WebCal.py @@ -28,7 +28,7 @@ Web Calendar generator. Refactoring. This is an ongoing job until this plugin is in a better shape. """ - +from __future__ import with_statement #------------------------------------------------------------------------ # # python modules