diff src/browser/sat_browser/contact_list.py @ 630:71abccd8d228 frontends_multi_profiles

browser side: contact_list update: - removed ContactPanel's "add" and "remove" method, and replaced them by a display which display all needed contact at once - first naive implementation of display: if the display change, clear and add all ContactBox. Need to be done in a more efficient way in the future - ContactBox are never deleted, and only hidden when not displayed. ContactBox are automatically created on first use (TODO: add a way to delete them for entities not in roster)
author Goffi <goffi@goffi.org>
date Mon, 23 Feb 2015 18:16:07 +0100
parents 57a651a5b31d
children 617f7a5c5312
line wrap: on
line diff
--- a/src/browser/sat_browser/contact_list.py	Mon Feb 23 18:14:07 2015 +0100
+++ b/src/browser/sat_browser/contact_list.py	Mon Feb 23 18:16:07 2015 +0100
@@ -131,13 +131,13 @@
 
 class ContactBox(VerticalPanel, ClickHandler, base_widget.DragLabel):
 
-    def __init__(self, host, jid_, name=None, click_listener=None, handle_menu=None):
+    def __init__(self, host, jid_, name=None, on_click=None, handle_menu=None):
         """
 
-        @param host (SatWebFrontend)
+        @param host (SatWebFrontend): %(doc_host)s
         @param jid_ (jid.JID): contact JID
         @param name (unicode): contact alias
-        @param click_listener (callable): click callback
+        @param on_click (callable): click callback
         @param handle_menu (bool): if True, bind a popup menu to the avatar
         """
         VerticalPanel.__init__(self, StyleName='contactBox', VerticalAlignment='middle')
@@ -148,10 +148,10 @@
         self.updateAvatar(host.getAvatarURL(jid_))
         self.add(self.avatar)
         self.add(self.label)
-        if click_listener:
+        if on_click:
             ClickHandler.__init__(self)
             self.addClickListener(self)
-            self.click_listener = click_listener
+            self._on_click = on_click
 
     def addMenus(self, menu_bar):
         menu_bar.addCachedMenus(C.MENU_ROSTER_JID_CONTEXT, {'jid': unicode(self.jid)})
@@ -171,7 +171,7 @@
         self.avatar.setUrl(url)
 
     def onClick(self, sender):
-        self.click_listener(self.jid)
+        self._on_click(self.jid)
 
 
 class GroupPanel(VerticalPanel):
@@ -220,14 +220,15 @@
 
 
 class BaseContactsPanel(VerticalPanel):
-    """Class that can be used to represent a contact list, but not necessarily
-    the one that is displayed on the left side. Special features like popup menu
-    panel or changing the contact states must be done in a sub-class."""
+    """ContactList graphic representation
+
+    Special features like popup menu panel or changing the contact states must be done in a sub-class.
+    """
 
     def __init__(self, host, handle_click=False, handle_menu=False):
         VerticalPanel.__init__(self)
         self.host = host
-        self.contacts = []
+        self._contacts = {} # entity jid to ContactBox map
         self.click_listener = None
         self.handle_menu = handle_menu
 
@@ -236,48 +237,47 @@
                 host.displayWidget(chat.Chat, contact_jid, type_=C.CHAT_ONE2ONE)
             self.click_listener = cb
 
-    def add(self, jid_, name=None):
-        """Add a contact to the list.
+    def display(self, jids):
+        """Display a contact in the list.
 
