Mercurial > libervia-backend
comparison sat_frontends/quick_frontend/quick_contact_list.py @ 2624:56f94936df1e
code style reformatting using black
author | Goffi <goffi@goffi.org> |
---|---|
date | Wed, 27 Jun 2018 20:14:46 +0200 |
parents | 81b70eeb710f |
children | ce1e15d59496 |
comparison
equal
deleted
inserted
replaced
2623:49533de4540b | 2624:56f94936df1e |
---|---|
20 """Contact List handling multi profiles at once, | 20 """Contact List handling multi profiles at once, |
21 should replace quick_contact_list module in the future""" | 21 should replace quick_contact_list module in the future""" |
22 | 22 |
23 from sat.core.i18n import _ | 23 from sat.core.i18n import _ |
24 from sat.core.log import getLogger | 24 from sat.core.log import getLogger |
25 | |
25 log = getLogger(__name__) | 26 log = getLogger(__name__) |
26 from sat.core import exceptions | 27 from sat.core import exceptions |
27 from sat_frontends.quick_frontend.quick_widgets import QuickWidget | 28 from sat_frontends.quick_frontend.quick_widgets import QuickWidget |
28 from sat_frontends.quick_frontend.constants import Const as C | 29 from sat_frontends.quick_frontend.constants import Const as C |
29 from sat_frontends.tools import jid | 30 from sat_frontends.tools import jid |
30 from collections import OrderedDict | 31 from collections import OrderedDict |
31 | 32 |
32 | 33 |
33 try: | 34 try: |
34 # FIXME: to be removed when an acceptable solution is here | 35 # FIXME: to be removed when an acceptable solution is here |
35 unicode('') # XXX: unicode doesn't exist in pyjamas | 36 unicode("") # XXX: unicode doesn't exist in pyjamas |
36 except (TypeError, AttributeError): # Error raised is not the same depending on | 37 except (TypeError, AttributeError): # Error raised is not the same depending on |
37 # pyjsbuild options | 38 # pyjsbuild options |
38 # XXX: pyjamas' max doesn't support key argument, so we implement it ourself | 39 # XXX: pyjamas' max doesn't support key argument, so we implement it ourself |
39 pyjamas_max = max | 40 pyjamas_max = max |
40 | 41 |
41 def max(iterable, key): | 42 def max(iterable, key): |
42 iter_cpy = list(iterable) | 43 iter_cpy = list(iterable) |
93 # do we show entities with notifications? | 94 # do we show entities with notifications? |
94 # if True, entities will be show even if they normally would not | 95 # if True, entities will be show even if they normally would not |
95 # (e.g. not in contact list) if they have notifications attached | 96 # (e.g. not in contact list) if they have notifications attached |
96 self.show_entities_with_notifs = True | 97 self.show_entities_with_notifs = True |
97 | 98 |
98 self.host.bridge.asyncGetParamA(C.SHOW_EMPTY_GROUPS, | 99 self.host.bridge.asyncGetParamA( |
99 "General", | 100 C.SHOW_EMPTY_GROUPS, |
100 profile_key=profile, | 101 "General", |
101 callback=self._showEmptyGroups) | 102 profile_key=profile, |
102 | 103 callback=self._showEmptyGroups, |
103 self.host.bridge.asyncGetParamA(C.SHOW_OFFLINE_CONTACTS, | 104 ) |
104 "General", | 105 |
105 profile_key=profile, | 106 self.host.bridge.asyncGetParamA( |
106 callback=self._showOfflineContacts) | 107 C.SHOW_OFFLINE_CONTACTS, |
108 "General", | |
109 profile_key=profile, | |
110 callback=self._showOfflineContacts, | |
111 ) | |
107 | 112 |
108 # FIXME: workaround for a pyjamas issue: calling hash on a class method always | 113 # FIXME: workaround for a pyjamas issue: calling hash on a class method always |
109 # return a different value if that method is defined directly within the | 114 # return a different value if that method is defined directly within the |
110 # class (with the "def" keyword) | 115 # class (with the "def" keyword) |
111 self.presenceListener = self.onPresenceUpdate | 116 self.presenceListener = self.onPresenceUpdate |
112 self.host.addListener('presence', self.presenceListener, [self.profile]) | 117 self.host.addListener("presence", self.presenceListener, [self.profile]) |
113 self.nickListener = self.onNickUpdate | 118 self.nickListener = self.onNickUpdate |
114 self.host.addListener('nick', self.nickListener, [self.profile]) | 119 self.host.addListener("nick", self.nickListener, [self.profile]) |
115 self.notifListener = self.onNotification | 120 self.notifListener = self.onNotification |
116 self.host.addListener('notification', self.notifListener, [self.profile]) | 121 self.host.addListener("notification", self.notifListener, [self.profile]) |
117 # notifListener only update the entity, so we can re-use it | 122 # notifListener only update the entity, so we can re-use it |
118 self.host.addListener('notificationsClear', self.notifListener, [self.profile]) | 123 self.host.addListener("notificationsClear", self.notifListener, [self.profile]) |
119 | 124 |
120 def _showEmptyGroups(self, show_str): | 125 def _showEmptyGroups(self, show_str): |
121 # Called only by __init__ | 126 # Called only by __init__ |
122 # self.update is not wanted here, as it is done by | 127 # self.update is not wanted here, as it is done by |
123 # handler when all profiles are ready | 128 # handler when all profiles are ready |
153 def roster_connected(self): | 158 def roster_connected(self): |
154 """Return all the bare JIDs of the roster entities that are connected. | 159 """Return all the bare JIDs of the roster entities that are connected. |
155 | 160 |
156 @return (set[jid.JID]) | 161 @return (set[jid.JID]) |
157 """ | 162 """ |
158 return set([entity for entity in self._roster | 163 return set( |
159 if self.getCache(entity, C.PRESENCE_SHOW) is not None]) | 164 [ |
165 entity | |
166 for entity in self._roster | |
167 if self.getCache(entity, C.PRESENCE_SHOW) is not None | |
168 ] | |
169 ) | |
160 | 170 |
161 @property | 171 @property |
162 def roster_entities_by_group(self): | 172 def roster_entities_by_group(self): |
163 """Return a dictionary binding the roster groups to their entities bare JIDs. | 173 """Return a dictionary binding the roster groups to their entities bare JIDs. |
164 | 174 |
165 This also includes the empty group (None key). | 175 This also includes the empty group (None key). |
166 @return (dict[unicode,set(jid.JID)]) | 176 @return (dict[unicode,set(jid.JID)]) |
167 """ | 177 """ |
168 return {group: self._groups[group]['jids'] for group in self._groups} | 178 return {group: self._groups[group]["jids"] for group in self._groups} |
169 | 179 |
170 @property | 180 @property |
171 def roster_groups_by_entities(self): | 181 def roster_groups_by_entities(self): |
172 """Return a dictionary binding the entities bare JIDs to their roster groups | 182 """Return a dictionary binding the entities bare JIDs to their roster groups |
173 | 183 |
174 @return (dict[jid.JID, set(unicode)]) | 184 @return (dict[jid.JID, set(unicode)]) |
175 """ | 185 """ |
176 result = {} | 186 result = {} |
177 for group, data in self._groups.iteritems(): | 187 for group, data in self._groups.iteritems(): |
178 for entity in data['jids']: | 188 for entity in data["jids"]: |
179 result.setdefault(entity, set()).add(group) | 189 result.setdefault(entity, set()).add(group) |
180 return result | 190 return result |
181 | 191 |
182 @property | 192 @property |
183 def selected(self): | 193 def selected(self): |
193 | 203 |
194 entities are not sorted | 204 entities are not sorted |
195 """ | 205 """ |
196 return self._cache.iteritems() | 206 return self._cache.iteritems() |
197 | 207 |
198 | |
199 @property | 208 @property |
200 def items(self): | 209 def items(self): |
201 """Return item representation for all visible entities in cache | 210 """Return item representation for all visible entities in cache |
202 | 211 |
203 entities are not sorted | 212 entities are not sorted |
204 key: bare jid, value: data | 213 key: bare jid, value: data |
205 """ | 214 """ |
206 return {jid_:cache for jid_, cache in self._cache.iteritems() | 215 return { |
207 if self.entityVisible(jid_)} | 216 jid_: cache |
208 | 217 for jid_, cache in self._cache.iteritems() |
218 if self.entityVisible(jid_) | |
219 } | |
209 | 220 |
210 def getItem(self, entity): | 221 def getItem(self, entity): |
211 """Return item representation of requested entity | 222 """Return item representation of requested entity |
212 | 223 |
213 @param entity(jid.JID): bare jid of entity | 224 @param entity(jid.JID): bare jid of entity |
273 | 284 |
274 if name is None: | 285 if name is None: |
275 # full cache is requested | 286 # full cache is requested |
276 return cache | 287 return cache |
277 | 288 |
278 if name in ('status', C.PRESENCE_STATUSES, C.PRESENCE_PRIORITY, C.PRESENCE_SHOW): | 289 if name in ("status", C.PRESENCE_STATUSES, C.PRESENCE_PRIORITY, C.PRESENCE_SHOW): |
279 # these data are related to the resource | 290 # these data are related to the resource |
280 if not entity.resource: | 291 if not entity.resource: |
281 main_resource = cache[C.CONTACT_MAIN_RESOURCE] | 292 main_resource = cache[C.CONTACT_MAIN_RESOURCE] |
282 if main_resource is None: | 293 if main_resource is None: |
283 # we ignore presence info if we don't have any resource in cache | 294 # we ignore presence info if we don't have any resource in cache |
285 return | 296 return |
286 cache = cache[C.CONTACT_RESOURCES].setdefault(main_resource, {}) | 297 cache = cache[C.CONTACT_RESOURCES].setdefault(main_resource, {}) |
287 else: | 298 else: |
288 cache = cache[C.CONTACT_RESOURCES].setdefault(entity.resource, {}) | 299 cache = cache[C.CONTACT_RESOURCES].setdefault(entity.resource, {}) |
289 | 300 |
290 if name == 'status': # XXX: we get the first status for 'status' key | 301 if name == "status": # XXX: we get the first status for 'status' key |
291 # TODO: manage main language for statuses | 302 # TODO: manage main language for statuses |
292 return cache[C.PRESENCE_STATUSES].get(C.PRESENCE_STATUSES_DEFAULT, '') | 303 return cache[C.PRESENCE_STATUSES].get(C.PRESENCE_STATUSES_DEFAULT, "") |
293 | 304 |
294 elif entity.resource: | 305 elif entity.resource: |
295 try: | 306 try: |
296 return cache[C.CONTACT_RESOURCES][entity.resource][name] | 307 return cache[C.CONTACT_RESOURCES][entity.resource][name] |
297 except KeyError: | 308 except KeyError: |
330 | 341 |
331 @param group: a valid (existing) group name | 342 @param group: a valid (existing) group name |
332 @param name: name of the data (can't be "jids") | 343 @param name: name of the data (can't be "jids") |
333 @param value: value to set | 344 @param value: value to set |
334 """ | 345 """ |
335 assert name is not 'jids' | 346 assert name is not "jids" |
336 self._groups[group][name] = value | 347 self._groups[group][name] = value |
337 | 348 |
338 def getGroupData(self, group, name=None): | 349 def getGroupData(self, group, name=None): |
339 """Return value associated to group data | 350 """Return value associated to group data |
340 | 351 |
386 @return (iter[jid.JID]): found special entities | 397 @return (iter[jid.JID]): found special entities |
387 """ | 398 """ |
388 for entity in self._specials: | 399 for entity in self._specials: |
389 if bare and entity.resource: | 400 if bare and entity.resource: |
390 continue | 401 continue |
391 if (special_type is not None | 402 if ( |
392 and self.getCache(entity, C.CONTACT_SPECIAL) != special_type): | 403 special_type is not None |
404 and self.getCache(entity, C.CONTACT_SPECIAL) != special_type | |
405 ): | |
393 continue | 406 continue |
394 yield entity | 407 yield entity |
395 | 408 |
396 def disconnect(self): | 409 def disconnect(self): |
397 # for now we just clear contacts on disconnect | 410 # for now we just clear contacts on disconnect |
438 was_visible = self.entityVisible(entity_bare) | 451 was_visible = self.entityVisible(entity_bare) |
439 | 452 |
440 if in_roster: | 453 if in_roster: |
441 self._roster.add(entity_bare) | 454 self._roster.add(entity_bare) |
442 | 455 |
443 cache = self._cache.setdefault(entity_bare, {C.CONTACT_RESOURCES: {}, | 456 cache = self._cache.setdefault( |
444 C.CONTACT_MAIN_RESOURCE: None, | 457 entity_bare, |
445 C.CONTACT_SELECTED: set()}) | 458 { |
459 C.CONTACT_RESOURCES: {}, | |
460 C.CONTACT_MAIN_RESOURCE: None, | |
461 C.CONTACT_SELECTED: set(), | |
462 }, | |
463 ) | |
446 | 464 |
447 # we don't want forbidden data in attributes | 465 # we don't want forbidden data in attributes |
448 assert not C.CONTACT_DATA_FORBIDDEN.intersection(attributes) | 466 assert not C.CONTACT_DATA_FORBIDDEN.intersection(attributes) |
449 | 467 |
450 # we set groups and fill self._groups accordingly | 468 # we set groups and fill self._groups accordingly |
452 if not groups: | 470 if not groups: |
453 groups = [None] # [None] is the default group | 471 groups = [None] # [None] is the default group |
454 if C.CONTACT_GROUPS in cache: | 472 if C.CONTACT_GROUPS in cache: |
455 # XXX: don't use set(cache[C.CONTACT_GROUPS]).difference(groups) because | 473 # XXX: don't use set(cache[C.CONTACT_GROUPS]).difference(groups) because |
456 # it won't work in Pyjamas if None is in cache[C.CONTACT_GROUPS] | 474 # it won't work in Pyjamas if None is in cache[C.CONTACT_GROUPS] |
457 for group in [group for group in cache[C.CONTACT_GROUPS] | 475 for group in [ |
458 if group not in groups]: | 476 group for group in cache[C.CONTACT_GROUPS] if group not in groups |
459 self._groups[group]['jids'].remove(entity_bare) | 477 ]: |
478 self._groups[group]["jids"].remove(entity_bare) | |
460 cache[C.CONTACT_GROUPS] = groups | 479 cache[C.CONTACT_GROUPS] = groups |
461 for group in groups: | 480 for group in groups: |
462 self._groups.setdefault(group, {}).setdefault('jids', set()).add( | 481 self._groups.setdefault(group, {}).setdefault("jids", set()).add( |
463 entity_bare) | 482 entity_bare |
483 ) | |
464 | 484 |
465 # special entities management | 485 # special entities management |
466 if C.CONTACT_SPECIAL in attributes: | 486 if C.CONTACT_SPECIAL in attributes: |
467 if attributes[C.CONTACT_SPECIAL] is None: | 487 if attributes[C.CONTACT_SPECIAL] is None: |
468 del attributes[C.CONTACT_SPECIAL] | 488 del attributes[C.CONTACT_SPECIAL] |
471 self._specials.add(entity) | 491 self._specials.add(entity) |
472 cache[C.CONTACT_MAIN_RESOURCE] = None | 492 cache[C.CONTACT_MAIN_RESOURCE] = None |
473 | 493 |
474 # now the attributes we keep in cache | 494 # now the attributes we keep in cache |
475 # XXX: if entity is a full jid, we store the value for the resource only | 495 # XXX: if entity is a full jid, we store the value for the resource only |
476 cache_attr = (cache[C.CONTACT_RESOURCES].setdefault(entity.resource, {}) | 496 cache_attr = ( |
477 if entity.resource else cache) | 497 cache[C.CONTACT_RESOURCES].setdefault(entity.resource, {}) |
498 if entity.resource | |
499 else cache | |
500 ) | |
478 for attribute, value in attributes.iteritems(): | 501 for attribute, value in attributes.iteritems(): |
479 if value is None: | 502 if value is None: |
480 # XXX: pyjamas hack: we need to use pop instead of del | 503 # XXX: pyjamas hack: we need to use pop instead of del |
481 try: | 504 try: |
482 cache_attr[attribute].pop(value) | 505 cache_attr[attribute].pop(value) |
483 except KeyError: | 506 except KeyError: |
484 pass | 507 pass |
485 else: | 508 else: |
486 if attribute == 'nick' and self.isSpecial(entity, C.CONTACT_SPECIAL_GROUP): | 509 if attribute == "nick" and self.isSpecial( |
510 entity, C.CONTACT_SPECIAL_GROUP | |
511 ): | |
487 # we don't want to keep nick for MUC rooms | 512 # we don't want to keep nick for MUC rooms |
488 # FIXME: this is here as plugin XEP-0054 can link resource's nick | 513 # FIXME: this is here as plugin XEP-0054 can link resource's nick |
489 # with bare jid which in the case of MUC | 514 # with bare jid which in the case of MUC |
490 # set the nick for the whole MUC | 515 # set the nick for the whole MUC |
491 # resulting in bad name displayed in some frontends | 516 # resulting in bad name displayed in some frontends |
516 | 541 |
517 if check_resource: | 542 if check_resource: |
518 selected = self._selected | 543 selected = self._selected |
519 else: | 544 else: |
520 selected = {selected.bare for selected in self._selected} | 545 selected = {selected.bare for selected in self._selected} |
521 return ((show is not None and show != C.PRESENCE_UNAVAILABLE) | 546 return ( |
522 or self.show_disconnected | 547 (show is not None and show != C.PRESENCE_UNAVAILABLE) |
523 or entity in selected | 548 or self.show_disconnected |
524 or (self.show_entities_with_notifs | 549 or entity in selected |
525 and next(self.host.getNotifs(entity.bare, profile=self.profile), None)) | 550 or ( |
526 ) | 551 self.show_entities_with_notifs |
552 and next(self.host.getNotifs(entity.bare, profile=self.profile), None) | |
553 ) | |
554 ) | |
527 | 555 |
528 def anyEntityVisible(self, entities, check_resources=False): | 556 def anyEntityVisible(self, entities, check_resources=False): |
529 """Tell if in a list of entities, at least one should be shown | 557 """Tell if in a list of entities, at least one should be shown |
530 | 558 |
531 @param entities (list[jid.JID]): list of jids | 559 @param entities (list[jid.JID]): list of jids |
562 self._roster.remove(entity_bare) | 590 self._roster.remove(entity_bare) |
563 except KeyError: | 591 except KeyError: |
564 pass | 592 pass |
565 del self._cache[entity_bare] | 593 del self._cache[entity_bare] |
566 for group in groups: | 594 for group in groups: |
567 self._groups[group]['jids'].remove(entity_bare) | 595 self._groups[group]["jids"].remove(entity_bare) |
568 if not self._groups[group]['jids']: | 596 if not self._groups[group]["jids"]: |
569 # FIXME: we use pop because of pyjamas: | 597 # FIXME: we use pop because of pyjamas: |
570 # http://wiki.goffi.org/wiki/Issues_with_Pyjamas/en | 598 # http://wiki.goffi.org/wiki/Issues_with_Pyjamas/en |
571 self._groups.pop(group) | 599 self._groups.pop(group) |
572 for iterable in (self._selected, self._specials): | 600 for iterable in (self._selected, self._specials): |
573 to_remove = set() | 601 to_remove = set() |
597 cache[C.CONTACT_MAIN_RESOURCE] = None | 625 cache[C.CONTACT_MAIN_RESOURCE] = None |
598 else: | 626 else: |
599 try: | 627 try: |
600 del cache[C.CONTACT_RESOURCES][entity.resource] | 628 del cache[C.CONTACT_RESOURCES][entity.resource] |
601 except KeyError: | 629 except KeyError: |
602 log.error(u"Presence unavailable received " | 630 log.error( |
603 u"for an unknown resource [{}]".format(entity)) | 631 u"Presence unavailable received " |
632 u"for an unknown resource [{}]".format(entity) | |
633 ) | |
604 if not cache[C.CONTACT_RESOURCES]: | 634 if not cache[C.CONTACT_RESOURCES]: |
605 cache[C.CONTACT_MAIN_RESOURCE] = None | 635 cache[C.CONTACT_MAIN_RESOURCE] = None |
606 else: | 636 else: |
607 if not entity.resource: | 637 if not entity.resource: |
608 log.warning(_(u"received presence from entity " | 638 log.warning( |
609 u"without resource: {}".format(entity))) | 639 _( |
640 u"received presence from entity " | |
641 u"without resource: {}".format(entity) | |
642 ) | |
643 ) | |
610 resources_data = cache[C.CONTACT_RESOURCES] | 644 resources_data = cache[C.CONTACT_RESOURCES] |
611 resource_data = resources_data.setdefault(entity.resource, {}) | 645 resource_data = resources_data.setdefault(entity.resource, {}) |
612 resource_data[C.PRESENCE_SHOW] = show | 646 resource_data[C.PRESENCE_SHOW] = show |
613 resource_data[C.PRESENCE_PRIORITY] = int(priority) | 647 resource_data[C.PRESENCE_PRIORITY] = int(priority) |
614 resource_data[C.PRESENCE_STATUSES] = statuses | 648 resource_data[C.PRESENCE_STATUSES] = statuses |
615 | 649 |
616 if entity.bare not in self._specials: | 650 if entity.bare not in self._specials: |
617 # we may have resources with no priority | 651 # we may have resources with no priority |
618 # (when a cached value is added for a not connected resource) | 652 # (when a cached value is added for a not connected resource) |
619 priority_resource = max(resources_data, | 653 priority_resource = max( |
620 key=lambda res: resources_data[res].get( | 654 resources_data, |
621 C.PRESENCE_PRIORITY, -2**32)) | 655 key=lambda res: resources_data[res].get( |
656 C.PRESENCE_PRIORITY, -2 ** 32 | |
657 ), | |
658 ) | |
622 cache[C.CONTACT_MAIN_RESOURCE] = priority_resource | 659 cache[C.CONTACT_MAIN_RESOURCE] = priority_resource |
623 if self.entityVisible(entity.bare): | 660 if self.entityVisible(entity.bare): |
624 update_type = C.UPDATE_MODIFY if was_visible else C.UPDATE_ADD | 661 update_type = C.UPDATE_MODIFY if was_visible else C.UPDATE_ADD |
625 self.update([entity], update_type, self.profile) | 662 self.update([entity], update_type, self.profile) |
626 elif was_visible: | 663 elif was_visible: |
632 @param entity(jid.JID): entity updated | 669 @param entity(jid.JID): entity updated |
633 @param new_nick(unicode): new nick of the entity | 670 @param new_nick(unicode): new nick of the entity |
634 @param profile: %(doc_profile)s | 671 @param profile: %(doc_profile)s |
635 """ | 672 """ |
636 assert profile == self.profile | 673 assert profile == self.profile |
637 self.setCache(entity, 'nick', new_nick) | 674 self.setCache(entity, "nick", new_nick) |
638 | 675 |
639 def onNotification(self, entity, notif, profile): | 676 def onNotification(self, entity, notif, profile): |
640 """Update entity with notification | 677 """Update entity with notification |
641 | 678 |
642 @param entity(jid.JID): entity updated | 679 @param entity(jid.JID): entity updated |
721 def update(self, entities=None, type_=None, profile=None): | 758 def update(self, entities=None, type_=None, profile=None): |
722 handler.update(entities, type_, profile) | 759 handler.update(entities, type_, profile) |
723 | 760 |
724 | 761 |
725 class QuickContactListHandler(object): | 762 class QuickContactListHandler(object): |
726 | |
727 def __init__(self, host): | 763 def __init__(self, host): |
728 super(QuickContactListHandler, self).__init__() | 764 super(QuickContactListHandler, self).__init__() |
729 self.host = host | 765 self.host = host |
730 global handler | 766 global handler |
731 if handler is not None: | 767 if handler is not None: |
732 raise exceptions.InternalError(u"QuickContactListHandler must be " | 768 raise exceptions.InternalError( |
733 u"instanciated only once") | 769 u"QuickContactListHandler must be " u"instanciated only once" |
770 ) | |
734 handler = self | 771 handler = self |
735 self._clist = {} # key: profile, value: ProfileContactList | 772 self._clist = {} # key: profile, value: ProfileContactList |
736 self._widgets = set() | 773 self._widgets = set() |
737 self._update_locked = False # se to True to ignore updates | 774 self._update_locked = False # se to True to ignore updates |
738 | 775 |
739 def __getitem__(self, profile): | 776 def __getitem__(self, profile): |
740 """Return ProfileContactList instance for the requested profile""" | 777 """Return ProfileContactList instance for the requested profile""" |
741 return self._clist[profile] | 778 return self._clist[profile] |
742 | 779 |
843 key: bare jid, value: data | 880 key: bare jid, value: data |
844 """ | 881 """ |
845 return self.items_sort(self.items) | 882 return self.items_sort(self.items) |
846 | 883 |
847 def items_sort(self, items): | 884 def items_sort(self, items): |
848 """sort items | 885 """sort items |
849 | 886 |
850 @param items(dict): items to sort (will be emptied !) | 887 @param items(dict): items to sort (will be emptied !) |
851 @return (OrderedDict): sorted items | 888 @return (OrderedDict): sorted items |
852 """ | 889 """ |
853 ordered_items = OrderedDict() | 890 ordered_items = OrderedDict() |
854 bare_jids = sorted(items.keys()) | 891 bare_jids = sorted(items.keys()) |
855 for jid_ in bare_jids: | 892 for jid_ in bare_jids: |
856 ordered_items[jid_] = items.pop(jid_) | 893 ordered_items[jid_] = items.pop(jid_) |
857 return ordered_items | 894 return ordered_items |
858 | 895 |
859 def register(self, widget): | 896 def register(self, widget): |
860 """Register a QuickContactList widget | 897 """Register a QuickContactList widget |
861 | 898 |
862 This method should only be used in QuickContactList | 899 This method should only be used in QuickContactList |
938 else: | 975 else: |
939 to_fill.update(self._clist.items()) | 976 to_fill.update(self._clist.items()) |
940 | 977 |
941 remaining = to_fill.difference(filled) | 978 remaining = to_fill.difference(filled) |
942 if remaining != to_fill: | 979 if remaining != to_fill: |
943 log.debug(u"Not re-filling already filled contact list(s) for {}".format( | 980 log.debug( |
944 u', '.join(to_fill.intersection(filled)))) | 981 u"Not re-filling already filled contact list(s) for {}".format( |
982 u", ".join(to_fill.intersection(filled)) | |
983 ) | |
984 ) | |
945 for profile in remaining: | 985 for profile in remaining: |
946 self._clist[profile]._fill() | 986 self._clist[profile]._fill() |
947 | 987 |
948 def clearContacts(self, keep_cache=False): | 988 def clearContacts(self, keep_cache=False): |
949 """Clear all the contact list | 989 """Clear all the contact list |
971 @param locked(bool): updates are forbidden if True | 1011 @param locked(bool): updates are forbidden if True |
972 @param do_update(bool): if True, a full update is done after unlocking | 1012 @param do_update(bool): if True, a full update is done after unlocking |
973 if set to False, widget state can be inconsistent, be sure to know | 1013 if set to False, widget state can be inconsistent, be sure to know |
974 what youa re doing! | 1014 what youa re doing! |
975 """ | 1015 """ |
976 log.debug(u"Contact lists updates are now {}".format( | 1016 log.debug( |
977 u"LOCKED" if locked else u"UNLOCKED")) | 1017 u"Contact lists updates are now {}".format( |
1018 u"LOCKED" if locked else u"UNLOCKED" | |
1019 ) | |
1020 ) | |
978 self._update_locked = locked | 1021 self._update_locked = locked |
979 if not locked and do_update: | 1022 if not locked and do_update: |
980 self.update() | 1023 self.update() |
981 | 1024 |
982 def update(self, entities=None, type_=None, profile=None): | 1025 def update(self, entities=None, type_=None, profile=None): |
985 widget.update(entities, type_, profile) | 1028 widget.update(entities, type_, profile) |
986 | 1029 |
987 | 1030 |
988 class QuickContactList(QuickWidget): | 1031 class QuickContactList(QuickWidget): |
989 """This class manage the visual representation of contacts""" | 1032 """This class manage the visual representation of contacts""" |
990 SINGLE=False | 1033 |
991 PROFILES_MULTIPLE=True | 1034 SINGLE = False |
1035 PROFILES_MULTIPLE = True | |
992 # Can be linked to no profile (e.g. at the early frontend start) | 1036 # Can be linked to no profile (e.g. at the early frontend start) |
993 PROFILES_ALLOW_NONE=True | 1037 PROFILES_ALLOW_NONE = True |
994 | 1038 |
995 def __init__(self, host, profiles): | 1039 def __init__(self, host, profiles): |
996 super(QuickContactList, self).__init__(host, None, profiles) | 1040 super(QuickContactList, self).__init__(host, None, profiles) |
997 | 1041 |
998 # options | 1042 # options |
999 # for next values, None means use indivual value per profile | 1043 # for next values, None means use indivual value per profile |
1000 # True or False mean override these values for all profiles | 1044 # True or False mean override these values for all profiles |
1001 self.show_disconnected = None # TODO | 1045 self.show_disconnected = None # TODO |
1002 self.show_empty_groups = None # TODO | 1046 self.show_empty_groups = None # TODO |
1003 self.show_resources = None # TODO | 1047 self.show_resources = None # TODO |
1004 self.show_status = None # TODO | 1048 self.show_status = None # TODO |
1005 | 1049 |
1006 def postInit(self): | 1050 def postInit(self): |
1007 """Method to be called by frontend after widget is initialised""" | 1051 """Method to be called by frontend after widget is initialised""" |
1008 handler.register(self) | 1052 handler.register(self) |
1009 | 1053 |