comparison 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
comparison
equal deleted inserted replaced
590:1bffc4c244c3 679:a90cc8fc9605
26 from pyjamas.ui.DialogBox import DialogBox 26 from pyjamas.ui.DialogBox import DialogBox
27 from pyjamas.ui import HasAlignment 27 from pyjamas.ui import HasAlignment
28 28
29 import dialog 29 import dialog
30 import list_manager 30 import list_manager
31 import contact 31 import contact_panel
32 import contact_list
33
34
35 unicode = str # FIXME: pyjamas workaround
32 36
33 37
34 class ContactGroupManager(list_manager.ListManager): 38 class ContactGroupManager(list_manager.ListManager):
35 """A manager for sub-panels to assign contacts to each group.""" 39
36 40 def __init__(self, container, keys, contacts, offsets, style):
37 def __init__(self, parent, keys_dict, contact_list, offsets, style): 41 """
38 list_manager.ListManager.__init__(self, parent, keys_dict, contact_list, offsets, style) 42 @param container (FlexTable): FlexTable parent widget
43 @param keys (dict{unicode: dict{unicode: unicode}}): dict binding items
44 keys to their display config data.
45 @param contacts (list): list of contacts
46 @param offsets (dict): define widgets positions offsets within container:
47 - "x_first": the x offset for the first widget's row on the grid
48 - "x": the x offset for all widgets rows, except the first one if "x_first" is defined
49 - "y": the y offset for all widgets columns on the grid
50 @param style (dict): define CSS styles
51 """
52 list_manager.ListManager.__init__(self, container, keys, contacts, offsets, style)
39 self.registerPopupMenuPanel(entries={"Remove group": {}}, 53 self.registerPopupMenuPanel(entries={"Remove group": {}},
40 callback=lambda sender, key: Timer(5, lambda timer: self.removeContactKey(sender, key))) 54 callback=lambda sender, key: Timer(5, lambda timer: self.removeContactKey(sender, key)))
41 55
42 def removeContactKey(self, sender, key): 56 def removeContactKey(self, sender, key):
43 key = sender.getText() 57 key = sender.getText()
44 58
45 def confirm_cb(answer): 59 def confirm_cb(answer):
46 if answer: 60 if answer:
47 list_manager.ListManager.removeContactKey(self, key) 61 list_manager.ListManager.removeItemKey(self, key)
48 self._parent.removeKeyFromAddGroupPanel(key) 62 self.container.removeKeyFromAddGroupPanel(key)
49 63
50 _dialog = dialog.ConfirmDialog(confirm_cb, text="Do you really want to delete the group '%s'?" % key) 64 _dialog = dialog.ConfirmDialog(confirm_cb, text="Do you really want to delete the group '%s'?" % key)
51 _dialog.show() 65 _dialog.show()
52 66
53 def removeFromRemainingList(self, contacts): 67 def removeFromRemainingList(self, contacts):
54 list_manager.ListManager.removeFromRemainingList(self, contacts) 68 list_manager.ListManager.removeFromRemainingList(self, contacts)
55 self._parent.updateContactList(contacts=contacts) 69 self.container.updateContactList(contacts)
56 70
57 def addToRemainingList(self, contacts, ignore_key=None): 71 def addToRemainingList(self, contacts, ignore_key=None):
58 list_manager.ListManager.addToRemainingList(self, contacts, ignore_key) 72 list_manager.ListManager.addToRemainingList(self, contacts, ignore_key)
59 self._parent.updateContactList(contacts=contacts) 73 self.container.updateContactList(contacts)
60 74
61 75
62 class ContactGroupEditor(DockPanel): 76 class ContactGroupEditor(DockPanel):
63 """Panel for the contact groups manager.""" 77 """A big panel including a ContactGroupManager and other UI stuff."""
64 78
65 def __init__(self, host, parent=None, onCloseCallback=None): 79 def __init__(self, host, container=None, onCloseCallback=None):
80 """
81
82 @param host (SatWebFrontend)
83 @param container (PanelBase): parent panel or None to display in a popup
84 @param onCloseCallback (callable)
85 """
66 DockPanel.__init__(self) 86 DockPanel.__init__(self)
67 self.host = host 87 self.host = host
68 88
69 # eventually display in a popup 89 # eventually display in a popup
70 if parent is None: 90 if container is None:
71 parent = DialogBox(autoHide=False, centered=True) 91 container = DialogBox(autoHide=False, centered=True)
72 parent.setHTML("Manage contact groups") 92 container.setHTML("Manage contact groups")
73 self._parent = parent 93 self.container = container
74 self._on_close_callback = onCloseCallback 94 self._on_close_callback = onCloseCallback
75 self.all_contacts = self.host.contact_panel.getContacts() 95
76 96 self.all_contacts = contact_list.JIDList(self.host.contact_list.roster_entities)
77 groups_list = self.host.contact_panel.groups.keys() 97 roster_entities_by_group = self.host.contact_list.roster_entities_by_group
78 groups_list.sort() 98 del roster_entities_by_group[None] # remove the empty group
79 99 roster_groups = roster_entities_by_group.keys()
80 self.add_group_panel = self.getAddGroupPanel(groups_list) 100 roster_groups.sort()
81 south_panel = self.getCloseSaveButtons() 101
82 center_panel = self.getContactGroupManager(groups_list) 102 self.add_group_panel = self.initAddGroupPanel(roster_groups)
83 east_panel = self.getContactList() 103 south_panel = self.initCloseSaveButtons()
104 center_panel = self.initContactGroupManager(roster_groups)
105 east_panel = self.initContactList()
84 106
85 self.add(self.add_group_panel, DockPanel.CENTER) 107 self.add(self.add_group_panel, DockPanel.CENTER)
86 self.add(east_panel, DockPanel.EAST) 108 self.add(east_panel, DockPanel.EAST)
87 self.add(center_panel, DockPanel.NORTH) 109 self.add(center_panel, DockPanel.NORTH)
88 self.add(south_panel, DockPanel.SOUTH) 110 self.add(south_panel, DockPanel.SOUTH)
95 self.setCellHorizontalAlignment(self.add_group_panel, HasAlignment.ALIGN_LEFT) 117 self.setCellHorizontalAlignment(self.add_group_panel, HasAlignment.ALIGN_LEFT)
96 self.setCellVerticalAlignment(south_panel, HasAlignment.ALIGN_BOTTOM) 118 self.setCellVerticalAlignment(south_panel, HasAlignment.ALIGN_BOTTOM)
97 self.setCellHorizontalAlignment(south_panel, HasAlignment.ALIGN_CENTER) 119 self.setCellHorizontalAlignment(south_panel, HasAlignment.ALIGN_CENTER)
98 120
99 # need to be done after the contact list has been initialized 121 # need to be done after the contact list has been initialized
100 self.groups.setContacts(self.host.contact_panel.groups) 122 self.groups.resetItems(roster_entities_by_group)
101 self.toggleContacts(showAll=True) 123 self.toggleContacts(showAll=True)
102 124
103 # Hide the contacts list from the main panel to not confuse the user 125 # Hide the contacts list from the main panel to not confuse the user
104 self.restore_contact_panel = False 126 self.restore_contact_panel = False
105 if self.host.contact_panel.getVisible(): 127 clist = self.host.contact_list
128 if clist.getVisible():
106 self.restore_contact_panel = True 129 self.restore_contact_panel = True
107 self.host.panel._contactsSwitch() 130 self.host.panel._contactsSwitch()
108 131
109 parent.add(self) 132 container.add(self)
110 parent.setVisible(True) 133 container.setVisible(True)
111 if isinstance(parent, DialogBox): 134 if isinstance(container, DialogBox):
112 parent.center() 135 container.center()
113 136
114 def getContactGroupManager(self, groups_list): 137 def initContactGroupManager(self, groups):
115 """Set the list manager for the groups""" 138 """Initialise the contact group manager.
116 flex_table = FlexTable(len(groups_list), 2) 139
140 @param groups (list[unicode]): contact groups
141 """
142 flex_table = FlexTable()
117 flex_table.addStyleName('contactGroupEditor') 143 flex_table.addStyleName('contactGroupEditor')
144
118 # overwrite the default style which has been set for rich text editor 145 # overwrite the default style which has been set for rich text editor
119 style = { 146 style = {"keyItem": "group",
120 "keyItem": "group", 147 "popupMenuItem": "popupMenuItem",
121 "popupMenuItem": "popupMenuItem", 148 "removeButton": "contactGroupRemoveButton",
122 "removeButton": "contactGroupRemoveButton", 149 "buttonCell": "contactGroupButtonCell",
123 "buttonCell": "contactGroupButtonCell", 150 "keyPanel": "contactGroupPanel"
124 "keyPanel": "contactGroupPanel" 151 }
125 } 152
126 self.groups = ContactGroupManager(flex_table, groups_list, self.all_contacts, style=style) 153 groups = {group: {} for group in groups}
127 self.groups.createWidgets() # widgets are automatically added to FlexTable 154 self.groups = ContactGroupManager(flex_table, groups, self.all_contacts, style=style)
155 self.groups.createWidgets() # widgets are automatically added to the FlexTable
156
128 # FIXME: clean that part which is dangerous 157 # FIXME: clean that part which is dangerous
129 flex_table.updateContactList = self.updateContactList 158 flex_table.updateContactList = self.updateContactList
130 flex_table.removeKeyFromAddGroupPanel = self.add_group_panel.groups.remove 159 flex_table.removeKeyFromAddGroupPanel = self.add_group_panel.groups.remove
160
131 return flex_table 161 return flex_table
132 162
133 def getAddGroupPanel(self, groups_list): 163 def initAddGroupPanel(self, groups):
134 """Add the 'Add group' panel to the FlexTable""" 164 """Initialise the 'Add group' panel.
135 165
136 def add_group_cb(text): 166 @param groups (list[unicode]): contact groups
137 self.groups.addContactKey(text) 167 """
168
169 def add_group_cb(key):
170 self.groups.addItemKey(key)
138 self.add_group_panel.textbox.setFocus(True) 171 self.add_group_panel.textbox.setFocus(True)
139 172
140 add_group_panel = dialog.AddGroupPanel(groups_list, add_group_cb) 173 add_group_panel = dialog.AddGroupPanel(groups, add_group_cb)
141 add_group_panel.addStyleName("addContactGroupPanel") 174 add_group_panel.addStyleName("addContactGroupPanel")
142 return add_group_panel 175 return add_group_panel
143 176
144 def getCloseSaveButtons(self): 177 def initCloseSaveButtons(self):
145 """Add the buttons to close the dialog / save the groups""" 178 """Add the buttons to close the dialog and save the groups."""
146 buttons = HorizontalPanel() 179 buttons = HorizontalPanel()
147 buttons.addStyleName("marginAuto") 180 buttons.addStyleName("marginAuto")
148 buttons.add(Button("Save", listener=self.closeAndSave)) 181 buttons.add(Button("Save", listener=self.closeAndSave))
149 buttons.add(Button("Cancel", listener=self.cancelWithoutSaving)) 182 buttons.add(Button("Cancel", listener=self.cancelWithoutSaving))
150 return buttons 183 return buttons
151 184
152 def getContactList(self): 185 def initContactList(self):
153 """Add the contact list to the DockPanel""" 186 """Add the contact list to the DockPanel."""
154 self.toggle = Button("", self.toggleContacts) 187 self.toggle = Button("", self.toggleContacts)
155 self.toggle.addStyleName("toggleAssignedContacts") 188 self.toggle.addStyleName("toggleAssignedContacts")
156 self.contacts = contact.GenericContactList(self.host) 189 self.contacts = contact_panel.ContactsPanel(self.host)
157 for contact_ in self.all_contacts: 190 for contact in self.all_contacts:
158 self.contacts.add(contact_) 191 self.contacts.add(contact)
159 contact_panel = VerticalPanel() 192 panel = VerticalPanel()
160 contact_panel.add(self.toggle) 193 panel.add(self.toggle)
161 contact_panel.add(self.contacts) 194 panel.add(self.contacts)
162 return contact_panel 195 return panel
163 196
164 def toggleContacts(self, sender=None, showAll=None): 197 def toggleContacts(self, sender=None, showAll=None):
165 """Callback for the toggle button""" 198 """Toggle the button to show contacts and the contact list.
166 if sender is None: 199
167 sender = self.toggle 200 @param sender (Button)
168 sender.showAll = showAll if showAll is not None else not sender.showAll 201 @param showAll (bool): if set, initialise with True to show all contacts
169 if sender.showAll: 202 or with False to show only the ones that are not assigned yet.
170 sender.setText("Hide assigned") 203 """
171 else: 204 self.toggle.showAll = (not self.toggle.showAll) if showAll is None else showAll
172 sender.setText("Show assigned") 205 self.toggle.setText("Hide assigned" if self.toggle.showAll else "Show assigned")
173 self.updateContactList(sender) 206 self.updateContactList()
174 207
175 def updateContactList(self, sender=None, contacts=None): 208 def updateContactList(self, contacts=None):
176 """Update the contact list regarding the toggle button""" 209 """Update the contact list's items visibility, depending of the toggle
210 button and the "contacts" attribute.
211
212 @param contacts (list): contacts to be updated, or None to update all.
213 """
177 if not hasattr(self, "toggle") or not hasattr(self.toggle, "showAll"): 214 if not hasattr(self, "toggle") or not hasattr(self.toggle, "showAll"):
178 return 215 return
179 sender = self.toggle
180 if contacts is not None: 216 if contacts is not None:
181 if not isinstance(contacts, list): 217 to_remove = set()
182 contacts = [contacts] 218 for contact in contacts:
183 for contact_ in contacts: 219 if contact not in self.all_contacts:
184 if contact_ not in self.all_contacts: 220 to_remove.add(contact)
185 contacts.remove(contact_) 221 for contact in to_remove:
222 contacts.remove(contact)
186 else: 223 else:
187 contacts = self.all_contacts 224 contacts = self.all_contacts
188 for contact_ in contacts: 225 for contact in contacts:
189 if sender.showAll: 226 if self.toggle.showAll:
190 self.contacts.getContactBox(contact_).setVisible(True) 227 self.contacts.getContactBox(contact).setVisible(True)
191 else: 228 else:
192 if contact_ in self.groups.remaining_list: 229 if contact in self.groups.items_remaining:
193 self.contacts.getContactBox(contact_).setVisible(True) 230 self.contacts.getContactBox(contact).setVisible(True)
194 else: 231 else:
195 self.contacts.getContactBox(contact_).setVisible(False) 232 self.contacts.getContactBox(contact).setVisible(False)
196 233
197 def __close(self): 234 def __close(self):
198 """Remove the widget from parent or close the popup.""" 235 """Remove the widget from parent or close the popup."""
199 if isinstance(self._parent, DialogBox): 236 if isinstance(self.container, DialogBox):
200 self._parent.hide() 237 self.container.hide()
201 self._parent.remove(self) 238 self.container.remove(self)
202 if self._on_close_callback is not None: 239 if self._on_close_callback is not None:
203 self._on_close_callback() 240 self._on_close_callback()
204 if self.restore_contact_panel: 241 if self.restore_contact_panel:
205 self.host.panel._contactsSwitch() 242 self.host.panel._contactsSwitch()
206 243
213 _dialog = dialog.ConfirmDialog(confirm_cb, text="Do you really want to cancel without saving?") 250 _dialog = dialog.ConfirmDialog(confirm_cb, text="Do you really want to cancel without saving?")
214 _dialog.show() 251 _dialog.show()
215 252
216 def closeAndSave(self): 253 def closeAndSave(self):
217 """Call bridge methods to save the changes and close the dialog""" 254 """Call bridge methods to save the changes and close the dialog"""
218 map_ = {} 255 old_groups_by_entity = contact_list.JIDDict(self.host.contact_list.roster_groups_by_entity)
219 for contact_ in self.all_contacts: 256 old_entities = old_groups_by_entity.keys()
220 map_[contact_] = set() 257 groups_by_entity = contact_list.JIDDict(self.groups.getKeysByItem())
221 contacts = self.groups.getContacts() 258 entities = groups_by_entity.keys()
222 for group in contacts.keys(): 259
223 for contact_ in contacts[group]: 260 for invalid in entities.difference(self.all_contacts):
224 try: 261 dialog.InfoDialog("Invalid contact(s)",
225 map_[contact_].add(group) 262 "The contact '%s' is not in your contact list but has been assigned to: '%s'." % (invalid, "', '".join(groups_by_entity[invalid])) +
226 except KeyError: 263 "Your changes could not be saved: please check your assignments and save again.", Width="400px").center()
227 dialog.InfoDialog("Invalid contact", 264 return
228 "The contact '%s' is not your contact list but it has been assigned to the group '%s'." % (contact_, group) + 265
229 "Your changes could not be saved: please check your assignments and save again.", Width="400px").center() 266 for entity in old_entities.difference(entities):
230 return 267 self.host.bridge.call('updateContact', None, unicode(entity), '', [])
231 for contact_ in map_.keys(): 268
232 groups = map_[contact_] 269 for entity, groups in groups_by_entity.iteritems():
233 current_groups = self.host.contact_panel.getContactGroups(contact_) 270 if entity not in old_groups_by_entity or groups != old_groups_by_entity[entity]:
234 if groups != current_groups: 271 self.host.bridge.call('updateContact', None, unicode(entity), '', list(groups))
235 self.host.bridge.call('updateContact', None, contact_, '', list(groups))
236 self.__close() 272 self.__close()