Basic implementation of the roster and one to one conversations

This commit is contained in:
louiz@4325f9fc-e183-4c21-96ce-0ab188b42d13 2010-09-26 18:01:38 +00:00
parent c486b78bbd
commit 390e952829
9 changed files with 512 additions and 119 deletions

View file

@ -58,13 +58,7 @@ def debug(string):
a CLI software
"""
fdes = open("/tmp/debug", 'a')
try:
fdes.write(string)
except:
try:
fdes.write(string.encode('utf-8'))
except:
fdes.write(string.encode('utf-8'))
fdes.write(string)
fdes.close()
def get_base64_from_file(path):

View file

@ -22,8 +22,32 @@ class Contact(object):
"""
def __init__(self, jid):
self._jid = JID(jid) # a SleekXMPP jid object
self._display_name = None
self.groups = [] # a list of groups the contact is in
self._display_name = ''
self._subscription = 'none'
self._ask = None
self._status = ''
self._presence = 'unavailable'
self._priority = 0
self._groups = [] # a list of groups the contact is in
def getJid(self):
def set_ask(self, ask):
self._ask = ask
def set_subscription(self, sub):
self._subscription = sub
def get_jid(self):
return self._jid
def __repr__(self):
return '%s' % self._jid
def set_priority(self, priority):
assert isinstance(priority, int)
self._priority = priority
def set_presence(self, pres):
self._presence = pres
def get_presence(self):
return self._presence

View file

