summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/gui.py51
-rw-r--r--src/roster.py13
-rw-r--r--src/tab.py36
-rw-r--r--src/window.py62
4 files changed, 140 insertions, 22 deletions
diff --git a/src/gui.py b/src/gui.py
index 7b60b4f1..f1d5eb0c 100644
--- a/src/gui.py
+++ b/src/gui.py
@@ -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:
diff --git a/src/roster.py b/src/roster.py
index 7191e5ca..09bc7b6b 100644
--- a/src/roster.py
+++ b/src/roster.py
@@ -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()
diff --git a/src/tab.py b/src/tab.py
index 04d55f80..e6a8cc41 100644
--- a/src/tab.py
+++ b/src/tab.py
@@ -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
diff --git a/src/window.py b/src/window.py
index eb2c157b..1c3f32d9 100644
--- a/src/window.py
+++ b/src/window.py
@@ -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: