Improve the roster search

- now case-insensitive
- search in the bare jid instead of userpart only (and still in roster
  names)
- do not display groups when searching
- display offline contacts
- do not expand resources if they were before the search
This commit is contained in:
mathieui 2013-06-13 01:00:53 +02:00
parent 2b4c06b6f8
commit 833faa4f37
3 changed files with 72 additions and 53 deletions

View file

@ -115,8 +115,6 @@ class Roster(object):
key=lambda x: x.name.lower() if x.name else ''
)
log.debug("Current groups: %s", group_list)
for sorting in sort.split(':'):
if sorting == 'reverse':
group_list = list(reversed(group_list))
@ -145,6 +143,28 @@ class Roster(object):
"""
return [self[jid] for jid in self.jids()]
def get_contacts_sorted_filtered(self, sort=''):
"""
Return a list of all the contacts sorted with a criteria
"""
contact_list = []
for contact in self.get_contacts():
if contact.bare_jid != self.jid:
if self.contact_filter:
if self.contact_filter[0](contact, self.contact_filter[1]):
contact_list.append(contact)
else:
contact_list.append(contact)
contact_list = sorted(contact_list, key=SORTING_METHODS['name'])
for sorting in sort.split(':'):
if sorting == 'reverse':
contact_list = list(reversed(contact_list))
else:
method = SORTING_METHODS.get(sorting, lambda x: 0)
contact_list = sorted(contact_list, key=method)
return contact_list
def save_to_config_file(self):
"""
Save various information to the config file
@ -184,28 +204,11 @@ class Roster(object):
def __len__(self):
"""
Return the number of line that would be printed
for the whole roster
Return the number of contacts
(used to return the display size, but now we have
the display cache in RosterWin for that)
"""
length = 0
show_offline = config.get('roster_show_offline', 'false') == 'true'
for group in self.groups.values():
if not show_offline and group.get_nb_connected_contacts() == 0:
continue
before = length
if not group.name in self.folded_groups:
for contact in group.get_contacts(self.contact_filter):
# We do not count the offline contacts (depending on config)
if not show_offline and\
len(contact) == 0:
continue
length += 1 # One for the contact's line
if not contact.folded(group.name):
# One for each resource, if the contact is unfolded
length += len(contact)
if not self.contact_filter or before != length:
length += 1 # One for the group's line itself if needed
return length
return len(self.contacts)
def __repr__(self):
ret = '== Roster:\nContacts:\n'

View file

@ -3012,6 +3012,8 @@ class RosterInfoTab(Tab):
self.input = windows.CommandInput("[Search]", self.on_search_terminate, self.on_search_terminate, self.set_roster_filter)
self.input.resize(1, self.width, self.height-1, 0)
self.input.disable_history()
roster.modified()
self.refresh()
return True
@refresh_wrapper.always
@ -3024,11 +3026,13 @@ class RosterInfoTab(Tab):
def set_roster_filter_slow(self, txt):
roster.contact_filter = (jid_and_name_match_slow, txt)
roster.modified()
self.refresh()
return False
def set_roster_filter(self, txt):
roster.contact_filter = (jid_and_name_match, txt)
roster.modified()
self.refresh()
return False
@ -3037,6 +3041,7 @@ class RosterInfoTab(Tab):
curses.curs_set(0)
roster.contact_filter = None
self.reset_help_message()
roster.modified()
return False
def on_close(self):
@ -3851,12 +3856,12 @@ def jid_and_name_match(contact, txt):
"""
Match jid with text precisely
"""
roster.modified()
if not txt:
return True
if txt in safeJID(contact.bare_jid).user:
txt = txt.lower()
if txt in safeJID(contact.bare_jid).bare.lower():
return True
if txt in contact.name:
if txt in contact.name.lower():
return True
return False
@ -3865,10 +3870,9 @@ def jid_and_name_match_slow(contact, txt):
A function used to know if a contact in the roster should
be shown in the roster
"""
roster.modified()
if not txt:
return True # Everything matches when search is empty
user = safeJID(contact.bare_jid).user
user = safeJID(contact.bare_jid).bare
if diffmatch(txt, user):
return True
if contact.name and diffmatch(txt, contact.name):

View file

@ -1742,10 +1742,13 @@ class RosterWin(Win):
Win.__init__(self)
self.pos = 0 # cursor position in the contact list
self.start_pos = 1 # position of the start of the display
self.roster_len = 0
self.selected_row = None
self.roster_cache = []
@property
def roster_len(self):
return len(self.roster_cache)
def move_cursor_down(self, number=1):
"""
Return True if we scrolled, False otherwise
@ -1809,28 +1812,36 @@ class RosterWin(Win):
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
# This is a search
if roster.contact_filter:
self.roster_cache = []
sort = config.get('roster_sort', 'jid:show') or 'jid:show'
for contact in roster.get_contacts_sorted_filtered(sort):
self.roster_cache.append(contact)
if not contact.folded(group.name):
for resource in contact.get_resources():
self.roster_cache.append(resource)
else:
show_offline = config.get('roster_show_offline', 'false') == 'true' or roster.contact_filter
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)
roster.last_built = datetime.now()
if self.selected_row in self.roster_cache:
self.pos = self.roster_cache.index(self.selected_row)
if self.pos < self.roster_len and self.roster_cache[self.pos] != self.selected_row:
self.pos = self.roster_cache.index(self.selected_row)
def refresh(self, roster):
"""
@ -1840,16 +1851,17 @@ class RosterWin(Win):
log.debug('Refresh: %s',self.__class__.__name__)
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)
# make sure we are within bounds
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)
y = 1
group = "none"
# scroll down if needed
if self.start_pos+self.height <= self.pos+2:
self.scroll_down(self.pos - self.start_pos - self.height + (self.height//2))
# draw the roster from the cache
if self.start_pos+self.height < self.pos:
self.start_pos = self.pos - (self.height//2)
for item in self.roster_cache[self.start_pos-1:self.start_pos+self.height]:
draw_selected = False
@ -1887,7 +1899,7 @@ class RosterWin(Win):
"""
self.addstr('Roster: %s/%s contacts' % (
roster.get_nb_connected_contacts(),
len(roster.contacts))
len(roster))
,to_curses_attr(get_theme().COLOR_INFORMATION_BAR))
self.finish_line(get_theme().COLOR_INFORMATION_BAR)