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

merged branch frontends_multi_profiles
author Goffi <goffi@goffi.org>
date Wed, 18 Mar 2015 16:15:18 +0100
parents 6d3142b782c3
children 9877607c719a
line wrap: on
line diff
--- a/src/browser/sat_browser/contact_group.py	Thu Feb 05 12:05:32 2015 +0100
+++ b/src/browser/sat_browser/contact_group.py	Wed Mar 18 16:15:18 2015 +0100
@@ -28,14 +28,28 @@
 
 import dialog
 import list_manager
-import contact
+import contact_panel
+import contact_list
+
+
+unicode = str  # FIXME: pyjamas workaround
 
 
 class ContactGroupManager(list_manager.ListManager):
-    """A manager for sub-panels to assign contacts to each group."""
 
-    def __init__(self, parent, keys_dict, contact_list, offsets, style):
-        list_manager.ListManager.__init__(self, parent, keys_dict, contact_list, offsets, style)
+    def __init__(self, container, keys, contacts, offsets, style):
+        """
+        @param container (FlexTable): FlexTable parent widget
+        @param keys (dict{unicode: dict{unicode: unicode}}): dict binding items
+            keys to their display config data.
+        @param contacts (list): list of contacts
+        @param offsets (dict): define widgets positions offsets within container:
+            - "x_first": the x offset for the first widget's row on the grid
+            - "x": the x offset for all widgets rows, except the first one if "x_first" is defined
+            - "y": the y offset for all widgets columns on the grid
+        @param style (dict): define CSS styles
+        """
+        list_manager.ListManager.__init__(self, container, keys, contacts, offsets, style)
         self.registerPopupMenuPanel(entries={"Remove group": {}},
                                     callback=lambda sender, key: Timer(5, lambda timer: self.removeContactKey(sender, key)))
 
@@ -44,43 +58,51 @@
 
         def confirm_cb(answer):
             if answer:
-                list_manager.ListManager.removeContactKey(self, key)
-                self._parent.removeKeyFromAddGroupPanel(key)
+                list_manager.ListManager.removeItemKey(self, key)
+                self.container.removeKeyFromAddGroupPanel(key)
 
         _dialog = dialog.ConfirmDialog(confirm_cb, text="Do you really want to delete the group '%s'?" % key)
         _dialog.show()
 
     def removeFromRemainingList(self, contacts):
         list_manager.ListManager.removeFromRemainingList(self, contacts)
-        self._parent.updateContactList(contacts=contacts)
+        self.container.updateContactList(contacts)
 
     def addToRemainingList(self, contacts, ignore_key=None):
         list_manager.ListManager.addToRemainingList(self, contacts, ignore_key)
-        self._parent.updateContactList(contacts=contacts)
+        self.container.updateContactList(contacts)
 
 
 class ContactGroupEditor(DockPanel):
-    """Panel for the contact groups manager."""
+    """A big panel including a ContactGroupManager and other UI stuff."""
+
+    def __init__(self, host, container=None, onCloseCallback=None):
+        """
 
-    def __init__(self, host, parent=None, onCloseCallback=None):
+        @param host (SatWebFrontend)
+        @param container (PanelBase): parent panel or None to display in a popup
+        @param onCloseCallback (callable)
+        """
         DockPanel.__init__(self)
         self.host = host
 
         # eventually display in a popup
-        if parent is None:
-            parent = DialogBox(autoHide=False, centered=True)
-            parent.setHTML("Manage contact groups")
-        self._parent = parent
+        if container is None:
+            container = DialogBox(autoHide=False, centered=True)
+            container.setHTML("Manage contact groups")
+        self.container = container
         self._on_close_callback = onCloseCallback
-        self.all_contacts = self.host.contact_panel.getContacts()
 
-        groups_list = self.host.contact_panel.groups.keys()
-        groups_list.sort()
+        self.all_contacts = contact_list.JIDList(self.host.contact_list.roster_entities)
+        roster_entities_by_group = self.host.contact_list.roster_entities_by_group
+        del roster_entities_by_group[None]  # remove the empty group
+        roster_groups = roster_entities_by_group.keys()
+        roster_groups.sort()
 
-        self.add_group_panel = self.getAddGroupPanel(groups_list)
-        south_panel = self.getCloseSaveButtons()
-        center_panel = self.getContactGroupManager(groups_list)
-        east_panel = self.getContactList()
+        self.add_group_panel = self.initAddGroupPanel(roster_groups)
+        south_panel = self.initCloseSaveButtons()
+        center_panel = self.initContactGroupManager(roster_groups)
+        east_panel = self.initContactList()
 
         self.add(self.add_group_panel, DockPanel.CENTER)
         self.add(east_panel, DockPanel.EAST)
