From a1ef835a37f1d1dd4fb861ba4cc3919941bfb6e4 Mon Sep 17 00:00:00 2001 From: mathieui Date: Sat, 11 May 2013 20:51:11 +0200 Subject: Handle the roster order cache as a real cache MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 “+++”. --- src/core.py | 18 ++++++++++++++++++ src/roster.py | 12 ++++++++++++ src/windows.py | 53 +++++++++++++++++++++++++++++++---------------------- 3 files changed, 61 insertions(+), 22 deletions(-) (limited to 'src') diff --git a/src/core.py b/src/core.py index 1d946df3..4c1566bd 100644 --- a/src/core.py +++ b/src/core.py @@ -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, { diff --git a/src/roster.py b/src/roster.py index b0f3cf65..5ddbdbb2 100644 --- a/src/roster.py +++ b/src/roster.py @@ -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 diff --git a/src/windows.py b/src/windows.py index 49e76ff5..0040ee0c 100644 --- a/src/windows.py +++ b/src/windows.py @@ -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) -- cgit v1.2.3