# HG changeset patch # User Goffi # Date 1424713647 -3600 # Node ID 7113d40533d65eb377bb9abfccdfa55c0c1aef9a # Parent 86ae737da6f362915eb89381f6c8c1e5931abf1d# Parent 66a547539185e9c991c96e7e96c861e2aa9d02c2 merged souliane changes diff -r 66a547539185 -r 7113d40533d6 src/browser/libervia_main.py --- a/src/browser/libervia_main.py Sun Feb 22 21:57:24 2015 +0100 +++ b/src/browser/libervia_main.py Mon Feb 23 18:47:27 2015 +0100 @@ -26,7 +26,7 @@ ### from sat_frontends.quick_frontend.quick_app import QuickApp -from sat_frontends.quick_frontend.quick_widgets import WidgetAlreadyExistsError +from sat_frontends.quick_frontend import quick_widgets from sat_frontends.tools.misc import InputHistory from sat_frontends.tools import strings @@ -114,6 +114,11 @@ return self.contact_lists[C.PROF_KEY_NONE] @property + def visible_widgets(self): + widgets_panel = self.tab_panel.getCurrentPanel() + return [wid for wid in widgets_panel.widgets if isinstance(wid, quick_widgets.QuickWidget)] + + @property def base_location(self): """Return absolute base url of this Libervia instance""" url = Window.getLocation().getHref() @@ -566,8 +571,8 @@ something, a new widget is always created, otherwise we look for an existing widget and re-use it if it's in the current tab. - @arg class_(class): see sat_frontends.quick_frontend.quick_widgets.getOrCreateWidget - @arg target: see sat_frontends.quick_frontend.quick_widgets.getOrCreateWidget + @arg class_(class): see quick_widgets.getOrCreateWidget + @arg target: see quick_widgets.getOrCreateWidget @arg dropped(bool): if True, assume the widget has been dropped @arg new_tab(unicode): if not None, it holds the name of a new tab to open for the widget. If None, use the default behavior. @@ -595,10 +600,15 @@ kwargs['on_existing_widget'] = C.WIDGET_RAISE try: wid = self.widgets.getOrCreateWidget(class_, target, *args, **kwargs) - except WidgetAlreadyExistsError: + except quick_widgets.WidgetAlreadyExistsError: kwargs['on_existing_widget'] = C.WIDGET_KEEP wid = self.widgets.getOrCreateWidget(class_, target, *args, **kwargs) - if wid.getWidgetsPanel() != self.tab_panel.getCurrentPanel(): + widgets_panel = wid.getParent(base_widget.WidgetsPanel, expect=False) + if widgets_panel is None: + # The widget exists but is hidden + self.addWidget(wid) + elif widgets_panel != self.tab_panel.getCurrentPanel(): + # the widget is on an other tab, so we add a new one here kwargs['on_existing_widget'] = C.WIDGET_RECREATE wid = self.widgets.getOrCreateWidget(class_, target, *args, **kwargs) self.addWidget(wid) diff -r 66a547539185 -r 7113d40533d6 src/browser/sat_browser/base_menu.py --- a/src/browser/sat_browser/base_menu.py Sun Feb 22 21:57:24 2015 +0100 +++ b/src/browser/sat_browser/base_menu.py Mon Feb 23 18:47:27 2015 +0100 @@ -37,7 +37,7 @@ import re -class MenuCmd: +class MenuCmd(object): """Return an object with an "execute" method that can be set to a menu item callback""" def __init__(self, object_, handler=None, data=None): @@ -58,7 +58,7 @@ self.callback(self.data) if self.data else self.callback() -class PluginMenuCmd: +class PluginMenuCmd(object): """Like MenuCmd, but instead of executing a method, it will command the bridge to launch an action""" def __init__(self, host, action_id, menu_data=None): diff -r 66a547539185 -r 7113d40533d6 src/browser/sat_browser/base_widget.py --- a/src/browser/sat_browser/base_widget.py Sun Feb 22 21:57:24 2015 +0100 +++ b/src/browser/sat_browser/base_widget.py Mon Feb 23 18:47:27 2015 +0100 @@ -20,7 +20,7 @@ import pyjd # this is dummy in pyjs from sat.core.log import getLogger log = getLogger(__name__) - +from sat.core import exceptions from sat.core.i18n import _ from sat_frontends.quick_frontend import quick_widgets @@ -172,7 +172,7 @@ if self == _new_panel: # We can't drop on ourself return # we need to remove the widget from the panel as it will be inserted elsewhere - widgets_panel = _new_panel.getWidgetsPanel() + widgets_panel = _new_panel.getParent(WidgetsPanel, expect=True) wid_row = widgets_panel.getWidgetCoords(_new_panel)[0] row_wids = widgets_panel.getLiberviaRowWidgets(wid_row) if len(row_wids) == 1 and wid_row == widgets_panel.getWidgetCoords(self)[0]: @@ -226,10 +226,13 @@ menu_styles.update(styles) base_menu.GenericMenuBar.__init__(self, host, vertical=vertical, styles=menu_styles) - if hasattr(parent, 'addMenus'): - # regroup all the dynamic menu categories in a sub-menu - sub_menu = WidgetSubMenuBar(host, vertical=True) + # regroup all the dynamic menu categories in a sub-menu + sub_menu = WidgetSubMenuBar(host, vertical=True) + try: parent.addMenus(sub_menu) + except (AttributeError, TypeError): # FIXME: pyjamas can throw a TypeError depending on compilation options + pass + else: if len(sub_menu.getCategories()) > 0: self.addCategory('', '', 'plugins', sub_menu) @@ -329,17 +332,15 @@ def getDebugName(self): return "%s (%s)" % (self, self._title.getText()) - def getWidgetsPanel(self, expect=True): - return self.getParent(WidgetsPanel, expect) - def getParent(self, class_=None, expect=True): """Return the closest ancestor of the specified class. Note: this method overrides pyjamas.ui.Widget.getParent @param class_: class of the ancestor to look for or None to return the first parent - @param expect: set to True if the parent is expected (print a message if not found) + @param expect: set to True if the parent is expected (raise an error if not found) @return: the parent/ancestor or None if it has not been found + @raise exceptions.InternalError: expect is True and no parent is found """ current = Widget.getParent(self) if class_ is None: @@ -347,7 +348,7 @@ while current is not None and not isinstance(current, class_): current = Widget.getParent(current) if current is None and expect: - log.error("Can't find parent %s for %s" % (class_, self)) + raise exceptions.InternalError("Can't find parent %s for %s" % (class_, self)) return current def onClick(self, sender): @@ -355,8 +356,8 @@ def onClose(self, sender): """ Called when the close button is pushed """ - _widgetspanel = self.getWidgetsPanel() - _widgetspanel.removeWidget(self) + widgets_panel = self.getParent(WidgetsPanel, expect=True) + widgets_panel.removeWidget(self) self.onQuit() self.host.widgets.deleteWidget(self) @@ -371,7 +372,7 @@ pass def onSetting(self, sender): - widpanel = self.getWidgetsPanel() + widpanel = self.getParent(WidgetsPanel, expect=True) row, col = widpanel.getIndex(self) body = VerticalPanel() @@ -803,7 +804,7 @@ log.error("No widget registered in LiberviaDragWidget !") return _new_panel = LiberviaDragWidget.current - _new_panel.getWidgetsPanel().removeWidget(_new_panel) + _new_panel.getParent(WidgetsPanel, expect=True).removeWidget(_new_panel) elif item_type in DropCell.drop_keys: _new_panel = DropCell.drop_keys[item_type](self.tab_panel.host, item) else: diff -r 66a547539185 -r 7113d40533d6 src/browser/sat_browser/contact_list.py --- a/src/browser/sat_browser/contact_list.py Sun Feb 22 21:57:24 2015 +0100 +++ b/src/browser/sat_browser/contact_list.py Mon Feb 23 18:47:27 2015 +0100 @@ -93,24 +93,31 @@ class ContactLabel(HTML): - def __init__(self, jid_, name=None): + """Display a contact in HTML, selecting best display (jid/nick/etc)""" + + def __init__(self, host, jid_): + # TODO: add a listener for nick changes HTML.__init__(self) - self.name = name or unicode(jid_) - self.waiting = False + self.host = host + self.jid = jid_.bare + self.nick = self.host.contact_lists[C.PROF_KEY_NONE].getCache(self.jid, "nick") + self.alert = False self.refresh() self.setStyleName('contactLabel') def refresh(self): - if self.waiting: - wait_html = "(*) " - self.setHTML("%(wait)s%(name)s" % {'wait': wait_html, - 'name': html_tools.html_sanitize(self.name)}) + alert_html = "(*) " if self.alert else "" + contact_html = html_tools.html_sanitize(self.nick or unicode(self.jid)) + html = "%(alert)s%(contact)s" % {'alert': alert_html, + 'contact': contact_html} + self.setHTML(html) - def setMessageWaiting(self, waiting): - """Show a visual indicator if message are waiting + def setAlert(self, alert): + """Show a visual indicator - @param waiting: True if message are waiting""" - self.waiting = waiting + @param alert: True if alert must be shown + """ + self.alert = alert self.refresh() @@ -131,37 +138,31 @@ class ContactBox(VerticalPanel, ClickHandler, base_widget.DragLabel): - def __init__(self, host, jid_, name=None, click_listener=None, handle_menu=None): + def __init__(self, parent, jid_): """ - - @param host (SatWebFrontend) + @param parent (ContactPanel): ContactPanel hosting this box @param jid_ (jid.JID): contact JID - @param name (unicode): contact alias - @param click_listener (callable): click callback - @param handle_menu (bool): if True, bind a popup menu to the avatar """ VerticalPanel.__init__(self, StyleName='contactBox', VerticalAlignment='middle') - base_widget.DragLabel.__init__(self, jid_, "CONTACT", host) - self.jid = jid_ - self.label = ContactLabel(jid_, name) - self.avatar = ContactMenuBar(self, host) if handle_menu else Image() - self.updateAvatar(host.getAvatarURL(jid_)) + ClickHandler.__init__(self) + base_widget.DragLabel.__init__(self, jid_, "CONTACT", parent.host) + self.jid = jid_.bare + self.label = ContactLabel(parent.host, self.jid) + self.avatar = ContactMenuBar(self, parent.host) if parent.handle_menu else Image() + self.updateAvatar(parent.host.getAvatarURL(self.jid)) self.add(self.avatar) self.add(self.label) - if click_listener: - ClickHandler.__init__(self) - self.addClickListener(self) - self.click_listener = click_listener + self.addClickListener(self) def addMenus(self, menu_bar): menu_bar.addCachedMenus(C.MENU_ROSTER_JID_CONTEXT, {'jid': unicode(self.jid)}) menu_bar.addCachedMenus(C.MENU_JID_CONTEXT, {'jid': unicode(self.jid)}) - def setMessageWaiting(self, waiting): - """Show a visual indicator if message are waiting + def setAlert(self, alert): + """Show a visual indicator - @param waiting: True if message are waiting""" - self.label.setMessageWaiting(waiting) + @param alert: True if alert indicator show be shown""" + self.label.setAlert(alert) def updateAvatar(self, url): """Update the avatar. @@ -171,7 +172,12 @@ self.avatar.setUrl(url) def onClick(self, sender): - self.click_listener(self.jid) + try: + self.parent.onClick(self.jid) + except AttributeError: + pass + else: + self.setAlert(False) class GroupPanel(VerticalPanel): @@ -220,64 +226,64 @@ 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): + def __init__(self, parent, handle_click=True, handle_menu=True): + """@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) + """ # FIXME VerticalPanel.__init__(self) - self.host = host - self.contacts = [] + self._parent = parent + self.host = parent.host + self._contacts = {} # entity jid to ContactBox map self.click_listener = None self.handle_menu = handle_menu if handle_click: def cb(contact_jid): - host.displayWidget(chat.Chat, contact_jid, type_=C.CHAT_ONE2ONE) - self.click_listener = cb + self.host.displayWidget(chat.Chat, contact_jid, type_=C.CHAT_ONE2ONE) + self.onClick = 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 + """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""" - assert isinstance(contact_jid, jid.JID) - for wid in self: - if isinstance(wid, ContactBox) and wid.jid == contact_jid: - return wid - return None + @return: ContactBox instance + """ + try: + return self._contacts[contact_jid.bare] + except KeyError: + box = ContactBox(self, contact_jid) + self._contacts[contact_jid.bare] = box + return box def updateAvatar(self, jid_, url): """Update the avatar of the given contact @@ -294,11 +300,12 @@ class ContactsPanel(BaseContactsPanel): """The contact list that is displayed on the left side.""" - def __init__(self, host): - BaseContactsPanel.__init__(self, host, handle_click=True, handle_menu=True) + def __init__(self, parent): + BaseContactsPanel.__init__(self, parent, handle_click=True, handle_menu=True) def setState(self, jid_, type_, state): """Change the appearance of the contact, according to the state + @param jid_ (jid.JID): jid.JID which need to change state @param type_ (unicode): one of "availability", "messageWaiting" @param state: @@ -308,18 +315,16 @@ 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.setAlert(state) class ContactTitleLabel(base_widget.DragLabel, Label, ClickHandler): + def __init__(self, host, text): Label.__init__(self, text) # , Element=DOM.createElement('div') self.setStyleName('contactTitle') @@ -337,11 +342,12 @@ def __init__(self, host): QuickContactList.__init__(self, host, C.PROF_KEY_NONE) SimplePanel.__init__(self) + self.host = host self.scroll_panel = ScrollPanel() self.vPanel = VerticalPanel() _title = ContactTitleLabel(host, 'Contacts') DOM.setStyleAttribute(_title.getElement(), "cursor", "pointer") - self._contacts_panel = ContactsPanel(host) + self._contacts_panel = ContactsPanel(self) self._contacts_panel.setStyleName('contactPanel') # FIXME: style doesn't exists ! self._group_panel = GroupPanel(self) @@ -383,15 +389,13 @@ self._group_panel.remove(group) ### JIDS ### - current_contacts = set(self._cache.keys()) - 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() @@ -479,10 +483,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 @@ -570,6 +576,7 @@ @param group (unicode): the group to check @return: boolean """ + raise Exception # FIXME: remove this method for jid_ in self.groups[group]: if self._contacts_panel.getContactBox(jid_).isVisible(): return True @@ -580,20 +587,21 @@ @return: boolean """ - return self.host.getCachedParam('General', C.SHOW_OFFLINE_CONTACTS) == 'true' + return C.bool(self.host.getCachedParam('General', C.SHOW_OFFLINE_CONTACTS)) def emtyGroupsToShow(self): """Tell if empty groups should be visible according to the user settings @return: boolean """ - return self.host.getCachedParam('General', C.SHOW_EMPTY_GROUPS) == 'true' + return C.bool(self.host.getCachedParam('General', C.SHOW_EMPTY_GROUPS)) def updatePresence(self, entity, show, priority, statuses): QuickContactList.updatePresence(self, entity, show, priority, statuses) 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 diff -r 66a547539185 -r 7113d40533d6 src/browser/sat_browser/panels.py --- a/src/browser/sat_browser/panels.py Sun Feb 22 21:57:24 2015 +0100 +++ b/src/browser/sat_browser/panels.py Mon Feb 23 18:47:27 2015 +0100 @@ -389,7 +389,7 @@ class PresenceStatusMenuBar(base_widget.WidgetMenuBar): def __init__(self, parent): styles = {'menu_bar': 'presence-button'} - base_widget.WidgetMenuBar.__init__(self, None, parent.host, styles=styles) + base_widget.WidgetMenuBar.__init__(self, parent, parent.host, styles=styles) self.button = self.addCategory(u"◉", u"◉", '') for presence, presence_i18n in C.PRESENCE.items(): html = u' %s' % (contact_list.buildPresenceStyle(presence), presence_i18n)