summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/contact.py127
-rw-r--r--src/gui.py60
-rw-r--r--src/roster.py13
-rw-r--r--src/tab.py16
-rw-r--r--src/window.py81
5 files changed, 230 insertions, 67 deletions
diff --git a/src/contact.py b/src/contact.py
index 8966289b..3a0ea30e 100644
--- a/src/contact.py
+++ b/src/contact.py
@@ -16,31 +16,16 @@
from sleekxmpp.xmlstream.jid import JID
-class Contact(object):
+class Resource(object):
"""
- Defines a roster item
+ Defines a roster item.
+ It's a precise resource.
"""
def __init__(self, jid):
- self._jid = JID(jid) # a SleekXMPP jid object
- self._display_name = None
- self._subscription = 'none'
- self._ask = None
+ self._jid = JID(jid) # Full jid
self._status = ''
self._presence = 'unavailable'
self._priority = 0
- self._groups = [] # a list of groups the contact is in
-
- def set_ask(self, ask):
- self._ask = ask
-
- def get_ask(self):
- return self._ask
-
- def set_subscription(self, sub):
- self._subscription = sub
-
- def get_subscription(self, sub):
- return self._subscription
def get_jid(self):
return self._jid
@@ -52,6 +37,9 @@ class Contact(object):
assert isinstance(priority, int)
self._priority = priority
+ def get_priority(self):
+ return self._priority
+
def set_presence(self, pres):
self._presence = pres
@@ -64,8 +52,109 @@ class Contact(object):
def set__status(self, s):
self._status = s
+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
+ to get the resource with the highest priority, etc
+ """
+ def __init__(self, bare_jid):
+ self._jid = bare_jid
+ self._resources = []
+ self._folded = True # Folded by default
+ self._display_name = None
+ self._subscription = 'none'
+ self._ask = None
+ self._groups = [] # a list of groups the contact is in
+
+ def get_bare_jid(self):
+ """
+ Just get the bare_jid or the contact
+ """
+ return self._jid
+
+ def get_highest_priority_resource(self):
+ """
+ There must be, at any time, at least ONE resource.
+ And they always should be ordered by priority.
+ """
+ ret = None
+ for resource in self._resources:
+ if not ret or ret.get_priority() < resource.get_priority():
+ ret = resource
+ return ret
+
+ def add_resource(self, resource):
+ """
+ Called, for example, when a new resource get offline
+ (the first, or any subsequent one)
+ """
+ # TODO sort by priority
+ self._resources.append(resource)
+
+ def remove_resource(self, resource):
+ """
+ Called, for example, when one resource goes offline.
+ """
+ self._resources.remove(resource)
+
+ def remove_resource_by_fulljid(self, fulljid):
+ """
+ Like 'remove_resource' but just by knowing the full jid
+ """
+ for resource in self._resources:
+ if resource.get_jid().full == fulljid:
+ self._resources.remove(resource)
+ return
+ assert False
+
+ def get_resource_by_fulljid(self, fulljid):
+ """
+ Return the resource with the given fulljid
+ """
+ for resource in self._resources:
+ if resource.get_jid().full == fulljid:
+ return resource
+ return None
+ def toggle_folded(self):
+ """
+ Fold if it's unfolded, and vice versa
+ """
+ self._folded = not self._folded
+
def set_name(self, name):
self._display_name = name
def get_name(self):
return self._display_name
+
+ def set_ask(self, ask):
+ self._ask = ask
+
+ def get_ask(self):
+ return self._ask
+
+ def set_subscription(self, sub):
+ self._subscription = sub
+
+ def get_subscription(self, sub):
+ return self._subscription
+
+ def get_nb_resources(self):
+ """
+ Get the number of connected resources
+ """
+ return len(self._resources)
+
+ def get_resources(self):
+ """
+ Return all resources
+ """
+ compare_resources = lambda x: x.get_priority()
+ return sorted(self._resources, key=compare_resources)
+
+ def __repr__(self):
+ ret = '<Contact: %s' % self._jid
+ for resource in self._resources:
+ ret += '\n\t\t%s'%resource
+ return ret + ' />\n'
diff --git a/src/gui.py b/src/gui.py
index 8ee82e59..0665c9a8 100644
--- a/src/gui.py
+++ b/src/gui.py
@@ -40,13 +40,12 @@ from tab import MucTab, InfoTab, PrivateTab, RosterInfoTab, ConversationTab
from user import User
from room import Room
from roster import Roster, RosterGroup
-from contact import Contact
+from contact import Contact, Resource
from message import Message
from text_buffer import TextBuffer
from keyboard import read_char
from common import jid_get_domain, is_jid
-from common import debug
# http://xmpp.org/extensions/xep-0045.html#errorstatus
ERROR_AND_STATUS_CODES = {
'401': _('A password is required'),
@@ -148,7 +147,7 @@ class Gui(object):
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)
+ self.xmpp.add_event_handler("changed_status", self.on_presence)
def grow_information_win(self):
"""
@@ -175,19 +174,28 @@ class Gui(object):
contact = self.roster.get_contact_by_jid(jid.bare)
if not contact:
return
- contact.set_presence('unavailable')
- self.information('%s is offline' % (contact.get_jid()), "Roster")
+ resource = contact.get_resource_by_fulljid(jid.full)
+ assert resource
+ self.information('%s is offline' % (resource.get_jid()), "Roster")
+ contact.remove_resource(resource)
+ if isinstance(self.current_tab(), RosterInfoTab):
+ self.refresh_window()
def on_got_online(self, presence):
jid = presence['from']
contact = self.roster.get_contact_by_jid(jid.bare)
if not contact:
+ # Todo, handle presence comming from contacts not in roster
return
+ resource = contact.get_resource_by_fulljid(jid.full)
+ assert not resource
+ resource = Resource(jid.full)
status = presence['type']
- priority = presence.getPriority()
- contact.set_presence(status)
- contact.set_priority(priority)
- self.information("%s is online (%s)" % (contact.get_jid(), status), "Roster")
+ priority = presence.getPriority() or 0
+ resource.set_presence(status)
+ resource.set_priority(priority)
+ contact.add_resource(resource)
+ self.information("%s is online (%s)" % (resource.get_jid().full, status), "Roster")
def on_connected(self, event):
"""
@@ -445,8 +453,6 @@ class Gui(object):
"""
When receiving "normal" messages (from someone in our roster)
"""
- from common import debug
- debug('MESSAGE: %s\n' % (message))
jid = message['from'].bare
room = self.get_conversation_by_jid(jid)
body = message['body']
@@ -463,9 +469,20 @@ class Gui(object):
def on_presence(self, presence):
"""
"""
- from common import debug
- debug('Presence: %s\n' % (presence))
- return
+ jid = presence['from']
+ # contact = ros
+ contact = self.roster.get_contact_by_jid(jid.bare)
+ if not contact:
+ return
+ resource = contact.get_resource_by_fulljid(jid.full)
+ if not resource:
+ return
+ status = presence['type']
+ priority = presence.getPriority() or 0
+ resource.set_presence(status)
+ resource.set_priority(priority)
+ if isinstance(self.current_tab(), RosterInfoTab):
+ self.refresh_window()
def on_roster_update(self, iq):
"""
@@ -491,7 +508,6 @@ class Gui(object):
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 call_for_resize(self):
@@ -1297,12 +1313,16 @@ class Gui(object):
when enter is pressed on the roster window
"""
if isinstance(roster_row, Contact):
- if not self.get_conversation_by_jid(roster_row.get_jid().bare):
- self.open_conversation_window(roster_row.get_jid().bare)
+ # roster_row.toggle_folded()
+ if not self.get_conversation_by_jid(roster_row.get_bare_jid()):
+ self.open_conversation_window(roster_row.get_bare_jid())
+ else:
+ self.focus_tab_named(roster_row.get_bare_jid())
+ if isinstance(roster_row, Resource):
+ if not self.get_conversation_by_jid(roster_row.get_jid().full):
+ self.open_conversation_window(roster_row.get_jid().full)
else:
- self.focus_tab_named(roster_row.get_jid().bare)
- elif isinstance(roster_row, RosterGroup):
- roster_row.folded = not roster_row.folded
+ self.focus_tab_named(roster_row.get_jid().full)
self.refresh_window()
def execute(self,line):
diff --git a/src/roster.py b/src/roster.py
index 72f885bc..03a5f93a 100644
--- a/src/roster.py
+++ b/src/roster.py
@@ -14,13 +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/>.
-from contact import Contact
-
-from common import debug
+from contact import Contact, Resource
class Roster(object):
def __init__(self):
- self._contacts = {} # key = jid; value = Contact()
+ self._contacts = {} # key = bare jid; value = Contact()
self._roster_groups = []
def add_contact(self, contact, jid):
@@ -97,6 +95,8 @@ class Roster(object):
if not group.folded:
for contact in group.get_contacts():
l += 1
+ if not contact._folded:
+ l += contact.get_nb_resources()
return l
def __repr__(self):
@@ -115,10 +115,10 @@ class RosterGroup(object):
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
@@ -143,3 +143,6 @@ class RosterGroup(object):
def __repr__(self):
return '<Roster_group: %s; %s>' % (self.name, self._contacts)
+
+ def toggle_folded(self):
+ self.folded = not self.folded
diff --git a/src/tab.py b/src/tab.py
index 4acfab08..35e71efe 100644
--- a/src/tab.py
+++ b/src/tab.py
@@ -29,8 +29,7 @@ import window
import theme
import curses
from roster import RosterGroup
-
-from common import debug
+from contact import Contact, Resource
class Tab(object):
"""
@@ -426,7 +425,10 @@ class RosterInfoTab(Tab):
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)
+ if key == ' ':
+ return self.on_space()
+ # In writting mode
+ # return self.input.do_command(key)
def on_lose_focus(self):
self._color_state = theme.COLOR_TAB_NORMAL
@@ -447,6 +449,12 @@ class RosterInfoTab(Tab):
def on_info_win_size_changed(self, _, __):
pass
+ def on_space(self):
+ selected_row = self.roster_win.get_selected_row()
+ if isinstance(selected_row, RosterGroup) or\
+ 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
@@ -478,7 +486,7 @@ class ConversationTab(Tab):
def refresh(self, tabs, informations, roster):
self.text_win.refresh(self._room)
- self.info_header.refresh(self._room, roster.get_contact_by_jid(self._room.name))
+ # self.info_header.refresh(self._room, roster.get_contact_by_jid(self._room.name))
self.info_win.refresh(informations)
self.tab_win.refresh(tabs, tabs[0])
self.input.refresh()
diff --git a/src/window.py b/src/window.py
index c8a05625..0e628a8c 100644
--- a/src/window.py
+++ b/src/window.py
@@ -27,7 +27,7 @@ from config import config
from threading import Lock
-from contact import Contact
+from contact import Contact, Resource
from roster import RosterGroup
from message import Line
@@ -35,8 +35,6 @@ from tab import MIN_WIDTH, MIN_HEIGHT
import theme
-from common import debug
-
g_lock = Lock()
class Win(object):
@@ -255,11 +253,13 @@ class ConversationInfoWin(InfoWin):
# contact can be None, if we receive a message
# from someone not in our roster. In this case, we display
# only the maximum information from the message we can get.
+ # Also, contact can be a resource, if we're talking to a
+ # specific resource.
with g_lock:
self._win.erase()
- self.write_room_name(contact, room)
- self.print_scroll_position(room)
- self.finish_line(theme.COLOR_INFORMATION_BAR)
+ # self.write_room_name(resource, room)
+ # self.print_scroll_position(room)
+ # self.finish_line(theme.COLOR_INFORMATION_BAR)
self._refresh()
def write_room_name(self, contact, room):
@@ -926,7 +926,7 @@ class RosterWin(Win):
'dnd':theme.COLOR_STATUS_DND,
'away':theme.COLOR_STATUS_AWAY,
'chat':theme.COLOR_STATUS_CHAT,
- 'unavailable': theme.COLOR_STATUS_UNAVAILABLE
+ 'unavailable':theme.COLOR_STATUS_UNAVAILABLE
}
# subscription_char = {'both': '
def __init__(self, height, width, y, x, parent_win, visible):
@@ -971,6 +971,7 @@ class RosterWin(Win):
self.draw_roster_information(roster)
y = 1
for group in roster.get_groups():
+ # This loop is really REALLY ugly :^)
if y-1 == self.pos:
self.selected_row = group
if y >= self.start_pos:
@@ -986,6 +987,15 @@ class RosterWin(Win):
if y >= self.start_pos:
self.draw_contact_line(y-self.start_pos+1, contact, y-1==self.pos)
y += 1
+ if not contact._folded:
+ for resource in contact.get_resources():
+ if y-1 == self.pos:
+ self.selected_row = resource
+ if y-self.start_pos+1 == self.height:
+ break
+ if y >= self.start_pos:
+ self.draw_resource_line(y-self.start_pos+1, resource, y-1==self.pos)
+ y += 1
if y-self.start_pos+1 == self.height:
break
if self.start_pos > 1:
@@ -1023,21 +1033,46 @@ class RosterWin(Win):
def draw_contact_line(self, y, contact, colored):
"""
- Draw on a line all informations about one contact
+ Draw on a line all informations about one contact.
+ This is basically the highest priority resource's informations
Use 'color' to draw the jid/display_name to show what is
- is currently selected contact in the list
- """
- color = RosterWin.color_show[contact.get_presence()]
+ the currently selected contact in the list
+ """
+ resource = contact.get_highest_priority_resource()
+ if not resource:
+ # There's no online resource
+ presence = 'unavailable'
+ folder = ' '
+ nb = ''
+ else:
+ presence = resource.get_presence()
+ folder = '[+]' if contact._folded else '[-]'
+ nb = '(%s)' % (contact.get_nb_resources(),)
+ color = RosterWin.color_show[presence]
if contact.get_name():
- display_name = '%s (%s)' % (contact.get_name(),
- contact.get_jid().bare)
+ display_name = '%s (%s) %s' % (contact.get_name(),
+ contact.get_bare_jid(), nb,)
else:
- display_name = '%s' % (contact.get_jid().bare,)
+ display_name = '%s %s' % (contact.get_bare_jid(), nb,)
self.addstr(y, 1, " ", curses.color_pair(color))
+ if resource:
+ self.addstr(y, 2, ' [+]' if contact._folded else ' [-]')
+ self.addstr(' ')
if colored:
- self.addstr(y, 4, display_name, curses.color_pair(14))
+ self.addstr(display_name, curses.color_pair(14))
else:
- self.addstr(y, 4, display_name)
+ self.addstr(display_name)
+
+ def draw_resource_line(self, y, resource, colored):
+ """
+ Draw a specific resource line
+ """
+ color = RosterWin.color_show[resource.get_presence()]
+ self.addstr(y, 4, " ", curses.color_pair(color))
+ if colored:
+ self.addstr(y, 6, resource.get_jid().full, curses.color_pair(14))
+ else:
+ self.addstr(y, 6, resource.get_jid().full)
def get_selected_row(self):
return self.selected_row
@@ -1051,12 +1086,17 @@ class ContactInfoWin(Win):
self._resize(height, width, y, x, stdscr, visible)
self.visible = visible
- def draw_contact_info(self, contact):
+ def draw_contact_info(self, resource, jid=None):
"""
draw the contact information
"""
- self.addstr(0, 0, contact.get_jid().full, curses.color_pair(theme.COLOR_INFORMATION_BAR))
- self.addstr(' (%s)'%(contact.get_presence(),), curses.color_pair(theme.COLOR_INFORMATION_BAR))
+ jid = jid or resource.get_jid().full
+ if resource:
+ presence = resource.get_presence()
+ else:
+ presence = 'unavailable'
+ self.addstr(0, 0, jid, curses.color_pair(theme.COLOR_INFORMATION_BAR))
+ self.addstr(' (%s)'%(presence,), curses.color_pair(theme.COLOR_INFORMATION_BAR))
self.finish_line(theme.COLOR_INFORMATION_BAR)
def draw_group_info(self, group):
@@ -1074,5 +1114,8 @@ class ContactInfoWin(Win):
if isinstance(selected_row, RosterGroup):
self.draw_group_info(selected_row)
elif isinstance(selected_row, Contact):
+ self.draw_contact_info(selected_row.get_highest_priority_resource(),
+ selected_row.get_bare_jid())
+ elif isinstance(selected_row, Resource):
self.draw_contact_info(selected_row)
self._refresh()