@@ -97,108 +119,123 @@
         self.setCellHorizontalAlignment(south_panel, HasAlignment.ALIGN_CENTER)
 
         # need to be done after the contact list has been initialized
-        self.groups.setContacts(self.host.contact_panel.groups)
+        self.groups.resetItems(roster_entities_by_group)
         self.toggleContacts(showAll=True)
 
         # Hide the contacts list from the main panel to not confuse the user
         self.restore_contact_panel = False
-        if self.host.contact_panel.getVisible():
+        clist = self.host.contact_list
+        if clist.getVisible():
             self.restore_contact_panel = True
             self.host.panel._contactsSwitch()
 
-        parent.add(self)
-        parent.setVisible(True)
-        if isinstance(parent, DialogBox):
-            parent.center()
+        container.add(self)
+        container.setVisible(True)
+        if isinstance(container, DialogBox):
+            container.center()
 
-    def getContactGroupManager(self, groups_list):
-        """Set the list manager for the groups"""
-        flex_table = FlexTable(len(groups_list), 2)
+    def initContactGroupManager(self, groups):
+        """Initialise the contact group manager.
+
+        @param groups (list[unicode]): contact groups
+        """
+        flex_table = FlexTable()
         flex_table.addStyleName('contactGroupEditor')
+
         # overwrite the default style which has been set for rich text editor
-        style = {
-           "keyItem": "group",
-           "popupMenuItem": "popupMenuItem",
-           "removeButton": "contactGroupRemoveButton",
-           "buttonCell": "contactGroupButtonCell",
-           "keyPanel": "contactGroupPanel"
-        }
-        self.groups = ContactGroupManager(flex_table, groups_list, self.all_contacts, style=style)
-        self.groups.createWidgets()  # widgets are automatically added to FlexTable
+        style = {"keyItem": "group",
+                 "popupMenuItem": "popupMenuItem",
+                 "removeButton": "contactGroupRemoveButton",
+                 "buttonCell": "contactGroupButtonCell",
+                 "keyPanel": "contactGroupPanel"
+                 }
+
+        groups = {group: {} for group in groups}
+        self.groups = ContactGroupManager(flex_table, groups, self.all_contacts, style=style)
+        self.groups.createWidgets()  # widgets are automatically added to the FlexTable
+
         # FIXME: clean that part which is dangerous
         flex_table.updateContactList = self.updateContactList
         flex_table.removeKeyFromAddGroupPanel = self.add_group_panel.groups.remove
+
         return flex_table
 
-    def getAddGroupPanel(self, groups_list):
-        """Add the 'Add group' panel to the FlexTable"""
+    def initAddGroupPanel(self, groups):
+        """Initialise the 'Add group' panel.
 
-        def add_group_cb(text):
-            self.groups.addContactKey(text)
+        @param groups (list[unicode]): contact groups
+        """
+
+        def add_group_cb(key):
+            self.groups.addItemKey(key)
             self.add_group_panel.textbox.setFocus(True)
 
-        add_group_panel = dialog.AddGroupPanel(groups_list, add_group_cb)
+        add_group_panel = dialog.AddGroupPanel(groups, add_group_cb)
         add_group_panel.addStyleName("addContactGroupPanel")
         return add_group_panel
 
-    def getCloseSaveButtons(self):
-        """Add the buttons to close the dialog / save the groups"""
+    def initCloseSaveButtons(self):
+        """Add the buttons to close the dialog and save the groups."""
         buttons = HorizontalPanel()
         buttons.addStyleName("marginAuto")
         buttons.add(Button("Save", listener=self.closeAndSave))
         buttons.add(Button("Cancel", listener=self.cancelWithoutSaving))
         return buttons
 
-    def getContactList(self):
-        """Add the contact list to the DockPanel"""
+    def initContactList(self):
+        """Add the contact list to the DockPanel."""
         self.toggle = Button("", self.toggleContacts)
         self.toggle.addStyleName("toggleAssignedContacts")
-        self.contacts = contact.GenericContactList(self.host)
-        for contact_ in self.all_contacts:
-            self.contacts.add(contact_)
-        contact_panel = VerticalPanel()
-        contact_panel.add(self.toggle)
-        contact_panel.add(self.contacts)
-        return contact_panel
+        self.contacts = contact_panel.ContactsPanel(self.host)
+        for contact in self.all_contacts:
+            self.contacts.add(contact)
+        panel = VerticalPanel()
+        panel.add(self.toggle)
+        panel.add(self.contacts)
+        return panel
 
     def toggleContacts(self, sender=None, showAll=None):