-        @param jid_ (jid.JID): jid_ of the contact
+        @param jids (list[jid.JID]): jids to display (the order is kept)
         @param name (unicode): optional name of the contact
         """
-        assert isinstance(jid_, jid.JID)
-        if jid_ in self.contacts:
+        # FIXME: we do a full clear and add boxes after, we should only remove recently hidden boxes and add new ones, and re-order
+        current = [box.jid for box in self.children if isinstance(box, ContactBox)]
+        if current == jids:
+            # the display doesn't change
             return
-        index = 0
-        for contact_ in self.contacts:
-            if contact_ > jid_:
-                break
-            index += 1
-        self.contacts.insert(index, jid_)
-        box = ContactBox(self.host, jid_, name, self.click_listener, self.handle_menu)
-        VerticalPanel.insert(self, box, index)
-
-    def remove(self, jid_):
-        box = self.getContactBox(jid_)
-        if not box:
-            return
-        VerticalPanel.remove(self, box)
-        self.contacts.remove(jid_)
+        self.clear()
+        for jid_ in jids:
+            assert isinstance(jid_, jid.JID)
+            box = self.getContactBox(jid_)
+            VerticalPanel.append(self, box)
 
     def isContactPresent(self, contact_jid):
         """Return True if a contact is present in the panel"""
-        return contact_jid in self.contacts
+        return contact_jid in self._contacts
 
     def getContacts(self):
-        return self.contacts
+        return self._contacts
 
-    def getContactBox(self, contact_jid):
-        """get the widget of a contact
+    def getContactBox(self, contact_jid, name=None, on_click=None, handle_menu=None):
+        """get the Contactbox of a contact
 
+        if the Contactbox doesn't exists, it will be created
         @param contact_jid (jid.JID): the contact
-        @return: ContactBox instance if present, else None"""
+        @param name (unicode): contact alias (used if ContactBox is created)
+        @param on_click (callable): click callback (used if ContactBox is created)
+        @param handle_menu (bool): if True, bind a popup menu to the avatar (used if ContactBox is created)
+        @return: ContactBox instance
+        """
         assert isinstance(contact_jid, jid.JID)
-        for wid in self:
-            if isinstance(wid, ContactBox) and wid.jid == contact_jid:
-                return wid
-        return None
+        try:
+            return self._contacts[contact_jid]
+        except KeyError:
+            box = ContactBox(self.host, contact_jid, name, on_click, handle_menu)
+            self._contacts[contact_jid] = box
+            return box
 
     def updateAvatar(self, jid_, url):
         """Update the avatar of the given contact
@@ -308,15 +308,12 @@
                 C.PRESENCE_UNAVAILABLE or None if not connected, else presence like RFC6121 #4.7.2.1"""
         assert type_ in ('availability', 'messageWaiting')
         contact_box = self.getContactBox(jid_)
-        if not contact_box:
-            log.warning("No contact box found for {}".format(jid_))
-        else:
-            if type_ == 'availability':
-                if state is None:
-                    state = C.PRESENCE_UNAVAILABLE
-                setPresenceStyle(contact_box.label, state)
-            elif type_ == 'messageWaiting':
-                contact_box.setMessageWaiting(state)
+        if type_ == 'availability':
+            if state is None:
+                state = C.PRESENCE_UNAVAILABLE
+            setPresenceStyle(contact_box.label, state)
+        elif type_ == 'messageWaiting':
+            contact_box.setMessageWaiting(state)
 
 
 class ContactTitleLabel(base_widget.DragLabel, Label, ClickHandler):
@@ -380,15 +377,13 @@
             self._group_panel.remove(group)
 
         ### JIDS ###
-        current_contacts = set([jid_ for jid_ in self._cache.keys() if self.entityToShow(jid_)])
-        shown_contacts = set(self._contacts_panel.getContacts())
-        new_contacts = current_contacts.difference(shown_contacts)
-        removed_contacts = shown_contacts.difference(current_contacts)
+        to_show = [jid_ for jid_ in self._cache.keys() if self.entityToShow(jid_)]
+        to_show.sort()
 
-        for contact in new_contacts:
-            self._contacts_panel.add(contact)
-        for contact in removed_contacts:
-            self._contacts_panel.remove(contact)
+        self._contacts_panel.display(to_show)
+
+        for jid_ in self._alerts:
+            self._contacts_panel.setState(jid_, "messageWaiting", True)
 
     def onWindowResized(self, width, height):
         contact_panel_elt = self.getElement()
@@ -480,10 +475,12 @@
     #     self.updateVisibility([jid_s], self.getContactGroups(jid_s))
 
     def setContactMessageWaiting(self, jid, waiting):
-        """Show an visual indicator that contact has send a message
+        """Show a visual indicator that contact has send a message
+
         @param jid: jid of the contact
         @param waiting: True if message are waiting"""
-        self._contacts_panel.setState(jid, "messageWaiting", waiting)
+        raise Exception("Should not be there")
+        # self._contacts_panel.setState(jid, "messageWaiting", waiting)
 
     # def getConnected(self, filter_muc=False):
     #     """return a list of all jid (bare jid) connected
@@ -596,6 +593,7 @@
         entity_bare = entity.bare
         show = self.getCache(entity_bare, C.PRESENCE_SHOW) # we use cache to have the show nformation of main resource only
         self._contacts_panel.setState(entity_bare, "availability", show)
+        self.update()
 
     # def updateVisibility(self, jids, groups):
     #     """Set the widgets visibility for the given contacts and groups