diff --git a/plugins/acronyms_expander.py b/plugins/acronyms_expander.py index a575fe555..6dec95614 100644 --- a/plugins/acronyms_expander.py +++ b/plugins/acronyms_expander.py @@ -38,11 +38,8 @@ class AcronymsExpanderPlugin(GajimPlugin): description = u'''Replaces acronyms (or other strings) with given expansions/substitutes.''' authors = [u'Mateusz Biliński '] homepage = u'http://blog.bilinski.it' - - #@log_calls('AcronymsExpanderPlugin') - #def __init__(self): - #super(AcronymsExpanderPlugin, self).__init__() + @log_calls('AcronymsExpanderPlugin') def init(self): self.config_dialog = None @@ -71,20 +68,20 @@ class AcronymsExpanderPlugin(GajimPlugin): ACRONYMS = self.config['ACRONYMS'] INVOKER = self.config['INVOKER'] t = tb.get_text(tb.get_start_iter(), tb.get_end_iter()) - log.debug('%s %d'%(t, len(t))) + #log.debug('%s %d'%(t, len(t))) if t and t[-1] == INVOKER: - log.debug("changing msg text") + #log.debug("changing msg text") base,sep,head=t[:-1].rpartition(INVOKER) - log.debug('%s | %s | %s'%(base, sep, head)) + #log.debug('%s | %s | %s'%(base, sep, head)) if head in ACRONYMS: head = ACRONYMS[head] log.debug("head: %s"%(head)) t = "".join((base, sep, head, INVOKER)) - log.debug("turning off notify") + #log.debug("turning off notify") tb.freeze_notify() - log.debug("setting text: '%s'"%(t)) + #log.debug("setting text: '%s'"%(t)) tb.set_text(t) - log.debug("turning on notify") + #log.debug("turning on notify") tb.thaw_notify() @log_calls('AcronymsExpanderPlugin') diff --git a/plugins/banner_tweaks/plugin.py b/plugins/banner_tweaks/plugin.py index ee5cf2500..ba059143e 100644 --- a/plugins/banner_tweaks/plugin.py +++ b/plugins/banner_tweaks/plugin.py @@ -94,7 +94,6 @@ http://trac.gajim.org/attachment/ticket/4133''' if self.config['show_banner_resource'] or self.config['banner_small_fonts']: banner_name_label = chat_control.xml.get_widget('banner_name_label') label_text = banner_name_label.get_label() - log.debug('label_text = "%s"'%(label_text)) contact = chat_control.contact jid = contact.jid diff --git a/plugins/length_notifier/config_dialog.glade b/plugins/length_notifier/config_dialog.glade index 0267bb184..be20bc638 100644 --- a/plugins/length_notifier/config_dialog.glade +++ b/plugins/length_notifier/config_dialog.glade @@ -1,6 +1,6 @@ - + @@ -12,40 +12,58 @@ 7 5 - + True - - - True - True - Message length at which notification is invoked. - 6 - 0 0 999999 1 10 10 - True - True - - - - False - False - - - - - True - - - - - - 1 - - + Message length at which notification is invoked. + 0 + Message length: + + + GTK_FILL + GTK_FILL + + + + + True + Background color of text entry field in chat window when notification is invoked. + 0 + Notification color: + + + 1 + 2 + GTK_FILL + GTK_FILL + + + + + True + JabberIDs that plugin should be used with (eg. restrict only to one microblogging bot). Use comma (without space) as separator. If empty plugin is used with every JID. + 0 + JabberIDs to include: + + + 2 + 3 + GTK_FILL + GTK_FILL + + + + + True + True + JabberIDs that plugin should be used with (eg. restrict only to one microblogging bot). Use comma (without space) as separator. If empty plugin is used with every JID. + + 1 2 - GTK_FILL + 2 + 3 GTK_FILL @@ -91,57 +109,39 @@ - + True - True - JabberIDs that plugin should be used with (eg. restrict only to one microblogging bot). If empty plugin is used with every JID. [not implemented] - - + + + True + True + Message length at which notification is invoked. + 6 + 0 0 999999 1 10 10 + True + True + + + + False + False + + + + + True + + + + + + 1 + + 1 2 - 2 - 3 - GTK_FILL - - - - - True - JabberIDs that plugin should be used with (eg. restrict only to one microblogging bot). If empty plugin is used with every JID. [not implemented] - 0 - JabberIDs to include: - - - 2 - 3 - GTK_FILL - GTK_FILL - - - - - True - Background color of text entry field in chat window when notification is invoked. - 0 - Notification color: - - - 1 - 2 - GTK_FILL - GTK_FILL - - - - - True - Message length at which notification is invoked. - 0 - Message length: - - GTK_FILL GTK_FILL diff --git a/plugins/length_notifier/length_notifier.py b/plugins/length_notifier/length_notifier.py index e9e5fbcd5..c8b45a284 100644 --- a/plugins/length_notifier/length_notifier.py +++ b/plugins/length_notifier/length_notifier.py @@ -40,10 +40,6 @@ class LengthNotifierPlugin(GajimPlugin): description = u'''Highlights message entry field in chat window when given length of message is exceeded.''' authors = [u'Mateusz Biliński '] homepage = u'http://blog.bilinski.it' - - #@log_calls('LengthNotifierPlugin') - #def __init__(self): - #super(LengthNotifierPlugin, self).__init__() @log_calls('LengthNotifierPlugin') def init(self): @@ -107,7 +103,8 @@ class LengthNotifierPlugin(GajimPlugin): if d['prev_color']: tv.modify_base(gtk.STATE_NORMAL, d['prev_color']) except AttributeError, error: - log.debug('Length Notifier Plugin was (probably) never connected with this chat window.\n Error: %s' % (error)) + pass + #log.debug('Length Notifier Plugin was (probably) never connected with this chat window.\n Error: %s' % (error)) @log_calls('LengthNotifierPlugin') def jid_is_ok(self, jid): diff --git a/plugins/roster_buttons/plugin.py b/plugins/roster_buttons/plugin.py index a1aa29482..a7ebb7d55 100644 --- a/plugins/roster_buttons/plugin.py +++ b/plugins/roster_buttons/plugin.py @@ -40,14 +40,9 @@ class RosterButtonsPlugin(GajimPlugin): description = u'''Adds quick action buttons to roster window.''' authors = [u'Mateusz Biliński '] homepage = u'http://blog.bilinski.it' - - #@log_calls('RosterButtonsPlugin') - #def __init__(self): - #super(RosterButtonsPlugin, self).__init__() @log_calls('RosterButtonsPlugin') def init(self): - #log.debug('self.__path__==%s'%(self.__path__)) self.GLADE_FILE_PATH = self.local_file_path('roster_buttons.glade') self.roster_vbox = gajim.interface.roster.xml.get_widget('roster_vbox2') diff --git a/src/chat_control.py b/src/chat_control.py index 9d53dfda1..07967bf60 100644 --- a/src/chat_control.py +++ b/src/chat_control.py @@ -294,7 +294,15 @@ class ChatControlBase(MessageControl): self.smooth = True + # PluginSystem: adding GUI extension point for ChatControlBase + # instance object (also subclasses, eg. ChatControl or GroupchatControl) gajim.plugin_manager.gui_extension_point('chat_control_base', self) + + def shutdown(self): + # PluginSystem: removing GUI extension points connected with ChatControlBase + # instance object + gajim.plugin_manager.remove_gui_extension_point('chat_control_base', self) + gajim.plugin_manager.remove_gui_extension_point('chat_control_base_draw_banner', self) def on_msg_textview_populate_popup(self, textview, menu): '''we override the default context menu and we prepend an option to switch languages''' @@ -1144,6 +1152,8 @@ class ChatControl(ChatControlBase): # restore previous conversation self.restore_conversation() + # PluginSystem: adding GUI extension point for this ChatControl + # instance object gajim.plugin_manager.gui_extension_point('chat_control', self) def on_avatar_eventbox_enter_notify_event(self, widget, event): @@ -2022,6 +2032,13 @@ class ChatControl(ChatControlBase): self.reset_kbd_mouse_timeout_vars() def shutdown(self): + # PluginSystem: calling shutdown of super class (ChatControlBase) to let it remove + # it's GUI extension points + super(ChatControl, self).shutdown() + # PluginSystem: removing GUI extension points connected with ChatControl + # instance object + gajim.plugin_manager.remove_gui_extension_point('chat_control', self) + # destroy banner tooltip - bug #pygtk for that! self.status_tooltip.destroy() diff --git a/src/groupchat_control.py b/src/groupchat_control.py index 84b31fa0a..5350190dd 100644 --- a/src/groupchat_control.py +++ b/src/groupchat_control.py @@ -1636,6 +1636,11 @@ class GroupchatControl(ChatControlBase): status = self.subject) def shutdown(self, status='offline'): + # PluginSystem: calling shutdown of super class (ChatControlBase) + # to let it remove it's GUI extension points + super(GroupchatControl, self).shutdown() + + # destroy banner tooltip - bug #pygtk for that! self.subject_tooltip.destroy() gajim.connections[self.account].send_gc_status(self.nick, self.room_jid, diff --git a/src/message_control.py b/src/message_control.py index ebff23368..268cd4e8c 100644 --- a/src/message_control.py +++ b/src/message_control.py @@ -28,7 +28,7 @@ TYPE_PM = 'pm' #################### -class MessageControl: +class MessageControl(object): '''An abstract base widget that can embed in the gtk.Notebook of a MessageWindow''' def __init__(self, type_id, parent_win, widget_name, contact, account, resource = None): diff --git a/src/plugins/gui.py b/src/plugins/gui.py index fff2a2ded..e74e86ef5 100644 --- a/src/plugins/gui.py +++ b/src/plugins/gui.py @@ -170,7 +170,7 @@ class PluginsWindow(object): @log_calls('PluginsWindow') def on_configure_plugin_button_clicked(self, widget): - log.debug('widget: %s'%(widget)) + #log.debug('widget: %s'%(widget)) selection = self.installed_plugins_treeview.get_selection() model, iter = selection.get_selected() if iter: diff --git a/src/plugins/helpers.py b/src/plugins/helpers.py index 9dce2bf93..4034a7ebc 100644 --- a/src/plugins/helpers.py +++ b/src/plugins/helpers.py @@ -50,7 +50,8 @@ class log_calls(object): Decorator class for functions to easily log when they are entered and left. ''' - filter_out_classes = ['PluginManager'] + filter_out_classes = ['GajimPlugin', 'GajimPluginConfig', + 'GajimPluginConfigDialog', 'PluginsWindow'] ''' List of classes from which no logs should be emited when methods are called, eventhough `log_calls` decorator is used. @@ -129,10 +130,11 @@ class Singleton(type): def __call__(cls,*args,**kw): if cls.instance is None: cls.instance=super(Singleton,cls).__call__(*args,**kw) - log.debug('%(classname)s - new instance created'%{ - 'classname' : cls.__name__}) + #log.debug('%(classname)s - new instance created'%{ + #'classname' : cls.__name__}) else: - log.debug('%(classname)s - returning already existing instance'%{ - 'classname' : cls.__name__}) + pass + #log.debug('%(classname)s - returning already existing instance'%{ + #'classname' : cls.__name__}) return cls.instance \ No newline at end of file diff --git a/src/plugins/plugin.py b/src/plugins/plugin.py index b08009072..d53b25960 100644 --- a/src/plugins/plugin.py +++ b/src/plugins/plugin.py @@ -130,10 +130,22 @@ class GajimPlugin(object): def save_config(self): self.config.save() - @log_calls('GajimPlugin') + @log_calls('GajimPlugin') def load_config(self): self.config.load() + def __eq__(self, plugin): + if self.short_name == plugin.short_name: + return True + + return False + + def __ne__(self, plugin): + if self.short_name != plugin.short_name: + return True + + return False + @log_calls('GajimPlugin') def local_file_path(self, file_name): return os.path.join(self.__path__, file_name) diff --git a/src/plugins/pluginmanager.py b/src/plugins/pluginmanager.py index 135a9b4a0..038417591 100644 --- a/src/plugins/pluginmanager.py +++ b/src/plugins/pluginmanager.py @@ -96,11 +96,11 @@ class PluginManager(object): for path in gajim.PLUGINS_DIRS: self.add_plugins(PluginManager.scan_dir_for_plugins(path)) - log.debug('plugins: %s'%(self.plugins)) + #log.debug('plugins: %s'%(self.plugins)) self._activate_all_plugins_from_global_config() - log.debug('active: %s'%(self.active_plugins)) + #log.debug('active: %s'%(self.active_plugins)) @log_calls('PluginManager') def _plugin_has_entry_in_global_config(self, plugin): @@ -120,11 +120,16 @@ class PluginManager(object): and adding class from reloaded module or ignoring adding plug-in? ''' plugin = plugin_class() - if not self._plugin_has_entry_in_global_config(plugin): - self._create_plugin_entry_in_global_config(plugin) - - self.plugins.append(plugin) - plugin.active = False + + if plugin not in self.plugins: + if not self._plugin_has_entry_in_global_config(plugin): + self._create_plugin_entry_in_global_config(plugin) + + self.plugins.append(plugin) + plugin.active = False + else: + log.info('Not loading plugin %s v%s from module %s (identified by short name: %s). Plugin already loaded.'%( + plugin.name, plugin.version, plugin.__module__, plugin.short_name)) @log_calls('PluginManager') def add_plugins(self, plugin_classes): @@ -134,7 +139,9 @@ class PluginManager(object): @log_calls('PluginManager') def gui_extension_point(self, gui_extpoint_name, *args): ''' - Invokes all handlers (from plugins) for particular GUI extension point. + Invokes all handlers (from plugins) for particular GUI extension point + and adds it to collection for further processing (eg. by plugins not active + yet). :param gui_extpoint_name: name of GUI extension point. :type gui_extpoint_name: unicode @@ -157,10 +164,69 @@ class PluginManager(object): self._add_gui_extension_point_call_to_list(gui_extpoint_name, *args) self._execute_all_handlers_of_gui_extension_point(gui_extpoint_name, *args) + + @log_calls('PluginManager') + def remove_gui_extension_point(self, gui_extpoint_name, *args): + ''' + Removes GUI extension point from collection held by `PluginManager`. + + From this point this particular extension point won't be visible + to plugins (eg. it won't invoke any handlers when plugin is activated). + + GUI extension point is removed completely (there is no way to recover it + from inside `PluginManager`). + + Removal is needed when instance object that given extension point was + connect with is destroyed (eg. ChatControl is closed or context menu + is hidden). + + Each `PluginManager.gui_extension_point` call should have a call of + `PluginManager.remove_gui_extension_point` related to it. + + :note: in current implementation different arguments mean different + extension points. The same arguments and the same name mean + the same extension point. + :todo: instead of using argument to identify which extpoint should be + removed, maybe add additional 'id' argument - this would work similar + hash in Python objects. 'id' would be calculated based on arguments + passed or on anything else (even could be constant). This would give + core developers (that add new extpoints) more freedom, but is this + necessary? + + :param gui_extpoint_name: name of GUI extension point. + :type gui_extpoint_name: unicode + :param args: arguments that `PluginManager.gui_extension_point` was + called with for this extension point. This is used (along with + extension point name) to identify element to be removed. + :type args: tuple + ''' + log.debug('name: %s\n args: %s'%(gui_extpoint_name, args)) + @log_calls('PluginManager') def _add_gui_extension_point_call_to_list(self, gui_extpoint_name, *args): - self.gui_extension_points.setdefault(gui_extpoint_name, []).append(args) + ''' + Adds GUI extension point call to list of calls. + + This is done only if such call hasn't been added already + (same extension point name and same arguments). + + :note: This is assumption that GUI extension points are different only + if they have different name or different arguments. + + :param gui_extpoint_name: GUI extension point name used to identify it + by plugins. + :type gui_extpoint_name: str + + :param args: parameters to be passed to extension point handlers + (typically and object that invokes `gui_extension_point`; however, + this can be practically anything) + :type args: tuple + + ''' + if ((gui_extpoint_name not in self.gui_extension_points) + or (args not in self.gui_extension_points[gui_extpoint_name])): + self.gui_extension_points.setdefault(gui_extpoint_name, []).append(args) @log_calls('PluginManager') def _execute_all_handlers_of_gui_extension_point(self, gui_extpoint_name, *args): @@ -287,57 +353,62 @@ class PluginManager(object): #log.debug(sys.path) for elem_name in dir_list: - log.debug('- "%s"'%(elem_name)) + #log.debug('- "%s"'%(elem_name)) file_path = os.path.join(path, elem_name) - log.debug(' "%s"'%(file_path)) + #log.debug(' "%s"'%(file_path)) module = None if os.path.isfile(file_path) and fnmatch.fnmatch(file_path,'*.py'): module_name = os.path.splitext(elem_name)[0] - log.debug('Possible module detected.') + #log.debug('Possible module detected.') try: module = __import__(module_name) - log.debug('Module imported.') + #log.debug('Module imported.') except ValueError, value_error: - log.debug('Module not imported successfully. ValueError: %s'%(value_error)) + pass + #log.debug('Module not imported successfully. ValueError: %s'%(value_error)) except ImportError, import_error: - log.debug('Module not imported successfully. ImportError: %s'%(import_error)) + pass + #log.debug('Module not imported successfully. ImportError: %s'%(import_error)) elif os.path.isdir(file_path): module_name = elem_name file_path += os.path.sep - log.debug('Possible package detected.') + #log.debug('Possible package detected.') try: module = __import__(module_name) - log.debug('Package imported.') + #log.debug('Package imported.') except ValueError, value_error: - log.debug('Package not imported successfully. ValueError: %s'%(value_error)) + pass + #log.debug('Package not imported successfully. ValueError: %s'%(value_error)) except ImportError, import_error: - log.debug('Package not imported successfully. ImportError: %s'%(import_error)) + pass + #log.debug('Package not imported successfully. ImportError: %s'%(import_error)) if module: - log.debug('Attributes processing started') + #log.debug('Attributes processing started') for module_attr_name in [attr_name for attr_name in dir(module) if not (attr_name.startswith('__') or attr_name.endswith('__'))]: module_attr = getattr(module, module_attr_name) - log.debug('%s : %s'%(module_attr_name, module_attr)) + #log.debug('%s : %s'%(module_attr_name, module_attr)) try: if issubclass(module_attr, GajimPlugin) and \ not module_attr is GajimPlugin: - log.debug('is subclass of GajimPlugin') + #log.debug('is subclass of GajimPlugin') #log.debug('file_path: %s\nabspath: %s\ndirname: %s'%(file_path, os.path.abspath(file_path), os.path.dirname(os.path.abspath(file_path)))) #log.debug('file_path: %s\ndirname: %s\nabspath: %s'%(file_path, os.path.dirname(file_path), os.path.abspath(os.path.dirname(file_path)))) module_attr.__path__ = os.path.abspath(os.path.dirname(file_path)) plugins_found.append(module_attr) except TypeError, type_error: - log.debug('module_attr: %s, error : %s'%( - module_name+'.'+module_attr_name, - type_error)) + pass + #log.debug('module_attr: %s, error : %s'%( + #module_name+'.'+module_attr_name, + #type_error)) - log.debug(module) + #log.debug(module) return plugins_found \ No newline at end of file