-        """Callback for the toggle button"""
-        if sender is None:
-            sender = self.toggle
-        sender.showAll = showAll if showAll is not None else not sender.showAll
-        if sender.showAll:
-            sender.setText("Hide assigned")
-        else:
-            sender.setText("Show assigned")
-        self.updateContactList(sender)
+        """Toggle the button to show contacts and the contact list.
 
-    def updateContactList(self, sender=None, contacts=None):
-        """Update the contact list regarding the toggle button"""
+        @param sender (Button)
+        @param showAll (bool): if set, initialise with True to show all contacts
+            or with False to show only the ones that are not assigned yet.
+        """
+        self.toggle.showAll = (not self.toggle.showAll) if showAll is None else showAll
+        self.toggle.setText("Hide assigned" if self.toggle.showAll else "Show assigned")
+        self.updateContactList()
+
+    def updateContactList(self, contacts=None):
+        """Update the contact list's items visibility, depending of the toggle
+        button and the "contacts" attribute.
+
+        @param contacts (list): contacts to be updated, or None to update all.
+        """
         if not hasattr(self, "toggle") or not hasattr(self.toggle, "showAll"):
             return
-        sender = self.toggle
         if contacts is not None:
-            if not isinstance(contacts, list):
-                contacts = [contacts]
-            for contact_ in contacts:
-                if contact_ not in self.all_contacts:
-                    contacts.remove(contact_)
+            to_remove = set()
+            for contact in contacts:
+                if contact not in self.all_contacts:
+                    to_remove.add(contact)
+            for contact in to_remove:
+                contacts.remove(contact)
         else:
             contacts = self.all_contacts
-        for contact_ in contacts:
-            if sender.showAll:
-                self.contacts.getContactBox(contact_).setVisible(True)
+        for contact in contacts:
+            if self.toggle.showAll:
+                self.contacts.getContactBox(contact).setVisible(True)
             else:
-                if contact_ in self.groups.remaining_list:
-                    self.contacts.getContactBox(contact_).setVisible(True)
+                if contact in self.groups.items_remaining:
+                    self.contacts.getContactBox(contact).setVisible(True)
                 else:
-                    self.contacts.getContactBox(contact_).setVisible(False)
+                    self.contacts.getContactBox(contact).setVisible(False)
 
     def __close(self):
         """Remove the widget from parent or close the popup."""
-        if isinstance(self._parent, DialogBox):
-            self._parent.hide()
-        self._parent.remove(self)
+        if isinstance(self.container, DialogBox):
+            self.container.hide()
+        self.container.remove(self)
         if self._on_close_callback is not None:
             self._on_close_callback()
         if self.restore_contact_panel:
@@ -215,22 +252,21 @@
 
     def closeAndSave(self):
         """Call bridge methods to save the changes and close the dialog"""
-        map_ = {}
-        for contact_ in self.all_contacts:
-            map_[contact_] = set()
-        contacts = self.groups.getContacts()
-        for group in contacts.keys():
-            for contact_ in contacts[group]:
-                try:
-                    map_[contact_].add(group)
-                except KeyError:
-                    dialog.InfoDialog("Invalid contact",
-                           "The contact '%s' is not your contact list but it has been assigned to the group '%s'." % (contact_, group) +
-                           "Your changes could not be saved: please check your assignments and save again.", Width="400px").center()
-                    return
-        for contact_ in map_.keys():
-            groups = map_[contact_]
-            current_groups = self.host.contact_panel.getContactGroups(contact_)
-            if groups != current_groups:
-                self.host.bridge.call('updateContact', None, contact_, '', list(groups))
+        old_groups_by_entity = contact_list.JIDDict(self.host.contact_list.roster_groups_by_entity)
+        old_entities = old_groups_by_entity.keys()
+        groups_by_entity = contact_list.JIDDict(self.groups.getKeysByItem())
+        entities = groups_by_entity.keys()
+
+        for invalid in entities.difference(self.all_contacts):
+            dialog.InfoDialog("Invalid contact(s)",
+                              "The contact '%s' is not in your contact list but has been assigned to: '%s'." % (invalid, "', '".join(groups_by_entity[invalid])) +
+                              "Your changes could not be saved: please check your assignments and save again.", Width="400px").center()
+            return
+
+        for entity in old_entities.difference(entities):
+            self.host.bridge.call('updateContact', None, unicode(entity), '', [])
+
+        for entity, groups in groups_by_entity.iteritems():
+            if entity not in old_groups_by_entity or groups != old_groups_by_entity[entity]:
+                self.host.bridge.call('updateContact', None, unicode(entity), '', list(groups))
         self.__close()