def update_order(self): 'Sends order to the sorter.' if hasattr(self, 'new_sorter'): order = deepcopy(self.order['contacts']) order['__groups__'] = self.order['groups'] on_thread('sorter').call(self.new_sorter.set_contact_order, order)
def search(self, s, cb = None): if not hasattr(self, 'new_sorter'): return assert isinstance(s, basestring) self._search_by = s self.reconfig_sorter(rebuild=False) self.rebuild_now() if cb is not None: on_thread('sorter').call(lambda: wx.CallAfter(cb, self._search_results))
def _on_account_offline(self, src): ''' Notifies the buddylist sorter than an account is now offline. ''' sorter = getattr(self.profile.blist, 'new_sorter', None) if sorter is None: return if is_im_account(src) or src is self.profile: log.info('informing the sorter that (%r, %r) went offline', src.username, src.protocol) on_thread('sorter').call(sorter.removeAccount, src.username, src.protocol)
def remove(self, mc, contact, explode=True, cleanup=True): if isinstance(mc, int): mcname = self.idstr(mc) if mc not in self: return contacts = self[mc].buddies else: mcname = mc.name contacts = mc.mcinfo.buddies log.info('removing %r from metacontact %r', contact, mcname) cd = contact_dict(contact) for d in list(contacts): if cd['name'] == d.name and cd['service'] == d.service: contacts.remove(d) # Contact inherits certain properties from the MetaContact. get, set = self.blist.get_contact_info, self.blist.set_contact_info sms, email = get(mcname, 'sms'), get(mcname, 'email') contact_obj = self.blist.contact_for_id(contact) if isinstance( contact, basestring) else contact def tolist(s): if isinstance(s, basestring): return [s] # "inherit" email addresses and sms numbers from the metacontact. if sms: new_sms = removedupes((tolist(get(mcname, 'sms')) or []) + sms) set(contact_obj, 'sms', new_sms) if email: new_email = removedupes((tolist(get(mcname, 'email')) or []) + email) set(contact_obj, 'email', new_email) info('removed %r from %r', contact, mc) if hasattr(self.blist, 'new_sorter'): on_thread('sorter').call(self.blist.new_sorter.removeContact, mcname) self.rebuild_buddy_mapping() self.blist._info_changed() if explode and len(contacts) == 1: self.remove(mc, contacts[0], True) self.remove_mc_entry(mc) elif explode and len(contacts) == 0: pass else: if cleanup: self.blist.rebuild()
def remove(self, mc, contact, explode = True, cleanup = True): if isinstance(mc, int): mcname = self.idstr(mc) if mc not in self: return contacts = self[mc].buddies else: mcname = mc.name contacts = mc.mcinfo.buddies log.info('removing %r from metacontact %r', contact, mcname) cd = contact_dict(contact) for d in list(contacts): if cd['name'] == d.name and cd['service'] == d.service: contacts.remove(d) # Contact inherits certain properties from the MetaContact. get, set = self.blist.get_contact_info, self.blist.set_contact_info sms, email = get(mcname, 'sms'), get(mcname, 'email') contact_obj = self.blist.contact_for_id(contact) if isinstance(contact, basestring) else contact def tolist(s): if isinstance(s, basestring): return [s] # "inherit" email addresses and sms numbers from the metacontact. if sms: new_sms = removedupes((tolist(get(mcname, 'sms')) or []) + sms) set(contact_obj, 'sms', new_sms) if email: new_email = removedupes((tolist(get(mcname, 'email')) or []) + email) set(contact_obj, 'email', new_email) info('removed %r from %r', contact, mc) if hasattr(self.blist, 'new_sorter'): on_thread('sorter').call(self.blist.new_sorter.removeContact, mcname) self.rebuild_buddy_mapping() self.blist._info_changed() if explode and len(contacts) == 1: self.remove(mc, contacts[0], True) self.remove_mc_entry(mc) elif explode and len(contacts) == 0: pass else: if cleanup: self.blist.rebuild()
def rebuild(self, *a): 'Triggers a full buddylist update.' if on_thread('sorter').now: # sorter thread should never trigger a rebuild return self.dirty = True
def resort(self, mock = False): assert on_thread('sorter').now rootgroups = [display_copy(g) for g in self.rootgroups if isinstance(g, GroupTypes)] self.personalities = self.track_personalities(rootgroups) metacontacts = self.safe_metacontacts(rootgroups) # Always collect metacontacts, but exit early here if sorting is paused. if self.sorting_paused:# or not profile.prefs_loaded: return metrics.event('Buddylist Sort') self._setup_blist_sorter() # invalidate all sorter knowledge of contacts. # results in more CPU usage, but until we put metacontact combining into the sorter # this might be necessary. self.new_sorter.removeAllContacts() newroots = rootgroups[:] + [metacontacts] for i, root in enumerate(newroots): root.name = "Root" + str(i) root._root = True root = DGroup('none', [], [], newroots) if mock: self.mock_root = make_mocklist(root) self.new_sorter.set_root(root) view = get_view_from_sorter(self.new_sorter) if getattr(self, '_search_by', ''): if len(view) > 0: contacts_group = view[0] # don't allow renaming, etc of the search "Contacts" group contacts_group._disallow_actions = True num_contacts = len(contacts_group) else: num_contacts = -1 self._search_results = self._search_results[1], num_contacts else: if pref('buddylist.hide_offline_dependant', False, bool): hide_offline_groups = not pref('buddylist.show_offline') and pref('buddylist.hide_offline_groups') else: hide_offline_groups = pref('buddylist.hide_offline_groups') if hide_offline_groups: view[:] = filter((lambda g: not offline_nonempty_group_re.match(g.display_string)), view) for g in view: remove_duplicate_contacts(g) self.add_search_entries(view) hooks.notify('buddylist.sorted', view) return view
def set_new(self, contacts, hidden_contacts): if self[:] != contacts or self.hidden != hidden_contacts: self[:] = contacts self.hidden[:] = hidden_contacts self.meta_dirty = True # when the contents of a MetaContact changes, the sorter needs to invalidate # it's knowledge of it sorter = getattr(self.manager.blist, 'new_sorter', None) if sorter is not None: def invalidate_sorter_contact(): sorter.removeContact(self.name) if on_thread('sorter').now: # if called from resort->collect, this is already true, invalidate_sorter_contact() else: # but sometimes we call it on the console for testing on_thread('sorter').call(invalidate_sorter_contact)
def _setup_blist_sorter(self): if hasattr(self, 'new_sorter'): return blist.set_group_type((DGroup, Group, MockGroup)) self.new_sorter = blist.BuddyListSorter() self.update_order() # link prefs cb = self._on_blist_sort_pref link = profile.prefs.link for prefname in ('buddylist.fakeroot_name', 'buddylist.show_offline', 'buddylist.group_offline', 'buddylist.show_mobile', 'buddylist.hide_offline_groups', 'buddylist.sortby'): link(prefname, cb) cb() assert on_thread('sorter').now self._reconfig_sorter(False)
def get_sorter(self): # TODO return 'New sorter diagnostic needs to happen on the sorter thread; plz impl on_thread("sorter").blocking_call kthx' sorter = profile.blist.new_sorter from util.threads.bgthread import on_thread def later(): s = ''.join(['expanded root:\n\n', sorter.dump_root(), '\n\ngathered tree:\n\n', sorter.dump_gather()]) s += '\n\npython tree:\n\n' from contacts.buddyliststore import dump_elem_tree root = sorter._gather() try: s += dump_elem_tree(root) finally: sorter._done_gather(root) # TODO: blocking_call not implemented return on_thread('sorter').blocking_call(later)
def update_data(self, data): """ Updates this store's current state with incoming data from the network. data should be a mapping containing 'metacontacts', 'order', and 'info' structures (see comment at top of file) """ rebuild = False # This method needs to substitute some defaultdicts for the normal # dictionaries that come back from the server. # Metacontact information #if data['metacontacts'] mc_dict = data.get('metacontacts', {}) if not isinstance(mc_dict, dict): log.critical('invalid metacontacts dictionary') mc_dict = {} # Contact information like SMS numbers and email addresses. self.info = defaultdict(dict) si = self.info if 'info' in data: for (k, v) in data['info'].iteritems(): if isinstance(k, str): cmpk = k.decode('utf8') else: cmpk = k if not isinstance(cmpk, unicode): continue if cmpk.startswith('Meta') or any((cmpk.endswith('_' + prot) for prot in protocols.iterkeys())): if any(v.values()): si[k] = v for c, v in si.iteritems(): for attr in ('email', 'sms'): if attr in v: self.contact_info_changed(c, attr, v[attr]) self.metacontacts = MetaContactManager(self, mc_dict) if hasattr(self, 'new_sorter'): on_thread('sorter').call(self.new_sorter.removeAllContacts) rebuild = True # Manual ordering of groups try: self.order = deepcopy(data['order']) self.order['groups'] = list(oset(self.order['groups'])) contacts = self._filtered_contacts() self.order['contacts'] = defaultdict(list) self.order['contacts'].update(contacts) except Exception: log.critical('error receiving order') self._init_order() # note: loading tofrom data from the network is deprecated. this data # now goes out to disk. see save/load_local_data if 'tofrom' in data and isinstance(data['tofrom'], dict) and \ 'im' in data['tofrom'] and 'email' in data['tofrom']: self.dispatch.set_tofrom(deepcopy(data['tofrom'])) if rebuild: self.rebuild() self.update_order()