changeset 1388:a025242bebe7

quick_frontend, primitivus: remove QuickChat.updateEntityState and QuickChat.setContactStates, use more general QuickChat.update (also called when joining the room to initialise the occupants list)
author souliane <souliane@mailoo.org>
date Tue, 24 Mar 2015 17:31:08 +0100
parents 3511ff4b40a0
children eb907923dce9
files frontends/src/primitivus/chat.py frontends/src/quick_frontend/quick_app.py frontends/src/quick_frontend/quick_chat.py frontends/src/quick_frontend/quick_games.py
diffstat 4 files changed, 96 insertions(+), 54 deletions(-) [+]
line wrap: on
line diff
--- a/frontends/src/primitivus/chat.py	Tue Mar 24 10:46:42 2015 +0100
+++ b/frontends/src/primitivus/chat.py	Tue Mar 24 17:31:08 2015 +0100
@@ -100,6 +100,7 @@
                 self.occupants_list = sat_widgets.GenericList([], option_type=sat_widgets.ClickableText, on_click=self._occupantsClicked)
                 self.occupants_panel = sat_widgets.VerticalSeparator(self.occupants_list)
                 self._appendOccupantsPanel()
+                self.host.addListener('presence', self.presenceListener, [profiles])
 
         self.day_change = time.strptime(time.strftime("%a %b %d 00:00:00  %Y"))  # struct_time of day changing time
         self.show_timestamp = True
@@ -148,24 +149,78 @@
             menu.addMenu(_("Action"), _("Send file"), self.onSendFileRequest)
         return menu
 
-    def setContactStates(self, contact_jid, states):
-        """Set a contact (one2one or MUC occupant) states.
+    def presenceListener(self, entity, show, priority, statuses, profile):
+        """Update entity's presence status
 
