Mercurial > libervia-web
view browser_side/contact.py @ 254:28d3315a8003
browser_side: isolate the basic stuff of RecipientManager in a new class ListManager:
- renamed most occurences of "recipient" to "contact" and "recipient type" to "contact key" or "list"
- data to represent the lists and autocomplete values are parametrized
- UI elements styles are set by default but can be ovewritten by a sub-class
- popup menu for the list Button element has to be set with registerPopupMenuPanel
- richtext UI uses the definitions from sat.tool.frontends.composition
Know issues:
- drag and drop AutoCompleteTextBox corrupts the list of remaining autocomplete values
- selecting an autocomplete value with the mouse and not keybord is not working properly
author | souliane <souliane@mailoo.org> |
---|---|
date | Sat, 09 Nov 2013 09:38:17 +0100 |
parents | b77940d8a9bf |
children | da0487f0a2e7 |
line wrap: on
line source
#!/usr/bin/python # -*- coding: utf-8 -*- """ Libervia: a Salut à Toi frontend Copyright (C) 2011, 2012, 2013 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.ScrollPanel import ScrollPanel from pyjamas.ui.VerticalPanel import VerticalPanel from pyjamas.ui.ClickListener import ClickHandler from pyjamas.ui.Label import Label from pyjamas.ui.HTML import HTML from jid import JID from pyjamas import Window from pyjamas import DOM from browser_side.panels import ChatPanel, MicroblogPanel, PopupMenuPanel, WebPanel from browser_side.tools import DragLabel, html_sanitize from __pyjamas__ import doc class GroupLabel(DragLabel, Label, ClickHandler): def __init__(self, host, group): self.group = group self.host = host Label.__init__(self, group) #, Element=DOM.createElement('div') self.setStyleName('group') DragLabel.__init__(self, group, "GROUP") ClickHandler.__init__(self) self.addClickListener(self) def onClick(self, sender): self.host.getOrCreateLiberviaWidget(MicroblogPanel, self.group) class ContactLabel(DragLabel, HTML, ClickHandler): def __init__(self, host, jid, name=None): HTML.__init__(self) self.host = host self.name = name or jid self.waiting = False self.jid = jid self._fill() self.setStyleName('contact') DragLabel.__init__(self, jid, "CONTACT") ClickHandler.__init__(self) self.addClickListener(self) def _fill(self): if self.waiting: _wait_html = "<b>(*)</b> " 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() def onClick(self, sender): self.host.getOrCreateLiberviaWidget(ChatPanel, self.jid) class GroupList(VerticalPanel): def __init__(self, parent): VerticalPanel.__init__(self) self.setStyleName('groupList') self._parent = parent def add(self, group): _item = GroupLabel(self._parent.host, 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 GenericContactList(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.""" def __init__(self, host): VerticalPanel.__init__(self) self.host = host self.contacts = set() def add(self, jid, name=None, item_cb=None): if jid in self.contacts: return self.contacts.add(jid) _item = ContactLabel(self.host, jid, name) DOM.setStyleAttribute(_item.getElement(), "cursor", "pointer") VerticalPanel.add(self, _item) if item_cb is not None: item_cb(_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 class ContactList(GenericContactList): """The contact list that is displayed on the left side.""" def __init__(self, host): GenericContactList.__init__(self, host) self.menu_entries = {"blog": {"title": "Public blog..."}} self.context_menu = PopupMenuPanel(entries=self.menu_entries, hide=self.contextMenuHide, callback=self.contextMenuCallback, vertical=False, menu_style="menu") def contextMenuHide(self, sender, key): """Return True if the item for that sender should be hidden.""" # TODO: enable the blogs of users that are on another server return JID(sender.jid).domain != self.host._defaultDomain def contextMenuCallback(self, sender, key): if key == "blog": # TODO: use the bare when all blogs can be retrieved node = JID(sender.jid).node web_panel = WebPanel(self.host, "/blog/%s" % node) self.host.addTab(web_panel, "%s's blog" % node) else: sender.onClick(sender) def add(self, jid, name=None): def item_cb(item): self.context_menu.registerRightClickSender(item) GenericContactList.add(self, jid, name, item_cb) 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, ClickHandler): def __init__(self, host, text): Label.__init__(self, text) #, Element=DOM.createElement('div') self.host = host self.setStyleName('contactTitle') DragLabel.__init__(self, text, "CONTACT_TITLE") ClickHandler.__init__(self) self.addClickListener(self) def onClick(self, sender): self.host.getOrCreateLiberviaWidget(MicroblogPanel, None) class ContactPanel(SimplePanel): """Manage the contacts and groups""" def __init__(self, host): SimplePanel.__init__(self) self.scroll_panel = ScrollPanel() self.host = host self.groups = {} self.connected = {} # jid connected as key and their status self.vPanel = VerticalPanel() _title = ContactTitleLabel(host, 'Contacts') DOM.setStyleAttribute(_title.getElement(), "cursor", "pointer") self._contact_list = ContactList(host) 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.scroll_panel.add(self.vPanel) self.add(self.scroll_panel) self.setStyleName('contactBox') Window.addWindowResizeListener(self) def onWindowResized(self, width, height): contact_panel_elt = self.getElement() _elts = doc().getElementsByClassName('gwt-TabBar') if not _elts.length: print ("ERROR: no TabBar found, it should exist !") tab_bar_h = height else: tab_bar_h = DOM.getAbsoluteTop(_elts.item(0)) or height # getAbsoluteTop can be 0 if tabBar is hidden ideal_height = tab_bar_h - DOM.getAbsoluteTop(contact_panel_elt) - 5 self.scroll_panel.setHeight("%s%s" % (ideal_height, "px")); 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 group in self.groups.keys(): 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")