@ -35,15 +35,17 @@ import theme
import multiuserchat as muc
from handler import Handler
from config import config
from tab import MucTab, InfoTab, PrivateTab, RosterInfoTab
from tab import MucTab, InfoTab, PrivateTab, RosterInfoTab, ConversationTab
from user import User
from room import Room
from roster import Roster
from contact import Contact
from message import Message
from text_buffer import TextBuffer
from keyboard import read_char
from common import is_jid_the_same, jid_get_domain, jid_get_resource, is_jid
from common import debug
# http://xmpp.org/extensions/xep-0045.html#errorstatus
ERROR_AND_STATUS_CODES = {
'401': _('A password is required'),
@ -75,8 +77,9 @@ class Gui(object):
self.init_curses(self.stdscr)
self.xmpp = xmpp
default_tab = InfoTab(self.stdscr, "Info") if self.xmpp.anon\
else RosterInfoTab(self.stdscr, self.xmpp.roster)
else RosterInfoTab(self.stdscr)
self.tabs = [default_tab]
self.roster = Roster()
# a unique buffer used to store global informations
# that are displayed in almost all tabs, in an
# information window.
@ -136,8 +139,10 @@ class Gui(object):
self.xmpp.add_event_handler("groupchat_presence", self.on_groupchat_presence)
self.xmpp.add_event_handler("groupchat_message", self.on_groupchat_message)
self.xmpp.add_event_handler("message", self.on_message)
self.xmpp.add_event_handler("presence", self.on_presence)
self.xmpp.add_event_handler("got_online" , self.on_got_online)
self.xmpp.add_event_handler("got_offline" , self.on_got_offline)
self.xmpp.add_event_handler("roster_update", self.on_roster_update)
# self.xmpp.add_event_handler("presence", self.on_presence)
def grow_information_win(self):
"""
@ -159,6 +164,25 @@ class Gui(object):
tab.on_info_win_size_changed(self.information_win_size, self.stdscr)
self.refresh_window()
def on_got_offline(self, presence):
jid = presence['from']
contact = self.roster.get_contact_by_jid(jid.bare)
if not contact:
return
contact.set_presence('unavailable')
self.information('%s is not offline' % (contact.get_jid()), "Roster")
def on_got_online(self, presence):
jid = presence['from']
contact = self.roster.get_contact_by_jid(jid.bare)
if not contact:
return
status = presence['type']
priority = presence.getPriority()
contact.set_presence(status)
contact.set_priority(priority)
self.information("%s is now online (%s)" % (contact.get_jid(), status), "Roster")
def on_connected(self, event):
"""
Called when we are connected and authenticated
@ -409,6 +433,15 @@ class Gui(object):
"""
from common import debug
debug('MESSAGE: %s\n' % (message))
jid = message['from'].bare
room = self.get_conversation_by_jid(jid)
if not room:
room = self.open_conversation_window(jid, False)
if not room:
return
body = message['body']
self.add_message_to_text_buffer(room, body, None, jid)
self.refresh_window()
return
def on_presence(self, presence):
@ -423,10 +456,24 @@ class Gui(object):
A subscription changed, or we received a roster item
after a roster request, etc
"""
from common import debug
debug("ROSTER UPDATE: %s\n" % (iq))
for subscriber in iq['roster']['items']:
debug("subscriber: %s\n" % (iq['roster']['items'][subscriber]['subscription']))
# debug('Roster Update: \n%s\n\n' % iq)
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)
if not contact:
contact = Contact(jid)
self.roster.add_contact(contact, jid)
if 'ask' in item.attrib:
contact.set_ask(item.attrib['ask'])
else:
contact.set_ask(None)
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])
if isinstance(self.current_tab(), RosterInfoTab):
# TODO refresh roster_win only
self.refresh_window()
def resize_window(self):
"""
@ -456,6 +503,16 @@ class Gui(object):
"""
return self.tabs[0]
def get_conversation_by_jid(self, jid):
"""
Return the room of the ConversationTab with the given jid
"""
for tab in self.tabs:
if isinstance(tab, ConversationTab):
if tab.get_name() == jid:
return tab.get_room()
return None
def get_room_by_name(self, name):
"""
returns the room that has this name
@ -489,7 +546,8 @@ 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.current_tab().refresh(self.tabs, self.information_buffer, self.roster)
doupdate()
def open_new_room(self, room, nick, focus=True):
"""
@ -584,6 +642,26 @@ class Gui(object):
self.add_message_to_text_buffer(room, _('You can join the room with an other nick, by typing "/join /other_nick"'))
self.refresh_window()
def open_conversation_window(self, room_name, focus=True):
"""
open a new conversation tab and focus it if needed
"""
r = Room(room_name, self.xmpp.fulljid)
new_tab = ConversationTab(self.stdscr, r, self.information_win_size)
# insert it in the rooms
if self.current_tab().nb == 0:
self.tabs.append(new_tab)
else:
for ta in self.tabs:
if ta.nb == 0:
self.tabs.insert(self.tabs.index(ta), new_tab)
break
if focus: # focus the room if needed
self.command_win('%s' % (new_tab.nb))
# self.window.new_room(r)
self.refresh_window()
return r
def open_private_window(self, room_name, user_nick, focus=True):
complete_jid = room_name+'/'+user_nick
for tab in self.tabs: # if the room exists, focus it and return
@ -1178,8 +1256,31 @@ class Gui(object):
if not key:
return
res = self.current_tab().on_input(key)
if key in ('^J', '\n'):
if not res:
return
if key in ('^J', '\n') and isinstance(res, str):
self.execute(res)
else:
# we did "enter" with an empty input in the roster
self.on_roster_enter_key(res)
def on_roster_enter_key(self, roster_row):
"""
when enter is pressed on the roster window
"""
if isinstance(roster_row, Contact):
r = Room(roster_row.get_jid().full, self.xmpp.fulljid)
new_tab = ConversationTab(self.stdscr, r, self.information_win_size)
debug('%s\n'% new_tab)
# insert it in the tabs
if self.current_tab().nb == 0:
self.tabs.append(new_tab)
else:
for ta in self.tabs:
if ta.nb == 0:
self.tabs.insert(self.tabs.index(ta), new_tab)
break
self.refresh_window()
def execute(self,line):
"""
@ -1203,6 +1304,10 @@ class Gui(object):
def command_say(self, line):
if isinstance(self.current_tab(), PrivateTab):
muc.send_private_message(self.xmpp, self.current_tab().get_name(), line)
elif isinstance(self.current_tab(), ConversationTab): # todo, special case
muc.send_private_message(self.xmpp, self.current_tab().get_name(), line)
if isinstance(self.current_tab(), PrivateTab) or\
isinstance(self.current_tab(), ConversationTab):
self.add_message_to_text_buffer(self.current_tab().get_room(), line, None, self.current_tab().get_room().own_nick)
elif isinstance(self.current_tab(), MucTab):
muc.send_groupchat_message(self.xmpp, self.current_tab().get_name(), line)

View file

@ -52,7 +52,7 @@ def read_char(s):
if first == 27:
(first, c) = get_next_byte(s)
if not isinstance(first, int):
return Nones
return None
return "M-"+chr(first)
if 194 <= first:
(code, c) = get_next_byte(s) # 2 bytes char

View file

@ -16,30 +16,146 @@
from contact import Contact
from common import debug
class Roster(object):
"""
Defines the roster
"""
def __init__(self):
self._contacts = {}
self._contacts = {} # key = jid; value = Contact()
self._roster_groups = []
def addContactToList(self, contact):
assert isinstance(contact, Contact)
assert contact not in self._contacts
self._contacts[contact.getJid().bare] = contact
def add_contact(self, contact, jid):
"""
Add a contact to the contact list
"""
assert jid not in self._contacts
self._contacts[jid] = contact
def getContacts(self):
def get_contact_len(self):
return len(self._contacts.keys())
def get_contact_by_jid(self, jid):
if jid in self._contacts:
return self._contacts[jid]
return None
def edit_groups_of_contact(self, contact, groups):
"""
returns all the contacts in a list
TODO: sorted
TODO: only some contacts (online only for example)
Edit the groups the contact is in
Add or remove RosterGroup if needed
"""
return [contact for contact in self._contacts.keys()]
# add the contact to each group he is in
for group in groups:
if group in contact._groups:
continue
else:
# create the group if it doesn't exist yet
contact._groups.append(group)
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:
# the contact is not in the group anymore
self.remove_contact_from_group(group, contact)
def remove_contact_from_group(self, group_name, contact):
"""
Remove the contact from the group.
Remove also the group if this makes it empty
"""
for group in self._roster_groups:
if group.name == group_name:
group.remove_contact(contact)
if group.is_empty():
self._roster_groups.remove(group)
return
def add_contact_to_group(self, group_name, contact):
"""
Add the contact to the group.
Create the group if it doesn't already exist
"""
for group in self._roster_groups:
if group.name == group_name:
group.add_contact(contact)
return
new_group = RosterGroup(group_name)
self._roster_groups.append(new_group)
new_group.add_contact(contact)
# def ordered_by_group(self, dic_roster, order):
# # ordered by contact
# for jid in dic_roster:
# if not dic_roster[jid]['in_roster']:
# continue
# self.contact_number += 1
# groups=dic_roster[jid]['groups']
# if not groups:
# groups = ['(none)']
# new_contact = Contact(jid, name=dic_roster[jid]['name'],
# groups=groups,
# subscription=dic_roster[jid]['subscription'],
# presence=dic_roster[jid]['presence'])
# for group in groups:
# self.add_contact_to_group(group, new_contact)
# # debug('Jid:%s, (%s)\n' % (jid, dic_roster[jid]))
# debug('\n')
def get_groups(self):
return self._roster_groups
def __len__(self):
return len(self._contacts)
"""
Return the number of line that would be printed
"""
l = 0
for group in self._roster_groups:
l += 1
if not group.folded:
for contact in group.get_contacts():
l += 1
return l
def getContact(self, bare_jid):
if bare_jid not in self._contacts:
return None
return self._contacts[bare_jid]
def __repr__(self):
ret = '== Roster:\nContacts:\n'
for contact in self._contacts:
ret += '%s\n' % (contact,)
ret += 'Groups\n'
for group in self._roster_groups:
ret += '%s\n' % (group,)
return ret + '\n'
class RosterGroup(object):
"""
A RosterGroup is a group containing contacts
It can be Friends/Family etc, but also can be
Online/Offline or whatever
"""
def __init__(self, name, folded=False):
# debug('New group: %s \n' % name)
self._contacts = []
self.name = name
self.folded = folded # if the group content is to be shown
def is_empty(self):
return len(self._contacts) == 0
def remove_contact(self, contact):
"""
Remove a Contact object to the list
"""
assert isinstance(contact, Contact)
assert contact in self._contacts
self._contacts.remove(contact)
def add_contact(self, contact):
"""
append a Contact object to the list
"""
assert isinstance(contact, Contact)
assert contact not in self._contacts
self._contacts.append(contact)
def get_contacts(self):
return self._contacts
def __repr__(self):
return '<Roster_group: %s; %s>' % (self.name, self._contacts)

View file

@ -44,7 +44,7 @@ class Tab(object):
else:
self.visible = True
def refresh(self, tabs, informations):
def refresh(self, tabs, informations, roster):
"""
Called on each screen refresh (when something has changed)
"""
@ -136,7 +136,7 @@ class InfoTab(Tab):
self.text_win.resize(self.height-2, self.width, 0, 0, stdscr, self.visible)
self.input.resize(1, self.width, self.height-1, 0, stdscr, self.visible)
def refresh(self, tabs, informations):
def refresh(self, tabs, informations, _):
self.text_win.refresh(informations)
self.tab_win.refresh(tabs, tabs[0])
self.input.refresh()
@ -206,7 +206,7 @@ class MucTab(Tab):
self.tab_win.resize(1, self.width, self.height-2, 0, stdscr, self.visible)
self.input.resize(1, self.width, self.height-1, 0, stdscr, self.visible)
def refresh(self, tabs, informations):
def refresh(self, tabs, informations, _):
self.topic_win.refresh(self._room.topic)
self.text_win.refresh(self._room)
self.v_separator.refresh()
@ -311,7 +311,7 @@ class PrivateTab(Tab):
self.tab_win.resize(1, self.width, self.height-2, 0, stdscr, self.visible)
self.input.resize(1, self.width, self.height-1, 0, stdscr, self.visible)
def refresh(self, tabs, informations):
def refresh(self, tabs, informations, _):
self.text_win.refresh(self._room)
self.info_header.refresh(self._room)
self.info_win.refresh(informations)
@ -358,12 +358,11 @@ class PrivateTab(Tab):
class RosterInfoTab(Tab):
"""
A tab, splitted in two, containg the roster and infos
A tab, splitted in two, containing the roster and infos
"""
def __init__(self, stdscr, roster):
def __init__(self, stdscr):
Tab.__init__(self, stdscr)
self.name = "Roster"
self.roster = roster
roster_width = self.width//2
info_width = self.width-roster_width-1
self.v_separator = window.VerticalSeparator(self.height-2, 1, 0, roster_width, stdscr, self.visible)
@ -383,8 +382,8 @@ class RosterInfoTab(Tab):
self.roster_win.resize(self.height-2, roster_width, 0, 0, stdscr, self.visible)
self.input.resize(1, self.width, self.height-1, 0, stdscr, self.visible)
def refresh(self, tabs, informations):
self.roster_win.refresh(self.roster)
def refresh(self, tabs, informations, roster):
self.roster_win.refresh(roster)
self.v_separator.refresh()
self.info_win.refresh(informations)
self.tab_win.refresh(tabs, tabs[0])
@ -400,6 +399,8 @@ 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()
return self.input.do_command(key)
def on_lose_focus(self):
@ -412,10 +413,81 @@ class RosterInfoTab(Tab):
return False
def on_scroll_down(self):
debug('TODO DOWN')
self.roster_win.move_cursor_down()
def on_scroll_up(self):
debug('TODO UP')
self.roster_win.move_cursor_up()
def on_info_win_size_changed(self):
return
def on_info_win_size_changed(self, _, __):
pass
def on_enter(self):
debug('%s\n' % (self.roster_win.get_selected_row()))
return self.roster_win.get_selected_row()
class ConversationTab(Tab):
"""
The tab containg a normal conversation (someone from our roster)
"""
def __init__(self, stdscr, room, info_win_size):
Tab.__init__(self, stdscr)
self.info_win_size = info_win_size
self._room = room
self.text_win = window.TextWin(self.height-2-self.info_win_size, self.width, 0, 0, stdscr, self.visible)
self.info_header = window.ConversationInfoWin(1, self.width, self.height-3-self.info_win_size, 0, stdscr, self.visible)
self.info_win = window.TextWin(self.info_win_size, self.width, self.height-2-self.info_win_size, 0, stdscr, self.visible)
self.tab_win = window.GlobalInfoBar(1, self.width, self.height-2, 0, stdscr, self.visible)
self.input = window.Input(1, self.width, self.height-1, 0, stdscr, self.visible)
def resize(self, stdscr):
Tab.resize(self, stdscr)
self.text_win.resize(self.height-2-self.info_win_size, self.width, 0, 0, stdscr, self.visible)
self.info_header.resize(1, self.width, self.height-3-self.info_win_size, 0, stdscr, self.visible)
self.info_win.resize(self.info_win_size, self.width, self.height-2-self.info_win_size, 0, stdscr, self.visible)
self.tab_win.resize(1, self.width, self.height-2, 0, stdscr, self.visible)
self.input.resize(1, self.width, self.height-1, 0, stdscr, self.visible)
def refresh(self, tabs, informations, _):
self.text_win.refresh(self._room)
self.info_header.refresh(self._room)
self.info_win.refresh(informations)
self.tab_win.refresh(tabs, tabs[0])
self.input.refresh()
def get_color_state(self):
if self._room.color_state == theme.COLOR_TAB_NORMAL or\
self._room.color_state == theme.COLOR_TAB_CURRENT:
return self._room.color_state
return theme.COLOR_TAB_PRIVATE
def set_color_state(self, color):
self._room.color_state = color
def get_name(self):
return self._room.name
def on_input(self, key):
return self.input.do_command(key)
def on_lose_focus(self):
self._room.set_color_state(theme.COLOR_TAB_NORMAL)
self._room.remove_line_separator()
self._room.add_line_separator()
def on_gain_focus(self):
self._room.set_color_state(theme.COLOR_TAB_CURRENT)
def on_scroll_up(self):
self._room.scroll_up(self.text_win.height-1)
def on_scroll_down(self):
self._room.scroll_down(self.text_win.height-1)
def on_info_win_size_changed(self, size, stdscr):
self.info_win_size = size
self.text_win.resize(self.height-2, self.width, 0, 0, stdscr, self.visible)
self.info_header.resize(1, self.width, self.height-3-self.info_win_size, 0, stdscr, self.visible)
self.info_win.resize(self.info_win_size, (self.width//10)*9, self.height-2-self.info_win_size, 0, stdscr, self.visible)
def get_room(self):
return self._room

View file

@ -74,7 +74,8 @@ COLOR_STATUS_NONE = 0
COLOR_STATUS_DND = 21
COLOR_STATUS_AWAY = 35
COLOR_STATUS_CHAT = 28
COLOR_STATUS_UNAVAILABLE = 57
COLOR_STATUS_ONLINE = 41
# Bars
COLOR_INFORMATION_BAR = 42
COLOR_TOPIC_BAR = 42

View file

@ -32,7 +32,6 @@ class User(object):
"""
def __init__(self, nick, affiliation, show, status, role):
from common import debug
debug('NEW USER: nick:%s, affiliation:%s, show:%s, status:%s, role:%s\n' % (nick, affiliation, show, status, role))
self.last_talked = datetime(1, 1, 1) # The oldest possible time
self.update(affiliation, show, status, role)
self.change_nick(nick)

