view browser_side/contact.py @ 62:12e889a683ce SàT v0.2.0

server side: misc stuff: - fixed lot of misuse in callbacks - chat history fixed - a visual indicator now appear when we have message waiting from a contact - fixed About dialog size issue in webkit - fixed Drag'n'Drop for webkit - defaut room to join is now libervia@conference.libervia.org
author Goffi <goffi@goffi.org>
date Tue, 31 May 2011 17:06:59 +0200
parents d5266c41ca24
children 9d8e79ac4c9c
line wrap: on
line source

#!/usr/bin/python
# -*- coding: utf-8 -*-

"""
Libervia: a Salut à Toi frontend
Copyright (C) 2011  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/>.
"""

import pyjd # this is dummy in pyjs
from pyjamas.ui.SimplePanel import SimplePanel
from pyjamas.ui.VerticalPanel import VerticalPanel
from pyjamas.ui.HorizontalPanel import HorizontalPanel
from pyjamas.ui.Label import Label
from pyjamas.ui.HTML import HTML
from pyjamas import Window
from pyjamas import DOM

from pyjamas.dnd import makeDraggable
from pyjamas.ui.DragWidget import DragWidget, DragContainer
from browser_side.tools import html_sanitize
from jid import JID

class DragLabel(DragWidget):

    def __init__(self, text, type):
        DragWidget.__init__(self)
        self._text = text
        self._type = type
    
    def onDragStart(self, event):
        dt = event.dataTransfer
        dt.setData('text/plain', "%s\n%s" % (self._text, self._type))
        dt.setDragImage(self.getElement(), 15, 15)

class GroupLabel(DragLabel, Label):
    def __init__(self, group):
        self.group = group
        Label.__init__(self, group) #, Element=DOM.createElement('div')
        self.setStyleName('group')
        DragLabel.__init__(self, group, "GROUP")
    

class ContactLabel(DragLabel, HTML):
    def __init__(self, jid, name=None):
        HTML.__init__(self)
        self.name = name or jid
        self.waiting = False
        self.jid=jid
        self._fill()
        self.setStyleName('contact')
        DragLabel.__init__(self, jid, "CONTACT")

    def _fill(self):
        if self.waiting:
            _wait_html = "<b>(*)</b>&nbsp;"
        self.setHTML("%(wait)s%(name)s" % {'wait': _wait_html,
                                           'name': html_sanitize(self.name)})

    def setMessageWaiting(self, waiting):
        """Show a visual indicator if message are waiting
        @param waiting: True if message are waiting"""
        self.waiting = waiting
        self._fill()

class GroupList(VerticalPanel):

    def __init__(self, parent):
        VerticalPanel.__init__(self)
        self._parent = parent

    def add(self, group):
        _item = GroupLabel(group)
        _item.addMouseListener(self._parent)
        DOM.setStyleAttribute(_item.getElement(), "cursor", "pointer")
        VerticalPanel.add(self, _item)

    def remove(self, group):
        for wid in self:
            if isinstance(wid, GroupLabel) and wid.group == group:
                VerticalPanel.remove(self, wid)
    
class ContactList(VerticalPanel):

    def __init__(self):
        VerticalPanel.__init__(self)
        self.contacts = set()

    def add(self, jid, name=None):
        if jid in self.contacts:
            return
        self.contacts.add(jid)
        _item = ContactLabel(jid, name)
        DOM.setStyleAttribute(_item.getElement(), "cursor", "pointer")
        VerticalPanel.add(self, _item)

    def remove(self, jid):
        wid = self.getContactLabel(jid)
        if not wid:
            return
        VerticalPanel.remove(self, wid)
        self.contacts.remove(jid)

    def isContactPresent(self, contact_jid):
        """Return True if a contact is present in the panel"""
        return contact_jid in self.contacts

    def getContacts(self):
        return self.contacts

    def getContactLabel(self, contact_jid):
        """get contactList widget of a contact
        @return: ContactLabel item if present, else None"""
        for wid in self:
            if isinstance(wid, ContactLabel) and wid.jid == contact_jid:
                return wid
        return None

    def setState(self, jid, type, state):
        """Change the appearance of the contact, according to the state
        @param jid: jid which need to change state
        @param type: one of availability, messageWaiting
        @param state:
            - for messageWaiting type:
                True if message are waiting
            - for availability type:
                'unavailable' if not connected, else presence like RFC6121 #4.7.2.1"""
        _item = self.getContactLabel(jid)
        if _item:
            if type == 'availability':
                if state == 'unavailable':
                    _item.removeStyleName('contactConnected')
                else:
                    _item.addStyleName('contactConnected')
            elif type == 'messageWaiting':
                _item.setMessageWaiting(state)