-        @param contact_jid (jid.JID): contact
-        @param states (dict{unicode: unicode}): new states
+        @param entity (jid.JID): entity updated
+        @param show: availability
+        @param priority: resource's priority
+        @param statuses: dict of statuses
+        @param profile: %(doc_profile)s
+        """
+        assert self.type == C.CHAT_GROUP
+        if entity.bare != self.target:
+            return
+        self.update(entity)
+
+    def update(self, entity=None):
+        """Update one or all entities.
+
+        @param entity (jid.JID): entity to update
         """
-        if self.type == C.CHAT_GROUP:
-            options = self.occupants_list.getAllValues()
-            for index in xrange(0, len(options)):
-                nick = options[index].value
-                if nick == contact_jid.resource:
-                    options[index] = (nick, "%s %s" % (u''.join(states.values()), nick))
-                    self.occupants_list.changeValues(options)
-                    break
-        else:
+        contact_list = self.host.contact_lists[self.profile]
+
+        if self.type == C.CHAT_ONE2ONE:  # only update the chat title
+            states = self.getEntityStates(self.target)
             self.title_dynamic = ' '.join([u'({})'.format(state) for state in states.values()])
+            self.host.redraw()
+            return
+
+        nicks = list(self.occupants)
+        if entity is None:  # rebuild all the occupants list
+            values = []
+            nicks.sort()
+            for nick in nicks:
+                values.append(self._buildOccupantMarkup(jid.newResource(self.target, nick)))
+            self.occupants_list.changeValues(values)
+        else:  # add, remove or update only one occupant
+            nick = entity.resource
+            show = contact_list.getCache(entity, C.PRESENCE_SHOW)
+            if show == C.PRESENCE_UNAVAILABLE:
+                self.occupants_list.deleteValue(nick)
+            else:
+                values = self.occupants_list.getAllValues()
+                if not values:  # room has just been created
+                    self.occupants_list.changeValues([self._buildOccupantMarkup(entity)])
+                else:  # add or update the occupant, keep the list sorted
+                    index = 0
+                    for entry in values:
+                        order = cmp(entry.value if hasattr(entry, 'value') else entry, nick)
+                        if order < 0:
+                            index += 1
+                            continue
+                        if order > 0:  # insert the occupant the occupant
+                            values.insert(index, self._buildOccupantMarkup(entity))
+                        else:  # update the occupant
+                            values[index] = self._buildOccupantMarkup(entity)
+                        self.occupants_list.changeValues(values)
+                        break
         self.host.redraw()
 
+    def _buildOccupantMarkup(self, entity):
+        """Return the option attributes for a MUC occupant.
+
+        @param nick (unicode): occupant nickname
+        """
+        contact_list = self.host.contact_lists[self.profile]
+        show = contact_list.getCache(entity, C.PRESENCE_SHOW)
+        states = self.getEntityStates(entity)
+        nick = entity.resource
+        # TODO: use entity_attr and return (nick, markup), but ListOption is unicode and not urwid.Text
+        show_icon, entity_attr = C.PRESENCE.get(show, (u'', u'default'))
+        text = "%s%s %s" % (u''.join(states.values()), show_icon, nick)
+        return (nick, text)
+
     def _occupantsClicked(self, list_wid, clicked_wid):
         assert self.type == C.CHAT_GROUP
         nick = clicked_wid.getValue().value
@@ -218,24 +273,6 @@
         self.chat_widget.header = urwid.AttrMap(self.subj_wid, 'title')
         self.host.redraw()
 
-    def addUser(self, param_nick):
-        """Add user if it is not in the group list"""
-        nick = unicode(param_nick)  # FIXME: should be done in DBus bridge
-        QuickChat.addUser(self, nick)
-        occupants = self.occupants_list.getAllValues()
-        if nick not in [occupants.value for occupants in occupants]:
-            occupants.append(nick)
-            occupants.sort(cmp=lambda a, b: cmp(a.value if hasattr(a, 'value') else a, b.value if hasattr(b, 'value') else b))
-            self.occupants_list.changeValues(occupants)
-        self.host.redraw()
-
-    def removeUser(self, param_nick):
-        """Remove a user from the group list"""
-        nick = unicode(param_nick)  # FIXME: should be done in DBus bridge
-        QuickChat.removeUser(self, nick)
-        self.occupants_list.deleteValue(nick)
-        self.host.redraw()
-
     def clearHistory(self):
         """Clear the content of this chat."""
         del self.content[:]
@@ -356,6 +393,11 @@
         self.host.addProgress(progress_id, filepath)
         self.host.showDialog(_(u"You file request has been sent, we are waiting for your contact answer"), title=_("File request sent"))
 
+    def onDelete(self):
+        QuickChat.onDelete(self)
+        if self.type == C.CHAT_GROUP:
+            self.host.removeListener('presence', self.presenceListener)
+
 
 quick_widgets.register(QuickChat, Chat)
 quick_widgets.register(quick_games.Tarot, game_tarot.TarotGame)
--- a/frontends/src/quick_frontend/quick_app.py	Tue Mar 24 10:46:42 2015 +0100
+++ b/frontends/src/quick_frontend/quick_app.py	Tue Mar 24 17:31:08 2015 +0100
@@ -534,6 +534,7 @@
         chat_widget = self.widgets.getOrCreateWidget(quick_chat.QuickChat, room_jid, type_=C.CHAT_GROUP, profile=profile)
         chat_widget.setUserNick(unicode(user_nick))
         self.contact_lists[profile].setSpecial(room_jid, C.CONTACT_SPECIAL_GROUP)
+        chat_widget.update()
 
     def roomLeftHandler(self, room_jid_s, profile):
         """Called when a MUC room is left"""
@@ -579,16 +580,20 @@
         @param state (unicode): new state
         @param profile (unicode): current profile
         """
+        log.debug(_(u"Received new chat state {} from {} [{}]").format(state, from_jid_s, profile))
         from_jid = jid.JID(from_jid_s) if from_jid_s != C.ENTITY_ALL else C.ENTITY_ALL
