frontend improved to take into account new SàT features
- quick_frontend: better use of contact management, it now manages nicks, avatars, and connected status - quick_frontend: disconnect and remove are now 2 separate methods for contact list - wix: new contact list using HTML items, and showing avatars. Groups are not showed for now - wix: contact status now use tuples, to keep order, human readable status and color of contact - wix: contact list is updated when avatar or nick is found - wix: fixed 'question' dialog, which is renamed in 'yes/no' - wix: action result are now ignored for unkwnown id - sortilege refactored to work again
This commit is contained in:
parent
94dd813a9a
commit
3b2d29b632
7 changed files with 293 additions and 203 deletions
|
|
@ -22,23 +22,23 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
from logging import debug, info, error
|
||||
from tools.jid import JID
|
||||
from sat_bridge_frontend.DBus import DBusBridgeFrontend
|
||||
from quick_frontend.quick_contact_management import QuickContactManagement
|
||||
|
||||
import pdb
|
||||
|
||||
class QuickApp():
|
||||
"""This class contain the main methods needed for the frontend"""
|
||||
|
||||
def __init__(self):
|
||||
self.rosterList = {}
|
||||
self.CM = QuickContactManagement() #a short name if more handy
|
||||
|
||||
## bridge ##
|
||||
self.bridge=DBusBridgeFrontend()
|
||||
self.bridge.register("newContact", self.newContact)
|
||||
self.bridge.register("newMessage", self.newMessage)
|
||||
self.bridge.register("presenceUpdate", self.presenceUpdate)
|
||||
self.bridge.register("subscribe", self.subscribe)
|
||||
self.bridge.register("paramUpdate", self.paramUpdate)
|
||||
self.bridge.register("contactDeleted", self.contactDeleted)
|
||||
self.bridge.register("updatedValue", self.updatedValue, "request")
|
||||
self.bridge.register("askConfirmation", self.askConfirmation, "request")
|
||||
self.bridge.register("actionResult", self.actionResult, "request")
|
||||
self.bridge.register("actionResultExt", self.actionResult, "request")
|
||||
|
|
@ -59,13 +59,23 @@ class QuickApp():
|
|||
for contact in self.bridge.getContacts():
|
||||
self.newContact(contact[0], contact[1], contact[2])
|
||||
|
||||
for status in self.bridge.getPresenceStatus():
|
||||
self.presenceUpdate(status[0], status[1], status[2], status[3], status[4])
|
||||
presences = self.bridge.getPresenceStatus()
|
||||
for contact in presences:
|
||||
for res in presences[contact]:
|
||||
jabber_id = contact+('/'+res if res else '')
|
||||
show = presences[contact][res][0]
|
||||
priority = presences[contact][res][1]
|
||||
statuses = presences[contact][res][2]
|
||||
self.presenceUpdate(jabber_id, show, priority, statuses)
|
||||
|
||||
waitingSub = self.bridge.getWaitingSub()
|
||||
for sub in waitingSub:
|
||||
self.subscribe(waitingSub[sub], sub)
|
||||
|
||||
|
||||
def newContact(self, JabberId, attributes, groups):
|
||||
jid=JID(JabberId)
|
||||
self.rosterList[jid.short]=(dict(attributes), list(groups))
|
||||
entity=JID(JabberId)
|
||||
self.rosterList[entity.short]=(dict(attributes), list(groups))
|
||||
|
||||
def newMessage(self, from_jid, msg, type, to_jid):
|
||||
sender=JID(from_jid)
|
||||
|
|
@ -76,64 +86,66 @@ class QuickApp():
|
|||
def setStatusOnline(self, online=True):
|
||||
pass
|
||||
|
||||
def presenceUpdate(self, jabber_id, type, show, status, priority):
|
||||
debug ("presence update for %s (type=%s, show=%s, status=%s)", jabber_id, type, show, status);
|
||||
jid=JID(jabber_id)
|
||||
debug ("jid.short=%s whoami.short=%s", jid.short, self.whoami.short)
|
||||
def presenceUpdate(self, jabber_id, show, priority, statuses):
|
||||
debug ("presence update for %s (show=%s, statuses=%s)", jabber_id, show, statuses);
|
||||
from_jid=JID(jabber_id)
|
||||
debug ("from_jid.short=%s whoami.short=%s", from_jid.short, self.whoami.short)
|
||||
|
||||
### subscription management ###
|
||||
if type=="subscribed":
|
||||
# this is a subscription confirmation, we just have to inform user
|
||||
self.showDialog("The contact %s has accepted your subscription" % jid.short, 'Subscription confirmation')
|
||||
return
|
||||
elif type=="unsubscribed":
|
||||
# this is a subscription refusal, we just have to inform user
|
||||
self.showDialog("The contact %s has refused your subscription" % jid.short, 'Subscription refusal', 'error')
|
||||
return
|
||||
elif type=="subscribe":
|
||||
# this is a subscrition request, we have to ask for user confirmation
|
||||
answer = self.showDialog("The contact %s wants to subscribe to your presence.\nDo you accept ?" % jid.short, 'Subscription confirmation', 'question')
|
||||
if answer:
|
||||
self.bridge.setPresence(type="subscribed", to=jid.short)
|
||||
else:
|
||||
self.bridge.setPresence(type="unsubscribed", to=jid.short)
|
||||
return
|
||||
### subscription management end ###
|
||||
|
||||
if jid.short==self.whoami.short:
|
||||
if from_jid.short==self.whoami.short:
|
||||
if not type:
|
||||
self.setStatusOnline(True)
|
||||
elif type=="unavailable":
|
||||
self.setStatusOnline(False)
|
||||
return
|
||||
|
||||
if not type:
|
||||
if show != 'unavailable':
|
||||
name=""
|
||||
group=""
|
||||
if self.rosterList.has_key(jid.short):
|
||||
if self.rosterList[jid.short][0].has_key("name"):
|
||||
name=self.rosterList[jid.short][0]["name"]
|
||||
if self.rosterList[jid.short][0].has_key("show"):
|
||||
name=self.rosterList[jid.short][0]["show"]
|
||||
if self.rosterList[jid.short][0].has_key("status"):
|
||||
name=self.rosterList[jid.short][0]["status"]
|
||||
if len(self.rosterList[jid.short][1]):
|
||||
group=self.rosterList[jid.short][1][0]
|
||||
if self.rosterList.has_key(from_jid.short):
|
||||
if self.rosterList[from_jid.short][0].has_key("name"):
|
||||
name=self.rosterList[from_jid.short][0]["name"]
|
||||
if len(self.rosterList[from_jid.short][1]):
|
||||
group=self.rosterList[from_jid.short][1][0]
|
||||
|
||||
#FIXME: must be moved in a plugin
|
||||
if jid.short in self.watched and not jid.short in self.onlineContact:
|
||||
self.showAlert("Watched jid [%s] is connected !" % jid.short)
|
||||
if from_jid.short in self.watched and not from_jid.short in self.onlineContact:
|
||||
self.showAlert("Watched jid [%s] is connected !" % from_jid.short)
|
||||
|
||||
self.onlineContact.add(jid) #FIXME onlineContact is useless with CM, must be removed
|
||||
self.CM.add(jid)
|
||||
self.contactList.replace(jid, show=show, status=status, name=name, group=group)
|
||||
self.onlineContact.add(from_jid) #FIXME onlineContact is useless with CM, must be removed
|
||||
self.CM.add(from_jid)
|
||||
self.CM.update(from_jid, 'name', name)
|
||||
self.CM.update(from_jid, 'show', show)
|
||||
self.CM.update(from_jid, 'statuses', statuses)
|
||||
self.CM.update(from_jid, 'group', group)
|
||||
cache = self.bridge.getProfileCache(from_jid)
|
||||
if cache.has_key('nick'):
|
||||
self.CM.update(from_jid, 'nick', cache['nick'])
|
||||
if cache.has_key('avatar'):
|
||||
self.CM.update(from_jid, 'avatar', self.bridge.getAvatarFile(cache['avatar']))
|
||||
self.contactList.replace(from_jid)
|
||||
|
||||
if show=="unavailable" and from_jid in self.onlineContact:
|
||||
self.onlineContact.remove(from_jid)
|
||||
self.CM.remove(from_jid)
|
||||
if not self.CM.isConnected(from_jid):
|
||||
self.contactList.disconnect(from_jid)
|
||||
|
||||
if type=="unavailable" and jid in self.onlineContact:
|
||||
self.onlineContact.remove(jid)
|
||||
self.CM.remove(jid)
|
||||
self.contactList.remove(jid)
|
||||
|
||||
def subscribe(self, type, raw_jid):
|
||||
"""Called when a subsciption maangement signal is received"""
|
||||
entity = JID(raw_jid)
|
||||
if type=="subscribed":
|
||||
# this is a subscription confirmation, we just have to inform user
|
||||
self.showDialog("The contact %s has accepted your subscription" % entity.short, 'Subscription confirmation')
|
||||
elif type=="unsubscribed":
|
||||
# this is a subscription refusal, we just have to inform user
|
||||
self.showDialog("The contact %s has refused your subscription" % entity.short, 'Subscription refusal', 'error')
|
||||
elif type=="subscribe":
|
||||
# this is a subscriptionn request, we have to ask for user confirmation
|
||||
answer = self.showDialog("The contact %s wants to subscribe to your presence.\nDo you accept ?" % entity.short, 'Subscription confirmation', 'yes/no')
|
||||
if answer:
|
||||
self.bridge.subscription("subscribed", entity.short)
|
||||
else:
|
||||
self.bridge.subscribed("unsubscribed", entity.short)
|
||||
|
||||
def showDialog(self, message, title, type="info"):
|
||||
raise NotImplementedError
|
||||
|
|
@ -151,13 +163,26 @@ class QuickApp():
|
|||
|
||||
def contactDeleted(self, jid):
|
||||
target = JID(jid)
|
||||
self.CM.remove(target)
|
||||
self.contactList.remove(self.CM.get_full(target))
|
||||
try:
|
||||
self.onlineContact.remove(target.short)
|
||||
except KeyError:
|
||||
pass
|
||||
self.contactList.remove(self.CM.get_full(jid))
|
||||
self.CM.remove(target)
|
||||
|
||||
|
||||
def updatedValue(self, name, data):
|
||||
print "toto"
|
||||
print "updatedValue", name, data
|
||||
if name == "profile_nick":
|
||||
target = JID(data['jid'])
|
||||
self.CM.update(target, 'nick', data['nick'])
|
||||
self.contactList.replace(target)
|
||||
elif name == "profile_avatar":
|
||||
target = JID(data['jid'])
|
||||
filename = self.bridge.getAvatarFile(data['avatar'])
|
||||
self.CM.update(target, 'avatar', filename)
|
||||
self.contactList.replace(target)
|
||||
|
||||
def askConfirmation(self, type, id, data):
|
||||
raise NotImplementedError
|
||||
|
||||
|
|
|
|||
|
|
@ -30,12 +30,12 @@ class QuickChatList(dict):
|
|||
dict.__init__(self)
|
||||
self.host = host
|
||||
|
||||
def __getitem__(self,name_param):
|
||||
name=JID(name_param).short
|
||||
if not self.has_key(name):
|
||||
def __getitem__(self,to_jid):
|
||||
target=JID(to_jid)
|
||||
if not self.has_key(target.short):
|
||||
#we have to create the chat win
|
||||
self[name] = self.createChat(name)
|
||||
return dict.__getitem__(self,name)
|
||||
self[target.short] = self.createChat(target)
|
||||
return dict.__getitem__(self,target.short)
|
||||
|
||||
def createChat(self, name):
|
||||
def createChat(self, target):
|
||||
raise NotImplementedError
|
||||
|
|
|
|||
|
|
@ -24,19 +24,27 @@ from tools.jid import JID
|
|||
|
||||
|
||||
class QuickContactList():
|
||||
"""This class manage the visual representation of contacts"""
|
||||
|
||||
def __init__(self):
|
||||
def __init__(self, CM):
|
||||
"""
|
||||
@param CM: instance of QuickContactManagement
|
||||
"""
|
||||
debug("Contact List init")
|
||||
self.jid_ids={}
|
||||
self.CM = CM
|
||||
|
||||
def replace(self, jid, name="", show="", status="", group=""):
|
||||
"""add a contact to the list"""
|
||||
def replace(self, jid):
|
||||
"""add a contact to the list if doesn't exist, else update it"""
|
||||
raise NotImplementedError
|
||||
|
||||
def disconnect(self, jid):
|
||||
"""mark a contact disconnected"""
|
||||
raise NotImplementedError
|
||||
|
||||
def remove(self, jid):
|
||||
"""remove a contact from the list"""
|
||||
raise NotImplementedError
|
||||
|
||||
def add(self, jid, name="", show="", status="", group=""):
|
||||
def add(self, jid):
|
||||
"""add a contact to the list"""
|
||||
raise NotImplementedError
|
||||
|
|
|
|||
|
|
@ -19,29 +19,68 @@ You should have received a copy of the GNU General Public License
|
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
from logging import debug, info, error
|
||||
from logging import debug, info, warning, error
|
||||
from tools.jid import JID
|
||||
import pdb
|
||||
|
||||
|
||||
class QuickContactManagement():
|
||||
"""This helper class manage the contacts and ease the use of nicknames and shortcuts"""
|
||||
### FIXME: is SàT a better place for all this stuff ??? ###
|
||||
|
||||
def __init__(self):
|
||||
self.__contactlist = {}
|
||||
|
||||
def add(self, jid_param):
|
||||
jid=JID(jid_param)
|
||||
self.__contactlist[jid.short] = {'name':jid, 'last_resource':jid.resource}
|
||||
def add(self, entity):
|
||||
"""Add contact to the list, update resources"""
|
||||
if not self.__contactlist.has_key(entity.short):
|
||||
self.__contactlist[entity.short] = {'resources':[]}
|
||||
if entity.resource in self.__contactlist[entity.short]['resources']:
|
||||
self.__contactlist[entity.short]['resources'].remove(entity.resource)
|
||||
self.__contactlist[entity.short]['resources'].append(entity.resource)
|
||||
|
||||
def remove(self, jid_param):
|
||||
jid=JID(jid_param)
|
||||
def getAttr(self, entity, name):
|
||||
"""Return a specific attribute of contact, or all attributes
|
||||
@param entity: jid of the contact
|
||||
@param name: name of the attribute
|
||||
@return: asked attribute"""
|
||||
if self.__contactlist.has_key(entity.short):
|
||||
if name == 'status': #FIXME: for the moment, we only use the first status
|
||||
if self.__contactlist[entity.short]['statuses']:
|
||||
return self.__contactlist[entity.short]['statuses'].values()[0]
|
||||
if self.__contactlist[entity.short].has_key(name):
|
||||
return self.__contactlist[entity.short][name]
|
||||
else:
|
||||
debug('Trying to get attribute for an unknown contact')
|
||||
return None
|
||||
|
||||
def isConnected(self, entity):
|
||||
"""Tell if the contact is online"""
|
||||
print "is connected (%s): %s" %(entity, str(self.__contactlist.has_key(entity.short))) #gof
|
||||
return self.__contactlist.has_key(entity.short)
|
||||
|
||||
def remove(self, entity):
|
||||
"""remove resource. If no more resource is online or is no resource is specified, contact is deleted"""
|
||||
try:
|
||||
del self.__contactlist[jid.short]
|
||||
if entity.resource:
|
||||
self.__contactlist[entity.short]['resources'].remove(entity.resource)
|
||||
if not entity.resource or not self.__contactlist[entity.short]['resources']:
|
||||
#no more resource available: the contact seems really disconnected
|
||||
del self.__contactlist[entity.short]
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
def get_full(self, jid_param):
|
||||
jid=JID(jid_param)
|
||||
return jid.short+'/'+self.__contactlist[jid.short]['last_resource']
|
||||
def update(self, entity, key, value):
|
||||
"""Update attribute of contact
|
||||
@param entity: jid of the contact
|
||||
@param key: name of the attribute
|
||||
@param value: value of the attribute
|
||||
"""
|
||||
if self.__contactlist.has_key(entity.short):
|
||||
self.__contactlist[entity.short][key] = value
|
||||
else:
|
||||
debug ('Trying to update an uknown contact: %s', entity.short)
|
||||
|
||||
def get_full(self, entity):
|
||||
return entity.short+'/'+self.__contactlist[entity.short]['resources'][-1]
|
||||
|
||||
|
|
|
|||
|
|
@ -52,6 +52,9 @@ class DBusBridgeFrontend(BridgeFrontend):
|
|||
def getPresenceStatus(self):
|
||||
return self.db_comm_iface.getPresenceStatus()
|
||||
|
||||
def getWaitingSub(self):
|
||||
return self.db_comm_iface.getWaitingSub()
|
||||
|
||||
def sendMessage(self, to, message):
|
||||
return self.db_comm_iface.sendMessage(to, message)
|
||||
|
||||
|
|
@ -64,14 +67,20 @@ class DBusBridgeFrontend(BridgeFrontend):
|
|||
def getProfile(self, target):
|
||||
return self.db_comm_iface.getProfile(target)
|
||||
|
||||
def getProfileCache(self, target):
|
||||
return self.db_comm_iface.getProfileCache(target)
|
||||
|
||||
def getAvatarFile(self, hash):
|
||||
return self.db_comm_iface.getAvatarFile(hash)
|
||||
|
||||
def in_band_register(self, target):
|
||||
return self.db_comm_iface.in_band_register(target)
|
||||
|
||||
def setPresence(self, to="", type="", show="", status="", priority=0):
|
||||
return self.db_comm_iface.setPresence(to, type, show, status, priority)
|
||||
def setPresence(self, to="", show="", priority=0, statuses={}):
|
||||
return self.db_comm_iface.setPresence(to, show, priority, statuses)
|
||||
|
||||
def subscription(self, type, entity):
|
||||
return self.db_comm_iface.subscription(type, entity)
|
||||
|
||||
def setParam(self, name, value, category):
|
||||
return self.db_comm_iface.setParam(name, value, category)
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@ from boxsizer import BoxSizer
|
|||
from quick_frontend.quick_chat_list import QuickChatList
|
||||
from quick_frontend.quick_contact_list import QuickContactList
|
||||
from quick_frontend.quick_app import QuickApp
|
||||
from quick_frontend.quick_contact_management import QuickContactManagement
|
||||
|
||||
### logging configuration FIXME: put this elsewhere ###
|
||||
logging.basicConfig(level=logging.CRITICAL, #TODO: configure it top put messages in a log file
|
||||
|
|
@ -80,8 +81,10 @@ class ChatList(QuickChatList):
|
|||
|
||||
class ContactList(Window, QuickContactList):
|
||||
|
||||
def __init__(self):
|
||||
QuickContactList.__init__(self)
|
||||
def __init__(self, host, CM):
|
||||
QuickContactList.__init__(self, CM)
|
||||
self.host = host
|
||||
self.jid_list = []
|
||||
self.__index=0 #indicate which contact is selected (ie: where we are)
|
||||
Window.__init__(self, stdscr, stdscr.getmaxyx()[0]-2,const_CONTACT_WIDTH,0,0, True, "Contact List", code=code)
|
||||
|
||||
|
|
@ -98,14 +101,15 @@ class ContactList(Window, QuickContactList):
|
|||
def registerEnterCB(self, CB):
|
||||
self.__enterCB=CB
|
||||
|
||||
def replace(self, jid, name="", show="", status="", group=""):
|
||||
def replace(self, jid):
|
||||
"""add a contact to the list"""
|
||||
self.jid_ids[jid] = name or jid
|
||||
name = self.CM.getAttr(jid,'name')
|
||||
self.jid_list.append(jid.short)
|
||||
self.update()
|
||||
|
||||
def indexUp(self):
|
||||
"""increment select contact index"""
|
||||
if self.__index < len(self.jid_ids)-1: #we dont want to select a missing contact
|
||||
if self.__index < len(self.jid_list)-1: #we dont want to select a missing contact
|
||||
self.__index = self.__index + 1
|
||||
self.update()
|
||||
|
||||
|
|
@ -115,11 +119,15 @@ class ContactList(Window, QuickContactList):
|
|||
self.__index = self.__index - 1
|
||||
self.update()
|
||||
|
||||
def disconnect(self, jid):
|
||||
"""for now, we just remove the contact"""
|
||||
self.remove(jid)
|
||||
|
||||
def remove(self, jid):
|
||||
"""remove a contact from the list"""
|
||||
del self.jid_ids[jid]
|
||||
if self.__index >= len(self.jid_ids) and self.__index > 0: #if select index is out of border, we put it on the last contact
|
||||
self.__index = len(self.jid_ids)-1
|
||||
self.jid_list.remove(jid.short)
|
||||
if self.__index >= len(self.jid_list) and self.__index > 0: #if select index is out of border, we put it on the last contact
|
||||
self.__index = len(self.jid_list)-1
|
||||
self.update()
|
||||
|
||||
def update(self):
|
||||
|
|
@ -127,13 +135,10 @@ class ContactList(Window, QuickContactList):
|
|||
if self.isHidden():
|
||||
return
|
||||
Window.update(self)
|
||||
viewList=[]
|
||||
for jid in self.jid_ids:
|
||||
viewList.append(self.jid_ids[jid])
|
||||
viewList.sort()
|
||||
self.jid_list.sort()
|
||||
begin=0 if self.__index<self.rHeight else self.__index-self.rHeight+1
|
||||
idx=0
|
||||
for item in viewList[begin:self.rHeight+begin]:
|
||||
for item in self.jid_list[begin:self.rHeight+begin]:
|
||||
attr = curses.A_REVERSE if ( self.isActive() and (idx+begin) == self.__index ) else 0
|
||||
centered = item.center(self.rWidth) ## it's nicer in the center :)
|
||||
self.addYXStr(idx, 0, centered, attr)
|
||||
|
|
@ -147,10 +152,10 @@ class ContactList(Window, QuickContactList):
|
|||
elif k == curses.KEY_DOWN:
|
||||
self.indexUp()
|
||||
elif k == ascii.NL:
|
||||
if not self.jid_ids:
|
||||
if not self.jid_list:
|
||||
return
|
||||
try:
|
||||
self.__enterCB(self.jid_ids.keys()[self.__index])
|
||||
self.__enterCB(self.jid_list[self.__index])
|
||||
except NameError:
|
||||
pass # TODO: thrown an error here
|
||||
|
||||
|
|
@ -170,6 +175,7 @@ class SortilegeApp(QuickApp):
|
|||
gobject.io_add_watch(0, gobject.IO_IN, self.loopCB)
|
||||
|
||||
## misc init stuff ##
|
||||
self.CM = QuickContactManagement()
|
||||
self.listWins=[]
|
||||
self.chatParams={'timestamp':True,
|
||||
'color':True,
|
||||
|
|
@ -190,7 +196,7 @@ class SortilegeApp(QuickApp):
|
|||
self.color(True)
|
||||
|
||||
## windows ##
|
||||
self.contactList = ContactList()
|
||||
self.contactList = ContactList(self, self.CM)
|
||||
self.editBar = EditBox(stdscr, "> ", self.code)
|
||||
self.editBar.activate(False)
|
||||
self.statusBar = StatusBar(stdscr, self.code)
|
||||
|
|
@ -237,10 +243,10 @@ class SortilegeApp(QuickApp):
|
|||
def showChat(self, chat):
|
||||
debug ("show chat")
|
||||
if self.currentChat:
|
||||
debug ("hide de %s", self.currentChat)
|
||||
debug ("hiding %s", self.currentChat)
|
||||
self.chat_wins[self.currentChat].hide()
|
||||
self.currentChat=chat
|
||||
debug ("show de %s", self.currentChat)
|
||||
debug ("showing %s", self.currentChat)
|
||||
self.chat_wins[self.currentChat].show()
|
||||
self.chat_wins[self.currentChat].update()
|
||||
|
||||
|
|
@ -268,8 +274,8 @@ class SortilegeApp(QuickApp):
|
|||
pass
|
||||
|
||||
|
||||
def presenceUpdate(self, jabber_id, type, show, status, priority):
|
||||
QuickApp.presenceUpdate(self, jabber_id, type, show, status, priority)
|
||||
def presenceUpdate(self, jabber_id, show, priority, statuses):
|
||||
QuickApp.presenceUpdate(self, jabber_id, show, priority, statuses)
|
||||
self.editBar.replace_cur()
|
||||
curses.doupdate()
|
||||
|
||||
|
|
|
|||
|
|
@ -34,24 +34,28 @@ from logging import debug, info, error
|
|||
from quick_frontend.quick_chat_list import QuickChatList
|
||||
from quick_frontend.quick_contact_list import QuickContactList
|
||||
from quick_frontend.quick_app import QuickApp
|
||||
from quick_frontend.quick_contact_management import QuickContactManagement
|
||||
from cgi import escape
|
||||
import sys
|
||||
|
||||
IMAGE_DIR = sys.path[0]+'/images'
|
||||
|
||||
msgOFFLINE = "offline"
|
||||
msgONLINE = "online"
|
||||
idCONNECT = 1
|
||||
idDISCONNECT = 2
|
||||
idEXIT = 3
|
||||
idPARAM = 4
|
||||
idADD_CONTACT = 5
|
||||
idREMOVE_CONTACT = 6
|
||||
idSHOW_PROFILE = 7
|
||||
idFIND_GATEWAYS = 8
|
||||
idCONNECT,\
|
||||
idDISCONNECT,\
|
||||
idEXIT,\
|
||||
idPARAM,\
|
||||
idADD_CONTACT,\
|
||||
idREMOVE_CONTACT,\
|
||||
idSHOW_PROFILE,\
|
||||
idFIND_GATEWAYS = range(8)
|
||||
const_DEFAULT_GROUP = "Unclassed"
|
||||
const_STATUS = {"Online":"",
|
||||
"Want to discuss":"chat",
|
||||
"AFK":"away",
|
||||
"Do Not Disturb":"dnd",
|
||||
"Away":"xa"}
|
||||
const_STATUS = [("", "Online", None),
|
||||
("chat", "Free for chat", "green"),
|
||||
("away", "AFK", "brown"),
|
||||
("dnd", "DND", "red"),
|
||||
("xa", "Away", "red")]
|
||||
|
||||
class ChatList(QuickChatList):
|
||||
"""This class manage the list of chat windows"""
|
||||
|
|
@ -59,100 +63,96 @@ class ChatList(QuickChatList):
|
|||
def __init__(self, host):
|
||||
QuickChatList.__init__(self, host)
|
||||
|
||||
def createChat(self, name):
|
||||
return Chat(name, self.host)
|
||||
def createChat(self, target):
|
||||
return Chat(target, self.host)
|
||||
|
||||
|
||||
|
||||
class ContactList(wx.TreeCtrl, QuickContactList):
|
||||
class ContactList(wx.SimpleHtmlListBox, QuickContactList):
|
||||
"""Customized control to manage contacts."""
|
||||
|
||||
def __init__(self, parent):
|
||||
wx.TreeCtrl.__init__(self, parent, style = wx.TR_HIDE_ROOT | wx.TR_HAS_BUTTONS)
|
||||
QuickContactList.__init__(self)
|
||||
self.jid_ids={}
|
||||
self.groups={}
|
||||
self.root=self.AddRoot("")
|
||||
self.Bind(wx.EVT_TREE_ITEM_ACTIVATED, self.onActivated, self)
|
||||
def __init__(self, parent, CM):
|
||||
wx.SimpleHtmlListBox.__init__(self, parent, -1)
|
||||
QuickContactList.__init__(self, CM)
|
||||
self.host = parent
|
||||
self.Bind(wx.EVT_LISTBOX_DCLICK, self.onActivated)
|
||||
|
||||
#icons
|
||||
isz = (16,16)
|
||||
il = wx.ImageList(isz[0], isz[1])
|
||||
self.icon_online = il.Add(wx.ArtProvider_GetBitmap(wx.ART_TICK_MARK, wx.ART_OTHER, isz))
|
||||
self.icon_unavailable = il.Add(wx.ArtProvider_GetBitmap(wx.ART_CROSS_MARK, wx.ART_OTHER, isz))
|
||||
self.AssignImageList(il)
|
||||
def __find_idx(self, jid, reverse=False):
|
||||
"""Find indexes of given jid in contact list
|
||||
@return: list of indexes"""
|
||||
result=[]
|
||||
for i in range(self.GetCount()):
|
||||
if self.GetClientData(i).short == jid.short:
|
||||
result.append(i)
|
||||
return result
|
||||
|
||||
self.__addNode(const_DEFAULT_GROUP)
|
||||
|
||||
def __addNode(self, label):
|
||||
"""Add an item container"""
|
||||
ret=self.AppendItem(self.root, label)
|
||||
self.SetPyData(ret, "[node]")
|
||||
self.SetItemBold(ret)
|
||||
self.groups[label]=ret
|
||||
|
||||
def replace(self, jid, name="", show="", status="", group=""):
|
||||
debug("status = %s show = %s",status, show)
|
||||
if not self.jid_ids.has_key(jid):
|
||||
self.add(jid, name, show, status, group)
|
||||
def replace(self, jid):
|
||||
debug("update %s" % jid)
|
||||
if not self.__find_idx(jid):
|
||||
self.add(jid)
|
||||
else:
|
||||
debug ("updating %s",jid)
|
||||
self.__presentItem(jid, name, show, status, group)
|
||||
for i in self.__find_idx(jid):
|
||||
self.SetString(i, self.__presentItem(jid))
|
||||
|
||||
def __presentItem(self, jid, name, show, status, group):
|
||||
def disconnect(self, jid):
|
||||
self.remove(jid) #for now, we only show online contacts
|
||||
|
||||
def __presentItem(self, jid):
|
||||
"""Make a nice presentation of the contact in the list."""
|
||||
id=self.jid_ids[jid]
|
||||
label= "%s [%s] \n %s" % ((name or jid), (show or "online"), status)
|
||||
self.SetItemText(id, label)
|
||||
name = self.CM.getAttr(jid,'name')
|
||||
nick = self.CM.getAttr(jid,'nick')
|
||||
show = filter(lambda x:x[0]==self.CM.getAttr(jid,'show'), const_STATUS)[0]
|
||||
#show[0]==shortcut
|
||||
#show[1]==human readable
|
||||
#show[2]==color (or None)
|
||||
show_html = "<font color='%s'>[%s]</font>" % (show[2], show[1]) if show[2] else ""
|
||||
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>
|
||||
<img height='64' width='64' src='%s' />
|
||||
</td>
|
||||
<td>
|
||||
<b>%s</b> %s<br />
|
||||
<i>%s</i>
|
||||
</td>
|
||||
</table>
|
||||
""" % (avatar,
|
||||
escape(nick or name or jid.node or jid.short),
|
||||
show_html,
|
||||
escape(status))
|
||||
|
||||
# icon
|
||||
if not show or show=="chat":
|
||||
self.SetItemImage(id, self.icon_online)
|
||||
else:
|
||||
self.SetItemImage(id, self.icon_unavailable)
|
||||
return html
|
||||
|
||||
#colour
|
||||
if not show:
|
||||
self.SetItemTextColour(id, wx.BLACK)
|
||||
elif show=="chat":
|
||||
self.SetItemTextColour(id, wx.GREEN)
|
||||
elif show=="away":
|
||||
self.SetItemTextColour(id, wx.BLUE)
|
||||
else:
|
||||
self.SetItemTextColour(id, wx.RED)
|
||||
|
||||
def add(self, jid, name="", show="", status="", group=""):
|
||||
def add(self, jid):
|
||||
"""add a contact to the list"""
|
||||
debug ("adding %s",jid)
|
||||
dest_group=group or const_DEFAULT_GROUP
|
||||
if not self.groups.has_key(dest_group):
|
||||
self.__addNode(dest_group)
|
||||
self.jid_ids[jid]=self.AppendItem(self.groups[dest_group], "")
|
||||
self.__presentItem(jid, name, show, status, group)
|
||||
self.SetPyData(self.jid_ids[jid], "[contact]"+jid)
|
||||
self.EnsureVisible(self.jid_ids[jid])
|
||||
self.Refresh() #FIXME: Best way ?
|
||||
idx = self.Append(self.__presentItem(jid))
|
||||
|
||||
self.SetClientData(idx, jid)
|
||||
|
||||
def remove(self, jid):
|
||||
"""remove a contact from the list"""
|
||||
debug ("removing %s",jid)
|
||||
self.Delete(self.jid_ids[jid])
|
||||
del self.jid_ids[jid]
|
||||
self.Refresh() #FIXME: Best way ?
|
||||
list_idx = self.__find_idx(jid)
|
||||
list_idx.reverse() #we me make some deletions, we have to reverse the order
|
||||
for i in list_idx:
|
||||
self.Delete(i)
|
||||
|
||||
def onActivated(self, event):
|
||||
"""Called when a contact is clicked or activated with keyboard."""
|
||||
if self.GetPyData(event.GetItem()).startswith("[contact]"):
|
||||
self.onActivatedCB(self.GetPyData(event.GetItem())[9:])
|
||||
else:
|
||||
event.Skip()
|
||||
data = self.getSelection()
|
||||
self.onActivatedCB(data)
|
||||
event.Skip()
|
||||
|
||||
def getSelection(self):
|
||||
"""Return the selected contact, or an empty string if there is not"""
|
||||
data = self.GetPyData(self.GetSelection())
|
||||
if not data or not data.startswith("[contact]"):
|
||||
return ""
|
||||
return JID(data[9:])
|
||||
if self.GetSelection() == wx.NOT_FOUND:
|
||||
return "" #FIXME: gof: à améliorer
|
||||
data = self.GetClientData(self.GetSelection())
|
||||
return data
|
||||
|
||||
def registerActivatedCB(self, cb):
|
||||
"""Register a callback with manage contact activation."""
|
||||
|
|
@ -163,10 +163,11 @@ class MainWindow(wx.Frame, QuickApp):
|
|||
|
||||
def __init__(self):
|
||||
wx.Frame.__init__(self,None, title="SAT Wix", size=(400,200))
|
||||
self.CM = QuickContactManagement() #gof:
|
||||
|
||||
|
||||
#Frame elements
|
||||
self.contactList = ContactList(self)
|
||||
self.contactList = ContactList(self, self.CM)
|
||||
self.contactList.registerActivatedCB(self.onContactActivated)
|
||||
self.chat_wins=ChatList(self)
|
||||
self.CreateStatusBar()
|
||||
|
|
@ -175,7 +176,7 @@ class MainWindow(wx.Frame, QuickApp):
|
|||
|
||||
#ToolBar
|
||||
self.tools=self.CreateToolBar()
|
||||
self.statusBox = wx.ComboBox(self.tools, -1, "Online", choices=const_STATUS.keys(),
|
||||
self.statusBox = wx.ComboBox(self.tools, -1, "Online", choices=[status[1] for status in const_STATUS],
|
||||
style=wx.CB_DROPDOWN | wx.CB_READONLY)
|
||||
self.tools.AddControl(self.statusBox)
|
||||
self.tools.AddSeparator()
|
||||
|
|
@ -186,7 +187,7 @@ class MainWindow(wx.Frame, QuickApp):
|
|||
self.tools.Disable()
|
||||
|
||||
#tray icon
|
||||
ticon = wx.Icon("images/tray_icon.xpm", wx.BITMAP_TYPE_XPM)
|
||||
ticon = wx.Icon(IMAGE_DIR+'/crystal/tray_icon.xpm', wx.BITMAP_TYPE_XPM)
|
||||
self.tray_icon = wx.TaskBarIcon()
|
||||
self.tray_icon.SetIcon(ticon, "Wix jabber client")
|
||||
wx.EVT_TASKBAR_LEFT_UP(self.tray_icon, self.onTrayClick)
|
||||
|
|
@ -252,10 +253,11 @@ class MainWindow(wx.Frame, QuickApp):
|
|||
flags = wx.OK | wx.ICON_INFORMATION
|
||||
elif type == 'error':
|
||||
flags = wx.OK | wx.ICON_ERROR
|
||||
elif type == 'question':
|
||||
flags = wx.OK | wx.ICON_QUESTION
|
||||
elif type == 'yes/no':
|
||||
flags = wx.YES_NO | wx.ICON_QUESTION
|
||||
else:
|
||||
flags = wx.OK | wx.ICON_INFORMATION
|
||||
error('unmanaged dialog type: %s', type)
|
||||
dlg = wx.MessageDialog(self, message, title, flags)
|
||||
answer = dlg.ShowModal()
|
||||
dlg.Destroy()
|
||||
|
|
@ -272,8 +274,8 @@ class MainWindow(wx.Frame, QuickApp):
|
|||
return
|
||||
|
||||
|
||||
def presenceUpdate(self, jabber_id, type, show, status, priority):
|
||||
QuickApp.presenceUpdate(self, jabber_id, type, show, status, priority)
|
||||
def presenceUpdate(self, jabber_id, show, priority, statuses):
|
||||
QuickApp.presenceUpdate(self, jabber_id, show, priority, statuses)
|
||||
|
||||
def askConfirmation(self, type, id, data):
|
||||
#TODO: refactor this in QuickApp
|
||||
|
|
@ -315,6 +317,9 @@ class MainWindow(wx.Frame, QuickApp):
|
|||
|
||||
def actionResult(self, type, id, data):
|
||||
debug ("actionResult: type = [%s] id = [%s] data = [%s]" % (type, id, data))
|
||||
if not id in self.current_action_ids:
|
||||
debug ('unknown id, ignoring')
|
||||
return
|
||||
if type == "SUPPRESS":
|
||||
self.current_action_ids.remove(id)
|
||||
elif type == "SUCCESS":
|
||||
|
|
@ -349,7 +354,6 @@ class MainWindow(wx.Frame, QuickApp):
|
|||
callback = self.current_action_ids_cb[id]
|
||||
del self.current_action_ids_cb[id]
|
||||
callback(data)
|
||||
print ("Dict of dict found as result")
|
||||
else:
|
||||
error ("FIXME FIXME FIXME: type [%s] not implemented" % type)
|
||||
raise NotImplementedError
|
||||
|
|
@ -384,10 +388,10 @@ class MainWindow(wx.Frame, QuickApp):
|
|||
|
||||
def onContactActivated(self, jid):
|
||||
debug ("onContactActivated: %s", jid)
|
||||
if self.chat_wins[jid].IsShown():
|
||||
self.chat_wins[jid].Hide()
|
||||
if self.chat_wins[jid.short].IsShown():
|
||||
self.chat_wins[jid.short].Hide()
|
||||
else:
|
||||
self.chat_wins[jid].Show()
|
||||
self.chat_wins[jid.short].Show()
|
||||
|
||||
def onConnectRequest(self, e):
|
||||
self.bridge.connect()
|
||||
|
|
@ -396,9 +400,9 @@ class MainWindow(wx.Frame, QuickApp):
|
|||
self.bridge.disconnect()
|
||||
|
||||
def __updateStatus(self):
|
||||
show = const_STATUS[self.statusBox.GetValue()]
|
||||
show = filter(lambda x:x[1] == self.statusBox.GetValue(), const_STATUS)[0][0]
|
||||
status = self.statusTxt.GetValue()
|
||||
self.bridge.setPresence(show=show, status=status)
|
||||
self.bridge.setPresence(show=show, statuses={'default':status}) #FIXME: manage multilingual statuses
|
||||
|
||||
def onStatusChange(self, e):
|
||||
debug("Status change request")
|
||||
|
|
@ -445,7 +449,7 @@ class MainWindow(wx.Frame, QuickApp):
|
|||
)
|
||||
|
||||
if dlg.ShowModal() == wx.ID_YES:
|
||||
info("Unsubsribing %s presence", target.short)
|
||||
info("Unsubscribing %s presence", target.short)
|
||||
self.bridge.delContact(target.short)
|
||||
|
||||
dlg.Destroy()
|
||||
|
|
@ -476,7 +480,6 @@ class MainWindow(wx.Frame, QuickApp):
|
|||
id = self.bridge.findGateways(self.whoami.domain)
|
||||
self.current_action_ids.add(id)
|
||||
self.current_action_ids_cb[id] = self.onGatewaysFound
|
||||
print "Find Gateways id=", id
|
||||
|
||||
def onGatewaysFound(self, data):
|
||||
"""Called when SàT has found the server gateways"""
|
||||
|
|
|
|||
Loading…
Reference in a new issue