View file

@ -28,15 +28,21 @@ from config import config
from threading import Lock
from message import Line
from tab import MIN_WIDTH, MIN_HEIGHT
import theme
from common import debug
g_lock = Lock()
class Win(object):
def __init__(self, height, width, y, x, parent_win):
self._resize(height, width, y, x, parent_win)
self._resize(height, width, y, x, parent_win, True)
def _resize(self, height, width, y, x, parent_win):
def _resize(self, height, width, y, x, parent_win, visible):
if not visible:
return
self.height, self.width, self.x, self.y = height, width, x, y
try:
self.win = curses.newwin(height, width, y, x)
@ -44,6 +50,8 @@ class Win(object):
from common import debug
debug('%s %s %s %s %s\n' % (height, width, y, x, parent_win))
raise
import os
os.abort()
# When resizing in a too little height (less than 3 lines)
# We don't need to resize the window, since this size
# just makes no sense
@ -72,6 +80,14 @@ class Win(object):
"""
self.win.addstr(*args)
def finish_line(self, color):
"""
Write colored spaces until the end of line
"""
(y, x) = self.win.getyx()
size = self.width-x
self.addnstr(' '*size, size, curses.color_pair(color))
class UserList(Win):
def __init__(self, height, width, y, x, parent_win, visible):
Win.__init__(self, height, width, y, x, parent_win)
@ -117,7 +133,7 @@ class UserList(Win):
self.visible = visible
if not visible:
return
self._resize(height, width, y, x, stdscr)
self._resize(height, width, y, x, stdscr, visible)
self.win.attron(curses.color_pair(theme.COLOR_VERTICAL_SEPARATOR))
self.win.vline(0, 0, curses.ACS_VLINE, self.height)
self.win.attroff(curses.color_pair(theme.COLOR_VERTICAL_SEPARATOR))
@ -128,7 +144,7 @@ class Topic(Win):
Win.__init__(self, height, width, y, x, parent_win)
def resize(self, height, width, y, x, stdscr, visible):
self._resize(height, width, y, x, stdscr)
self._resize(height, width, y, x, stdscr, visible)
def refresh(self, topic):
if not self.visible:
@ -150,7 +166,7 @@ class GlobalInfoBar(Win):
Win.__init__(self, height, width, y, x, parent_win)
def resize(self, height, width, y, x, stdscr, visible):
self._resize(height, width, y, x, stdscr)
self._resize(height, width, y, x, stdscr, visible)
def refresh(self, tabs, current):
if not self.visible:
@ -199,14 +215,6 @@ class InfoWin(Win):
plus = ' -PLUS(%s)-' % text_buffer.pos
self.addnstr(plus, len(plus), curses.color_pair(theme.COLOR_SCROLLABLE_NUMBER) | curses.A_BOLD)
def finish_line(self):
"""
Write colored spaces until the end of line
"""
(y, x) = self.win.getyx()
size = self.width-x
self.addnstr(' '*size, size, curses.color_pair(theme.COLOR_INFORMATION_BAR))
class PrivateInfoWin(InfoWin):
"""
The live above the information window, displaying informations
@ -216,7 +224,7 @@ class PrivateInfoWin(InfoWin):
InfoWin.__init__(self, height, width, y, x, parent_win, visible)
def resize(self, height, width, y, x, stdscr, visible):
self._resize(height, width, y, x, stdscr)
self._resize(height, width, y, x, stdscr, visible)
def refresh(self, room):
if not self.visible:
@ -225,7 +233,7 @@ class PrivateInfoWin(InfoWin):
self.win.erase()
self.write_room_name(room)
self.print_scroll_position(room)
self.finish_line()
self.finish_line(theme.COLOR_INFORMATION_BAR)
self.win.refresh()
g_lock.release()
@ -235,6 +243,34 @@ class PrivateInfoWin(InfoWin):
txt = ' from room %s' % room_name
self.addnstr(txt, len(txt), curses.color_pair(theme.COLOR_INFORMATION_BAR))
class ConversationInfoWin(InfoWin):
"""
The line above the information window, displaying informations
about the MUC user we are talking to
"""
def __init__(self, height, width, y, x, parent_win, visible):
InfoWin.__init__(self, height, width, y, x, parent_win, visible)
def resize(self, height, width, y, x, stdscr, visible):
self._resize(height, width, y, x, stdscr, visible)
def refresh(self, room):
if not self.visible:
return
g_lock.acquire()
self.win.erase()
self.write_room_name(room)
self.print_scroll_position(room)
self.finish_line(theme.COLOR_INFORMATION_BAR)
self.win.refresh()
g_lock.release()
def write_room_name(self, room):
# (room_name, nick) = room.name.split('/', 1)
# self.addnstr(nick, len(nick), curses.color_pair(13))
txt = '%s' % room.name
self.addnstr(txt, len(txt), curses.color_pair(theme.COLOR_INFORMATION_BAR))
class MucInfoWin(InfoWin):
"""
The line just above the information window, displaying informations
@ -244,7 +280,7 @@ class MucInfoWin(InfoWin):
InfoWin.__init__(self, height, width, y, x, parent_win, visible)
def resize(self, height, width, y, x, stdscr, visible):
self._resize(height, width, y, x, stdscr)
self._resize(height, width, y, x, stdscr, visible)
def refresh(self, room):
if not self.visible:
@ -256,7 +292,7 @@ class MucInfoWin(InfoWin):
self.write_disconnected(room)
self.write_role(room)
self.print_scroll_position(room)
self.finish_line()
self.finish_line(theme.COLOR_INFORMATION_BAR)
self.win.refresh()
g_lock.release()
@ -291,8 +327,6 @@ class MucInfoWin(InfoWin):
"""
Write our own role and affiliation
"""
from common import debug
own_user = None
for user in room.users:
if user.nick == room.own_nick:
@ -430,7 +464,6 @@ class TextWin(Win):
self.win.attroff(curses.color_pair(color))
else: # Special messages like join or quit
from common import debug
special_words = {
theme.CHAR_JOIN: theme.COLOR_JOIN_CHAR,
theme.CHAR_QUIT: theme.COLOR_QUIT_CHAR,
@ -507,7 +540,7 @@ class TextWin(Win):
def resize(self, height, width, y, x, stdscr, visible):
self.visible = visible
self._resize(height, width, y, x, stdscr)
self._resize(height, width, y, x, stdscr, visible)
class Input(Win):
"""
@ -554,11 +587,14 @@ class Input(Win):
self.last_completion = None # Contains the last nickname completed,
# if last key was a tab
def is_empty(self):
return len(self.text) == 0
def resize(self, height, width, y, x, stdscr, visible):
self.visible = visible
if not visible:
return
self._resize(height, width, y, x, stdscr)
self._resize(height, width, y, x, stdscr, visible)
self.win.clear()
self.addnstr(0, 0, self.text, self.width-1)
@ -896,7 +932,7 @@ class VerticalSeparator(Win):
def resize(self, height, width, y, x, stdscr, visible):
self.visible = visible
self._resize(height, width, y, x, stdscr)
self._resize(height, width, y, x, stdscr, visible)
if not visible:
return
@ -906,70 +942,116 @@ class VerticalSeparator(Win):
self.rewrite_line()
class RosterWin(Win):
color_show = {'xa':theme.COLOR_STATUS_XA,
'none':theme.COLOR_STATUS_ONLINE,
'':theme.COLOR_STATUS_ONLINE,
'available':theme.COLOR_STATUS_ONLINE,
'dnd':theme.COLOR_STATUS_DND,
'away':theme.COLOR_STATUS_AWAY,
'chat':theme.COLOR_STATUS_CHAT,
'unavailable': theme.COLOR_STATUS_UNAVAILABLE
}
def __init__(self, height, width, y, x, parent_win, visible):
self.visible = visible
Win.__init__(self, height, width, y, x, parent_win)
self.pos = 0 # position in the contact list
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
def resize(self, height, width, y, x, stdscr, visible):
self._resize(height, width, y, x, stdscr)
self._resize(height, width, y, x, stdscr, visible)
self.visible = visible
def move_cursor_down(self):
if self.pos < self.roster_len-1:
self.pos += 1
if self.pos == self.start_pos-1 + self.height-1:
self.scroll_down()
def move_cursor_up(self):
if self.pos > 0:
self.pos -= 1
if self.pos == self.start_pos-2:
self.scroll_up()
def scroll_down(self):
self.start_pos += 8
def scroll_up(self):
self.start_pos -= 8
def refresh(self, roster):
"""
We get the roster object
"""
from common import debug
debug('anus%s, %s' % (roster, self.visible))
if not self.visible:
return
g_lock.acquire()
# debug('Len roster: %s, pos: %s, startpos: %s\n(%s:%s)' % (len(roster), self.pos, self.start_pos, self.width, self.height))
self.roster_len = len(roster)
self.win.erase()
# TODO, two ways of scrolling
# currently: always centered
if self.pos > self.height//2 and\
self.pos + self.height//2 < len(roster.getContacts()):
# We are centered
begin = True
end = True
pos = self.height//2
contacts = roster.getContacts()[self.pos-pos:self.pos+pos+1]
elif self.pos <= self.height//2:
# we are at the beginning of the list
pos = self.pos
contacts = roster.getContacts()[:self.height]
begin = False
if self.height < len(roster.getContacts()):
end = True
else:
end = False
else:
# we are at the end of the list
pos = self.height - (len(roster.getContacts()) - self.pos)
contacts = roster.getContacts()[-self.height:]
begin = True
end = False
cpt = 0 # ipair ou chais plus quoi
for contact in contacts:
if cpt == pos:
self.draw_contact_line(contact, cpt, 0, 3)
else:
self.draw_contact_line(contact, cpt, 0)
cpt += 1
if end:
self.win.addstr(self.height-1, 0, '++++')
if begin:
self.win.addstr(0, 0, '++++')
self.draw_roster_information(roster)
y = 1
for group in roster.get_groups():
if y-1 == self.pos:
self.selected_row = group
if y >= self.start_pos:
self.draw_group(y-self.start_pos+1, group, y-1==self.pos)
y += 1
for contact in group.get_contacts():
if y-1 == self.pos:
self.selected_row = contact
if y-self.start_pos+1 == self.height:
break
if y >= self.start_pos:
self.draw_contact_line(y-self.start_pos+1, contact, y-1==self.pos)
y += 1
if y-self.start_pos+1 == self.height:
break
if self.start_pos > 1:
self.draw_plus(1)
if self.start_pos + self.height-2 < self.roster_len:
self.draw_plus(self.height-1)
self.win.refresh()
g_lock.release()
def draw_contact_line(self, contact, x, y, color=None):
def draw_plus(self, y):
"""
Draw the indicator that shows that
the list is longer that what is displayed
"""
self.win.addstr(y, self.width-4, '+++', curses.color_pair(42))
def draw_roster_information(self, roster):
"""
"""
self.win.addstr('%s contacts' % roster.get_contact_len(), curses.color_pair(12))
self.finish_line(12)
def draw_group(self, y, group, colored):
"""
Draw a groupname on a line
"""
if colored:
self.addstr(y, 0, group.name, curses.color_pair(34))
else:
self.addstr(y, 0, group.name)
def draw_contact_line(self, y, contact, colored):
"""
Draw on a line all informations about one contact
Use 'color' to draw the jid/display_name to show what is
is currently selected contact in the list
"""
if color:
self.win.addstr(x, y, contact.getJid().full, curses.color_pair(color))
color = RosterWin.color_show[contact.get_presence()]
self.win.addstr(y, 1, "!", curses.color_pair(color))
if colored:
self.win.addstr(y, 2, contact.get_jid().bare, curses.color_pair(34))
else:
self.win.addstr(x, y, contact.getJid().full)
self.win.addstr(y, 2, contact.get_jid().bare)
def get_selected_row(self):
return self.selected_row