diff frontends/wix/main_window.py @ 51:8c67ea98ab91

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
author Goffi <goffi@goffi.org>
date Thu, 07 Jan 2010 00:17:27 +1100
parents 874de3020e1c
children 6455fb62ff83
line wrap: on
line diff
--- a/frontends/wix/main_window.py	Thu Jan 07 00:05:15 2010 +1100
+++ b/frontends/wix/main_window.py	Thu Jan 07 00:17:27 2010 +1100
@@ -34,24 +34,28 @@
 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 @@
     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 replace(self, jid):
+        debug("update %s" % jid)
+        if not self.__find_idx(jid):
+            self.add(jid)
+        else:
+            for i in self.__find_idx(jid):
+                self.SetString(i, self.__presentItem(jid))
 
-    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 disconnect(self, jid):
+        self.remove(jid) #for now, we only show online contacts
 
-    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)
-        else:
-            debug ("updating %s",jid)
-            self.__presentItem(jid, name, show, status, group)
-
-    def __presentItem(self, jid, name, show, status, group):
+    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 @@
 
     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 @@
 
         #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 @@
         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 @@
             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 @@
         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 @@
 
     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 @@
                 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 @@
 
     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 @@
         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 @@
                               )
 
         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 @@
         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"""