class ContactTitleLabel(DragLabel, Label):
    def __init__(self, text):
        Label.__init__(self, text) #, Element=DOM.createElement('div')
        self.setStyleName('contactTitle')
        DragLabel.__init__(self, text, "CONTACT_TITLE")

class ContactPanel(SimplePanel):
    """Manage the contacts and groups"""
    
    def __init__(self, host):
        SimplePanel.__init__(self)
        self.host = host
        self.groups={}
        self.connected = {} #jid connected as key and their status

        self.vPanel = VerticalPanel()
        _title = ContactTitleLabel('Contacts')
        DOM.setStyleAttribute(_title.getElement(), "cursor", "pointer")

        self._contact_list = ContactList()
        self._contact_list.setStyleName('contactList')
        self._groupList = GroupList(self)
        self._groupList.setStyleName('groupList')
        
        self.vPanel.add(_title)
        self.vPanel.add(self._groupList)
        self.vPanel.add(self._contact_list)
        self.add(self.vPanel)
        self.setStyleName('contactBox')

    def updateContact(self, jid, attributes, groups):
        """Add a contact to the panel if it doesn't exist, update it else
        @param jid: jid
        @attributes: cf SàT Bridge API's newContact
        @param groups: list of groups"""
        _current_groups = self.getContactGroups(jid)
        _new_groups = set(groups)
        _key = "@%s: "

        for group in _current_groups.difference(_new_groups):
            #We remove the contact from the groups where he isn't anymore
            self.groups[group].remove(jid)
            if not self.groups[group]:
                #The group is now empty, we must remove it
                del self.groups[group]
                self._groupList.remove(group)
                self.host.uni_box.removeKey(_key % group)

        for group in _new_groups.difference(_current_groups):
            #We add the contact to the groups he joined
            if not self.groups.has_key(group):
                self.groups[group] = set()
                self._groupList.add(group)
                self.host.uni_box.addKey(_key % group)
            self.groups[group].add(jid)
        
        #We add the contact to contact list, it will check if contact already exists
        self._contact_list.add(jid)

    def removeContact(self, jid):
        """Remove contacts from groups where he is and contact list"""
        self.updateContact(jid, {}, []) #we remove contact from every group
        self._contact_list.remove(jid)

    def setConnected(self, jid, resource, availability, priority, statuses):
        """Set connection status"""
        if availability=='unavailable':
            if self.connected.has_key(jid):
                if self.connected[jid].has_key(resource):
                    del self.connected[jid][resource]
                if not self.connected[jid]:
                    del self.connected[jid]
        else: 
            if not self.connected.has_key(jid):
                self.connected[jid] = {}
            self.connected[jid][resource] = (availability, priority, statuses)
        self._contact_list.setState(jid, "availability", availability)

    def setContactMessageWaiting(self, jid, waiting):
        """Show an visual indicator that contact has send a message
        @param jid: jid of the contact
        @param waiting: True if message are waiting"""
        self._contact_list.setState(jid, "messageWaiting", waiting)



    def getConnected(self):
        """return a list of all jid (bare jid) connected"""
        return self.connected.keys()

    def getContactGroups(self, contact_jid):
        """Get groups where contact is
       @param group: string of single group, or list of string
       @param contact_jid: jid to test
        """
        result=set()
        for group in self.groups:
            if self.isContactInGroup(group, contact_jid):
                result.add(group)
        return result

    def isContactInGroup(self, group, contact_jid):
       """Test if the contact_jid is in the group
       @param group: string of single group, or list of string
       @param contact_jid: jid to test
       @return: True if contact_jid is in on of the groups"""
       if group in self.groups and contact_jid in self.groups[group]:
           return True
       return False

    def isContactInRoster(self, contact_jid):
        """Test if the contact is in our roster list"""
        for _contact_label in self._contact_list:
            if contact_jid == _contact_label.jid:
                return True
        return False

    def getContacts(self):
        return self._contact_list.getContacts()

    def getGroups(self):
        return self.groups.keys()

    def onMouseMove(self, sender, x, y):
        pass
        
    def onMouseDown(self, sender, x, y):
        pass

    def onMouseUp(self, sender, x, y):
        pass

    def onMouseEnter(self, sender):
        if isinstance(sender, GroupLabel):
            for contact in self._contact_list:
                if contact.jid in self.groups[sender.group]:
                    contact.addStyleName("selected")
    
    def onMouseLeave(self, sender):
        if isinstance(sender, GroupLabel):
            for contact in self._contact_list:
                if contact.jid in self.groups[sender.group]:
                    contact.removeStyleName("selected")