primitivus: contact list enhancement
- primitivus: contact list now display groups and sexy name (instead of bare jid) - primitivus: contact list now show an alert when somebody want to contact you, and its chat window is not on the screen - primitivus: cursor is now visible when going to chat window (useful to go back in logs)
This commit is contained in:
parent
61f6edae54
commit
5f3463b147
6 changed files with 180 additions and 50 deletions
|
|
@ -48,7 +48,14 @@ class ChatText(urwid.FlowWidget):
|
|||
return self.display_widget(size, focus).rows(size, focus)
|
||||
|
||||
def render(self, size, focus=False):
|
||||
return self.display_widget(size, focus).render(size, focus)
|
||||
canvas = urwid.CompositeCanvas(self.display_widget(size, focus).render(size, focus))
|
||||
if focus:
|
||||
canvas.set_cursor(self.get_cursor_coords(size))
|
||||
return canvas
|
||||
|
||||
def get_cursor_coords(self, size):
|
||||
#(maxcol,) = size
|
||||
return 0, 0
|
||||
|
||||
def display_widget(self, size, focus):
|
||||
my_mess = (self.from_jid.resource == self.parent.nick) if self.parent.type == "group" else (self.from_jid.short == self.my_jid.short) #mymess = True if message comes from local user
|
||||
|
|
@ -134,7 +141,7 @@ class Chat(urwid.WidgetWrap, QuickChat):
|
|||
|
||||
|
||||
def __buildPresentList(self):
|
||||
self.present_wid = custom_widgets.GenericList([],option_type = custom_widgets.UnselectableText)
|
||||
self.present_wid = custom_widgets.GenericList([],option_type = custom_widgets.ClickableText)
|
||||
return self.present_wid
|
||||
|
||||
def __appendPresentPanel(self):
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
|
||||
import urwid
|
||||
from quick_frontend.quick_contact_list import QuickContactList
|
||||
from tools.jid import JID
|
||||
import custom_widgets
|
||||
|
||||
|
||||
|
|
@ -29,13 +30,13 @@ class ContactList(urwid.WidgetWrap, QuickContactList):
|
|||
|
||||
def __init__(self, host, CM, on_click=None, on_change=None, user_data=None):
|
||||
self.host = host
|
||||
|
||||
self.list_wid = custom_widgets.GenericList([], style=['single','no_first_select'], align='left', on_click=self.__contactClicked, on_change=on_change)
|
||||
|
||||
self.selected = None
|
||||
self.groups={}
|
||||
self.alert_jid=set()
|
||||
|
||||
#we now build the widget
|
||||
frame_body = self.list_wid
|
||||
frame = urwid.Frame(frame_body)
|
||||
self.main_widget = custom_widgets.LabelLine(frame, custom_widgets.SurroundedText(_("Contacts")))
|
||||
self.frame = urwid.Frame(self.__buildList())
|
||||
self.main_widget = custom_widgets.LabelLine(self.frame, custom_widgets.SurroundedText(_("Contacts")))
|
||||
urwid.WidgetWrap.__init__(self, self.main_widget)
|
||||
if on_click:
|
||||
urwid.connect_signal(self, 'click', on_click, user_data)
|
||||
|
|
@ -44,38 +45,126 @@ class ContactList(urwid.WidgetWrap, QuickContactList):
|
|||
QuickContactList.__init__(self, CM)
|
||||
|
||||
def __contains__(self, jid):
|
||||
contacts = self.list_wid.getAllValues()
|
||||
return jid.short in contacts
|
||||
for group in self.groups:
|
||||
if jid.short in self.groups[group][1]:
|
||||
return True
|
||||
return False
|
||||
|
||||
def __contactClicked(self, list_wid):
|
||||
def setFocus(self, name):
|
||||
"""give focus to the first group or contact with the given name"""
|
||||
idx = 0
|
||||
for widget in self.frame.body.body:
|
||||
if widget.getValue() == name:
|
||||
self.frame.body.set_focus(idx)
|
||||
return
|
||||
idx+=1
|
||||
|
||||
def putAlert(self, jid):
|
||||
"""Put an alert on the jid to get attention from user (e.g. for new message)"""
|
||||
self.alert_jid.add(jid.short)
|
||||
self.frame.body = self.__buildList()
|
||||
self.host.redraw()
|
||||
|
||||
def __groupClicked(self, group_wid):
|
||||
group = self.groups[group_wid.getValue()]
|
||||
group[0] = not group[0]
|
||||
self.frame.body = self.__buildList()
|
||||
self.host.redraw()
|
||||
self.setFocus(group_wid.getValue())
|
||||
|
||||
def __contactClicked(self, contact_wid, selected):
|
||||
self.selected = contact_wid.data
|
||||
for widget in self.frame.body.body:
|
||||
if widget.__class__ == custom_widgets.SelectableText:
|
||||
widget.setState(widget.data == self.selected, invisible=True)
|
||||
if self.selected in self.alert_jid:
|
||||
self.alert_jid.remove(self.selected)
|
||||
self.frame.body = self.__buildList()
|
||||
self.host.redraw()
|
||||
self._emit('click')
|
||||
|
||||
def __buildContact(self, content, param_contacts):
|
||||
"""Add contact representation in widget list
|
||||
@param content: widget list, e.g. SimpleListWalker
|
||||
@param contacts: list of JID"""
|
||||
contacts = list(param_contacts)
|
||||
contacts.sort()
|
||||
for contact in contacts:
|
||||
jid=JID(contact)
|
||||
name = self.CM.getAttr(jid,'name')
|
||||
nick = self.CM.getAttr(jid,'nick')
|
||||
display = nick or name or jid.node or jid.short
|
||||
header = '(*) ' if contact in self.alert_jid else ''
|
||||
widget = custom_widgets.SelectableText(display, selected = contact==self.selected, header=header, data=contact)
|
||||
if contact in self.alert_jid:
|
||||
widget.setAttribute('default','alert')
|
||||
content.append(widget)
|
||||
urwid.connect_signal(widget, 'change', self.__contactClicked)
|
||||
|
||||
def __buildList(self):
|
||||
"""Build the main contact list widget"""
|
||||
content = urwid.SimpleListWalker([])
|
||||
group_keys = self.groups.keys()
|
||||
group_keys.sort()
|
||||
for key in group_keys:
|
||||
unfolded = self.groups[key][0]
|
||||
if key!=None:
|
||||
header = '[-]' if unfolded else '[+]'
|
||||
widget = custom_widgets.ClickableText(key,header=header+' ')
|
||||
content.append(widget)
|
||||
urwid.connect_signal(widget, 'click', self.__groupClicked)
|
||||
if unfolded:
|
||||
self.__buildContact(content, self.groups[key][1])
|
||||
return urwid.ListBox(content)
|
||||
|
||||
def get_contact(self):
|
||||
"""Return contact currently selected"""
|
||||
return self.list_wid.getSelectedValue()
|
||||
return self.selected
|
||||
|
||||
def clear_contacts(self):
|
||||
"""clear all the contact list"""
|
||||
self.list_wid.changeValues([])
|
||||
self.groups={}
|
||||
|
||||
def replace(self, jid, groups=None):
|
||||
def replace(self, jid, groups=[None]):
|
||||
"""add a contact to the list if doesn't exist, else update it"""
|
||||
contacts = self.list_wid.getAllValues()
|
||||
assert groups.__class__ == list
|
||||
assert jid.__class__ == JID
|
||||
if not groups:
|
||||
groups=[None]
|
||||
for group in groups:
|
||||
if not self.groups.has_key(group):
|
||||
self.groups[group] = [True,set()] #[unfold,list_of_contacts]
|
||||
self.groups[group][1].add(jid.short)
|
||||
self.frame.body = self.__buildList()
|
||||
self.host.redraw()
|
||||
|
||||
|
||||
"""contacts = self.list_wid.getAllValues()
|
||||
if jid.short not in contacts:
|
||||
contacts.append(jid.short)
|
||||
contacts.sort()
|
||||
self.list_wid.changeValues(contacts)
|
||||
self._emit('change')
|
||||
self._emit('change')"""
|
||||
|
||||
def disconnect(self, jid):
|
||||
"""mark a contact disconnected"""
|
||||
self.remove(jid)
|
||||
self.remove(jid.short)
|
||||
|
||||
def remove(self, jid):
|
||||
"""remove a contact from the list"""
|
||||
self.list_wid.deleteValue(jid.short)
|
||||
groups_to_remove = []
|
||||
for group in self.groups:
|
||||
contacts = self.groups[group][1]
|
||||
if jid.short in contacts:
|
||||
contacts.remove(jid.short)
|
||||
if not len(contacts):
|
||||
groups_to_remove.append(group)
|
||||
for group in groups_to_remove:
|
||||
del self.groups[group]
|
||||
self.frame.body = self.__buildList()
|
||||
self.host.redraw()
|
||||
|
||||
def add(self, jid, param_groups=None):
|
||||
def add(self, jid, param_groups=[None]):
|
||||
"""add a contact to the list"""
|
||||
self.replace(jid)
|
||||
self.replace(jid,param_groups)
|
||||
|
||||
|
|
|
|||
|
|
@ -95,13 +95,31 @@ class SelectableText(urwid.FlowWidget):
|
|||
"""Text which can be selected with space"""
|
||||
signals = ['change']
|
||||
|
||||
def __init__(self, text, align='left'):
|
||||
def __init__(self, text, align='left', header='', select_attr=None, default_attr=None, selected = False, data=None):
|
||||
self.text=unicode(text)
|
||||
self.header=header
|
||||
if data:
|
||||
self.data=data
|
||||
if select_attr:
|
||||
self.selected = select_attr
|
||||
if default_attr:
|
||||
self.default = default_attr
|
||||
self.align = align
|
||||
self.__selected=False
|
||||
self.__selected=selected
|
||||
|
||||
def getValue(self):
|
||||
return self.text
|
||||
|
||||
def setAttribute(self, name, value):
|
||||
"""Change attribut used for rendering widget
|
||||
@param name: one of
|
||||
-default: when not selected
|
||||
-selected: when selected
|
||||
@param value: name of the attribute
|
||||
/!\ the attribute name followed by _focus is used when widget has focus"""
|
||||
assert name in ['default', 'selected']
|
||||
self.__setattr__(name,value)
|
||||
self._invalidate()
|
||||
|
||||
def setState(self, selected, invisible=False):
|
||||
"""Change state
|
||||
|
|
@ -132,15 +150,24 @@ class SelectableText(urwid.FlowWidget):
|
|||
return self.display_widget(size, focus).render(size, focus)
|
||||
|
||||
def display_widget(self, size, focus):
|
||||
attr = 'selected' if self.__selected else 'default'
|
||||
try:
|
||||
select_attr = self.selected
|
||||
except AttributeError:
|
||||
select_attr = 'selected'
|
||||
try:
|
||||
default_attr = self.default
|
||||
except AttributeError:
|
||||
default_attr = 'default'
|
||||
attr = select_attr if self.__selected else default_attr
|
||||
if focus:
|
||||
attr+="_focus"
|
||||
return urwid.Text((attr,self.text), align=self.align)
|
||||
return urwid.Text((attr,self.header+self.text), align=self.align)
|
||||
|
||||
class UnselectableText(SelectableText):
|
||||
class ClickableText(SelectableText):
|
||||
signals = SelectableText.signals + ['click']
|
||||
|
||||
def setState(self, selected, invisible=False):
|
||||
pass
|
||||
self._emit('click')
|
||||
|
||||
class GenericList(urwid.WidgetWrap):
|
||||
signals = ['click','change']
|
||||
|
|
|
|||
|
|
@ -60,6 +60,8 @@ const_PALETTE = [('title', 'black', 'light gray', 'standout,underline'),
|
|||
('selected_focus', 'default,bold', 'dark red'),
|
||||
('default', 'default', 'default'),
|
||||
('default_focus', 'default,bold', 'default'),
|
||||
('alert', 'default,underline', 'default'),
|
||||
('alert_focus', 'default,bold,underline', 'default'),
|
||||
('date', 'light gray', 'default'),
|
||||
('my_nick', 'dark red,bold', 'default'),
|
||||
('other_nick', 'dark cyan,bold', 'default'),
|
||||
|
|
@ -125,8 +127,6 @@ class PrimitivusApp(QuickApp):
|
|||
#The main widget is not built (probably in Profile Manager)
|
||||
pass
|
||||
|
||||
|
||||
|
||||
def __buildMainWidget(self):
|
||||
self.contactList = ContactList(self, self.CM, on_click = self.contactSelected, on_change=lambda w: self.redraw())
|
||||
#self.center_part = urwid.Columns([('weight',2,self.contactList),('weight',8,Chat('',self))])
|
||||
|
|
@ -166,6 +166,14 @@ class PrimitivusApp(QuickApp):
|
|||
profile_key=self.profile)
|
||||
editBar.set_edit_text('')
|
||||
|
||||
def newMessage(self, from_jid, msg, type, to_jid, profile):
|
||||
if not self.check_profile(profile):
|
||||
return
|
||||
QuickApp.newMessage(self, from_jid, msg, type, to_jid, profile)
|
||||
sender = JID(from_jid)
|
||||
if JID(self.contactList.selected).short != sender.short:
|
||||
self.contactList.putAlert(sender)
|
||||
|
||||
def onJoinRoom(self, button, edit):
|
||||
self.removePopUp()
|
||||
room_jid = JID(edit.get_edit_text())
|
||||
|
|
|
|||
|
|
@ -67,7 +67,7 @@ class QuickApp():
|
|||
self.current_action_ids = set()
|
||||
self.current_action_ids_cb = {}
|
||||
|
||||
def __check_profile(self, profile):
|
||||
def check_profile(self, profile):
|
||||
"""Tell if the profile is currently followed by the application"""
|
||||
return profile in self.profiles.keys()
|
||||
|
||||
|
|
@ -160,7 +160,7 @@ class QuickApp():
|
|||
|
||||
def connected(self, profile):
|
||||
"""called when the connection is made"""
|
||||
if not self.__check_profile(profile):
|
||||
if not self.check_profile(profile):
|
||||
return
|
||||
debug(_("Connected"))
|
||||
self.setStatusOnline(True)
|
||||
|
|
@ -169,7 +169,7 @@ class QuickApp():
|
|||
|
||||
def disconnected(self, profile):
|
||||
"""called when the connection is closed"""
|
||||
if not self.__check_profile(profile):
|
||||
if not self.check_profile(profile):
|
||||
return
|
||||
debug(_("Disconnected"))
|
||||
self.CM.clear()
|
||||
|
|
@ -177,13 +177,13 @@ class QuickApp():
|
|||
self.setStatusOnline(False)
|
||||
|
||||
def newContact(self, JabberId, attributes, groups, profile):
|
||||
if not self.__check_profile(profile):
|
||||
if not self.check_profile(profile):
|
||||
return
|
||||
entity=JID(JabberId)
|
||||
self.rosterList[entity.short]=(dict(attributes), list(groups))
|
||||
|
||||
def newMessage(self, from_jid, msg, type, to_jid, profile):
|
||||
if not self.__check_profile(profile):
|
||||
if not self.check_profile(profile):
|
||||
return
|
||||
sender=JID(from_jid)
|
||||
addr=JID(to_jid)
|
||||
|
|
@ -196,7 +196,7 @@ class QuickApp():
|
|||
pass
|
||||
|
||||
def presenceUpdate(self, jabber_id, show, priority, statuses, profile):
|
||||
if not self.__check_profile(profile):
|
||||
if not self.check_profile(profile):
|
||||
return
|
||||
debug (_("presence update for %(jid)s (show=%(show)s, priority=%(priority)s, statuses=%(statuses)s) [profile:%(profile)s]") % {'jid':jabber_id, 'show':show, 'priority':priority, 'statuses':statuses, 'profile':profile});
|
||||
from_jid=JID(jabber_id)
|
||||
|
|
@ -242,7 +242,7 @@ class QuickApp():
|
|||
|
||||
def roomJoined(self, room_id, room_service, room_nicks, user_nick, profile):
|
||||
"""Called when a MUC room is joined"""
|
||||
if not self.__check_profile(profile):
|
||||
if not self.check_profile(profile):
|
||||
return
|
||||
debug (_("Room [%(room_name)s] joined by %(profile)s, users presents:%(users)s") % {'room_name':room_id+'@'+room_service, 'profile': profile, 'users':room_nicks})
|
||||
room_jid=room_id+'@'+room_service
|
||||
|
|
@ -254,7 +254,7 @@ class QuickApp():
|
|||
|
||||
def roomUserJoined(self, room_id, room_service, user_nick, user_data, profile):
|
||||
"""Called when an user joined a MUC room"""
|
||||
if not self.__check_profile(profile):
|
||||
if not self.check_profile(profile):
|
||||
return
|
||||
room_jid=room_id+'@'+room_service
|
||||
if self.chat_wins.has_key(room_jid):
|
||||
|
|
@ -263,7 +263,7 @@ class QuickApp():
|
|||
|
||||
def roomUserLeft(self, room_id, room_service, user_nick, user_data, profile):
|
||||
"""Called when an user joined a MUC room"""
|
||||
if not self.__check_profile(profile):
|
||||
if not self.check_profile(profile):
|
||||
return
|
||||
room_jid=room_id+'@'+room_service
|
||||
if self.chat_wins.has_key(room_jid):
|
||||
|
|
@ -272,7 +272,7 @@ class QuickApp():
|
|||
|
||||
def roomNewSubject(self, room_id, room_service, subject, profile):
|
||||
"""Called when subject of MUC room change"""
|
||||
if not self.__check_profile(profile):
|
||||
if not self.check_profile(profile):
|
||||
return
|
||||
room_jid=room_id+'@'+room_service
|
||||
if self.chat_wins.has_key(room_jid):
|
||||
|
|
@ -280,7 +280,7 @@ class QuickApp():
|
|||
debug (_("new subject for room [%(room_jid)s]: %(subject)s") % {'room_jid':room_jid, "subject":subject})
|
||||
|
||||
def tarotGameStarted(self, room_jid, referee, players, profile):
|
||||
if not self.__check_profile(profile):
|
||||
if not self.check_profile(profile):
|
||||
return
|
||||
debug (_("Tarot Game Started \o/"))
|
||||
if self.chat_wins.has_key(room_jid):
|
||||
|
|
@ -288,7 +288,7 @@ class QuickApp():
|
|||
debug (_("new Tarot game started by [%(referee)s] in room [%(room_jid)s] with %(players)s") % {'referee':referee, 'room_jid':room_jid, 'players':[str(player) for player in players]})
|
||||
|
||||
def tarotGameNew(self, room_jid, hand, profile):
|
||||
if not self.__check_profile(profile):
|
||||
if not self.check_profile(profile):
|
||||
return
|
||||
debug (_("New Tarot Game"))
|
||||
if self.chat_wins.has_key(room_jid):
|
||||
|
|
@ -296,21 +296,21 @@ class QuickApp():
|
|||
|
||||
def tarotChooseContrat(self, room_jid, xml_data, profile):
|
||||
"""Called when the player has too select his contrat"""
|
||||
if not self.__check_profile(profile):
|
||||
if not self.check_profile(profile):
|
||||
return
|
||||
debug (_("Tarot: need to select a contrat"))
|
||||
if self.chat_wins.has_key(room_jid):
|
||||
self.chat_wins[room_jid].getGame("Tarot").chooseContrat(xml_data)
|
||||
|
||||
def tarotShowCards(self, room_jid, game_stage, cards, data, profile):
|
||||
if not self.__check_profile(profile):
|
||||
if not self.check_profile(profile):
|
||||
return
|
||||
debug (_("Show cards"))
|
||||
if self.chat_wins.has_key(room_jid):
|
||||
self.chat_wins[room_jid].getGame("Tarot").showCards(game_stage, cards, data)
|
||||
|
||||
def tarotMyTurn(self, room_jid, profile):
|
||||
if not self.__check_profile(profile):
|
||||
if not self.check_profile(profile):
|
||||
return
|
||||
debug (_("My turn to play"))
|
||||
if self.chat_wins.has_key(room_jid):
|
||||
|
|
@ -318,21 +318,21 @@ class QuickApp():
|
|||
|
||||
def tarotScore(self, room_jid, xml_data, winners, loosers, profile):
|
||||
"""Called when the game is finished and the score are updated"""
|
||||
if not self.__check_profile(profile):
|
||||
if not self.check_profile(profile):
|
||||
return
|
||||
debug (_("Tarot: score received"))
|
||||
if self.chat_wins.has_key(room_jid):
|
||||
self.chat_wins[room_jid].getGame("Tarot").showScores(xml_data, winners, loosers)
|
||||
|
||||
def tarotCardsPlayed(self, room_jid, player, cards, profile):
|
||||
if not self.__check_profile(profile):
|
||||
if not self.check_profile(profile):
|
||||
return
|
||||
debug (_("Card(s) played (%(player)s): %(cards)s") % {"player":player, "cards":cards})
|
||||
if self.chat_wins.has_key(room_jid):
|
||||
self.chat_wins[room_jid].getGame("Tarot").cardsPlayed(player, cards)
|
||||
|
||||
def tarotInvalidCards(self, room_jid, phase, played_cards, invalid_cards, profile):
|
||||
if not self.__check_profile(profile):
|
||||
if not self.check_profile(profile):
|
||||
return
|
||||
debug (_("Cards played are not valid: %s") % invalid_cards)
|
||||
if self.chat_wins.has_key(room_jid):
|
||||
|
|
@ -340,7 +340,7 @@ class QuickApp():
|
|||
|
||||
def subscribe(self, type, raw_jid, profile):
|
||||
"""Called when a subsciption management signal is received"""
|
||||
if not self.__check_profile(profile):
|
||||
if not self.check_profile(profile):
|
||||
return
|
||||
entity = JID(raw_jid)
|
||||
if type=="subscribed":
|
||||
|
|
@ -364,7 +364,7 @@ class QuickApp():
|
|||
pass #FIXME
|
||||
|
||||
def paramUpdate(self, name, value, namespace, profile):
|
||||
if not self.__check_profile(profile):
|
||||
if not self.check_profile(profile):
|
||||
return
|
||||
debug(_("param update: [%(namespace)s] %(name)s = %(value)s") % {'namespace':namespace, 'name':name, 'value':value})
|
||||
if (namespace,name) == ("Connection", "JabberID"):
|
||||
|
|
@ -374,7 +374,7 @@ class QuickApp():
|
|||
self.profiles[profile]['watched']=value.split()
|
||||
|
||||
def contactDeleted(self, jid, profile):
|
||||
if not self.__check_profile(profile):
|
||||
if not self.check_profile(profile):
|
||||
return
|
||||
target = JID(jid)
|
||||
self.CM.remove(target)
|
||||
|
|
|
|||
|
|
@ -95,7 +95,6 @@ class ContactList(wx.SimpleHtmlListBox, QuickContactList):
|
|||
status = self.CM.getAttr(jid,'status') or ''
|
||||
avatar = self.CM.getAttr(jid,'avatar') or IMAGE_DIR+'/empty_avatar.png'
|
||||
|
||||
#XXX: yes table I know :) but wxHTML* doesn't support CSS
|
||||
html = """
|
||||
<table border='0'>
|
||||
<td>
|
||||
|
|
|
|||
Loading…
Reference in a new issue