summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorFlorent Le Coz <louiz@louiz.org>2011-11-12 16:51:24 +0100
committerFlorent Le Coz <louiz@louiz.org>2011-11-12 16:51:24 +0100
commite67e22766f0da948ee9f310d29278371ef9b0563 (patch)
tree736b13f2f7f217ef94bec95404d7fa8a87c16d87 /src
parent75ae1772e49a59b373c26d1c942f25edd473921c (diff)
parent05ef3594894e0bcbe80b98e81c2a2659ea01855f (diff)
downloadpoezio-e67e22766f0da948ee9f310d29278371ef9b0563.tar.gz
poezio-e67e22766f0da948ee9f310d29278371ef9b0563.tar.bz2
poezio-e67e22766f0da948ee9f310d29278371ef9b0563.tar.xz
poezio-e67e22766f0da948ee9f310d29278371ef9b0563.zip
Merge branch 'master' of http://git.louiz.org/poezio
Diffstat (limited to 'src')
-rw-r--r--src/connection.py8
-rw-r--r--src/contact.py81
-rw-r--r--src/core.py159
-rw-r--r--src/data_forms.py2
-rw-r--r--src/events.py14
-rw-r--r--src/plugin.py49
-rw-r--r--src/plugin_manager.py68
-rw-r--r--src/roster.py2
-rw-r--r--src/tabs.py168
-rw-r--r--src/windows.py64
-rw-r--r--src/xhtml.py4
11 files changed, 439 insertions, 180 deletions
diff --git a/src/connection.py b/src/connection.py
index f407dfe9..0be94097 100644
--- a/src/connection.py
+++ b/src/connection.py
@@ -51,7 +51,7 @@ class Connection(sleekxmpp.ClientXMPP):
self.register_plugin('xep_0085')
if config.get('send_poezio_info', 'true') == 'true':
info = {'name':'poezio',
- 'version':'0.7.2-dev'}
+ 'version':'0.7.5-dev'}
if config.get('send_os_info', 'true') == 'true':
info['os'] = common.get_os_info()
self.register_plugin('xep_0092', pconfig=info)
@@ -63,9 +63,11 @@ class Connection(sleekxmpp.ClientXMPP):
# With anon auth.
# (domain, config.get('port', 5222))
custom_host = config.get('custom_host', '')
- custom_port = config.get('custom_port', -1)
- if custom_host and custom_port != -1:
+ custom_port = config.get('custom_port', 5222)
+ if custom_host:
res = self.connect((custom_host, custom_port), reattempt=False)
+ elif custom_port != 5222:
+ res = self.connect((self.boundjid.host, custom_port), reattempt=False)
else:
res = self.connect(reattempt=False)
if not res:
diff --git a/src/contact.py b/src/contact.py
index 99c24a32..1874ff59 100644
--- a/src/contact.py
+++ b/src/contact.py
@@ -26,30 +26,37 @@ class Resource(object):
self._presence = 'unavailable'
self._priority = 0
- def get_jid(self):
+ @property
+ def jid(self):
return self._jid
def __repr__(self):
return '%s' % self._jid
- def set_priority(self, priority):
- assert isinstance(priority, int)
- self._priority = priority
-
- def get_priority(self):
+ @property
+ def priority(self):
return self._priority
- def set_presence(self, pres):
- self._presence = pres
+ @priority.setter
+ def priority(self, value):
+ assert isinstance(value, int)
+ self._priority = value
- def get_presence(self):
+ @property
+ def presence(self):
return self._presence
- def get_status(self):
+ @presence.setter
+ def presence(self, value):
+ self._presence = value
+
+ @property
+ def status(self):
return self._status
- def set_status(self, s):
- self._status = s
+ @status.setter
+ def status(self, value):
+ self._status = value
class Contact(object):
"""
@@ -66,16 +73,14 @@ class Contact(object):
self._ask = None
self._groups = [] # a list of groups the contact is in
- def get_groups(self):
- """
- Return the groups the contact is in
- """
+ @property
+ def groups(self):
+ """Groups the contact is in"""
return self._groups
- def get_bare_jid(self):
- """
- Just get the bare_jid or the contact
- """
+ @property
+ def bare_jid(self):
+ """The bare_jid or the contact"""
return self._jid
def get_highest_priority_resource(self):
@@ -84,7 +89,7 @@ class Contact(object):
"""
ret = None
for resource in self._resources:
- if not ret or ret.get_priority() < resource.get_priority():
+ if not ret or ret.priority < resource.priority:
ret = resource
return ret
@@ -94,7 +99,7 @@ class Contact(object):
(the first, or any subsequent one)
"""
def f(o):
- return o.get_priority()
+ return o.priority
self._resources.append(resource)
self._resources = sorted(self._resources, key=f, reverse=True)
@@ -109,7 +114,7 @@ class Contact(object):
Like 'remove_resource' but just by knowing the full jid
"""
for resource in self._resources:
- if resource.get_jid().full == fulljid:
+ if resource.jid == fulljid:
self._resources.remove(resource)
return
assert False
@@ -119,7 +124,7 @@ class Contact(object):
Return the resource with the given fulljid
"""
for resource in self._resources:
- if resource.get_jid().full == fulljid:
+ if resource.jid.full == fulljid:
return resource
return None
@@ -129,24 +134,30 @@ class Contact(object):
"""
self._folded = not self._folded
- def set_name(self, name):
- self._display_name = name
-
- def get_name(self):
+ @property
+ def name(self):
return self._display_name
- def set_ask(self, ask):
- self._ask = ask
+ @name.setter
+ def name(self, value):
+ self._display_name = value
- def get_ask(self):
+ @property
+ def ask(self):
return self._ask
- def set_subscription(self, sub):
- self._subscription = sub
+ @ask.setter
+ def ask(self, value):
+ self._ask = value
- def get_subscription(self):
+ @property
+ def subscription(self):
return self._subscription
+ @subscription.setter
+ def subscription(self, value):
+ self._subscription = value
+
def get_nb_resources(self):
"""
Get the number of connected resources
@@ -157,7 +168,7 @@ class Contact(object):
"""
Return all resources, sorted by priority
"""
- compare_resources = lambda x: x.get_priority()
+ compare_resources = lambda x: x.priority
return sorted(self._resources, key=compare_resources)
def __repr__(self):
diff --git a/src/core.py b/src/core.py
index e2ba8ce1..1f93324d 100644
--- a/src/core.py
+++ b/src/core.py
@@ -15,6 +15,7 @@ import threading
import traceback
from datetime import datetime
+from xml.etree import cElementTree as ET
from inspect import getargspec
@@ -25,6 +26,7 @@ import singleton
import collections
from sleekxmpp.xmlstream.stanzabase import JID
+from sleekxmpp.xmlstream.stanzabase import StanzaBase
log = logging.getLogger(__name__)
@@ -131,6 +133,8 @@ class Core(object):
'load': (self.command_load, _('Usage: /load <plugin>\nLoad: Load the specified plugin'), self.plugin_manager.completion_load),
'unload': (self.command_unload, _('Usage: /unload <plugin>\nUnload: Unload the specified plugin'), self.plugin_manager.completion_unload),
'plugins': (self.command_plugins, _('Usage: /plugins\nPlugins: Show the plugins in use.'), None),
+ 'presence': (self.command_presence, _('Usage: /presence <JID> [type] [status]\nPresence: Send a directed presence to <JID> and using [type] and [status] if provided.'), None),
+ 'rawxml': (self.command_rawxml, _('Usage: /rawxml\nRawXML: Send a custom xml stanza.'), None),
}
self.key_func = {
@@ -177,8 +181,6 @@ class Core(object):
self.connected_events = {}
- self.autoload_plugins()
-
def autoload_plugins(self):
plugins = config.get('plugins_autoload', '')
for plugin in plugins.split():
@@ -325,6 +327,7 @@ class Core(object):
tab = self.get_tab_of_conversation_with_jid(message['from'], False)
if not tab:
return False
+ self.events.trigger('normal_chatstate', message, tab)
tab.chatstate = state
if tab == self.current_tab():
tab.refresh_info_header()
@@ -335,6 +338,7 @@ class Core(object):
tab = self.get_tab_by_name(message['from'].full, tabs.PrivateTab)
if not tab:
return
+ self.events.trigger('private_chatstate', message, tab)
tab.chatstate = state
if tab == self.current_tab():
tab.refresh_info_header()
@@ -346,6 +350,7 @@ class Core(object):
room_from = message.getMucroom()
tab = self.get_tab_by_name(room_from, tabs.MucTab)
if tab and tab.get_user_by_name(nick):
+ self.events.trigger('muc_chatstate', message, tab)
tab.get_user_by_name(nick).chatstate = state
if tab == self.current_tab():
tab.user_win.refresh(tab.users)
@@ -372,13 +377,13 @@ class Core(object):
return
# If a resource got offline, display the message in the conversation with this
# precise resource.
- self.add_information_message_to_conversation_tab(jid.full, '\x195}%s is \x191}offline' % (resource.get_jid().full))
+ self.add_information_message_to_conversation_tab(jid.full, '\x195}%s is \x191}offline' % (resource.jid.full))
contact.remove_resource(resource)
# Display the message in the conversation with the bare JID only if that was
# the only resource online (i.e. now the contact is completely disconnected)
if not contact.get_highest_priority_resource(): # No resource left: that was the last one
self.add_information_message_to_conversation_tab(jid.bare, '\x195}%s is \x191}offline' % (jid.bare))
- self.information('\x193}%s \x195}is \x191}offline' % (resource.get_jid().bare), "Roster")
+ self.information('\x193}%s \x195}is \x191}offline' % (resource.jid.bare), "Roster")
if isinstance(self.current_tab(), tabs.RosterInfoTab):
self.refresh_window()
@@ -392,21 +397,22 @@ class Core(object):
resource = contact.get_resource_by_fulljid(jid.full)
assert not resource
resource = Resource(jid.full)
+ self.events.trigger('normal_presence', presence, resource)
status = presence['type']
status_message = presence['status']
priority = presence.getPriority() or 0
- resource.set_status(status_message)
- resource.set_presence(status)
- resource.set_priority(priority)
+ resource.status = status_message
+ resource.presence = status
+ resource.priority = priority
self.add_information_message_to_conversation_tab(jid.full, '\x195}%s is \x194}online' % (jid.full))
if not contact.get_highest_priority_resource():
# No connected resource yet: the user's just connecting
if time.time() - self.connection_time > 12:
# We do not display messages if we recently logged in
if status_message:
- self.information("\x193}%s \x195}is \x194}online\x195} (\x19o%s\x195})" % (resource.get_jid().bare, status_message), "Roster")
+ self.information("\x193}%s \x195}is \x194}online\x195} (\x19o%s\x195})" % (resource.jid.bare, status_message), "Roster")
else:
- self.information("\x193}%s \x195}is \x194}online\x195}" % resource.get_jid().bare, "Roster")
+ self.information("\x193}%s \x195}is \x194}online\x195}" % resource.jid.bare, "Roster")
self.add_information_message_to_conversation_tab(jid.bare, '\x195}%s is \x194}online' % (jid.bare))
contact.add_resource(resource)
if isinstance(self.current_tab(), tabs.RosterInfoTab):
@@ -453,13 +459,16 @@ class Core(object):
Called when we are connected and authenticated
"""
self.connection_time = time.time()
+ self.autoload_plugins()
self.information(_("Authentication success."))
self.information(_("Your JID is %s") % self.xmpp.boundjid.full)
if not self.xmpp.anon:
# request the roster
self.xmpp.getRoster()
# send initial presence
- self.xmpp.makePresence().send()
+ pres = self.xmpp.make_presence()
+ self.events.trigger('send_normal_presence', pres)
+ pres.send()
rooms = config.get('rooms', '')
if rooms == '' or not isinstance(rooms, str):
return
@@ -487,6 +496,7 @@ class Core(object):
from_room = presence['from'].bare
tab = self.get_tab_by_name(from_room, tabs.MucTab)
if tab:
+ self.events.trigger('muc_presence', presence, tab)
tab.handle_presence(presence)
def rename_private_tabs(self, room_name, old_nick, new_nick):
@@ -560,13 +570,14 @@ class Core(object):
jid = message['from']
nick_from = jid.resource
room_from = jid.bare
+ body = xhtml.get_body_from_message_stanza(message)
tab = self.get_tab_by_name(jid.full, tabs.PrivateTab) # get the tab with the private conversation
if not tab: # It's the first message we receive: create the tab
- tab = self.open_private_window(room_from, nick_from, False)
+ if body:
+ tab = self.open_private_window(room_from, nick_from, False)
if not tab:
return
- self.events.trigger('private_msg', message)
- body = xhtml.get_body_from_message_stanza(message)
+ self.events.trigger('private_msg', message, tab)
if not body:
return
tab.add_message(body, time=None, nickname=nick_from,
@@ -604,8 +615,8 @@ class Core(object):
conversation = self.get_tab_by_name(jid.bare, tabs.ConversationTab)
if not conversation:
if create:
- # We create the conversation with the bare Jid if nothing was found
- conversation = self.open_conversation_window(jid.bare, False)
+ # We create the conversation with the full Jid if nothing was found
+ conversation = self.open_conversation_window(jid.full, False)
else:
conversation = None
return conversation
@@ -615,15 +626,17 @@ class Core(object):
When receiving "normal" messages (from someone in our roster)
"""
jid = message['from']
- self.events.trigger('conversation_msg', message)
body = xhtml.get_body_from_message_stanza(message)
+ conversation = self.get_tab_of_conversation_with_jid(jid, create=False)
if not body:
if message['type'] == 'error':
self.information(self.get_error_message_from_error_stanza(message), 'Error')
return
conversation = self.get_tab_of_conversation_with_jid(jid, create=True)
+ self.events.trigger('conversation_msg', message, conversation)
+ body = xhtml.get_body_from_message_stanza(message)
if roster.get_contact_by_jid(jid.bare):
- remote_nick = roster.get_contact_by_jid(jid.bare).get_name() or jid.user
+ remote_nick = roster.get_contact_by_jid(jid.bare).name or jid.user
else:
remote_nick = jid.user
conversation._text_buffer.add_message(body, nickname=remote_nick, nick_color=get_theme().COLOR_REMOTE_USER)
@@ -645,18 +658,24 @@ class Core(object):
jid = presence['from']
contact = roster.get_contact_by_jid(jid.bare)
if not contact:
- return
- resource = contact.get_resource_by_fulljid(jid.full)
+ resource = None
+ else:
+ resource = contact.get_resource_by_fulljid(jid.full)
+ self.events.trigger('normal_presence', presence, resource)
if not resource:
return
status = presence['type']
status_message = presence['status']
priority = presence.getPriority() or 0
- resource.set_presence(status)
- resource.set_priority(priority)
- resource.set_status(status_message)
+ resource.presence = status
+ resource.priority = priority
+ resource.status = status_message
+ tab = self.get_tab_of_conversation_with_jid(jid, create=False)
if isinstance(self.current_tab(), tabs.RosterInfoTab):
self.refresh_window()
+ elif self.current_tab() == tab:
+ tab.refresh()
+ self.doupdate()
def on_roster_update(self, iq):
"""
@@ -670,19 +689,19 @@ class Core(object):
contact = Contact(jid)
roster.add_contact(contact, jid)
if 'ask' in item.attrib:
- contact.set_ask(item.attrib['ask'])
+ contact.ask = item.attrib['ask']
else:
- contact.set_ask(None)
+ contact.ask = None
if 'name' in item.attrib:
- contact.set_name(item.attrib['name'])
+ contact.name = item.attrib['name']
else:
- contact.set_name(None)
+ contact.name = None
if item.attrib['subscription']:
- contact.set_subscription(item.attrib['subscription'])
+ contact.subscription = item.attrib['subscription']
groups = item.findall('{jabber:iq:roster}group')
roster.edit_groups_of_contact(contact, [group.text for group in groups])
if item.attrib['subscription'] == 'remove':
- roster.remove_contact(contact.get_bare_jid())
+ roster.remove_contact(contact.bare_jid)
if isinstance(self.current_tab(), tabs.RosterInfoTab):
self.refresh_window()
@@ -690,16 +709,30 @@ class Core(object):
"""
Triggered whenever a presence stanza with a type of subscribe, subscribed, unsubscribe, or unsubscribed is received.
"""
+ jid = presence['from'].bare
+ contact = roster.get_contact_by_jid(jid)
if presence['type'] == 'subscribe':
- jid = presence['from'].bare
- contact = roster.get_contact_by_jid(jid)
if not contact:
contact = Contact(jid)
roster.add_contact(contact, jid)
+ log.debug("CONTACT: %s" % contact)
+ if contact.subscription in ('from', 'both'):
+ log.debug('FROM OR BOTH')
+ return
+ elif contact.subscription in ('to'):
+ log.debug('TO')
+ self.xmpp.sendPresence(pto=jid, ptype='subscribed')
+ self.xmpp.sendPresence(pto=jid, ptype='')
+ return
roster.edit_groups_of_contact(contact, [])
- contact.set_ask('asked')
+ contact.ask = 'asked'
self.get_tab_by_number(0).state = 'highlight'
self.information('%s wants to subscribe to your presence'%jid, 'Roster')
+ elif presence['type'] == 'unsubscribed':
+ self.information('%s unsubscribed you from his presence'%jid, 'Roster')
+ elif presence['type'] == 'unsubscribe':
+ self.information('%s unsubscribed from your presence'%jid, 'Roster')
+
if isinstance(self.current_tab(), tabs.RosterInfoTab):
self.refresh_window()
@@ -1056,7 +1089,7 @@ class Core(object):
if tab.get_user_by_name(nick_from) and\
tab.get_user_by_name(nick_from) in tab.ignores:
return
- self.events.trigger('muc_msg', message)
+ self.events.trigger('muc_msg', message, tab)
body = xhtml.get_body_from_message_stanza(message)
if body:
date = date if delayed == True else None
@@ -1124,6 +1157,7 @@ class Core(object):
if msg:
pres['status'] = msg
pres['type'] = show
+ self.events.trigger('send_normal_presence', pres)
pres.send()
current = self.current_tab()
if isinstance(current, tabs.MucTab) and current.joined and show in ('away', 'xa'):
@@ -1135,6 +1169,44 @@ class Core(object):
if isinstance(current, tabs.MucTab) and current.joined and show not in ('away', 'xa'):
current.send_chat_state('active')
+ def command_rawxml(self, arg):
+ """"
+ /rawxml <xml stanza>
+ """
+ if not arg:
+ return
+
+ try:
+ StanzaBase(self.xmpp, xml=ET.fromstring(arg)).send()
+ except:
+ import traceback
+ self.information(_('Could not send custom stanza'), 'Error')
+ log.debug(_("Could not send custom stanza:\n") + traceback.format_exc())
+
+ def command_presence(self, arg):
+ """
+ /presence <JID> [type] [status]
+ """
+ args = common.shell_split(arg)
+ if len(args) == 1:
+ jid, type, status = args[0], None, None
+ elif len(args) == 2:
+ jid, type, status = args[0], args[1], None
+ elif len(args) == 3:
+ jid, type, status = args[0], args[1], args[2]
+ else:
+ return
+ if type == 'available':
+ type = None
+ try:
+ pres = self.xmpp.make_presence(pto=jid, ptype=type, pstatus=status)
+ self.events.trigger('send_normal_presence', pres)
+ pres.send()
+ except :
+ import traceback
+ self.information(_('Could not send directed presence'), 'Error')
+ log.debug(_("Could not send directed presence:\n") + traceback.format_exc())
+
def completion_status(self, the_input):
return the_input.auto_completion([status for status in possible_show], ' ')
@@ -1371,7 +1443,12 @@ class Core(object):
else:
tab.own_nick = nick
tab.users = []
- self.enable_private_tabs(room)
+ if tab and tab.joined:
+ self.enable_private_tabs(room)
+ tab.state = "normal"
+ if tab == self.current_tab():
+ tab.refresh()
+ self.doupdate()
def get_bookmark_nickname(self, room_name):
"""
@@ -1532,7 +1609,9 @@ class Core(object):
Displays an informational message in the "Info" buffer
"""
nb_lines = self.information_buffer.add_message(msg, nickname=typ)
- if typ != '' and typ.lower() in config.get('information_buffer_popup_on',
+ if isinstance(self.current_tab(), tabs.RosterInfoTab):
+ self.refresh_window()
+ elif typ != '' and typ.lower() in config.get('information_buffer_popup_on',
'error roster warning help info').split():
popup_time = config.get('popup_time', 4) + (nb_lines - 1) * 2
self.pop_information_win_up(nb_lines, popup_time)
@@ -1585,15 +1664,15 @@ class Core(object):
when enter is pressed on the roster window
"""
if isinstance(roster_row, Contact):
- if not self.get_conversation_by_jid(roster_row.get_bare_jid()):
- self.open_conversation_window(roster_row.get_bare_jid())
+ if not self.get_conversation_by_jid(roster_row.bare_jid):
+ self.open_conversation_window(roster_row.bare_jid)
else:
- self.focus_tab_named(roster_row.get_bare_jid())
+ self.focus_tab_named(roster_row.bare_jid)
if isinstance(roster_row, Resource):
- if not self.get_conversation_by_jid(roster_row.get_jid().full):
- self.open_conversation_window(roster_row.get_jid().full)
+ if not self.get_conversation_by_jid(roster_row.jid.full):
+ self.open_conversation_window(roster_row.jid.full)
else:
- self.focus_tab_named(roster_row.get_jid().full)
+ self.focus_tab_named(roster_row.jid.full)
self.refresh_window()
def remove_timed_event(self, event):
diff --git a/src/data_forms.py b/src/data_forms.py
index 8445d3d2..8f19e41b 100644
--- a/src/data_forms.py
+++ b/src/data_forms.py
@@ -23,6 +23,7 @@ class DataFormsTab(Tab):
A tab contaning various window type, displaying
a form that the user needs to fill.
"""
+ plugin_commands = {}
def __init__(self, form, on_cancel, on_send, kwargs):
Tab.__init__(self)
self._form = form
@@ -41,6 +42,7 @@ class DataFormsTab(Tab):
self.key_func['^G'] = self.on_cancel
self.key_func['^Y'] = self.on_send
self.resize()
+ self.update_commands()
def on_cancel(self):
self._on_cancel(self._form)
diff --git a/src/events.py b/src/events.py
index 22d60ddf..e94acb80 100644
--- a/src/events.py
+++ b/src/events.py
@@ -20,14 +20,22 @@ class EventHandler(object):
"""
def __init__(self):
self.events = {
- # when you are highlighted in a muc tab
'highlight': [],
'muc_say': [],
+ 'muc_say_after': [],
'conversation_say': [],
+ 'conversation_say_after': [],
'private_say': [],
+ 'private_say_after': [],
'conversation_msg': [],
'private_msg': [],
'muc_msg': [],
+ 'normal_chatstate': [],
+ 'muc_chatstate': [],
+ 'private_chatstate': [],
+ 'normal_presence': [],
+ 'muc_presence': [],
+ 'send_normal_presence': [],
}
def add_event_handler(self, name, callback, position=0):
@@ -35,7 +43,7 @@ class EventHandler(object):
Add a callback to a given event.
Note that if that event name doesn’t exist, it just returns False.
If it was successfully added, it returns True
- position: 0 means insert a the beginning, -1 means end
+ position: 0 means insert at the beginning, -1 means end
"""
if name not in self.events:
return False
@@ -49,7 +57,7 @@ class EventHandler(object):
def trigger(self, name, *args, **kwargs):
"""
- Call all the callbacks associated to the given event name
+ Call all the callbacks associated to the given event name.
"""
callbacks = self.events[name]
for callback in callbacks:
diff --git a/src/plugin.py b/src/plugin.py
index 80bc4dfc..4dd88697 100644
--- a/src/plugin.py
+++ b/src/plugin.py
@@ -50,9 +50,7 @@ class SafetyMetaclass(type):
class BasePlugin(object, metaclass=SafetyMetaclass):
"""
- Class that all plugins derive from. Any methods beginning with command_
- are interpreted as a command and beginning with on_ are interpreted as
- event handlers
+ Class that all plugins derive from.
"""
def __init__(self, plugin_manager, core, plugins_conf_dir):
@@ -65,28 +63,57 @@ class BasePlugin(object, metaclass=SafetyMetaclass):
self.init()
def init(self):
+ """
+ Method called at the creation of the plugin.
+ Do not overwrite __init__ and use this instead.
+ """
pass
def cleanup(self):
+ """
+ Called when the plugin is unloaded.
+ Overwrite this if you want to erase or save things before the plugin is disabled.
+ """
pass
def unload(self):
self.cleanup()
def add_command(self, name, handler, help, completion=None):
+ """
+ Add a global command.
+ You cannot overwrite the existing commands.
+ """
return self.plugin_manager.add_command(self.__module__, name, handler, help, completion)
def del_command(self, name):
+ """
+ Remove a global command.
+ This only works if the command was added by the plugin
+ """
return self.plugin_manager.del_command(self.__module__, name)
- def add_event_handler(self, event_name, handler):
- return self.plugin_manager.add_event_handler(self.__module__, event_name, handler)
+ def add_tab_command(self, tab_type, name, handler, help, completion=None):
+ """
+ Add a command only for a type of tab.
+ """
+ return self.plugin_manager.add_tab_command(self.__module__, tab_type, name, handler, help, completion)
+
+ def del_tab_command(self, tab_type, name):
+ """
+ Delete a command added through add_tab_command.
+ """
+ return self.plugin_manager.del_tab_command(self.__module__, tab_type, name)
+
+ def add_event_handler(self, event_name, handler, position=0):
+ """
+ Add an event handler to the event event_name.
+ An optional position in the event handler list can be provided.
+ """
+ return self.plugin_manager.add_event_handler(self.__module__, event_name, handler, position)
def del_event_handler(self, event_name, handler):
+ """
+ Remove 'handler' from the event list for 'event_name'.
+ """
return self.plugin_manager.del_event_handler(self.__module__, event_name, handler)
-
- def add_poezio_event_handler(self, event_name, handler, position=0):
- return self.plugin_manager.add_poezio_event_handler(self.__module__, event_name, handler, position)
-
- def del_poezio_event_handler(self, event_name, handler):
- return self.plugin_manager.del_poezio_event_handler(self.__module__, event_name, handler)
diff --git a/src/plugin_manager.py b/src/plugin_manager.py
index bdf94a5b..fe4d2b7e 100644
--- a/src/plugin_manager.py
+++ b/src/plugin_manager.py
@@ -1,6 +1,7 @@
import imp
import os
import sys
+import tabs
from config import config
from gettext import gettext as _
@@ -34,9 +35,9 @@ class PluginManager(object):
self.plugins = {} # module name -> plugin object
self.commands = {} # module name -> dict of commands loaded for the module
self.event_handlers = {} # module name -> list of event_name/handler pairs loaded for the module
- self.poezio_event_handlers = {}
+ self.tab_commands = {} #module name -> dict of tab types; tab type -> commands loaded by the module
- def load(self, name):
+ def load(self, name, notify=True):
if name in self.plugins:
self.unload(name)
@@ -60,25 +61,31 @@ class PluginManager(object):
self.modules[name] = module
self.commands[name] = {}
+ self.tab_commands[name] = {}
self.event_handlers[name] = []
- self.poezio_event_handlers[name] = []
self.plugins[name] = module.Plugin(self, self.core, plugins_conf_dir)
+ if notify:
+ self.core.information('Plugin %s loaded' % name, 'Info')
- def unload(self, name):
+ def unload(self, name, notify=True):
if name in self.plugins:
try:
for command in self.commands[name].keys():
del self.core.commands[command]
+ for tab in list(self.tab_commands[name].keys()):
+ for command in self.tab_commands[name][tab]:
+ self.del_tab_command(name, getattr(tabs, tab), command[0])
+ del self.tab_commands[name][tab]
for event_name, handler in self.event_handlers[name]:
- self.core.xmpp.del_event_handler(event_name, handler)
- for handler in self.poezio_event_handlers[name]:
- self.core.events.del_event_handler(None, handler)
+ self.del_event_handler(name, event_name, handler)
self.plugins[name].unload()
del self.plugins[name]
del self.commands[name]
+ del self.tab_commands[name]
del self.event_handlers[name]
- del self.poezio_event_handlers[name]
+ if notify:
+ self.core.information('Plugin %s unloaded' % name, 'Info')
except Exception as e:
import traceback
self.core.information(_("Could not unload plugin (may not be safe to try again): ") + traceback.format_exc())
@@ -89,6 +96,29 @@ class PluginManager(object):
if name in self.core.commands:
del self.core.commands[name]
+ def add_tab_command(self, module_name, tab_type, name, handler, help, completion=None):
+ commands = self.tab_commands[module_name]
+ t = tab_type.__name__
+ if not t in commands:
+ commands[t] = []
+ commands[t].append((name, handler, help, completion))
+ for tab in self.core.tabs:
+ if isinstance(tab, tab_type):
+ tab.add_plugin_command(name, handler, help, completion)
+
+ def del_tab_command(self, module_name, tab_type, name):
+ commands = self.tab_commands[module_name]
+ t = tab_type.__name__
+ if not t in commands:
+ return
+ for command in commands[t]:
+ if command[0] == name:
+ commands[t].remove(command)
+ del tab_type.plugin_commands[name]
+ for tab in self.core.tabs:
+ if isinstance(tab, tab_type) and name in tab.commands:
+ del tab.commands[name]
+
def add_command(self, module_name, name, handler, help, completion=None):
if name in self.core.commands:
raise Exception(_("Command '%s' already exists") % (name,))
@@ -97,26 +127,22 @@ class PluginManager(object):
commands[name] = (handler, help, completion)
self.core.commands[name] = (handler, help, completion)
- def add_event_handler(self, module_name, event_name, handler):
+ def add_event_handler(self, module_name, event_name, handler, position=0):
eh = self.event_handlers[module_name]
eh.append((event_name, handler))
- self.core.xmpp.add_event_handler(event_name, handler)
+ if event_name in self.core.events.events:
+ self.core.events.add_event_handler(event_name, handler, position)
+ else:
+ self.core.xmpp.add_event_handler(event_name, handler)
def del_event_handler(self, module_name, event_name, handler):
- self.core.xmpp.del_event_handler(event_name, handler)
+ if event_name in self.core.events.events:
+ self.core.events.del_event_handler(None, handler)
+ else:
+ self.core.xmpp.del_event_handler(event_name, handler)
eh = self.event_handlers[module_name]
eh = list(filter(lambda e : e != (event_name, handler), eh))
- def add_poezio_event_handler(self, module_name, event_name, handler, position):
- eh = self.poezio_event_handlers[module_name]
- eh.append(handler)
- self.core.events.add_event_handler(event_name, handler, position)
-
- def del_poezio_event_handler(self, module_name, event_name, handler):
- self.core.events.del_event_handler(None, handler)
- eh = self.poezio_event_handlers[module_name]
- eh = list(filter(lambda e : e != handler, eh))
-
def completion_load(self, the_input):
"""
completion function that completes the name of the plugins, from
diff --git a/src/roster.py b/src/roster.py
index df84d4d8..5f214bb0 100644
--- a/src/roster.py
+++ b/src/roster.py
@@ -234,7 +234,7 @@ class RosterGroup(object):
def compare_contact(a):
if not a.get_highest_priority_resource():
return 0
- show = a.get_highest_priority_resource().get_presence()
+ show = a.get_highest_priority_resource()
if show not in PRESENCE_PRIORITY:
return 5
return PRESENCE_PRIORITY[show]
diff --git a/src/tabs.py b/src/tabs.py
index 6f100741..472a15fa 100644
--- a/src/tabs.py
+++ b/src/tabs.py
@@ -46,6 +46,7 @@ from os import getenv, path
from logger import logger
from datetime import datetime, timedelta
+from xml.etree import cElementTree as ET
SHOW_NAME = {
'dnd': _('busy'),
@@ -91,6 +92,7 @@ class Tab(object):
# and use them in on_input
self.commands = {} # and their own commands
+
@property
def core(self):
if not Tab.tab_core:
@@ -220,6 +222,18 @@ class Tab(object):
def on_input(self, key):
pass
+ def add_plugin_command(self, name, handler, help, completion=None):
+ if name in self.plugin_commands or name in self.commands:
+ return
+ self.plugin_commands[name] = (handler, help, completion)
+ self.commands[name] = (handler, help, completion)
+ self.update_commands()
+
+ def update_commands(self):
+ for c in self.plugin_commands:
+ if not c in self.commands:
+ self.commands[name] = self.plugin_commands[c]
+
def on_lose_focus(self):
"""
called when this tab loses the focus.
@@ -275,6 +289,7 @@ class ChatTab(Tab):
Also, ^M is already bound to on_enter
And also, add the /say command
"""
+ plugin_commands = {}
def __init__(self):
Tab.__init__(self)
self._text_buffer = TextBuffer()
@@ -294,7 +309,9 @@ class ChatTab(Tab):
self.commands['say'] = (self.command_say,
_("""Usage: /say <message>\nSay: Just send the message.
Useful if you want your message to begin with a '/'."""), None)
+ self.commands['xhtml'] = (self.command_xhtml, _("Usage: /xhtml <custom xhtml>\nXHTML: Send custom XHTML."), None)
self.chat_state = None
+ self.update_commands()
def last_words_completion(self):
"""
@@ -324,6 +341,29 @@ class ChatTab(Tab):
self.command_say(xhtml.convert_simple_to_full_colors(txt))
self.cancel_paused_delay()
+ def command_xhtml(self, arg):
+ """"
+ /xhtml <custom xhtml>
+ """
+ if not arg:
+ return
+ try:
+ body = xhtml.clean_text(xhtml.xhtml_to_poezio_colors(arg))
+ ET.fromstring(arg)
+ except:
+ self.core.information('Could not send custom xhtml', 'Error')
+ return
+
+ msg = self.core.xmpp.make_message(self.get_name())
+ msg['body'] = body
+ msg['xhtml_im'] = arg
+ if isinstance(self, MucTab):
+ msg['type'] = 'groupchat'
+ if isinstance(self, ConversationTab):
+ self.core.add_message_to_text_buffer(self._text_buffer, body, None, self.core.own_nick)
+ self.refresh()
+ msg.send()
+
def send_chat_state(self, state, always_send=False):
"""
Send an empty chatstate message
@@ -402,6 +442,7 @@ class MucTab(ChatTab):
It contains an userlist, an input, a topic, an information and a chat zone
"""
message_type = 'groupchat'
+ plugin_commands = {}
def __init__(self, jid, nick):
ChatTab.__init__(self)
self.own_nick = nick
@@ -445,6 +486,7 @@ class MucTab(ChatTab):
self.commands['clear'] = (self.command_clear,
_('Usage: /clear\nClear: Clear the current buffer.'), None)
self.resize()
+ self.update_commands()
def scroll_user_list_up(self):
self.user_win.scroll_up()
@@ -722,13 +764,14 @@ class MucTab(ChatTab):
# trigger the event BEFORE looking for colors.
# This lets a plugin insert \x19xxx} colors, that will
# be converted in xhtml.
- self.core.events.trigger('muc_say', msg)
+ self.core.events.trigger('muc_say', msg, self)
if msg['body'].find('\x19') != -1:
msg['xhtml_im'] = xhtml.poezio_colors_to_html(msg['body'])
msg['body'] = xhtml.clean_text(msg['body'])
if config.get('send_chat_states', 'true') == 'true' and self.remote_wants_chatstates is not False:
msg['chat_state'] = needed
self.cancel_paused_delay()
+ self.core.events.trigger('muc_say_after', msg, self)
msg.send()
self.chat_state = needed
@@ -1027,7 +1070,6 @@ class MucTab(ChatTab):
self.core.doupdate()
hide_exit_join = config.get('hide_exit_join', -1) if config.get('hide_exit_join', -1) >= -1 else -1
if hide_exit_join == -1 or user.has_talked_since(hide_exit_join):
- log.debug("\n\nALLO: USERCOLOR: %s\n\n" % user.color.__repr__())
color = user.color[0] if config.get('display_user_color_in_join_part', '') == 'true' else 3
if not jid.full:
leave_msg = _('\x191}%(spec)s \x19%(color)d}%(nick)s\x195} has left the room') % {'nick':from_nick, 'color':color, 'spec':get_theme().CHAR_QUIT}
@@ -1186,6 +1228,7 @@ class PrivateTab(ChatTab):
The tab containg a private conversation (someone from a MUC)
"""
message_type = 'chat'
+ plugin_commands = {}
def __init__(self, name, nick):
ChatTab.__init__(self)
self.own_nick = nick
@@ -1204,6 +1247,7 @@ class PrivateTab(ChatTab):
self.resize()
self.parent_muc = self.core.get_tab_by_name(JID(name).bare, MucTab)
self.on = True
+ self.update_commands()
def completion(self):
self.complete_commands(self.input)
@@ -1217,7 +1261,7 @@ class PrivateTab(ChatTab):
# trigger the event BEFORE looking for colors.
# This lets a plugin insert \x19xxx} colors, that will
# be converted in xhtml.
- self.core.events.trigger('private_say', msg)
+ self.core.events.trigger('private_say', msg, self)
self.core.add_message_to_text_buffer(self._text_buffer, msg['body'], None, self.core.own_nick or self.own_nick)
if msg['body'].find('\x19') != -1:
msg['xhtml_im'] = xhtml.poezio_colors_to_html(msg['body'])
@@ -1225,6 +1269,7 @@ class PrivateTab(ChatTab):
if config.get('send_chat_states', 'true') == 'true' and self.remote_wants_chatstates is not False:
needed = 'inactive' if self.core.status.show in ('xa', 'away') else 'active'
msg['chat_state'] = needed
+ self.core.events.trigger('private_say_after', msg, self)
msg.send()
self.cancel_paused_delay()
self.text_win.refresh()
@@ -1381,6 +1426,7 @@ class RosterInfoTab(Tab):
"""
A tab, splitted in two, containing the roster and infos
"""
+ plugin_commands = {}
def __init__(self):
Tab.__init__(self)
self.name = "Roster"
@@ -1415,6 +1461,7 @@ class RosterInfoTab(Tab):
self.commands['import'] = (self.command_import, _("Usage: /import [/path/to/file]\nImport: Import your contacts from /path/to/file if specified, or $HOME/poezio_contacts if not."), None)
self.commands['clear_infos'] = (self.command_clear_infos, _("Usage: /clear_infos\nClear Infos: Use this command to clear the info buffer."), None)
self.resize()
+ self.update_commands()
def resize(self):
if not self.visible:
@@ -1450,8 +1497,8 @@ class RosterInfoTab(Tab):
args = args.split()
if not args:
item = self.roster_win.selected_row
- if isinstance(item, Contact) and item.get_ask() == 'asked':
- jid = item.get_bare_jid()
+ if isinstance(item, Contact) and item.ask == 'asked':
+ jid = item.bare_jid
else:
self.core.information('No subscription to deny')
return
@@ -1471,7 +1518,6 @@ class RosterInfoTab(Tab):
self.core.information(_('No JID specified'), 'Error')
return
self.core.xmpp.sendPresence(pto=jid, ptype='subscribe')
- self.core.xmpp.sendPresence(pto=jid, ptype='subscribed')
def command_name(self, args):
"""
@@ -1488,10 +1534,10 @@ class RosterInfoTab(Tab):
self.core.information(_('No such JID in roster'), 'Error')
return
- groups = set(contact.get_groups())
- subscription = contact.get_subscription()
+ groups = set(contact.groups)
+ subscription = contact.subscription
if self.core.xmpp.update_roster(jid, name=name, groups=groups, subscription=subscription):
- contact.set_name(name)
+ contact.name = name
def command_groupadd(self, args):
"""
@@ -1508,7 +1554,7 @@ class RosterInfoTab(Tab):
self.core.information(_('No such JID in roster'), 'Error')
return
- new_groups = set(contact.get_groups())
+ new_groups = set(contact.groups)
if group in new_groups:
self.core.information(_('JID already in group'), 'Error')
return
@@ -1519,8 +1565,8 @@ class RosterInfoTab(Tab):
except KeyError:
pass
- name = contact.get_name()
- subscription = contact.get_subscription()
+ name = contact.name
+ subscription = contact.subscription
if self.core.xmpp.update_roster(jid, name=name, groups=new_groups, subscription=subscription):
roster.edit_groups_of_contact(contact, new_groups)
@@ -1539,7 +1585,7 @@ class RosterInfoTab(Tab):
self.core.information(_('No such JID in roster'), 'Error')
return
- new_groups = set(contact.get_groups())
+ new_groups = set(contact.groups)
try:
new_groups.remove('none')
except KeyError:
@@ -1549,8 +1595,8 @@ class RosterInfoTab(Tab):
return
new_groups.remove(group)
- name = contact.get_name()
- subscription = contact.get_subscription()
+ name = contact.name
+ subscription = contact.subscription
if self.core.xmpp.update_roster(jid, name=name, groups=new_groups, subscription=subscription):
roster.edit_groups_of_contact(contact, new_groups)
@@ -1564,13 +1610,17 @@ class RosterInfoTab(Tab):
else:
item = self.roster_win.selected_row
if isinstance(item, Contact):
- jid = item.get_bare_jid()
+ jid = item.bare_jid
else:
self.core.information('No roster item to remove')
return
+ self.core.xmpp.sendPresence(pto=jid, ptype='unavailable')
self.core.xmpp.sendPresence(pto=jid, ptype='unsubscribe')
self.core.xmpp.sendPresence(pto=jid, ptype='unsubscribed')
- self.core.xmpp.del_roster_item(jid=jid)
+ try:
+ self.core.xmpp.del_roster_item(jid=jid)
+ except:
+ pass
def command_import(self, arg):
"""
@@ -1625,7 +1675,7 @@ class RosterInfoTab(Tab):
"""
From with any JID presence in the roster
"""
- jids = [contact.get_bare_jid() for contact in roster.get_contacts()]
+ jids = [contact.bare_jid for contact in roster.get_contacts()]
return the_input.auto_completion(jids, '')
def completion_name(self, the_input):
@@ -1635,7 +1685,7 @@ class RosterInfoTab(Tab):
n += 1
if n == 2:
- jids = [contact.get_bare_jid() for contact in roster.get_contacts()]
+ jids = [contact.bare_jid for contact in roster.get_contacts()]
return the_input.auto_completion(jids, '')
return False
@@ -1646,7 +1696,7 @@ class RosterInfoTab(Tab):
n += 1
if n == 2:
- jids = [contact.get_bare_jid() for contact in roster.get_contacts()]
+ jids = [contact.bare_jid for contact in roster.get_contacts()]
return the_input.auto_completion(jids, '')
elif n == 3:
groups = [group.name for group in roster.get_groups() if group.name != 'none']
@@ -1661,13 +1711,13 @@ class RosterInfoTab(Tab):
n += 1
if n == 2:
- jids = [contact.get_bare_jid() for contact in roster.get_contacts()]
+ jids = [contact.bare_jid for contact in roster.get_contacts()]
return the_input.auto_completion(jids, '')
elif n == 3:
contact = roster.get_contact_by_jid(args[1])
if not contact:
return False
- groups = list(contact.get_groups())
+ groups = list(contact.groups)
try:
groups.remove('none')
except ValueError:
@@ -1680,8 +1730,8 @@ class RosterInfoTab(Tab):
Complete the first argument from the list of the
contact with ask=='subscribe'
"""
- jids = [contact.get_bare_jid() for contact in roster.get_contacts()\
- if contact.get_ask() == 'asked']
+ jids = [contact.bare_jid for contact in roster.get_contacts()\
+ if contact.ask == 'asked']
return the_input.auto_completion(jids, '')
def command_accept(self, args):
@@ -1691,14 +1741,20 @@ class RosterInfoTab(Tab):
args = args.split()
if not args:
item = self.roster_win.selected_row
- if isinstance(item, Contact) and item.get_ask() == 'asked':
- jid = item.get_bare_jid()
+ if isinstance(item, Contact) and item.ask == 'asked':
+ jid = item.bare_jid
else:
self.core.information('No subscription to accept')
return
else:
jid = args[0]
self.core.xmpp.sendPresence(pto=jid, ptype='subscribed')
+ self.core.xmpp.sendPresence(pto=jid, ptype='')
+ contact = roster.get_contact_by_jid(jid)
+ if not contact:
+ return
+ if contact.subscription in ('to', 'none'):
+ self.core.xmpp.sendPresence(pto=jid, ptype='subscribe')
def refresh(self):
if self.need_resize:
@@ -1864,6 +1920,8 @@ class ConversationTab(ChatTab):
"""
The tab containg a normal conversation (not from a MUC)
"""
+ plugin_commands = {}
+ additional_informations = {}
message_type = 'chat'
def __init__(self, jid):
ChatTab.__init__(self)
@@ -1882,6 +1940,18 @@ class ConversationTab(ChatTab):
self.commands['version'] = (self.command_version, _('Usage: /version\nVersion: Get the software version of the current interlocutor (usually its XMPP client and Operating System).'), None)
self.commands['info'] = (self.command_info, _('Usage: /info\nInfo: Get the status of the contact.'), None)
self.resize()
+ self.update_commands()
+
+ @staticmethod
+ def add_information_element(plugin_name, callback):
+ """
+ Lets a plugin add its own information to the ConversationInfoWin
+ """
+ ConversationTab.additional_informations[plugin_name] = callback
+
+ @staticmethod
+ def remove_information_element(plugin_name):
+ del ConversationTab.additional_informations[plugin_name]
def completion(self):
self.complete_commands(self.input)
@@ -1894,7 +1964,7 @@ class ConversationTab(ChatTab):
# and before displaying the message in the window
# This lets a plugin insert \x19xxx} colors, that will
# be converted in xhtml.
- self.core.events.trigger('conversation_say', msg)
+ self.core.events.trigger('conversation_say', msg, self)
self.core.add_message_to_text_buffer(self._text_buffer, msg['body'], None, self.core.own_nick)
if msg['body'].find('\x19') != -1:
msg['xhtml_im'] = xhtml.poezio_colors_to_html(msg['body'])
@@ -1902,6 +1972,7 @@ class ConversationTab(ChatTab):
if config.get('send_chat_states', 'true') == 'true' and self.remote_wants_chatstates is not False:
needed = 'inactive' if self.core.status.show in ('xa', 'away') else 'active'
msg['chat_state'] = needed
+ self.core.events.trigger('conversation_say_after', msg, self)
msg.send()
logger.log_message(JID(self.get_name()).bare, self.core.own_nick, line)
self.cancel_paused_delay()
@@ -1916,7 +1987,7 @@ class ConversationTab(ChatTab):
else:
resource = contact.get_highest_priority_resource()
if resource:
- self._text_buffer.add_message("\x195}Status: %s\x193}" %resource.get_status(), None, None, None, None, None)
+ self._text_buffer.add_message("\x195}Status: %s\x193}" %resource.status, None, None, None, None, None)
self.refresh()
self.core.doupdate()
@@ -1954,13 +2025,13 @@ class ConversationTab(ChatTab):
log.debug(' TAB Refresh: %s'%self.__class__.__name__)
self.text_win.refresh()
self.upper_bar.refresh(self.get_name(), roster.get_contact_by_jid(self.get_name()))
- self.info_header.refresh(self.get_name(), roster.get_contact_by_jid(self.get_name()), self.text_win, self.chatstate)
+ self.info_header.refresh(self.get_name(), roster.get_contact_by_jid(self.get_name()), self.text_win, self.chatstate, ConversationTab.additional_informations)
self.info_win.refresh()
self.tab_win.refresh()
self.input.refresh()
def refresh_info_header(self):
- self.info_header.refresh(self.get_name(), roster.get_contact_by_jid(self.get_name()), self.text_win, self.chatstate)
+ self.info_header.refresh(self.get_name(), roster.get_contact_by_jid(self.get_name()), self.text_win, self.chatstate, ConversationTab.additional_informations)
self.input.refresh()
def get_name(self):
@@ -1976,17 +2047,38 @@ class ConversationTab(ChatTab):
return False
def on_lose_focus(self):
+ contact = roster.get_contact_by_jid(self.get_name())
+ jid = JID(self.get_name())
+ if contact:
+ if jid.resource:
+ resource = contact.get_resource_by_fulljid(jid.full)
+ else:
+ resource = contact.get_highest_priority_resource()
+ else:
+ resource = None
self.state = 'normal'
self.text_win.remove_line_separator()
self.text_win.add_line_separator()
- if config.get('send_chat_states', 'true') == 'true' and not self.input.get_text() or not self.input.get_text().startswith('//'):
- self.send_chat_state('inactive')
+ if config.get('send_chat_states', 'true') == 'true' and (not self.input.get_text() or not self.input.get_text().startswith('//')):
+ if resource:
+ self.send_chat_state('inactive')
def on_gain_focus(self):
+ contact = roster.get_contact_by_jid(self.get_name())
+ jid = JID(self.get_name())
+ if contact:
+ if jid.resource:
+ resource = contact.get_resource_by_fulljid(jid.full)
+ else:
+ resource = contact.get_highest_priority_resource()
+ else:
+ resource = None
+
self.state = 'current'
curses.curs_set(1)
- if config.get('send_chat_states', 'true') == 'true' and not self.input.get_text() or not self.input.get_text().startswith('//'):
- self.send_chat_state('active')
+ if config.get('send_chat_states', 'true') == 'true' and (not self.input.get_text() or not self.input.get_text().startswith('//')):
+ if resource:
+ self.send_chat_state('active')
def on_scroll_up(self):
self.text_win.scroll_up(self.text_win.height-1)
@@ -2016,6 +2108,7 @@ class MucListTab(Tab):
A tab listing rooms from a specific server, displaying various information,
scrollable, and letting the user join them, etc
"""
+ plugin_commands = {}
def __init__(self, server):
Tab.__init__(self)
self.state = 'normal'
@@ -2046,6 +2139,7 @@ class MucListTab(Tab):
self.listview.refresh()
self.tab_win.refresh()
self.input.refresh()
+ self.update_commands()
def resize(self):
if not self.visible:
@@ -2220,7 +2314,7 @@ def jid_and_name_match(contact, txt):
"""
if not txt:
return True
- if txt in JID(contact.get_bare_jid()).user:
+ if txt in JID(contact.bare_jid).user:
return True
return False
@@ -2231,9 +2325,9 @@ def jid_and_name_match_slow(contact, txt):
"""
if not txt:
return True # Everything matches when search is empty
- user = JID(contact.get_bare_jid()).user
+ user = JID(contact.bare_jid).user
if diffmatch(txt, user):
return True
- if contact.get_name() and diffmatch(txt, contact.get_name()):
+ if contact.name and diffmatch(txt, contact.name):
return True
return False
diff --git a/src/windows.py b/src/windows.py
index 2253b871..d7471d40 100644
--- a/src/windows.py
+++ b/src/windows.py
@@ -368,19 +368,19 @@ class ConversationInfoWin(InfoWin):
about the user we are talking to
"""
color_show = {'xa': lambda: get_theme().COLOR_STATUS_XA,
- 'none': lambda: get_theme().COLOR_STATUS_ONLINE,
- '': lambda: get_theme().COLOR_STATUS_ONLINE,
- 'available': lambda: get_theme().COLOR_STATUS_ONLINE,
- 'dnd': lambda: get_theme().COLOR_STATUS_DND,
- 'away': lambda: get_theme().COLOR_STATUS_AWAY,
- 'chat': lambda: get_theme().COLOR_STATUS_CHAT,
- 'unavailable': lambda: get_theme().COLOR_STATUS_UNAVAILABLE
+ 'none': lambda: get_theme().COLOR_STATUS_ONLINE,
+ '': lambda: get_theme().COLOR_STATUS_ONLINE,
+ 'available': lambda: get_theme().COLOR_STATUS_ONLINE,
+ 'dnd': lambda: get_theme().COLOR_STATUS_DND,
+ 'away': lambda: get_theme().COLOR_STATUS_AWAY,
+ 'chat': lambda: get_theme().COLOR_STATUS_CHAT,
+ 'unavailable': lambda: get_theme().COLOR_STATUS_UNAVAILABLE
}
def __init__(self):
InfoWin.__init__(self)
- def refresh(self, jid, contact, window, chatstate):
+ def refresh(self, jid, contact, window, chatstate, informations):
# contact can be None, if we receive a message
# from someone not in our roster. In this case, we display
# only the maximum information from the message we can get.
@@ -405,9 +405,17 @@ class ConversationInfoWin(InfoWin):
self.write_resource_information(resource)
self.print_scroll_position(window)
self.write_chatstate(chatstate)
+ self.write_additional_informations(informations, jid)
self.finish_line(get_theme().COLOR_INFORMATION_BAR)
self._refresh()
+ def write_additional_informations(self, informations, jid):
+ """
+ Write all informations added by plugins by getting the
+ value returned by the callbacks.
+ """
+ for key in informations:
+ self.addstr(informations[key](jid), to_curses_attr(get_theme().COLOR_INFORMATION_BAR))
def write_resource_information(self, resource):
"""
Write the informations about the resource
@@ -415,7 +423,7 @@ class ConversationInfoWin(InfoWin):
if not resource:
presence = "unavailable"
else:
- presence = resource.get_presence()
+ presence = resource.presence
color = RosterWin.color_show[presence]()
self.addstr('[', to_curses_attr(get_theme().COLOR_INFORMATION_BAR))
self.addstr(" ", to_curses_attr(color))
@@ -428,7 +436,7 @@ class ConversationInfoWin(InfoWin):
if not contact:
self.addstr("(contact not in roster)", to_curses_attr(get_theme().COLOR_INFORMATION_BAR))
return
- display_name = contact.get_name() or contact.get_bare_jid()
+ display_name = contact.name or contact.bare_jid
self.addstr('%s '%(display_name), to_curses_attr(get_theme().COLOR_INFORMATION_BAR))
def write_contact_jid(self, jid):
@@ -468,7 +476,7 @@ class ConversationStatusMessageWin(InfoWin):
self._refresh()
def write_status_message(self, resource):
- self.addstr(resource.get_status(), to_curses_attr(get_theme().COLOR_INFORMATION_BAR))
+ self.addstr(resource.status, to_curses_attr(get_theme().COLOR_INFORMATION_BAR))
class MucInfoWin(InfoWin):
"""
@@ -1471,14 +1479,14 @@ class RosterWin(Win):
presence = 'unavailable'
nb = ''
else:
- presence = resource.get_presence()
+ presence = resource.presence
nb = ' (%s)' % (contact.get_nb_resources(),)
color = RosterWin.color_show[presence]()
- if contact.get_name():
- display_name = '%s (%s)%s' % (contact.get_name(),
- contact.get_bare_jid(), nb,)
+ if contact.name:
+ display_name = '%s (%s)%s' % (contact.name,
+ contact.bare_jid, nb,)
else:
- display_name = '%s%s' % (contact.get_bare_jid(), nb,)
+ display_name = '%s%s' % (contact.bare_jid, nb,)
self.addstr(y, 0, ' ')
self.addstr(" ", to_curses_attr(color))
if resource:
@@ -1488,7 +1496,7 @@ class RosterWin(Win):
self.addstr(display_name, to_curses_attr(get_theme().COLOR_SELECTED_ROW))
else:
self.addstr(display_name)
- if contact.get_ask() == 'asked':
+ if contact.ask == 'asked':
self.addstr('?', to_curses_attr(get_theme().COLOR_HIGHLIGHT_NICK))
self.finish_line()
@@ -1496,12 +1504,12 @@ class RosterWin(Win):
"""
Draw a specific resource line
"""
- color = RosterWin.color_show[resource.get_presence()]()
+ color = RosterWin.color_show[resource.presence]()
self.addstr(y, 4, " ", to_curses_attr(color))
if colored:
- self.addstr(y, 6, resource.get_jid().full, to_curses_attr(get_theme().COLOR_SELECTED_ROW))
+ self.addstr(y, 6, resource.jid.full, to_curses_attr(get_theme().COLOR_SELECTED_ROW))
else:
- self.addstr(y, 6, resource.get_jid().full)
+ self.addstr(y, 6, resource.jid.full)
self.finish_line()
def get_selected_row(self):
@@ -1517,22 +1525,22 @@ class ContactInfoWin(Win):
"""
resource = contact.get_highest_priority_resource()
if contact:
- jid = contact.get_bare_jid()
+ jid = contact.bare_jid
else:
- jid = jid or resource.get_jid().full
+ jid = jid or resource.jid.full
if resource:
- presence = resource.get_presence()
+ presence = resource.presence
else:
presence = 'unavailable'
self.addstr(0, 0, '%s (%s)'%(jid, presence,), to_curses_attr(get_theme().COLOR_INFORMATION_BAR))
self.finish_line(get_theme().COLOR_INFORMATION_BAR)
- self.addstr(1, 0, 'Subscription: %s' % (contact.get_subscription(),))
- if contact.get_ask():
+ self.addstr(1, 0, 'Subscription: %s' % (contact.subscription,))
+ if contact.ask:
self.addstr(' ')
- if contact.get_ask() == 'asked':
- self.addstr('Ask: %s' % (contact.get_ask(),), to_curses_attr(get_theme().COLOR_HIGHLIGHT_NICK))
+ if contact.ask == 'asked':
+ self.addstr('Ask: %s' % (contact.ask,), to_curses_attr(get_theme().COLOR_HIGHLIGHT_NICK))
else:
- self.addstr('Ask: %s' % (contact.get_ask(),))
+ self.addstr('Ask: %s' % (contact.ask,))
self.finish_line()
diff --git a/src/xhtml.py b/src/xhtml.py
index 99e0bf01..e7a045fa 100644
--- a/src/xhtml.py
+++ b/src/xhtml.py
@@ -258,7 +258,9 @@ def xhtml_to_poezio_colors(text):
if key == 'background-color':
pass#shell += '\x191'
elif key == 'color':
- shell += '\x19%d}' % get_color(value)
+ color = get_color(value)
+ if color != -1:
+ shell += '\x19%d}' % color
elif key == 'font-style':
shell += '\x19i'
elif key == 'font-weight':