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

merged branch frontends_multi_profiles
author Goffi <goffi@goffi.org>
date Wed, 18 Mar 2015 16:15:18 +0100
parents a8fddccf5b84
children 9877607c719a
line wrap: on
line diff
--- a/src/browser/sat_browser/dialog.py	Thu Feb 05 12:05:32 2015 +0100
+++ b/src/browser/sat_browser/dialog.py	Wed Mar 18 16:15:18 2015 +0100
@@ -19,7 +19,10 @@
 
 from sat.core.log import getLogger
 log = getLogger(__name__)
+
+from constants import Const as C
 from sat_frontends.tools.misc import DEFAULT_MUC
+from sat_frontends.tools import jid
 
 from pyjamas.ui.VerticalPanel import VerticalPanel
 from pyjamas.ui.Grid import Grid
@@ -37,19 +40,26 @@
 from pyjamas.ui.MouseListener import MouseWheelHandler
 from pyjamas import Window
 
-import base_panels
+import base_panel
 
 
 # List here the patterns that are not allowed in contact group names
 FORBIDDEN_PATTERNS_IN_GROUP = ()
 
 
+unicode = str # XXX: pyjama doesn't manage unicode
+
+
 class RoomChooser(Grid):
     """Select a room from the rooms you already joined, or create a new one"""
 
     GENERATE_MUC = "<use random name>"
 
     def __init__(self, host, default_room=DEFAULT_MUC):
+        """
+
+        @param host (SatWebFrontend)
+        """
         Grid.__init__(self, 2, 2, Width='100%')
         self.host = host
 
@@ -70,7 +80,19 @@
 
         self.exist_radio.setVisible(False)
         self.rooms_list.setVisible(False)
-        self.setRooms()
+        self.refreshOptions()
+
+    @property
+    def room(self):
+        """Get the room that has been selected or entered by the user
+
+        @return: jid.JID or None to let the backend generate a new name
+        """
+        if self.exist_radio.getChecked():
+            values = self.rooms_list.getSelectedValues()
+            return jid.JID(values[0]) if values else None
+        value = self.box.getText()
+        return None if value in ('', self.GENERATE_MUC) else jid.JID(value)
 
     def onFocus(self, sender):
         if sender == self.rooms_list:
@@ -85,21 +107,17 @@
             if self.box.getText() == "":
                 self.box.setText(self.GENERATE_MUC)
 
-    def setRooms(self):
-        for room in self.host.room_list:
+    def refreshOptions(self):
+        """Refresh the already joined room list"""
+        contact_list = self.host.contact_list
+        muc_rooms = contact_list.getSpecials(C.CONTACT_SPECIAL_GROUP)
+        for room in muc_rooms:
             self.rooms_list.addItem(room.bare)
-        if len(self.host.room_list) > 0:
+        if len(muc_rooms) > 0:
             self.exist_radio.setVisible(True)
             self.rooms_list.setVisible(True)
             self.exist_radio.setChecked(True)
 
-    def getRoom(self):
-        if self.exist_radio.getChecked():
-            values = self.rooms_list.getSelectedValues()
-            return "" if values == [] else values[0]
-        value = self.box.getText()
-        return "" if value == self.GENERATE_MUC else value
-
 
 class ContactsChooser(VerticalPanel):
     """Select one or several connected contacts"""
@@ -120,9 +138,9 @@
         elif nb_contact is not None:
             nb_contact = (nb_contact, nb_contact)
         if nb_contact is None:
-            log.warning("Need to select as many contacts as you want")
+            log.debug("Need to select as many contacts as you want")
         else:
-            log.warning("Need to select between %d and %d contacts" % nb_contact)
+            log.debug("Need to select between %d and %d contacts" % nb_contact)
         self.nb_contact = nb_contact
         self.ok_button = ok_button
         VerticalPanel.__init__(self, Width='100%')
@@ -132,34 +150,41 @@
         self.contacts_list.addStyleName('contactsChooser')
         self.contacts_list.addChangeListener(self.onChange)
         self.add(self.contacts_list)
-        self.setContacts()
+        self.refreshOptions()
         self.onChange()
 
+    @property
+    def contacts(self):
+        """Return the selected contacts.
+
+        @return: list[jid.JID]
+        """
+        return [jid.JID(contact) for contact in self.contacts_list.getSelectedValues(True)]
+
     def onChange(self, sender=None):
         if self.ok_button is None:
             return
         if self.nb_contact:
             selected = len(self.contacts_list.getSelectedValues(True))
-            if  selected >= self.nb_contact[0] and selected <= self.nb_contact[1]:
+            if selected >= self.nb_contact[0] and selected <= self.nb_contact[1]:
                 self.ok_button.setEnabled(True)
             else:
                 self.ok_button.setEnabled(False)
 
