Command mode in roster tab, toggle offline contacts with 'o' and sort contacts by show

parent d6e37883
......@@ -14,6 +14,11 @@
# You should have received a copy of the GNU General Public License
# along with Poezio. If not, see <http://www.gnu.org/licenses/>.
"""
Defines the Resource and Contact classes
"""
from sleekxmpp.xmlstream.stanzabase import JID
class Resource(object):
......@@ -55,7 +60,7 @@ class Resource(object):
class Contact(object):
"""
This a way to gather multiple resources from the same bare JID.
This class contains zero or more esource class and useful methods
This class contains zero or more Resource object and useful methods
to get the resource with the highest priority, etc
"""
def __init__(self, bare_jid):
......@@ -116,6 +121,7 @@ class Contact(object):
if resource.get_jid().full == fulljid:
return resource
return None
def toggle_folded(self):
"""
Fold if it's unfolded, and vice versa
......@@ -148,7 +154,7 @@ class Contact(object):
def get_resources(self):
"""
Return all resources
Return all resources, sorted by priority
"""
compare_resources = lambda x: x.get_priority()
return sorted(self._resources, key=compare_resources)
......
......@@ -171,8 +171,6 @@ class Gui(object):
self.refresh_window()
def on_got_offline(self, presence):
from common import debug
debug('OFFLINE: %s\n' % presence)
jid = presence['from']
contact = self.roster.get_contact_by_jid(jid.bare)
if not contact:
......@@ -185,8 +183,6 @@ class Gui(object):
self.refresh_window()
def on_got_online(self, presence):
from common import debug
debug('ONLINE: %s\n' % presence)
jid = presence['from']
contact = self.roster.get_contact_by_jid(jid.bare)
if not contact:
......@@ -537,7 +533,7 @@ class Gui(object):
Resize the whole screen
"""
with resize_lock:
self.resize_timer = None
# self.resize_timer = None
for tab in self.tabs:
tab.resize(self.stdscr)
self.refresh_window()
......
......@@ -22,9 +22,6 @@ import sleekxmpp
from xml.etree import cElementTree as ET
from common import debug
def send_private_message(xmpp, jid, line):
"""
Send a private message
......@@ -54,7 +51,6 @@ def change_show(xmpp, jid, own_nick, show, status):
pres['type'] = show
if status:
pres['status'] = status
debug('Change presence: %s\n' % (pres))
pres.send()
def change_subject(xmpp, jid, subject):
......
......@@ -14,6 +14,12 @@
# You should have received a copy of the GNU General Public License
# along with Poezio. If not, see <http://www.gnu.org/licenses/>.
"""
Defines the Roster and RosterGroup classes
"""
from config import config
from contact import Contact, Resource
class Roster(object):
......@@ -29,9 +35,15 @@ class Roster(object):
self._contacts[jid] = contact
def get_contact_len(self):
"""
Return the number of contacts in this group
"""
return len(self._contacts.keys())
def get_contact_by_jid(self, jid):
"""
Returns the contact with the given bare JID
"""
if jid in self._contacts:
return self._contacts[jid]
return None
......@@ -42,15 +54,15 @@ class Roster(object):
Add or remove RosterGroup if needed
"""
# add the contact to each group he is in
if not len(groups):
# If the contact hasn't any group, we put her in
# the virtual default 'none' group
if not len(groups):
groups = ['none']
for group in groups:
if group in contact._groups:
continue
else:
if group not in contact._groups:
# create the group if it doesn't exist yet
contact._groups.append(group)
self.add_contact_to_group(group, contact)
self.add_contact_to_group(group, contact)
# remove the contact from each group he is not in
for group in contact._groups:
if group not in groups:
......@@ -60,7 +72,7 @@ class Roster(object):
def remove_contact_from_group(self, group_name, contact):
"""
Remove the contact from the group.
Remove also the group if this makes it empty
Delete the group if this makes it empty
"""
for group in self._roster_groups:
if group.name == group_name:
......@@ -76,28 +88,40 @@ class Roster(object):
"""
for group in self._roster_groups:
if group.name == group_name:
group.add_contact(contact)
if not group.has_contact(contact):
group.add_contact(contact)
return
new_group = RosterGroup(group_name)
self._roster_groups.append(new_group)
new_group.add_contact(contact)
def get_groups(self):
"""
Returns the list of groups
"""
return self._roster_groups
def __len__(self):
"""
Return the number of line that would be printed
for the whole roster
"""
l = 0
length = 0
for group in self._roster_groups:
l += 1
if group.get_nb_connected_contacts() == 0:
continue
length += 1 # One for the group's line itself
if not group.folded:
for contact in group.get_contacts():
l += 1
# We do not count the offline contacts (depending on config)
if config.get('roster_show_offline', 'false') == 'false' and\
contact.get_nb_resources() == 0:
continue
length += 1 # One for the contact's line
if not contact._folded:
l += contact.get_nb_resources()
return l
# One for each resource, if the contact is unfolded
length += contact.get_nb_resources()
return length
def __repr__(self):
ret = '== Roster:\nContacts:\n'
......@@ -108,6 +132,13 @@ class Roster(object):
ret += '%s\n' % (group,)
return ret + '\n'
PRESENCE_PRIORITY = {'unavailable': 0,
'xa': 1,
'away': 2,
'dnd': 3,
'': 4,
'available': 4}
class RosterGroup(object):
"""
A RosterGroup is a group containing contacts
......@@ -122,6 +153,15 @@ class RosterGroup(object):
def is_empty(self):
return len(self._contacts) == 0
def has_contact(self, contact):
"""
Return a bool, telling if the contact
is already in the group
"""
if contact in self._contacts:
return True
return False
def remove_contact(self, contact):
"""
Remove a Contact object to the list
......@@ -139,10 +179,27 @@ class RosterGroup(object):
self._contacts.append(contact)
def get_contacts(self):
return self._contacts
def compare_contact(a):
if not a.get_highest_priority_resource():
return 0
show = a.get_highest_priority_resource().get_presence()
if show not in PRESENCE_PRIORITY:
return 5
return PRESENCE_PRIORITY[show]
return sorted(self._contacts, key=compare_contact, reverse=True)
def toggle_folded(self):
self.folded = not self.folded
def __repr__(self):
return '<Roster_group: %s; %s>' % (self.name, self._contacts)
def toggle_folded(self):
self.folded = not self.folded
def __len__(self):
return len(self._contacts)
def get_nb_connected_contacts(self):
l = 0
for contact in self._contacts:
if contact.get_highest_priority_resource():
l += 1
return l
......@@ -28,6 +28,7 @@ MIN_HEIGHT = 16
import window
import theme
import curses
from config import config
from roster import RosterGroup
from contact import Contact, Resource
......@@ -357,7 +358,10 @@ class PrivateTab(Tab):
def on_gain_focus(self):
self._room.set_color_state(theme.COLOR_TAB_CURRENT)
curses.curs_set(1)
if not self.input.input_mode:
curses.curs_set(1)
else:
curses.curs_set(0)
def on_scroll_up(self):
self._room.scroll_up(self.text_win.height-1)
......@@ -382,6 +386,16 @@ class RosterInfoTab(Tab):
A tab, splitted in two, containing the roster and infos
"""
def __init__(self, stdscr):
self.single_key_commands = {
"^J": self.on_enter,
"^M": self.on_enter,
"\n": self.on_enter,
' ': self.on_space,
"/": self.on_slash,
"KEY_UP": self.move_cursor_up,
"KEY_DOWN": self.move_cursor_down,
"o": self.toggle_offline_show,
}
Tab.__init__(self, stdscr)
self.name = "Roster"
roster_width = self.width//2
......@@ -391,7 +405,7 @@ class RosterInfoTab(Tab):
self.info_win = window.TextWin(self.height-2, info_width, 0, roster_width+1, stdscr, self.visible)
self.roster_win = window.RosterWin(self.height-2-3, roster_width, 0, 0, stdscr, self.visible)
self.contact_info_win = window.ContactInfoWin(3, roster_width, self.height-2-3, 0, stdscr, self.visible)
self.input = window.Input(1, self.width, self.height-1, 0, stdscr, self.visible)
self.input = window.Input(1, self.width, self.height-1, 0, stdscr, self.visible, False, "Enter commands with “/”. “o”: toggle offline show")
self.set_color_state(theme.COLOR_TAB_NORMAL)
def resize(self, stdscr):
......@@ -423,12 +437,34 @@ class RosterInfoTab(Tab):
self._color_state = color
def on_input(self, key):
if key in ('\n', '^J', '^M') and self.input.is_empty():
return self.on_enter()
if key == ' ':
return self.on_space()
# In writting mode
# return self.input.do_command(key)
if self.input.input_mode:
ret = self.input.do_command(key)
# if the input is empty, go back to command mode
if self.input.is_empty():
self.input.input_mode = False
curses.curs_set(0)
self.input.rewrite_text()
return ret
if key in self.single_key_commands:
return self.single_key_commands[key]()
def toggle_offline_show(self):
"""
Show or hide offline contacts
"""
option = 'roster_show_offline'
if config.get(option, 'false') == 'false':
config.set_and_save(option, 'true')
else:
config.set_and_save(option, 'false')
return True
def on_slash(self):
"""
'/' is pressed, we enter "input mode"
"""
self.input.input_mode = True
curses.curs_set(1)
self.on_input("/") # we add the slash
def on_lose_focus(self):
self._color_state = theme.COLOR_TAB_NORMAL
......@@ -440,11 +476,21 @@ class RosterInfoTab(Tab):
def add_message(self):
return False
def on_scroll_down(self):
def move_cursor_down(self):
self.roster_win.move_cursor_down()
return True
def on_scroll_up(self):
def move_cursor_up(self):
self.roster_win.move_cursor_up()
return True
def on_scroll_down(self):
# Scroll info win
pass
def on_scroll_up(self):
# Scroll info down
pass
def on_info_win_size_changed(self, _, __):
pass
......
......@@ -31,7 +31,6 @@ class User(object):
keep trace of an user in a Room
"""
def __init__(self, nick, affiliation, show, status, role):
from common import debug
self.last_talked = datetime(1, 1, 1) # The oldest possible time
self.update(affiliation, show, status, role)
self.change_nick(nick)
......
......@@ -518,8 +518,11 @@ class TextWin(Win):
class Input(Win):
"""
The line where text is entered
It can be in input mode or in commmand mode.
Command mode means that single_key_commands can be entered, handled
by the Tab object, while this input just displays an help text.
"""
def __init__(self, height, width, y, x, stdscr, visible):
def __init__(self, height, width, y, x, stdscr, visible, input_mode=True, help_text=''):
self.key_func = {
"KEY_LEFT": self.key_left,
"M-D": self.key_left,
......@@ -548,6 +551,8 @@ class Input(Win):
}
Win.__init__(self, height, width, y, x, stdscr)
self.input_mode = input_mode
self.help_text = help_text # the text displayed in command_mode
self.visible = visible
self.history = []
self.text = ''
......@@ -875,8 +880,12 @@ class Input(Win):
"""
with g_lock:
self.clear_text()
self.addstr(self.text[self.line_pos:self.line_pos+self.width-1])
self.addstr(0, self.pos, '') # WTF, this works but .move() doesn't...
if self.input_mode:
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…
self._refresh()
def refresh(self):
......@@ -928,7 +937,7 @@ class RosterWin(Win):
'chat':theme.COLOR_STATUS_CHAT,
'unavailable':theme.COLOR_STATUS_UNAVAILABLE
}
# subscription_char = {'both': '
def __init__(self, height, width, y, x, parent_win, visible):
self.visible = visible
Win.__init__(self, height, width, y, x, parent_win)
......@@ -967,10 +976,14 @@ class RosterWin(Win):
return
with g_lock:
self.roster_len = len(roster)
while self.roster_len and self.pos >= self.roster_len:
self.move_cursor_up()
self._win.erase()
self.draw_roster_information(roster)
y = 1
for group in roster.get_groups():
if group.get_nb_connected_contacts() == 0:
continue # Ignore empty groups
# This loop is really REALLY ugly :^)
if y-1 == self.pos:
self.selected_row = group
......@@ -980,6 +993,10 @@ class RosterWin(Win):
if group.folded:
continue
for contact in group.get_contacts():
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:
......@@ -1007,7 +1024,7 @@ class RosterWin(Win):
def draw_plus(self, y):
"""
Draw the indicator that shows that
the list is longer that what is displayed
the list is longer than what is displayed
"""
self.addstr(y, self.width-5, '++++', curses.color_pair(42))
......
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