Basic search in the roster (based on contact JIDs)

parent 2863eebd
......@@ -39,7 +39,7 @@ from config import config
from tab import MucTab, InfoTab, PrivateTab, RosterInfoTab, ConversationTab
from user import User
from room import Room
from roster import Roster, RosterGroup
from roster import Roster, RosterGroup, roster
from contact import Contact, Resource
from message import Message
from text_buffer import TextBuffer
......@@ -81,7 +81,7 @@ class Gui(object):
else RosterInfoTab(self.stdscr)
default_tab.on_gain_focus()
self.tabs = [default_tab]
self.roster = Roster()
# self.roster = Roster()
# a unique buffer used to store global informations
# that are displayed in almost all tabs, in an
# information window.
......@@ -150,6 +150,8 @@ class Gui(object):
self.xmpp.add_event_handler("roster_update", self.on_roster_update)
self.xmpp.add_event_handler("changed_status", self.on_presence)
# self.__debug_fill_roster()
def grow_information_win(self):
"""
"""
......@@ -172,7 +174,7 @@ class Gui(object):
def on_got_offline(self, presence):
jid = presence['from']
contact = self.roster.get_contact_by_jid(jid.bare)
contact = roster.get_contact_by_jid(jid.bare)
if not contact:
return
resource = contact.get_resource_by_fulljid(jid.full)
......@@ -184,7 +186,7 @@ class Gui(object):
def on_got_online(self, presence):
jid = presence['from']
contact = self.roster.get_contact_by_jid(jid.bare)
contact = roster.get_contact_by_jid(jid.bare)
if not contact:
# Todo, handle presence comming from contacts not in roster
return
......@@ -471,8 +473,7 @@ class Gui(object):
"""
"""
jid = presence['from']
# contact = ros
contact = self.roster.get_contact_by_jid(jid.bare)
contact = roster.get_contact_by_jid(jid.bare)
if not contact:
return
resource = contact.get_resource_by_fulljid(jid.full)
......@@ -485,6 +486,34 @@ class Gui(object):
if isinstance(self.current_tab(), RosterInfoTab):
self.refresh_window()
def __debug_fill_roster(self):
for i in range(10):
jid = 'contact%s@fion%s.org'%(i,i)
contact = Contact(jid)
contact.set_ask('wat')
contact.set_subscription('both')
roster.add_contact(contact, jid)
contact.set_name('%s %s fion'%(i,i))
roster.edit_groups_of_contact(contact, ['hello'])
for i in range(10):
jid = 'test%s@bernard%s.org'%(i,i)
contact = Contact(jid)
contact.set_ask('wat')
contact.set_subscription('both')
roster.add_contact(contact, jid)
contact.set_name('%s test'%(i))
roster.edit_groups_of_contact(contact, ['hello'])
for i in range(10):
jid = 'pouet@top%s.org'%(i)
contact = Contact(jid)
contact.set_ask('wat')
contact.set_subscription('both')
roster.add_contact(contact, jid)
contact.set_name('%s oula'%(i))
roster.edit_groups_of_contact(contact, ['hello'])
if isinstance(self.current_tab(), RosterInfoTab):
self.refresh_window()
def on_roster_update(self, iq):
"""
A subscription changed, or we received a roster item
......@@ -492,10 +521,10 @@ class Gui(object):
"""
for item in iq.findall('{jabber:iq:roster}query/{jabber:iq:roster}item'):
jid = item.attrib['jid']
contact = self.roster.get_contact_by_jid(jid)
contact = roster.get_contact_by_jid(jid)
if not contact:
contact = Contact(jid)
self.roster.add_contact(contact, jid)
roster.add_contact(contact, jid)
if 'ask' in item.attrib:
contact.set_ask(item.attrib['ask'])
else:
......@@ -507,7 +536,7 @@ class Gui(object):
if item.attrib['subscription']:
contact.set_subscription(item.attrib['subscription'])
groups = item.findall('{jabber:iq:roster}group')
self.roster.edit_groups_of_contact(contact, [group.text for group in groups])
roster.edit_groups_of_contact(contact, [group.text for group in groups])
if isinstance(self.current_tab(), RosterInfoTab):
self.refresh_window()
......@@ -602,7 +631,7 @@ class Gui(object):
Refresh everything
"""
self.current_tab().set_color_state(theme.COLOR_TAB_CURRENT)
self.current_tab().refresh(self.tabs, self.information_buffer, self.roster)
self.current_tab().refresh(self.tabs, self.information_buffer, roster)
self.doupdate()
def open_new_room(self, room, nick, focus=True):
......@@ -738,7 +767,7 @@ class Gui(object):
own_nick = room.own_nick
r = Room(complete_jid, own_nick) # PrivateRoom here
new_tab = PrivateTab(self.stdscr, r, self.information_win_size)
# insert it in the rooms
# insert it in the tabs
if self.current_tab().nb == 0:
self.tabs.append(new_tab)
else:
......
......@@ -24,6 +24,9 @@ from contact import Contact, Resource
class Roster(object):
def __init__(self):
self._contact_filter = None # A tuple(function, *args)
# function to filter contacts,
# on search, for example
self._contacts = {} # key = bare jid; value = Contact()
self._roster_groups = []
......@@ -112,7 +115,7 @@ class Roster(object):
continue
length += 1 # One for the group's line itself
if not group.folded:
for contact in group.get_contacts():
for contact in group.get_contacts(self._contact_filter):
# We do not count the offline contacts (depending on config)
if config.get('roster_show_offline', 'false') == 'false' and\
contact.get_nb_resources() == 0:
......@@ -178,7 +181,7 @@ class RosterGroup(object):
assert contact not in self._contacts
self._contacts.append(contact)
def get_contacts(self):
def get_contacts(self, contact_filter):
def compare_contact(a):
if not a.get_highest_priority_resource():
return 0
......@@ -186,7 +189,9 @@ class RosterGroup(object):
if show not in PRESENCE_PRIORITY:
return 5
return PRESENCE_PRIORITY[show]
return sorted(self._contacts, key=compare_contact, reverse=True)
contact_list = self._contacts if not contact_filter\
else [contact for contact in self._contacts if contact_filter[0](contact, contact_filter[1])]
return sorted(contact_list, key=compare_contact, reverse=True)
def toggle_folded(self):
self.folded = not self.folded
......@@ -203,3 +208,5 @@ class RosterGroup(object):
if contact.get_highest_priority_resource():
l += 1
return l
roster = Roster()
......@@ -29,7 +29,7 @@ import window
import theme
import curses
from config import config
from roster import RosterGroup
from roster import RosterGroup, roster
from contact import Contact, Resource
class Tab(object):
......@@ -395,6 +395,7 @@ class RosterInfoTab(Tab):
"KEY_UP": self.move_cursor_up,
"KEY_DOWN": self.move_cursor_down,
"o": self.toggle_offline_show,
"^F": self.start_search,
}
Tab.__init__(self, stdscr)
self.name = "Roster"
......@@ -439,11 +440,14 @@ class RosterInfoTab(Tab):
def on_input(self, key):
if self.input.input_mode:
ret = self.input.do_command(key)
roster._contact_filter = (jid_and_name_match, self.input.text)
# if the input is empty, go back to command mode
if self.input.is_empty():
if self.input.is_empty() and not self.input._instructions:
self.input.input_mode = False
curses.curs_set(0)
self.input.rewrite_text()
if self.input._instructions:
return True
return ret
if key in self.single_key_commands:
return self.single_key_commands[key]()
......@@ -458,6 +462,7 @@ class RosterInfoTab(Tab):
else:
config.set_and_save(option, 'false')
return True
def on_slash(self):
"""
'/' is pressed, we enter "input mode"
......@@ -501,10 +506,27 @@ class RosterInfoTab(Tab):
isinstance(selected_row, Contact):
selected_row.toggle_folded()
return True
def on_enter(self):
selected_row = self.roster_win.get_selected_row()
return selected_row
def start_search(self):
"""
Start the search. The input should appear with a short instruction
in it.
"""
curses.curs_set(1)
roster._contact_filter = (jid_and_name_match, self.input.text)
self.input.input_mode = True
self.input.start_command(self.on_search_terminate, self.on_search_terminate, '[search]')
return True
def on_search_terminate(self, txt):
curses.curs_set(0)
roster._contact_filter = None
return True
def just_before_refresh(self):
return
......@@ -578,3 +600,13 @@ class ConversationTab(Tab):
def just_before_refresh(self):
return
def jid_and_name_match(contact, txt):
"""
A function used to know if a contact in the roster should
be shown in the roster
"""
# TODO: search in nickname, and use libdiff
if txt in contact.get_bare_jid():
return True
return False
......@@ -28,7 +28,7 @@ from config import config
from threading import Lock
from contact import Contact, Resource
from roster import RosterGroup
from roster import RosterGroup, roster
from message import Line
from tab import MIN_WIDTH, MIN_HEIGHT
......@@ -546,8 +546,8 @@ class Input(Win):
'M-f': self.jump_word_right,
"KEY_BACKSPACE": self.key_backspace,
'^?': self.key_backspace,
'^J': self.get_text,
'\n': self.get_text,
'^J': self.on_enter,
'\n': self.on_enter,
}
Win.__init__(self, height, width, y, x, stdscr)
......@@ -564,6 +564,51 @@ class Input(Win):
self.hit_list = [] # current possible completion (normal)
self.last_completion = None # Contains the last nickname completed,
# if last key was a tab
# These are used when the user is entering a comand
self._on_cancel = None
self._on_terminate = None
self._instructions = "" # a string displayed before the input, read-only
def on_enter(self):
"""
Called when Enter is pressed
"""
if not self._instructions:
return self.get_text()
self.on_terminate()
return True
def start_command(self, on_cancel, on_terminate, instructions):
"""
Start a command, with an instruction, and two callbacks.
on_terminate is called when the command is successfull
on_cancel is called when the command is canceled
"""
assert isinstance(instructions, str)
self._on_cancel = on_cancel
self._on_terminate = on_terminate
self._instructions = instructions
def cancel_command(self):
"""
Call it to cancel the current command
"""
self._on_cancel()
self._on_cancel = None
self._on_terminate = None
self._instructions = ''
return self.get_text()
def on_terminate(self):
"""
Call it to terminate the command. Returns the content of the input
"""
txt = self.get_text()
self._on_terminate(txt)
self._on_terminate = None
self._on_cancel = None
self._instructions = ''
return txt
def is_empty(self):
return len(self.text) == 0
......@@ -881,11 +926,17 @@ class Input(Win):
with g_lock:
self.clear_text()
if self.input_mode:
self.addstr(self._instructions, curses.color_pair(theme.COLOR_INFORMATION_BAR))
if self._instructions:
self.addstr(' ')
self.addstr(self.text[self.line_pos:self.line_pos+self.width-1])
else:
self.addstr(self.help_text, curses.color_pair(theme.COLOR_INFORMATION_BAR))
self.finish_line(theme.COLOR_INFORMATION_BAR)
self.addstr(0, self.pos, '') # WTF, this works but .move() doesn't…
cursor_pos = self.pos
if self._instructions:
cursor_pos += 1 + len(self._instructions)
self.addstr(0, cursor_pos, '') # WTF, this works but .move() doesn't…
self._refresh()
def refresh(self):
......@@ -992,11 +1043,10 @@ class RosterWin(Win):
y += 1
if group.folded:
continue
for contact in group.get_contacts():
for contact in group.get_contacts(roster._contact_filter):
if config.get('roster_show_offline', 'false') == 'false' and\
contact.get_nb_resources() == 0:
continue
if y-1 == self.pos:
self.selected_row = contact
if y-self.start_pos+1 == self.height:
......
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