diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/connection.py | 8 | ||||
-rw-r--r-- | src/contact.py | 81 | ||||
-rw-r--r-- | src/core.py | 159 | ||||
-rw-r--r-- | src/data_forms.py | 2 | ||||
-rw-r--r-- | src/events.py | 14 | ||||
-rw-r--r-- | src/plugin.py | 49 | ||||
-rw-r--r-- | src/plugin_manager.py | 68 | ||||
-rw-r--r-- | src/roster.py | 2 | ||||
-rw-r--r-- | src/tabs.py | 168 | ||||
-rw-r--r-- | src/windows.py | 64 | ||||
-rw-r--r-- | src/xhtml.py | 4 |
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': |