-    def setContacts(self, selected=[]):
-        """Fill the list with the connected contacts
-        @param select: list of the contacts to select by default
+    def refreshOptions(self, keep_selected=False):
+        """Fill the list with the connected contacts.
+
+        @param keep_selected (boolean): if True, keep the current selection
         """
+        selection = self.contacts if keep_selected else []
         self.contacts_list.clear()
-        contacts = self.host.contact_panel.getConnected(filter_muc=True)
+        contacts = self.host.contact_list.roster_entities_connected
         self.contacts_list.setVisibleItemCount(10 if len(contacts) > 5 else 5)
         self.contacts_list.addItem("")
         for contact in contacts:
-            if contact not in [room.bare for room in self.host.room_list]:
-                self.contacts_list.addItem(contact)
-        self.contacts_list.setItemTextSelection(selected)
-
-    def getContacts(self):
-        return self.contacts_list.getSelectedValues(True)
+            self.contacts_list.addItem(contact)
+        if selection:
+            self.contacts_list.setItemTextSelection([unicode(contact) for contact in selection])
 
 
 class RoomAndContactsChooser(DialogBox):
@@ -182,7 +207,7 @@
         self.room_panel = RoomChooser(host, "" if visible == (False, True) else DEFAULT_MUC)
         self.contact_panel = ContactsChooser(host, nb_contact, ok_button)
 
-        self.stack_panel = base_panels.ToggleStackPanel(Width="100%")
+        self.stack_panel = base_panel.ToggleStackPanel(Width="100%")
         self.stack_panel.add(self.room_panel, visible=visible[0])
         self.stack_panel.add(self.contact_panel, visible=visible[1])
         self.stack_panel.addStackChangeListener(self)
@@ -198,60 +223,72 @@
         self.setHTML(title)
         self.show()
 
-        # needed to update the contacts list when someone logged in/out
-        self.host.room_contacts_chooser = self
+        # FIXME: workaround for a pyjamas issue: calling hash on a class method always return a different value if that method is defined directly within the class (with the "def" keyword)
+        self.presenceListener = self.refreshContactList
+        # update the contacts list when someone logged in/out
+        self.host.addListener('presence', self.presenceListener, [C.PROF_KEY_NONE])
+
+    @property
+    def room(self):
+        """Get the room that has been selected or entered by the user
 
-    def getRoom(self, asSuffix=False):
-        room = self.room_panel.getRoom()
-        if asSuffix:
-            return room if room == "" else ": %s" % room
-        else:
-            return room
+        @return: jid.JID or None
+        """
+        return self.room_panel.room
 
-    def getContacts(self, asSuffix=False):
-        contacts = self.contact_panel.getContacts()
-        if asSuffix:
-            return "" if contacts == [] else ": %s" % ", ".join(contacts)
-        else:
-            return contacts
+    @property
+    def contacts(self):
+        """Return the selected contacts.
+
+        @return: list[jid.JID]
+        """
+        return self.contact_panel.contacts
 
     def onStackChanged(self, sender, index, visible=None):
         if visible is None:
             visible = sender.getWidget(index).getVisible()
         if index == 0:
-            sender.setStackText(0, self.title_room + ("" if visible else self.getRoom(True)))
+            suffix = "" if (visible or not self.room) else ": %s" % self.room
+            sender.setStackText(0, self.title_room + suffix)
         elif index == 1:
-            sender.setStackText(1, self.title_invite + ("" if visible else self.getContacts(True)))
+            suffix = "" if (visible or not self.contacts) else ": %s" % ", ".join([unicode(contact) for contact in self.contacts])
+            sender.setStackText(1, self.title_invite + suffix)
 
-    def resetContacts(self):
-        """Called when someone log in/out to update the list"""
-        self.contact_panel.setContacts(self.getContacts())
+    def refreshContactList(self, *args, **kwargs):
+        """Called when someone log in/out to update the list.
+
+        @param args: set by the event call but not used here
+        """
+        self.contact_panel.refreshOptions(keep_selected=True)
 
     def onOK(self, sender):
-        room_jid = self.getRoom()
-        if room_jid != "" and "@" not in room_jid:
+        room = self.room  # pyjamas issue: you need to use an intermediate variable to access a property's method
+        if room and not room.is_valid():
             Window.alert('You must enter a room jid in the form room@chat.%s' % self.host._defaultDomain)
             return
         self.hide()
-        self.callback(room_jid, self.getContacts())
+        self.callback(room, self.contacts)
 
     def onCancel(self, sender):
         self.hide()
 
     def hide(self):
-        self.host.room_contacts_chooser = None
+        self.host.removeListener('presence', self.presenceListener)
         DialogBox.hide(self, autoClosed=True)
 
 
 class GenericConfirmDialog(DialogBox):
 
-    def __init__(self, widgets, callback, title='Confirmation', prompt=None, **kwargs):
+    def __init__(self, widgets, callback, title='Confirmation', prompt_widgets=None, **kwargs):
         """
         Dialog to confirm an action
         @param widgets (list[Widget]): widgets to attach
-        @param callback: method to call when a button is clicked
+        @param callback (callable): method to call when a button is pressed,
+            with the following arguments:
+                - result (bool): set to True if the dialog has been confirmed
+                - *args: a list of unicode (the values for the prompt_widgets)
         @param title: title of the dialog
