comparison frontends/src/primitivus/contact_list.py @ 1938:011eff37e21d

quick frontend, primitivus: quickContactList refactored to handle several profiles at once
author Goffi <goffi@goffi.org>
date Mon, 18 Apr 2016 18:31:13 +0200
parents 2daf7b4c6756
children 02d21a589be2
comparison
equal deleted inserted replaced
1937:14a33c2b1b2a 1938:011eff37e21d
26 from sat_frontends.primitivus.keys import action_key_map as a_key 26 from sat_frontends.primitivus.keys import action_key_map as a_key
27 from sat_frontends.primitivus.widget import PrimitivusWidget 27 from sat_frontends.primitivus.widget import PrimitivusWidget
28 from sat_frontends.tools import jid 28 from sat_frontends.tools import jid
29 from sat.core import log as logging 29 from sat.core import log as logging
30 log = logging.getLogger(__name__) 30 log = logging.getLogger(__name__)
31 from sat_frontends.quick_frontend import quick_widgets
31 32
32 33
33 class ContactList(PrimitivusWidget, QuickContactList): 34 class ContactList(PrimitivusWidget, QuickContactList):
35 PROFILES_MULTIPLE=False
36 PROFILES_ALLOW_NONE=False
34 signals = ['click','change'] 37 signals = ['click','change']
35 38 # FIXME: Only single profile is managed so far
36 def __init__(self, host, on_click=None, on_change=None, user_data=None, profile=None): 39
37 QuickContactList.__init__(self, host, profile) 40 def __init__(self, host, target, on_click=None, on_change=None, user_data=None, profiles=None):
41 QuickContactList.__init__(self, host, profiles)
42 self.contact_list = self.host.contact_lists[self.profile]
38 43
39 #we now build the widget 44 #we now build the widget
40 self.status_bar = StatusBar(host) 45 self.status_bar = StatusBar(host)
41 self.frame = sat_widgets.FocusFrame(self._buildList(), None, self.status_bar) 46 self.frame = sat_widgets.FocusFrame(self._buildList(), None, self.status_bar)
42 PrimitivusWidget.__init__(self, self.frame, _(u'Contacts')) 47 PrimitivusWidget.__init__(self, self.frame, _(u'Contacts'))
43 if on_click: 48 if on_click:
44 urwid.connect_signal(self, 'click', on_click, user_data) 49 urwid.connect_signal(self, 'click', on_click, user_data)
45 if on_change: 50 if on_change:
46 urwid.connect_signal(self, 'change', on_change, user_data) 51 urwid.connect_signal(self, 'change', on_change, user_data)
47 52
48 def update(self): 53 def update(self, entities=None, type_=None, profile=None):
49 """Update display, keep focus""" 54 """Update display, keep focus"""
55 # FIXME: full update is done each time, must handle entities, type_ and profile
50 widget, position = self.frame.body.get_focus() 56 widget, position = self.frame.body.get_focus()
51 self.frame.body = self._buildList() 57 self.frame.body = self._buildList()
52 if position: 58 if position:
53 try: 59 try:
54 self.frame.body.focus_position = position 60 self.frame.body.focus_position = position
63 if key in sat_widgets.FOCUS_KEYS: 69 if key in sat_widgets.FOCUS_KEYS:
64 if (key == a_key['FOCUS_SWITCH'] or (key == a_key['FOCUS_UP'] and self.frame.focus_position == 'body') or 70 if (key == a_key['FOCUS_SWITCH'] or (key == a_key['FOCUS_UP'] and self.frame.focus_position == 'body') or
65 (key == a_key['FOCUS_DOWN'] and self.frame.focus_position == 'footer')): 71 (key == a_key['FOCUS_DOWN'] and self.frame.focus_position == 'footer')):
66 return key 72 return key
67 if key == a_key['STATUS_HIDE']: #user wants to (un)hide contacts' statuses 73 if key == a_key['STATUS_HIDE']: #user wants to (un)hide contacts' statuses
68 self.show_status = not self.show_status 74 self.contact_list.show_status = not self.contact_list.show_status
69 self.update() 75 self.update()
70 elif key == a_key['DISCONNECTED_HIDE']: #user wants to (un)hide disconnected contacts 76 elif key == a_key['DISCONNECTED_HIDE']: #user wants to (un)hide disconnected contacts
71 self.host.bridge.setParam(C.SHOW_OFFLINE_CONTACTS, C.boolConst(not self.show_disconnected), "General", profile_key=self.profile) 77 self.host.bridge.setParam(C.SHOW_OFFLINE_CONTACTS, C.boolConst(not self.contact_list.show_disconnected), "General", profile_key=self.profile)
72 elif key == a_key['RESOURCES_HIDE']: #user wants to (un)hide contacts resources 78 elif key == a_key['RESOURCES_HIDE']: #user wants to (un)hide contacts resources
73 self.showResources(not self.show_resources) 79 self.contact_list.showResources(not self.contact_list.show_resources)
74 self.update() 80 self.update()
75 return super(ContactList, self).keypress(size, key) 81 return super(ContactList, self).keypress(size, key)
82
83 # QuickWidget methods
84
85 @staticmethod
86 def getWidgetHash(target, profiles):
87 profiles = sorted(profiles)
88 return tuple(profiles)
76 89
77 # modify the contact list 90 # modify the contact list
78 91
79 def setFocus(self, text, select=False): 92 def setFocus(self, text, select=False):
80 """give focus to the first element that matches the given text. You can also 93 """give focus to the first element that matches the given text. You can also
120 133
121 # events 134 # events
122 135
123 def _groupClicked(self, group_wid): 136 def _groupClicked(self, group_wid):
124 group = group_wid.getValue() 137 group = group_wid.getValue()
125 data = self.getGroupData(group) 138 data = self.contact_list.getGroupData(group)
126 data[C.GROUP_DATA_FOLDED] = not data.setdefault(C.GROUP_DATA_FOLDED, False) 139 data[C.GROUP_DATA_FOLDED] = not data.setdefault(C.GROUP_DATA_FOLDED, False)
127 self.setFocus(group) 140 self.setFocus(group)
128 self.update() 141 self.update()
129 142
130 def _contactClicked(self, use_bare_jid, contact_wid, selected): 143 def _contactClicked(self, use_bare_jid, contact_wid, selected):
134 If True, all jids in self._alerts with the same bare jid has contact_wid.data will be removed 147 If True, all jids in self._alerts with the same bare jid has contact_wid.data will be removed
135 @param contact_wid: widget of the contact, must have the entity set in data attribute 148 @param contact_wid: widget of the contact, must have the entity set in data attribute
136 @param selected: boolean returned by the widget, telling if it is selected 149 @param selected: boolean returned by the widget, telling if it is selected
137 """ 150 """
138 entity = contact_wid.data 151 entity = contact_wid.data
139 self.removeAlerts(entity, use_bare_jid) 152 self.contact_list.removeAlerts(entity, use_bare_jid)
140 self.host.modeHint(C.MODE_INSERTION) 153 self.host.modeHint(C.MODE_INSERTION)
141 self._emit('click', entity) 154 self._emit('click', entity)
142
143 def onNickUpdate(self, entity, new_nick, profile):
144 self.update()
145 155
146 # Methods to build the widget 156 # Methods to build the widget
147 157
148 def _buildEntityWidget(self, entity, keys=None, use_bare_jid=False, with_alert=True, with_show_attr=True, markup_prepend=None, markup_append = None): 158 def _buildEntityWidget(self, entity, keys=None, use_bare_jid=False, with_alert=True, with_show_attr=True, markup_prepend=None, markup_append = None):
149 """Build one contact markup data 159 """Build one contact markup data
161 @param markup_append (list): markup to append to the generated one before building the widget 171 @param markup_append (list): markup to append to the generated one before building the widget
162 @return (list): markup data are expected by Urwid text widgets 172 @return (list): markup data are expected by Urwid text widgets
163 """ 173 """
164 markup = [] 174 markup = []
165 if use_bare_jid: 175 if use_bare_jid:
166 selected = {entity.bare for entity in self._selected} 176 selected = {entity.bare for entity in self.contact_list._selected}
167 else: 177 else:
168 selected = self._selected 178 selected = self.contact_list._selected
169 if keys is None: 179 if keys is None:
170 entity_txt = entity 180 entity_txt = entity
171 else: 181 else:
172 cache = self.getCache(entity) 182 cache = self.contact_list.getCache(entity)
173 for key in keys: 183 for key in keys:
174 if key.startswith('cache_'): 184 if key.startswith('cache_'):
175 entity_txt = cache.get(key[6:]) 185 entity_txt = cache.get(key[6:])
176 else: 186 else:
177 entity_txt = getattr(entity, key) 187 entity_txt = getattr(entity, key)
179 break 189 break
180 if not entity_txt: 190 if not entity_txt:
181 entity_txt = entity 191 entity_txt = entity
182 192
183 if with_show_attr: 193 if with_show_attr:
184 show = self.getCache(entity, C.PRESENCE_SHOW) 194 show = self.contact_list.getCache(entity, C.PRESENCE_SHOW)
185 if show is None: 195 if show is None:
186 show = C.PRESENCE_UNAVAILABLE 196 show = C.PRESENCE_UNAVAILABLE
187 show_icon, entity_attr = C.PRESENCE.get(show, ('', 'default')) 197 show_icon, entity_attr = C.PRESENCE.get(show, ('', 'default'))
188 markup.insert(0, u"{} ".format(show_icon)) 198 markup.insert(0, u"{} ".format(show_icon))
189 else: 199 else:
190 entity_attr = 'default' 200 entity_attr = 'default'
191 201
192 alerts_count = self.getAlerts(entity, use_bare_jid=use_bare_jid) 202 alerts_count = len(self.contact_list.getAlerts(entity, use_bare_jid=use_bare_jid))
193 if with_alert and alerts_count: 203 if with_alert and alerts_count:
194 entity_attr = 'alert' 204 entity_attr = 'alert'
195 header = C.ALERT_HEADER % alerts_count 205 header = C.ALERT_HEADER % alerts_count
196 else: 206 else:
197 header = '' 207 header = ''
219 if not entities: 229 if not entities:
220 return 230 return
221 widgets = [] # list of built widgets 231 widgets = [] # list of built widgets
222 232
223 for entity in entities: 233 for entity in entities:
224 if entity in self._specials or not self.entityToShow(entity): 234 if entity in self.contact_list._specials or not self.contact_list.entityToShow(entity):
225 continue 235 continue
226 markup_extra = [] 236 markup_extra = []
227 if self.show_resources: 237 if self.contact_list.show_resources:
228 for resource in self.getCache(entity, C.CONTACT_RESOURCES): 238 for resource in self.contact_list.getCache(entity, C.CONTACT_RESOURCES):
229 resource_disp = ('resource_main' if resource == self.getCache(entity, C.CONTACT_MAIN_RESOURCE) else 'resource', "\n " + resource) 239 resource_disp = ('resource_main' if resource == self.getCache(entity, C.CONTACT_MAIN_RESOURCE) else 'resource', "\n " + resource)
230 markup_extra.append(resource_disp) 240 markup_extra.append(resource_disp)
231 if self.show_status: 241 if self.contact_list.show_status:
232 status = self.getCache(jid.JID('%s/%s' % (entity, resource)), 'status') 242 status = self.contact_list.getCache(jid.JID('%s/%s' % (entity, resource)), 'status')
233 status_disp = ('status', "\n " + status) if status else "" 243 status_disp = ('status', "\n " + status) if status else ""
234 markup_extra.append(status_disp) 244 markup_extra.append(status_disp)
235 245
236 246
237 else: 247 else:
238 if self.show_status: 248 if self.contact_list.show_status:
239 status = self.getCache(entity, 'status') 249 status = self.contact_list.getCache(entity, 'status')
240 status_disp = ('status', "\n " + status) if status else "" 250 status_disp = ('status', "\n " + status) if status else ""
241 markup_extra.append(status_disp) 251 markup_extra.append(status_disp)
242 widget = self._buildEntityWidget(entity, ('cache_nick', 'cache_name', 'node'), use_bare_jid=True, markup_append=markup_extra) 252 widget = self._buildEntityWidget(entity, ('cache_nick', 'cache_name', 'node'), use_bare_jid=True, markup_append=markup_extra)
243 widgets.append(widget) 253 widgets.append(widget)
244 254
247 for widget in widgets: 257 for widget in widgets:
248 content.append(widget) 258 content.append(widget)
249 259
250 def _buildSpecials(self, content): 260 def _buildSpecials(self, content):
251 """Build the special entities""" 261 """Build the special entities"""
252 specials = list(self._specials) 262 specials = list(self.contact_list._specials)
253 specials.sort() 263 specials.sort()
254 extra_shown = set() 264 extra_shown = set()
255 for entity in specials: 265 for entity in specials:
256 # the special widgets 266 # the special widgets
257 widget = self._buildEntityWidget(entity, ('cache_nick', 'cache_name', 'node'), with_show_attr=False) 267 widget = self._buildEntityWidget(entity, ('cache_nick', 'cache_name', 'node'), with_show_attr=False)
258 content.append(widget) 268 content.append(widget)
259 269
260 # resources which must be displayed (e.g. MUC private conversations) 270 # resources which must be displayed (e.g. MUC private conversations)
261 extras = [extra for extra in self._special_extras if extra.bare == entity.bare] 271 extras = [extra for extra in self.contact_list._special_extras if extra.bare == entity.bare]
262 extras.sort() 272 extras.sort()
263 for extra in extras: 273 for extra in extras:
264 widget = self._buildEntityWidget(extra, ('resource',), markup_prepend = ' ') 274 widget = self._buildEntityWidget(extra, ('resource',), markup_prepend = ' ')
265 content.append(widget) 275 content.append(widget)
266 extra_shown.add(extra) 276 extra_shown.add(extra)
267 277
268 # entities which must be visible but not resource of current special entities 278 # entities which must be visible but not resource of current special entities
269 for extra in self._special_extras.difference(extra_shown): 279 for extra in self.contact_list._special_extras.difference(extra_shown):
270 widget = self._buildEntityWidget(extra, ('resource',)) 280 widget = self._buildEntityWidget(extra, ('resource',))
271 content.append(widget) 281 content.append(widget)
272 282
273 def _buildList(self): 283 def _buildList(self):
274 """Build the main contact list widget""" 284 """Build the main contact list widget"""
275 content = urwid.SimpleListWalker([]) 285 content = urwid.SimpleListWalker([])
276 286
277 self._buildSpecials(content) 287 self._buildSpecials(content)
278 if self._specials: 288 if self.contact_list._specials:
279 content.append(urwid.Divider('=')) 289 content.append(urwid.Divider('='))
280 290
281 groups = list(self._groups) 291 groups = list(self.contact_list._groups)
282 groups.sort(key=lambda x: x.lower() if x else x) 292 groups.sort(key=lambda x: x.lower() if x else x)
283 for group in groups: 293 for group in groups:
284 data = self.getGroupData(group) 294 data = self.contact_list.getGroupData(group)
285 folded = data.get(C.GROUP_DATA_FOLDED, False) 295 folded = data.get(C.GROUP_DATA_FOLDED, False)
286 jids = list(data['jids']) 296 jids = list(data['jids'])
287 if group is not None and (self.anyEntityToShow(jids) or self.show_empty_groups): 297 if group is not None and (self.contact_list.anyEntityToShow(jids) or self.contact_list.show_empty_groups):
288 header = '[-]' if not folded else '[+]' 298 header = '[-]' if not folded else '[+]'
289 widget = sat_widgets.ClickableText(group, header=header + ' ') 299 widget = sat_widgets.ClickableText(group, header=header + ' ')
290 content.append(widget) 300 content.append(widget)
291 urwid.connect_signal(widget, 'click', self._groupClicked) 301 urwid.connect_signal(widget, 'click', self._groupClicked)
292 if not folded: 302 if not folded:
293 self._buildEntities(content, jids) 303 self._buildEntities(content, jids)
294 not_in_roster = set(self._cache).difference(self._roster).difference(self._specials).difference((self.whoami.bare,)) 304 not_in_roster = set(self.contact_list._cache).difference(self.contact_list._roster).difference(self.contact_list._specials).difference((self.contact_list.whoami.bare,))
295 if not_in_roster: 305 if not_in_roster:
296 content.append(urwid.Divider('-')) 306 content.append(urwid.Divider('-'))
297 self._buildEntities(content, not_in_roster) 307 self._buildEntities(content, not_in_roster)
298 308
299 return urwid.ListBox(content) 309 return urwid.ListBox(content)
310
311 quick_widgets.register(QuickContactList, ContactList)