Commit a1ef835a authored by mathieui's avatar mathieui

Handle the roster order cache as a real cache

When an external (or internal) event may cause the order of the
cache to be modified, or new elements to be added, schedule it
for a rebuild. Otherwise, don’t, and only rebuild it when
refreshing (that should improve refresh speed a lot).

Also, if the position in the roster is further than the total size
of the roster, go back to the top instead of displaying an empty
window with “+++”.
parent cb61d5ef
......@@ -152,6 +152,7 @@ class Core(object):
self.plugin_manager = PluginManager(self)
self.events = events.EventHandler()
# global commands, available from all tabs
# a command is tuple of the form:
# (the function executing the command. Takes a string as argument,
......@@ -298,6 +299,8 @@ class Core(object):
log.debug("Reloading the config…")
config.__init__(config.file_name)
log.debug("Config reloaded.")
# in case some roster options have changed
roster.modified()
def exit_from_signal(self, *args, **kwargs):
"""
......@@ -2060,6 +2063,8 @@ class Core(object):
elif option == 'plugins_conf_dir':
path = os.path.expanduser(value)
self.plugin_manager.on_plugins_conf_dir_change(path)
# in case some roster options have changed
roster.modified()
self.call_for_resize()
self.information(*info)
......@@ -2791,6 +2796,7 @@ class Core(object):
contact = roster[message['from'].bare]
if not contact:
return
roster.modified()
item = message['pubsub_event']['items']['item']
old_mood = contact.mood
if item.xml.find('{http://jabber.org/protocol/mood}mood'):
......@@ -2820,6 +2826,7 @@ class Core(object):
contact = roster[message['from'].bare]
if not contact:
return
roster.modified()
item = message['pubsub_event']['items']['item']
old_activity = contact.activity
if item.xml.find('{http://jabber.org/protocol/activity}activity'):
......@@ -2855,6 +2862,7 @@ class Core(object):
contact = roster[message['from'].bare]
if not contact:
return
roster.modified()
item = message['pubsub_event']['items']['item']
old_tune = contact.tune
if item.xml.find('{http://jabber.org/protocol/tune}tune'):
......@@ -3085,6 +3093,7 @@ class Core(object):
contact.pending_in = True
self.information('%s wants to subscribe to your presence' % jid, 'Roster')
self.get_tab_by_number(0).state = 'highlight'
roster.modified()
if isinstance(self.current_tab(), tabs.RosterInfoTab):
self.refresh_window()
......@@ -3096,6 +3105,9 @@ class Core(object):
self.information('%s accepted your contact proposal' % jid, 'Roster')
if contact.pending_out:
contact.pending_out = False
roster.modified()
if isinstance(self.current_tab(), tabs.RosterInfoTab):
self.refresh_window()
......@@ -3105,6 +3117,7 @@ class Core(object):
contact = roster[jid]
if not contact:
return
roster.modified()
self.information('%s does not want to receive your status anymore.' % jid, 'Roster')
self.get_tab_by_number(0).state = 'highlight'
if isinstance(self.current_tab(), tabs.RosterInfoTab):
......@@ -3116,6 +3129,7 @@ class Core(object):
contact = roster[jid]
if not contact:
return
roster.modified()
if contact.pending_out:
self.information('%s rejected your contact proposal' % jid, 'Roster')
contact.pending_out = False
......@@ -3137,6 +3151,7 @@ class Core(object):
tab.unlock()
if contact is None:
return
roster.modified()
contact.error = None
self.events.trigger('normal_presence', presence, contact[jid.full])
tab = self.get_conversation_by_jid(jid, create=False)
......@@ -3151,6 +3166,7 @@ class Core(object):
contact = roster[jid.bare]
if not contact:
return
roster.modified()
contact.error = presence['error']['type'] + ': ' + presence['error']['condition']
def on_got_offline(self, presence):
......@@ -3168,6 +3184,7 @@ class Core(object):
self.add_information_message_to_conversation_tab(jid.full, '\x195}%s is \x191}offline' % (jid.full))
self.add_information_message_to_conversation_tab(jid.bare, '\x195}%s is \x191}offline' % (jid.bare))
self.information('\x193}%s \x195}is \x191}offline' % (jid.bare), 'Roster')
roster.modified()
if isinstance(self.current_tab(), tabs.RosterInfoTab):
self.refresh_window()
......@@ -3182,6 +3199,7 @@ class Core(object):
if contact is None:
# Todo, handle presence coming from contacts not in roster
return
roster.modified()
if not logger.log_roster_change(jid.bare, 'got online'):
self.information(_('Unable to write in the log file'), 'Error')
resource = Resource(jid.full, {
......
......@@ -17,6 +17,7 @@ from contact import Contact
from roster_sorting import SORTING_METHODS, GROUP_SORTING_METHODS
from os import path as p
from datetime import datetime
from common import safeJID
from sleekxmpp import JID
from sleekxmpp.exceptions import IqError, IqTimeout
......@@ -43,6 +44,17 @@ class Roster(object):
self.groups = {}
self.contacts = {}
# Used for caching roster infos
self.last_built = datetime.now()
self.last_modified = datetime.now()
def modified(self):
self.last_modified = datetime.now()
@property
def needs_rebuild(self):
return self.last_modified >= self.last_built
def __getitem__(self, key):
"""Get a Contact from his bare JID"""
key = safeJID(key).bare
......
......@@ -1830,34 +1830,43 @@ class RosterWin(Win):
self.start_pos = 1
return self.start_pos != pos
def build_roster_cache(self, roster):
"""
Regenerates the roster cache if needed
"""
with g_lock:
if roster.needs_rebuild:
log.debug('The roster has changed, rebuilding the cache…')
show_offline = config.get('roster_show_offline', 'false') == 'true'
sort = config.get('roster_sort', 'jid:show') or 'jid:show'
group_sort = config.get('roster_group_sort', 'name') or 'name'
self.roster_cache = []
# build the cache
for group in roster.get_groups(group_sort):
contacts_filtered = group.get_contacts(roster.contact_filter)
if (not show_offline and group.get_nb_connected_contacts() == 0) or not contacts_filtered:
continue # Ignore empty groups
self.roster_cache.append(group)
if group.folded:
continue # ignore folded groups
for contact in group.get_contacts(roster.contact_filter, sort):
if not show_offline and len(contact) == 0:
continue # ignore offline contacts
self.roster_cache.append(contact)
if not contact.folded(group.name):
for resource in contact.get_resources():
self.roster_cache.append(resource)
def refresh(self, roster):
"""
We get the roster object
We display a number of lines from the roster cache
(and rebuild it if needed)
"""
log.debug('Refresh: %s',self.__class__.__name__)
self.roster_cache = []
show_offline = config.get('roster_show_offline', 'false') == 'true'
sort = config.get('roster_sort', 'jid:show') or 'jid:show'
group_sort = config.get('roster_group_sort', 'name') or 'name'
# build the cache
for group in roster.get_groups(group_sort):
contacts_filtered = group.get_contacts(roster.contact_filter)
if (not show_offline and group.get_nb_connected_contacts() == 0) or not contacts_filtered:
continue # Ignore empty groups
self.roster_cache.append(group)
if group.folded:
continue # ignore folded groups
for contact in group.get_contacts(roster.contact_filter, sort):
if not show_offline and len(contact) == 0:
continue # ignore offline contacts
self.roster_cache.append(contact)
if not contact.folded(group.name):
for resource in contact.get_resources():
self.roster_cache.append(resource)
self.build_roster_cache(roster)
with g_lock:
self.roster_len = len(roster);
self.move_cursor_up(self.roster_len - self.pos if self.pos >= self.roster_len else 0)
self.move_cursor_up(self.roster_len + self.pos if self.pos >= self.roster_len else 0)
self._win.erase()
self._win.move(0, 0)
self.draw_roster_information(roster)
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment