diff --git a/data/glade/plugins_window.glade b/data/glade/plugins_window.glade
new file mode 100644
index 000000000..d8996b53e
--- /dev/null
+++ b/data/glade/plugins_window.glade
@@ -0,0 +1,602 @@
+
+
+
+
+
+ 650
+ 500
+ 6
+ Plugins
+ 650
+ 500
+
+
+
+ True
+ 6
+
+
+ True
+ True
+
+
+ True
+ True
+ 250
+ True
+
+
+ True
+ True
+ 6
+ GTK_POLICY_AUTOMATIC
+ GTK_POLICY_AUTOMATIC
+
+
+ True
+ True
+ True
+
+
+
+
+ False
+ True
+
+
+
+
+ True
+ 6
+ 6
+
+
+ True
+ 6
+
+
+ True
+ 0
+ <empty>
+ True
+ True
+ True
+
+
+
+
+ True
+
+
+
+
+
+ 1
+
+
+
+
+ False
+
+
+
+
+ True
+ 6
+
+
+ True
+ Version:
+
+
+ False
+
+
+
+
+ True
+ 0
+ <empty>
+ True
+
+
+ 1
+
+
+
+
+ False
+ 1
+
+
+
+
+ True
+ 6
+
+
+ True
+ 0
+ Authors:
+
+
+ False
+
+
+
+
+ True
+ 0
+ 0
+ <empty>
+ PANGO_WRAP_WORD_CHAR
+ True
+ PANGO_ELLIPSIZE_END
+
+
+ 1
+
+
+
+
+ False
+ 2
+
+
+
+
+ True
+
+
+ True
+ Homepage:
+
+
+ False
+
+
+
+
+ True
+ True
+ True
+ homepage url
+ GTK_RELIEF_NONE
+ False
+ 0
+ 0
+
+
+
+ False
+ 1
+
+
+
+
+ False
+ 3
+
+
+
+
+ True
+
+
+ True
+
+
+ True
+ Descrition:
+
+
+ False
+
+
+
+
+ True
+
+
+
+
+
+ 1
+
+
+
+
+ False
+
+
+
+
+ True
+ False
+ True
+ 6
+ False
+ GTK_WRAP_WORD
+ 6
+ 6
+ 1
+ Plug-in decription should be displayed here. This text will be erased during PluginsWindow initialization.
+
+
+ 1
+
+
+
+
+ 4
+
+
+
+
+ True
+ 5
+ GTK_BUTTONBOX_END
+
+
+ True
+ True
+ True
+ 0
+
+
+ True
+
+
+ True
+ gtk-cancel
+
+
+
+
+ True
+ Uninstall
+
+
+ 1
+
+
+
+
+
+
+
+
+ True
+ True
+ True
+ 0
+
+
+ True
+
+
+ True
+ gtk-preferences
+
+
+
+
+ True
+ Configure
+
+
+ 1
+
+
+
+
+
+
+ 1
+
+
+
+
+ False
+ 5
+
+
+
+
+ True
+ True
+
+
+
+
+
+
+ True
+ Installed
+
+
+ tab
+ False
+
+
+
+
+ True
+ True
+
+
+ True
+ True
+ GTK_POLICY_AUTOMATIC
+ GTK_POLICY_AUTOMATIC
+
+
+ True
+ True
+ True
+
+
+
+
+ False
+ True
+
+
+
+
+ True
+
+
+ True
+
+
+ True
+ <empty>
+ True
+
+
+ False
+
+
+
+
+ True
+
+
+
+
+
+ 1
+
+
+
+
+ False
+
+
+
+
+ True
+
+
+ True
+ Version:
+
+
+ False
+
+
+
+
+ True
+ <empty>
+ True
+
+
+ False
+ 1
+
+
+
+
+ False
+ 1
+
+
+
+
+ True
+
+
+ True
+ Authors:
+
+
+ False
+
+
+
+
+ True
+ <empty>
+ True
+
+
+ False
+ 1
+
+
+
+
+ False
+ 2
+
+
+
+
+ True
+
+
+ True
+ Homepage:
+
+
+ False
+
+
+
+
+ True
+ True
+ True
+ button
+ GTK_RELIEF_NONE
+ False
+ 0
+
+
+
+ False
+ 1
+
+
+
+
+ False
+ 3
+
+
+
+
+ True
+
+
+ True
+
+
+ True
+ Descrition:
+
+
+ False
+
+
+
+
+ True
+
+
+
+
+
+ 1
+
+
+
+
+ False
+
+
+
+
+ True
+ True
+
+
+ 1
+
+
+
+
+ 4
+
+
+
+
+ True
+ GTK_BUTTONBOX_END
+
+
+ True
+ True
+ True
+ button
+ 0
+
+
+
+
+ True
+ True
+ True
+ button
+ 0
+
+
+ 1
+
+
+
+
+ False
+ 5
+
+
+
+
+ True
+ True
+
+
+
+
+ 1
+
+
+
+
+ Available
+
+
+ tab
+ 1
+ False
+
+
+
+
+
+
+
+
+ tab
+
+
+
+
+
+
+ True
+ 15
+ GTK_BUTTONBOX_END
+
+
+ True
+ True
+ True
+ gtk-close
+ True
+ 0
+
+
+
+
+
+ False
+ 1
+
+
+
+
+
+
diff --git a/data/glade/roster_window.glade b/data/glade/roster_window.glade
index c480f6879..86ca8f2d1 100644
--- a/data/glade/roster_window.glade
+++ b/data/glade/roster_window.glade
@@ -193,6 +193,21 @@
+
+
+
diff --git a/launch.sh b/launch.sh
index cfaf9cd68..d816e833c 100755
--- a/launch.sh
+++ b/launch.sh
@@ -15,4 +15,4 @@ if [ "x${OS}" == "xDarwin" ]; then
fi
cd ${BASE}/src
-exec -a gajim python -t gajim.py $@
+exec -a gajim python2.5 -t gajim.py $@
diff --git a/plugins/length_notifier.py b/plugins/length_notifier.py
index af85ded5f..7c9f2422a 100644
--- a/plugins/length_notifier.py
+++ b/plugins/length_notifier.py
@@ -32,12 +32,12 @@ from plugins import GajimPlugin
from plugins.helpers import log, log_calls
class LengthNotifierPlugin(GajimPlugin):
- name = 'Message Length Notifier'
- short_name = 'length_notifier'
- version = '0.1'
- description = '''Highlights message entry field in chat window when given
-length of message is exceeded.'''
- authors = ['Mateusz Biliński ']
+ name = u'Message Length Notifier'
+ short_name = u'length_notifier'
+ version = u'0.1'
+ 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):
diff --git a/src/gajim.py b/src/gajim.py
index 19c98d5cd..69d64e0c4 100755
--- a/src/gajim.py
+++ b/src/gajim.py
@@ -26,6 +26,8 @@
##
import os
+import pycallgraph
+
if os.name == 'nt':
import warnings
@@ -602,6 +604,8 @@ def on_exit():
if sys.platform == 'darwin':
import osx
osx.shutdown()
+
+ #pycallgraph.make_dot_graph('common.xmpp-only.dot', format='dot')
import atexit
atexit.register(on_exit)
@@ -3231,6 +3235,9 @@ class Interface:
gajim.ipython_window = window
def __init__(self):
+ #filter_func = pycallgraph.GlobbingFilter(include=['common.xmpp.*'])
+ #pycallgraph.start_trace(filter_func=filter_func)
+
gajim.interface = self
# This is the manager and factory of message windows set by the module
self.msg_win_mgr = None
diff --git a/src/plugins/gui.py b/src/plugins/gui.py
new file mode 100644
index 000000000..c4628958b
--- /dev/null
+++ b/src/plugins/gui.py
@@ -0,0 +1,166 @@
+# -*- coding: utf-8 -*-
+
+## This file is part of Gajim.
+##
+## Gajim is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published
+## by the Free Software Foundation; version 3 only.
+##
+## Gajim is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with Gajim. If not, see .
+##
+
+'''
+GUI classes related to plug-in management.
+
+:author: Mateusz Biliński
+:since: 06/06/2008
+:copyright: Copyright (2008) Mateusz Biliński
+:license: GPL
+'''
+
+__all__ = ['PluginsWindow']
+
+import pango
+import gtk, gobject
+
+import gtkgui_helpers
+from common import gajim
+
+from plugins.helpers import log_calls, log
+
+class PluginsWindow(object):
+ '''Class for Plugins window'''
+
+ @log_calls('PluginsWindow')
+ def __init__(self):
+ '''Initialize Plugins window'''
+ self.xml = gtkgui_helpers.get_glade('plugins_window.glade')
+ self.window = self.xml.get_widget('plugins_window')
+ self.window.set_transient_for(gajim.interface.roster.window)
+
+ widgets_to_extract = ('plugins_notebook',
+ 'plugin_name_label',
+ 'plugin_version_label',
+ 'plugin_authors_label',
+ 'plugin_homepage_linkbutton',
+ 'plugin_description_textview',
+ 'uninstall_plugin_button',
+ 'configure_plugin_button',
+ 'installed_plugins_treeview')
+
+ for widget_name in widgets_to_extract:
+ setattr(self, widget_name, self.xml.get_widget(widget_name))
+
+ attr_list = pango.AttrList()
+ attr_list.insert(pango.AttrWeight(pango.WEIGHT_BOLD, 0, -1))
+ self.plugin_name_label.set_attributes(attr_list)
+
+ self.installed_plugins_model = gtk.ListStore(gobject.TYPE_PYOBJECT,
+ gobject.TYPE_STRING,
+ gobject.TYPE_BOOLEAN)
+ self.installed_plugins_treeview.set_model(self.installed_plugins_model)
+
+ renderer = gtk.CellRendererText()
+ col = gtk.TreeViewColumn(_('Plugin'), renderer, text=1)
+ self.installed_plugins_treeview.append_column(col)
+
+ renderer = gtk.CellRendererToggle()
+ renderer.set_property('activatable', True)
+ renderer.connect('toggled', self.installed_plugins_toggled_cb)
+ col = gtk.TreeViewColumn(_('Active'), renderer, active=2)
+ self.installed_plugins_treeview.append_column(col)
+
+ # connect signal for selection change
+ selection = self.installed_plugins_treeview.get_selection()
+ selection.connect('changed',
+ self.installed_plugins_treeview_selection_changed)
+ selection.set_mode(gtk.SELECTION_SINGLE)
+
+ self._clear_installed_plugin_info()
+
+ self.fill_installed_plugins_model()
+
+ self.xml.signal_autoconnect(self)
+
+ self.plugins_notebook.set_current_page(0)
+
+ self.window.show_all()
+ gtkgui_helpers.possibly_move_window_in_current_desktop(self.window)
+
+ @log_calls('PluginsWindow')
+ def installed_plugins_treeview_selection_changed(self, treeview_selection):
+ model, iter = treeview_selection.get_selected()
+ if iter:
+ plugin = model.get_value(iter, 0)
+ plugin_name = model.get_value(iter, 1)
+ is_active = model.get_value(iter, 2)
+
+ self._display_installed_plugin_info(plugin)
+ else:
+ self._clear_installed_plugin_info()
+
+ def _display_installed_plugin_info(self, plugin):
+ self.plugin_name_label.set_text(plugin.name)
+ self.plugin_version_label.set_text(plugin.version)
+ self.plugin_authors_label.set_text(", ".join(plugin.authors))
+ self.plugin_homepage_linkbutton.set_uri(plugin.homepage)
+ self.plugin_homepage_linkbutton.set_label(plugin.homepage)
+ self.plugin_homepage_linkbutton.set_property('sensitive', True)
+
+ desc_textbuffer = self.plugin_description_textview.get_buffer()
+ desc_textbuffer.set_text(plugin.description)
+ self.plugin_description_textview.set_property('sensitive', True)
+ self.uninstall_plugin_button.set_property('sensitive', True)
+ self.configure_plugin_button.set_property('sensitive', True)
+
+ def _clear_installed_plugin_info(self):
+ self.plugin_name_label.set_text('')
+ self.plugin_version_label.set_text('')
+ self.plugin_authors_label.set_text('')
+ self.plugin_homepage_linkbutton.set_uri('')
+ self.plugin_homepage_linkbutton.set_label('')
+ self.plugin_homepage_linkbutton.set_property('sensitive', False)
+
+ desc_textbuffer = self.plugin_description_textview.get_buffer()
+ desc_textbuffer.set_text('')
+ self.plugin_description_textview.set_property('sensitive', False)
+ self.uninstall_plugin_button.set_property('sensitive', False)
+ self.configure_plugin_button.set_property('sensitive', False)
+
+ @log_calls('PluginsWindow')
+ def fill_installed_plugins_model(self):
+ pm = gajim.plugin_manager
+ self.installed_plugins_model.clear()
+ self.installed_plugins_model.set_sort_column_id(0, gtk.SORT_ASCENDING)
+
+ for plugin_class in pm.plugins:
+ self.installed_plugins_model.append([plugin_class,
+ plugin_class.name,
+ plugin_class._active])
+
+ @log_calls('PluginsWindow')
+ def installed_plugins_toggled_cb(self, cell, path):
+ is_active = self.installed_plugins_model[path][2]
+ plugin_class = self.installed_plugins_model[path][0]
+
+ if is_active:
+ gajim.plugin_manager.deactivate_plugin(plugin_class._instance)
+ else:
+ gajim.plugin_manager.activate_plugin(plugin_class)
+
+ self.installed_plugins_model[path][2] = not is_active
+
+ @log_calls('PluginsWindow')
+ def on_plugins_window_destroy(self, widget):
+ '''Close window'''
+ del gajim.interface.instances['plugins']
+
+ @log_calls('PluginsWindow')
+ def on_close_button_clicked(self, widget):
+ self.window.destroy()
\ No newline at end of file
diff --git a/src/plugins/plugin.py b/src/plugins/plugin.py
index c8bb95af9..fd49c6a44 100644
--- a/src/plugins/plugin.py
+++ b/src/plugins/plugin.py
@@ -30,7 +30,7 @@ class GajimPlugin(object):
'''
Base class for implementing Gajim plugins.
'''
- name = ''
+ name = u''
'''
Name of plugin.
@@ -38,7 +38,7 @@ class GajimPlugin(object):
:type: unicode
'''
- short_name = ''
+ short_name = u''
'''
Short name of plugin.
@@ -49,7 +49,7 @@ class GajimPlugin(object):
:todo: decide whether we really need this one, because class name (with
module name) can act as such short name
'''
- version = ''
+ version = u''
'''
Version of plugin.
@@ -61,7 +61,7 @@ class GajimPlugin(object):
same plugin class but with different version and we want only the newest
one to be active - is such policy good?
'''
- description = ''
+ description = u''
'''
Plugin description.
@@ -78,6 +78,15 @@ class GajimPlugin(object):
:todo: should we decide on any particular format of author strings?
Especially: should we force format of giving author's e-mail?
'''
+ homepage = u''
+ '''
+ URL to plug-in's homepage.
+
+ :type: unicode
+
+ :todo: should we check whether provided string is valid URI? (Maybe
+ using 'property')
+ '''
gui_extension_points = {}
'''
Extension points that plugin wants to connect with.
diff --git a/src/plugins/pluginmanager.py b/src/plugins/pluginmanager.py
index b9e113793..359ec3805 100644
--- a/src/plugins/pluginmanager.py
+++ b/src/plugins/pluginmanager.py
@@ -94,7 +94,7 @@ class PluginManager(object):
'''
for path in gajim.PLUGINS_DIRS:
- self.plugins.extend(PluginManager.scan_dir_for_plugins(path))
+ self._add_plugins(PluginManager.scan_dir_for_plugins(path))
log.debug('plugins: %s'%(self.plugins))
@@ -102,6 +102,22 @@ class PluginManager(object):
log.debug('active: %s'%(self.active_plugins))
+
+ @log_calls('PluginManager')
+ def _add_plugin(self, plugin_class):
+ '''
+ :todo: what about adding plug-ins that are already added? Module reload
+ and adding class from reloaded module or ignoring adding plug-in?
+ '''
+ plugin_class._active = False
+ plugin_class._instance = None
+ self.plugins.append(plugin_class)
+
+ @log_calls('PluginManager')
+ def _add_plugins(self, plugin_classes):
+ for plugin_class in plugin_classes:
+ self._add_plugin(plugin_class)
+
@log_calls('PluginManager')
def gui_extension_point(self, gui_extpoint_name, *args):
'''
@@ -124,7 +140,6 @@ class PluginManager(object):
call 'self._deactivate_plugin()' or sth similar.
Looking closer - we only rewrite tuples here. Real check should be
made in method that invokes gui_extpoints handlers.
-
'''
self._add_gui_extension_point_call_to_list(gui_extpoint_name, *args)
@@ -141,13 +156,13 @@ class PluginManager(object):
handlers[0](*args)
@log_calls('PluginManager')
- def activate_plugin(self, plugin):
+ def activate_plugin(self, plugin_class):
'''
:param plugin: plugin to be activated
:type plugin: class object of `GajimPlugin` subclass
'''
- plugin_object = plugin()
+ plugin_object = plugin_class()
success = True
@@ -156,6 +171,8 @@ class PluginManager(object):
if success:
self.active_plugins.append(plugin_object)
+ plugin_class._instance = plugin_object
+ plugin_class._active = True
return success
@@ -165,8 +182,9 @@ class PluginManager(object):
# for each handled GUI extension point)
for gui_extpoint_name, gui_extpoint_handlers in \
plugin_object.gui_extension_points.iteritems():
- for gui_extension_point_args in self.gui_extension_points[gui_extpoint_name]:
- gui_extpoint_handlers[1](*gui_extension_point_args)
+ if gui_extpoint_name in self.gui_extension_points:
+ for gui_extension_point_args in self.gui_extension_points[gui_extpoint_name]:
+ gui_extpoint_handlers[1](*gui_extension_point_args)
# remove GUI extension points handlers (provided by plug-in) from
# handlers list
@@ -176,6 +194,8 @@ class PluginManager(object):
# removing plug-in from active plug-ins list
self.active_plugins.remove(plugin_object)
+ plugin_object.__class__._active = False
+ del plugin_object
def deactivate_all_plugins(self):
for plugin_object in self.active_plugins:
diff --git a/src/roster_window.py b/src/roster_window.py
index 9af3d59b6..76b86c81a 100644
--- a/src/roster_window.py
+++ b/src/roster_window.py
@@ -44,6 +44,8 @@ import message_control
import adhoc_commands
import notify
import features_window
+import plugins
+import plugins.gui
from common import gajim
from common import helpers
@@ -3129,6 +3131,12 @@ class RosterWindow:
gajim.interface.instances['preferences'].window.present()
else:
gajim.interface.instances['preferences'] = config.PreferencesWindow()
+
+ def on_plugins_menuitem_activate(self, widget):
+ if gajim.interface.instances.has_key('plugins'):
+ gajim.interface.instances['plugins'].window.present()
+ else:
+ gajim.interface.instances['plugins'] = plugins.gui.PluginsWindow()
def on_publish_tune_checkbutton_toggled(self, widget, account):
if widget.get_active():