+        contact_list = self.contact_lists[profile]
         for widget in self.widgets.getWidgets(quick_chat.QuickChat):
             if profile != widget.profile:
                 continue
             to_display = C.USER_CHAT_STATES[state] if (state and widget.type == C.CHAT_GROUP) else state
             if widget.type == C.CHAT_GROUP and from_jid_s == C.ENTITY_ALL:
                 for occupant in [jid.newResource(widget.target, nick) for nick in widget.occupants]:
-                    widget.updateEntityState(occupant, 'chat_state', to_display)
+                    contact_list.setCache(occupant, 'chat_state', to_display)
+                    widget.update(occupant)
             elif from_jid.bare == widget.target.bare:  # roster contact or MUC occupant
-                widget.updateEntityState(from_jid, 'chat_state', to_display)
+                contact_list.setCache(from_jid, 'chat_state', to_display)
+                widget.update(from_jid)
 
     def personalEventHandler(self, sender, event_type, data):
         """Called when a PEP event is received.
--- a/frontends/src/quick_frontend/quick_chat.py	Tue Mar 24 10:46:42 2015 +0100
+++ b/frontends/src/quick_frontend/quick_chat.py	Tue Mar 24 17:31:08 2015 +0100
@@ -206,30 +206,19 @@
         """
         raise NotImplementedError
 
-    def updateEntityState(self, entity, type_, value):
-        """Update a state value for the given entity.
+    def getEntityStates(self, entity):
+        """Retrieve states for an entity.
 
-        @param entity (jid.JID): entity to update
-        @param type_ (unicode): type of state (e.g. 'chat_state')
-        @param value (unicode): new value
+        @param entity (jid.JID): entity
+        @return: OrderedDict{unicode: unicode}
         """
-        contact_list = self.host.contact_lists[self.profile]
-        contact_list.setCache(entity, type_, value)
+        states = OrderedDict()
         clist = self.host.contact_lists[self.profile]
-        states = OrderedDict()
         for key in self.visible_states:
             value = clist.getCache(entity, key)
             if value:
                 states[key] = value
-        self.setContactStates(entity, states)
-
-    def setContactStates(self, contact_jid, states):
-        """Set a contact (one2one or MUC occupant) states.
-
-        @param contact_jid (jid.JID): contact
-        @param states (dict{unicode: unicode}): new states
-        """
-        raise NotImplementedError
+        return states
 
     def addGamePanel(self, widget):
         """Insert a game panel to this Chat dialog.
@@ -245,5 +234,12 @@
         """
         raise NotImplementedError
 
+    def update(self, entity=None):
+        """Update one or all entities.
+
+        @param entity (jid.JID): entity to update
+        """
+        raise NotImplementedError
+
 
 quick_widgets.register(QuickChat)
--- a/frontends/src/quick_frontend/quick_games.py	Tue Mar 24 10:46:42 2015 +0100
+++ b/frontends/src/quick_frontend/quick_games.py	Tue Mar 24 17:31:08 2015 +0100
@@ -59,12 +59,11 @@
             chat_widget.visible_states.append(cls._game_name)
         symbols = games.SYMBOLS[cls._game_name]
         index = 0
+        contact_list = host.contact_lists[profile]
         for occupant in chat_widget.occupants:
             occupant_jid = jid.newResource(room_jid, occupant)
-            if occupant in players:
-                chat_widget.updateEntityState(occupant_jid, cls._game_name, symbols[index % len(symbols)])
-            else:
-                chat_widget.updateEntityState(occupant_jid, cls._game_name, None)
+            contact_list.setCache(occupant_jid, cls._game_name, symbols[index % len(symbols)] if occupant in players else None)
+            chat_widget.update(occupant_jid)
 
         if suffix == "Players" or chat_widget.nick not in players:
             return  # waiting for other players to join, or not playing