diff src/browser/sat_browser/contact_panel.py @ 679:a90cc8fc9605

merged branch frontends_multi_profiles
author Goffi <goffi@goffi.org>
date Wed, 18 Mar 2015 16:15:18 +0100
parents 849ffb24d5bf
children e876f493dccc
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/browser/sat_browser/contact_panel.py	Wed Mar 18 16:15:18 2015 +0100
@@ -0,0 +1,247 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Libervia: a Salut à Toi frontend
+# Copyright (C) 2011, 2012, 2013, 2014 Jérôme Poisson <goffi@goffi.org>
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+""" Contacts / jids related panels """
+
+import pyjd  # this is dummy in pyjs
+from sat.core.log import getLogger
+log = getLogger(__name__)
+from sat_frontends.tools import jid
+
+from pyjamas.ui.AbsolutePanel import AbsolutePanel
+from pyjamas.ui.VerticalPanel import VerticalPanel
+from pyjamas.ui.HTML import HTML
+
+import html_tools
+import contact_widget
+from constants import Const as C
+
+
+# FIXME: must be removed
+class Occupant(HTML):
+    """Occupant of a MUC room"""
+
+    def __init__(self, nick, state=None, special=""):
+        """
+        @param nick: the user nickname
+        @param state: the user chate state (XEP-0085)
+        @param special: a string of symbols (e.g: for activities)
+        """
+        HTML.__init__(self, StyleName="occupant")
+        self.nick = nick
+        self._state = state
+        self.special = special
+        self._refresh()
+
+    def __str__(self):
+        return self.nick
+
+    def setState(self, state):
+        self._state = state
+        self._refresh()
+
+    def addSpecial(self, special):
+        """@param special: unicode"""
+        if special not in self.special:
+            self.special += special
+            self._refresh()
+
+    def removeSpecials(self, special):
+        """@param special: unicode or list"""
+        if not isinstance(special, list):
+            special = [special]
+        for symbol in special:
+            self.special = self.special.replace(symbol, "")
+            self._refresh()
+
+    def _refresh(self):
+        state = (' %s' % C.MUC_USER_STATES[self._state]) if self._state else ''
+        special = "" if len(self.special) == 0 else " %s" % self.special
+        self.setHTML("%s%s%s" % (html_tools.html_sanitize(self.nick), special, state))
+
+
+class ContactsPanel(VerticalPanel):
+    """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, merge_resources=True, contacts_click=None,
+                 contacts_style=None, contacts_menus=True,
+                 contacts_display=C.CONTACT_DEFAULT_DISPLAY):
+        """
+
+        @param host (SatWebFrontend): host instance
+        @param merge_resources (bool): if True, the entities sharing the same
+            bare JID will also share the same contact box.
+        @param contacts_click (callable): click callback for the contact boxes
+        @param contacts_style (unicode): CSS style name for the contact boxes
+        @param contacts_menus (tuple): define the menu types that fit this
+            contact panel, with values from the menus type constants.
+        @param contacts_display (tuple): prioritize the display methods of the
+            contact's label with values in ("jid", "nick", "bare", "resource")
+        """
+        VerticalPanel.__init__(self)
+        self.host = host
+        self.merge_resources = merge_resources
+        self._contacts = {}  # entity jid to ContactBox map
+        self.click_listener = None
+
+        if contacts_click is not None:
+            self.onClick = contacts_click
+
+        self.contacts_style = contacts_style
+        self.contacts_menus = contacts_menus
+        self.contacts_display = contacts_display
+
+    def _key(self, contact_jid):
+        """Return internal key for this contact.
+
+        @param contact_jid (jid.JID): contact JID
+        @return: jid.JID
+        """
+        return contact_jid.bare if self.merge_resources else contact_jid
+
+    def setList(self, jids):
+        """set all contacts in the list in one shot.
+
+        @param jids (list[jid.JID]): jids to display (the order is kept)
+        @param name (unicode): optional name of the contact
+        """
+        # 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, contact_widget.ContactBox)]
+        if current == jids:
+            # the display doesn't change
+            return
+        self.clear()
+        for contact_jid in jids:
+            assert isinstance(contact_jid, jid.JID)
+            self.addContact(contact_jid)
+
+    def getContactBox(self, contact_jid):
+        """Get a contact box for a contact, add it if it doesn't exist yet.
+
+        @param contact_jid (jid.JID): contact JID
+        @return: ContactBox
+        """
+        try:
+            return self._contacts[self._key(contact_jid)]
+        except KeyError:
+            box = contact_widget.ContactBox(self.host, contact_jid,
+                                            style_name=self.contacts_style,
+                                            display=self.contacts_display,
+                                            plugin_menu_context=self.contacts_menus)
+            self._contacts[self._key(contact_jid)] = box
+            return box
+
+    def addContact(self, contact_jid):
+        """Add a contact to the list.
+
+        @param contact_jid (jid.JID): contact JID
+        """
+        box = self.getContactBox(contact_jid)
+        if box not in self.children:
+            VerticalPanel.append(self, box)
+
+    def removeContact(self, contact_jid):
+        """Remove a contact from the list.
+
+        @param contact_jid (jid.JID): contact JID
+        """
+        box = self._contacts.pop(self._key(contact_jid))
+        VerticalPanel.remove(self, box)
+
+    def updateAvatar(self, contact_jid, url):
+        """Update the avatar of the given contact
+
+        @param contact_jid (jid.JID): contact JID
+        @param url (unicode): image url
+        """
+        try:
+            self.getContactBox(contact_jid).updateAvatar(url)
+        except TypeError:
+            pass
+
+    def updateNick(self, contact_jid, new_nick):
+        """Update the avatar of the given contact.
+
+        @param contact_jid (jid.JID): contact JID
+        @param new_nick (unicode): new nick of the contact
+        """
+        try:
+            self.getContactBox(contact_jid).updateNick(new_nick)
+        except TypeError:
+            pass
+
+
+
+# FIXME: must be removed and ContactsPanel must be used instead
+class OccupantsList(AbsolutePanel):
+    """Panel user to show occupants of a room"""
+
+    def __init__(self):
+        AbsolutePanel.__init__(self)
+        self.occupants_list = {}
+        self.setStyleName('occupantsList')
+
+    def addOccupant(self, nick):
+        if nick in self.occupants_list:
+            return
+        _occupant = Occupant(nick)
+        self.occupants_list[nick] = _occupant
+        self.add(_occupant)
+
+    def removeOccupant(self, nick):
+        try:
+            self.remove(self.occupants_list[nick])
+        except KeyError:
+            log.error("trying to remove an unexisting nick")
+
+    def getOccupantBox(self, nick):
+        """Get the widget element of the given nick.
+
+        @return: Occupant
+        """
+        try:
+            return self.occupants_list[nick]
+        except KeyError:
+            return None
+
+    def clear(self):
+        self.occupants_list.clear()
+        AbsolutePanel.clear(self)
+
+    def updateSpecials(self, occupants=[], html=""):
+        """Set the specified html "symbol" to the listed occupants,
+        and eventually remove it from the others (if they got it).
+        This is used for example to visualize who is playing a game.
+        @param occupants: list of the occupants that need the symbol
+        @param html: unicode symbol (actually one character or more)
+        or a list to assign different symbols of the same family.
+        """
+        index = 0
+        special = html
+        for occupant in self.occupants_list.keys():
+            if occupant in occupants:
+                if isinstance(html, list):
+                    special = html[index]
+                    index = (index + 1) % len(html)
+                self.occupants_list[occupant].addSpecial(special)
+            else:
+                self.occupants_list[occupant].removeSpecials(html)