comparison src/browser/sat_browser/contact_group.py @ 600:32dbbc941123 frontends_multi_profiles

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