-        @param prompt (TextBox, list[TextBox]): input widgets from which to retrieve
+        @param prompt_widgets (list[TextBox]): input widgets from which to retrieve
         the string value(s) to be passed to the callback when OK button is pressed.
         If None, OK button will return "True". Cancel button always returns "False".
         """
@@ -261,16 +298,14 @@
         if added_style:
             self.addStyleName(added_style)
 
-        if prompt is None:
-            prompt = []
-        elif isinstance(prompt, TextBox):
-            prompt = [prompt]
+        if prompt_widgets is None:
+            prompt_widgets = []
 
         content = VerticalPanel()
         content.setWidth('100%')
         for wid in widgets:
             content.add(wid)
-            if wid in prompt:
+            if wid in prompt_widgets:
                 wid.setWidth('100%')
         button_panel = HorizontalPanel()
         button_panel.addStyleName("marginAuto")
@@ -281,11 +316,12 @@
         content.add(button_panel)
         self.setHTML(title)
         self.setWidget(content)
-        self.prompt = prompt
+        self.prompt_widgets = prompt_widgets
 
     def onConfirm(self, sender):
         self.hide()
-        result = [box.getText() for box in self.prompt] if self.prompt else [True]
+        result = [True]
+        result.extend([box.getText() for box in self.prompt_widgets])
         self.callback(*result)
 
     def onCancel(self, sender):
@@ -294,8 +330,8 @@
 
     def show(self):
         DialogBox.show(self)
-        if self.prompt:
-            self.prompt[0].setFocus(True)
+        if self.prompt_widgets:
+            self.prompt_widgets[0].setFocus(True)
 
 
 class ConfirmDialog(GenericConfirmDialog):
@@ -328,7 +364,7 @@
         _body.add(main_widget)
         _body.setCellWidth(main_widget, '100%')
         _body.setCellHeight(main_widget, '100%')
-        if not 'NO_CLOSE' in options:
+        if 'NO_CLOSE' not in options:
             _close_button = Button("Close", self.onClose)
             _body.add(_close_button)
             _body.setCellHorizontalAlignment(_close_button, HasAlignment.ALIGN_CENTER)
@@ -357,19 +393,18 @@
     def __init__(self, callback, textes=None, values=None, title='User input', **kwargs):
         """Prompt the user for one or more input(s).
 
-        @param callback (callable): method to call when clicking OK
-        @param textes (str, list[str]): HTML textes to display before the inputs
-        @param values (str, list[str]): default values for each input
-        @param title (str): dialog title
+        @param callback (callable): method to call when a button is pressed,
+            with the following arguments:
+                - result (bool): set to True if the dialog has been confirmed
+                - *args: a list of unicode (the values entered by the user)
+        @param textes (list[unicode]): HTML textes to display before the inputs
+        @param values (list[unicode]): default values for each input
+        @param title (unicode): dialog title
         """
         if textes is None:
             textes = ['']  # display a single input without any description
-        elif not isinstance(textes, list):
-            textes = [textes]  # allow to pass a single string instead of a list
         if values is None:
             values = []
-        elif not isinstance(values, list):
-            values = [values]  # allow to pass a single string instead of a list
         all_widgets = []
         prompt_widgets = []
         for count in xrange(len(textes)):
@@ -388,7 +423,7 @@
 
     def onEventPreview(self, event):
         if event.type in ["keydown", "keypress", "keyup"] and event.keyCode == KEY_ESCAPE:
-            #needed to prevent request cancellation in Firefox
+            # needed to prevent request cancellation in Firefox
             event.preventDefault()
         return PopupPanel.onEventPreview(self, event)
 
@@ -528,7 +563,7 @@
         minus_button = Button("-", self.onMinus)
         self.box = WheelTextBox()
         self.box.setVisibleLength(visible_len)
-        self.box.setText(str(value))
+        self.box.setText(unicode(value))
         self.box.addInputListener(self)
         self.box.addMouseWheelListener(self)
         plus_button = Button("+", self.onPlus)
@@ -551,19 +586,19 @@
     def setValue(self, value):
         """Change the value and fire valueChange listeners"""
         self.value = value
-        self.box.setText(str(value))
+        self.box.setText(unicode(value))
         self._callListeners()
 
     def onMinus(self, sender, step=1):
         self.value = max(0, self.value - step)
-        self.box.setText(str(self.value))
+        self.box.setText(unicode(self.value))
         self._callListeners()
 
     def onPlus(self, sender, step=1):
         self.value += step
         if self.value_max:
             self.value = min(self.value, self.value_max)
-        self.box.setText(str(self.value))
+        self.box.setText(unicode(self.value))
         self._callListeners()
 
     def onInput(self, sender):
@@ -574,7 +609,7 @@
             pass
         if self.value_max:
             self.value = min(self.value, self.value_max)
-        self.box.setText(str(self.value))
+        self.box.setText(unicode(self.value))
         self._callListeners()
 
     def onMouseWheel(self, sender, velocity):