comparison src/browser/sat_browser/contact_group.py @ 736:fe3c2357a8c9

fixes/improve ListManager and contact group manager + better PEP-8 compliance
author souliane <souliane@mailoo.org>
date Thu, 19 Nov 2015 11:41:03 +0100
parents 16079280a39e
children f8a7a046ff9c
comparison
equal deleted inserted replaced
735:e4ae8e2b0afd 736:fe3c2357a8c9
15 # GNU Affero General Public License for more details. 15 # GNU Affero General Public License for more details.
16 16
17 # You should have received a copy of the GNU Affero General Public License 17 # You should have received a copy of the GNU Affero General Public License
18 # along with this program. If not, see <http://www.gnu.org/licenses/>. 18 # along with this program. If not, see <http://www.gnu.org/licenses/>.
19 19
20 from pyjamas.ui.FlexTable import FlexTable
21 from pyjamas.ui.DockPanel import DockPanel
22 from pyjamas.Timer import Timer
23 from pyjamas.ui.Button import Button 20 from pyjamas.ui.Button import Button
21 from pyjamas.ui.CheckBox import CheckBox
22 from pyjamas.ui.Label import Label
24 from pyjamas.ui.HorizontalPanel import HorizontalPanel 23 from pyjamas.ui.HorizontalPanel import HorizontalPanel
25 from pyjamas.ui.VerticalPanel import VerticalPanel 24 from pyjamas.ui.VerticalPanel import VerticalPanel
26 from pyjamas.ui.DialogBox import DialogBox 25 from pyjamas.ui.DialogBox import DialogBox
26 from pyjamas.ui.ScrollPanel import ScrollPanel
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_panel 31 import contact_panel
32 import contact_list 32 import contact_list
33 from sat_frontends.tools import jid
33 34
34 35
35 unicode = str # FIXME: pyjamas workaround 36 unicode = str # FIXME: pyjamas workaround
36 37
37 38
38 class ContactGroupManager(list_manager.ListManager): 39 class ContactGroupManager(list_manager.ListManager):
39 40
40 def __init__(self, container, keys, contacts, offsets, style): 41 def __init__(self, editor, data, contacts, offsets):
41 """ 42 """
42 @param container (FlexTable): FlexTable parent widget 43 @param container (FlexTable): FlexTable parent widget
43 @param keys (dict{unicode: dict{unicode: unicode}}): dict binding items 44 @param keys (dict{unicode: dict{unicode: unicode}}): dict binding items
44 keys to their display config data. 45 keys to their display config data.
45 @param contacts (list): list of contacts 46 @param contacts (list): list of contacts
46 @param offsets (dict): define widgets positions offsets within container: 47 """
47 - "x_first": the x offset for the first widget's row on the grid 48 self.editor = editor
48 - "x": the x offset for all widgets rows, except the first one if "x_first" is defined 49 list_manager.ListManager.__init__(self, data, contacts)
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)
53 self.registerPopupMenuPanel(entries={"Remove group": {}}, 50 self.registerPopupMenuPanel(entries={"Remove group": {}},
54 callback=lambda sender, key: Timer(5, lambda timer: self.removeContactKey(sender, key))) 51 callback=lambda sender, key: self.removeGroup(sender))
55 52
56 def removeContactKey(self, sender, key): 53 def removeGroup(self, sender):
57 key = sender.getText() 54 group = sender.getHTML()
58 55
59 def confirm_cb(answer): 56 def confirm_cb(answer):
60 if answer: 57 if answer:
61 list_manager.ListManager.removeItemKey(self, key) 58 list_manager.ListManager.removeList(self, group)
62 self.container.removeKeyFromAddGroupPanel(key) 59 self.editor.add_group_panel.groups.remove(group)
63 60
64 _dialog = dialog.ConfirmDialog(confirm_cb, text="Do you really want to delete the group '%s'?" % key) 61 _dialog = dialog.ConfirmDialog(confirm_cb, text="Do you really want to delete the group '%s'?" % group)
65 _dialog.show() 62 _dialog.show()
66 63
67 def removeFromRemainingList(self, contacts): 64 def tag(self, contacts):
68 list_manager.ListManager.removeFromRemainingList(self, contacts) 65 list_manager.ListManager.tag(self, contacts)
69 self.container.updateContactList(contacts) 66 self.editor.updateContactList(contacts)
70 67
71 def addToRemainingList(self, contacts, ignore_key=None): 68 def untag(self, contacts, ignore_key=None):
72 list_manager.ListManager.addToRemainingList(self, contacts, ignore_key) 69 list_manager.ListManager.untag(self, contacts, ignore_key)
73 self.container.updateContactList(contacts) 70 self.editor.updateContactList(contacts)
74 71
75 72
76 class ContactGroupEditor(DockPanel): 73 class ContactGroupEditor(VerticalPanel):
77 """A big panel including a ContactGroupManager and other UI stuff.""" 74 """A big panel including a ContactGroupManager and other UI stuff."""
78 75
79 def __init__(self, host, container=None, onCloseCallback=None): 76 def __init__(self, host, container=None, onCloseCallback=None):
80 """ 77 """
81 78
82 @param host (SatWebFrontend) 79 @param host (SatWebFrontend)
83 @param container (PanelBase): parent panel or None to display in a popup 80 @param container (PanelBase): parent panel or None to display in a popup
84 @param onCloseCallback (callable) 81 @param onCloseCallback (callable)
85 """ 82 """
86 DockPanel.__init__(self) 83 VerticalPanel.__init__(self, StyleName="contactGroupEditor")
87 self.host = host 84 self.host = host
88 85
89 # eventually display in a popup 86 # eventually display in a popup
90 if container is None: 87 if container is None:
91 container = DialogBox(autoHide=False, centered=True) 88 container = DialogBox(autoHide=False, centered=True)
97 roster_entities_by_group = self.host.contact_list.roster_entities_by_group 94 roster_entities_by_group = self.host.contact_list.roster_entities_by_group
98 del roster_entities_by_group[None] # remove the empty group 95 del roster_entities_by_group[None] # remove the empty group
99 roster_groups = roster_entities_by_group.keys() 96 roster_groups = roster_entities_by_group.keys()
100 roster_groups.sort() 97 roster_groups.sort()
101 98
99 # groups on the left
100 manager = self.initContactGroupManager(roster_entities_by_group)
102 self.add_group_panel = self.initAddGroupPanel(roster_groups) 101 self.add_group_panel = self.initAddGroupPanel(roster_groups)
102 left_container = VerticalPanel(Width="100%")
103 left_container.add(manager)
104 left_container.add(self.add_group_panel)
105 left_container.setCellHorizontalAlignment(self.add_group_panel, HasAlignment.ALIGN_CENTER)
106 left_panel = ScrollPanel(left_container, StyleName="contactGroupManager")
107 left_panel.setAlwaysShowScrollBars(True)
108
109 # contact list on the right
110 east_panel = ScrollPanel(self.initContactList(), StyleName="contactGroupRoster")
111 east_panel.setAlwaysShowScrollBars(True)
112
103 south_panel = self.initCloseSaveButtons() 113 south_panel = self.initCloseSaveButtons()
104 center_panel = self.initContactGroupManager(roster_groups) 114
105 east_panel = self.initContactList() 115 main_panel = HorizontalPanel()
106 116 main_panel.add(left_panel)
107 self.add(self.add_group_panel, DockPanel.CENTER) 117 main_panel.add(east_panel)
108 self.add(east_panel, DockPanel.EAST) 118 self.add(Label("You get here an over whole view of your contact groups. There are two ways to assign your contacts to an existing group: write them into auto-completed textboxes or use the right panel to drag and drop them into the group."))
109 self.add(center_panel, DockPanel.NORTH) 119 self.add(main_panel)
110 self.add(south_panel, DockPanel.SOUTH) 120 self.add(south_panel)
111 121
112 self.setCellHorizontalAlignment(center_panel, HasAlignment.ALIGN_LEFT)
113 self.setCellVerticalAlignment(center_panel, HasAlignment.ALIGN_TOP)
114 self.setCellHorizontalAlignment(east_panel, HasAlignment.ALIGN_RIGHT)
115 self.setCellVerticalAlignment(east_panel, HasAlignment.ALIGN_TOP)
116 self.setCellVerticalAlignment(self.add_group_panel, HasAlignment.ALIGN_BOTTOM)
117 self.setCellHorizontalAlignment(self.add_group_panel, HasAlignment.ALIGN_LEFT)
118 self.setCellVerticalAlignment(south_panel, HasAlignment.ALIGN_BOTTOM)
119 self.setCellHorizontalAlignment(south_panel, HasAlignment.ALIGN_CENTER) 122 self.setCellHorizontalAlignment(south_panel, HasAlignment.ALIGN_CENTER)
120 123
121 # need to be done after the contact list has been initialized 124 # need to be done after the contact list has been initialized
122 self.groups.resetItems(roster_entities_by_group) 125 self.updateContactList()
123 self.toggleContacts(showAll=True)
124 126
125 # Hide the contacts list from the main panel to not confuse the user 127 # Hide the contacts list from the main panel to not confuse the user
126 self.restore_contact_panel = False 128 self.restore_contact_panel = False
127 clist = self.host.contact_list 129 clist = self.host.contact_list
128 if clist.getVisible(): 130 if clist.getVisible():
132 container.add(self) 134 container.add(self)
133 container.setVisible(True) 135 container.setVisible(True)
134 if isinstance(container, DialogBox): 136 if isinstance(container, DialogBox):
135 container.center() 137 container.center()
136 138
137 def initContactGroupManager(self, groups): 139 def initContactGroupManager(self, data):
138 """Initialise the contact group manager. 140 """Initialise the contact group manager.
139 141
140 @param groups (list[unicode]): contact groups 142 @param groups (list[unicode]): contact groups
141 """ 143 """
142 flex_table = FlexTable() 144 self.groups = ContactGroupManager(self, data, self.all_contacts)
143 flex_table.addStyleName('contactGroupEditor') 145 return self.groups
144
145 # overwrite the default style which has been set for rich text editor
146 style = {"keyItem": "group",
147 "popupMenuItem": "popupMenuItem",
148 "removeButton": "contactGroupRemoveButton",
149 "buttonCell": "contactGroupButtonCell",
150 "keyPanel": "contactGroupPanel"
151 }
152
153 groups = {group: {} for group in groups}
154 self.groups = ContactGroupManager(flex_table, groups, self.all_contacts, style=style)
155 self.groups.createWidgets() # widgets are automatically added to the FlexTable
156
157 # FIXME: clean that part which is dangerous
158 flex_table.updateContactList = self.updateContactList
159 flex_table.removeKeyFromAddGroupPanel = self.add_group_panel.groups.remove
160
161 return flex_table
162 146
163 def initAddGroupPanel(self, groups): 147 def initAddGroupPanel(self, groups):
164 """Initialise the 'Add group' panel. 148 """Initialise the 'Add group' panel.
165 149
166 @param groups (list[unicode]): contact groups 150 @param groups (list[unicode]): contact groups
167 """ 151 """
168 152
169 def add_group_cb(key): 153 def add_group_cb(key):
170 self.groups.addItemKey(key) 154 self.groups.addList(key)
171 self.add_group_panel.textbox.setFocus(True) 155 self.add_group_panel.textbox.setFocus(True)
172 156
173 add_group_panel = dialog.AddGroupPanel(groups, add_group_cb) 157 add_group_panel = dialog.AddGroupPanel(groups, add_group_cb)
174 add_group_panel.addStyleName("addContactGroupPanel") 158 add_group_panel.addStyleName("addContactGroupPanel")
175 return add_group_panel 159 return add_group_panel
176 160
177 def initCloseSaveButtons(self): 161 def initCloseSaveButtons(self):
178 """Add the buttons to close the dialog and save the groups.""" 162 """Add the buttons to close the dialog and save the groups."""
179 buttons = HorizontalPanel() 163 buttons = HorizontalPanel()
180 buttons.addStyleName("marginAuto") 164 buttons.addStyleName("marginAuto")
165 buttons.add(Button("Cancel", listener=self.cancelWithoutSaving))
181 buttons.add(Button("Save", listener=self.closeAndSave)) 166 buttons.add(Button("Save", listener=self.closeAndSave))
182 buttons.add(Button("Cancel", listener=self.cancelWithoutSaving))
183 return buttons 167 return buttons
184 168
185 def initContactList(self): 169 def initContactList(self):
186 """Add the contact list to the DockPanel.""" 170 """Add the contact list to the DockPanel."""
187 self.toggle = Button("", self.toggleContacts) 171
172 self.toggle = CheckBox("Hide assigned contacts")
173 self.toggle.addClickListener(lambda dummy: self.updateContactList())
188 self.toggle.addStyleName("toggleAssignedContacts") 174 self.toggle.addStyleName("toggleAssignedContacts")
189 self.contacts = contact_panel.ContactsPanel(self.host) 175 self.contacts = contact_panel.ContactsPanel(self.host)
190 for contact in self.all_contacts: 176 for contact in self.all_contacts:
191 self.contacts.updateContactBox(contact) 177 self.contacts.updateContactBox(contact)
192 panel = VerticalPanel() 178 panel = VerticalPanel()
193 panel.add(self.toggle) 179 panel.add(self.toggle)
194 panel.add(self.contacts) 180 panel.add(self.contacts)
195 return panel 181 return panel
196 182
197 def toggleContacts(self, sender=None, showAll=None):
198 """Toggle the button to show contacts and the contact list.
199
200 @param sender (Button)
201 @param showAll (bool): if set, initialise with True to show all contacts
202 or with False to show only the ones that are not assigned yet.
203 """
204 self.toggle.showAll = (not self.toggle.showAll) if showAll is None else showAll
205 self.toggle.setText("Hide assigned" if self.toggle.showAll else "Show assigned")
206 self.updateContactList()
207
208 def updateContactList(self, contacts=None): 183 def updateContactList(self, contacts=None):
209 """Update the contact list's items visibility, depending of the toggle 184 """Update the contact list's items visibility, depending of the toggle
210 button and the "contacts" attribute. 185 checkbox and the "contacts" attribute.
211 186
212 @param contacts (list): contacts to be updated, or None to update all. 187 @param contacts (list): contacts to be updated, or None to update all.
213 """ 188 """
214 if not hasattr(self, "toggle") or not hasattr(self.toggle, "showAll"): 189 if not hasattr(self, "toggle"):
215 return 190 return
216 if contacts is not None: 191 if contacts is not None:
217 to_remove = set() 192 contacts = [jid.JID(contact) for contact in contacts]
218 for contact in contacts: 193 contacts = set(contacts).intersection(self.all_contacts)
219 if contact not in self.all_contacts:
220 to_remove.add(contact)
221 for contact in to_remove:
222 contacts.remove(contact)
223 else: 194 else:
224 contacts = self.all_contacts 195 contacts = self.all_contacts
196
225 for contact in contacts: 197 for contact in contacts:
226 if self.toggle.showAll: 198 if not self.toggle.getChecked(): # show all contacts
227 self.contacts.updateContactBox(contact).setVisible(True) 199 self.contacts.updateContactBox(contact).setVisible(True)
228 else: 200 else: # show only non-assigned contacts
229 if contact in self.groups.items_remaining: 201 if contact in self.groups.untagged:
230 self.contacts.updateContactBox(contact).setVisible(True) 202 self.contacts.updateContactBox(contact).setVisible(True)
231 else: 203 else:
232 self.contacts.updateContactBox(contact).setVisible(False) 204 self.contacts.updateContactBox(contact).setVisible(False)
233 205
234 def __close(self): 206 def __close(self):
252 224
253 def closeAndSave(self): 225 def closeAndSave(self):
254 """Call bridge methods to save the changes and close the dialog""" 226 """Call bridge methods to save the changes and close the dialog"""
255 old_groups_by_entity = contact_list.JIDDict(self.host.contact_list.roster_groups_by_entity) 227 old_groups_by_entity = contact_list.JIDDict(self.host.contact_list.roster_groups_by_entity)
256 old_entities = old_groups_by_entity.keys() 228 old_entities = old_groups_by_entity.keys()
257 groups_by_entity = contact_list.JIDDict(self.groups.getKeysByItem()) 229 result = {jid.JID(item): keys for item, keys in self.groups.getKeysByItem().iteritems()}
230 groups_by_entity = contact_list.JIDDict(result)
258 entities = groups_by_entity.keys() 231 entities = groups_by_entity.keys()
259 232
260 for invalid in entities.difference(self.all_contacts): 233 for invalid in entities.difference(self.all_contacts):
261 dialog.InfoDialog("Invalid contact(s)", 234 dialog.InfoDialog("Invalid contact(s)",
262 "The contact '%s' is not in your contact list but has been assigned to: '%s'." % (invalid, "', '".join(groups_by_entity[invalid])) + 235 "The contact '%s' is not in your contact list but has been assigned to: '%s'." % (invalid, "', '".join(groups_by_entity[invalid])) +