Mercurial > libervia-web
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])) + |