""" Contacts Listbox with filtering support """ # Copyright (C) 2004 Henning Jacobs # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # $Id: ContactListWidget.py 92 2004-11-28 15:34:44Z henning $ from Tkinter import * import Pmw import InputWidgets import ToolTip import string import Set import broadcaster import Preferences class ContactListWidget(Frame): DISPLAYFIELD = 'DisplayName' SORTBY = 'SortName' def __init__(self, master, model, **kws): Frame.__init__(self, master) self.model = model self.selectcommand = kws.get('selectcommand', None) self.dblclickcommand = kws.get('dblclickcommand', None) self.categories = Set.Set("All", "No Category") dispfield = Preferences.get('client.contactlist_displayfield') if dispfield: self.DISPLAYFIELD = dispfield sortby = Preferences.get('client.contactlist_sortby') if sortby: self.SORTBY = sortby self.selFilter = InputWidgets.SearchableCombobox(self, command=self.applyFilter, label_text="Filter:") self.selFilter.pack() ToolTip.ToolTip(self.selFilter, "filter contact list by category") kws = {} if sys.platform != 'win32': # Thin Scrollbars: kws['horizscrollbar_borderwidth'] = 1 kws['horizscrollbar_width'] = 10 kws['vertscrollbar_borderwidth'] = 1 kws['vertscrollbar_width'] = 10 self.listbox = Pmw.ScrolledListBox(self, scrollmargin=0, selectioncommand=self._listboxClick, dblclickcommand=self._listboxDblClick, listbox_highlightthickness=0, **kws) self.listbox.pack(fill=BOTH,expand=TRUE) self.registerAtBroadcaster() def registerAtBroadcaster(self): "Register our Widget's Callback Handlers" broadcaster.Register(self.updateList, source='Contacts', title='Opened') broadcaster.Register(self.updateList, source='Contacts', title='Closed') # Import Hook not needed because New and Save will do the job: #broadcaster.Register(self.updateList, # source='Contacts', title='Imported') broadcaster.Register(self.onContactSave, source='Contact', title='Saved') broadcaster.Register(self.onContactNew, source='Contact', title='Added') broadcaster.Register(self.onContactDel, source='Contact', title='Deleted') def getUpdatedContactData(self): """Returns handle, DisplayName and Categories of the modified contact Call only from a broadcaster callback function""" handle = broadcaster.CurrentData()['handle'] if broadcaster.CurrentTitle() != "Deleted": attrlist = self.model.QueryAttributes([handle], (self.DISPLAYFIELD,'Categories')) return (handle, attrlist[0]) else: return (handle, None) def onContactNew(self): handle, attr = self.getUpdatedContactData() self._contacthandles.insert(0, handle) self._contactdispnames.insert(0, attr[0]) self._contactcategories.insert(0, attr[1]) self._filteredhandles.insert(0, handle) self._filtereddispnames.insert(0, attr[0]) self.buildCategories() self.applyFilter() # Do not do selectContact(handle) now, # because we don't know if the contact is in our filtered list. def onContactDel(self): handle, attr = self.getUpdatedContactData() idx = self._contacthandles.index(handle) del self._contacthandles[idx] del self._contactdispnames[idx] del self._contactcategories[idx] try: filtidx = self._filteredhandles.index(handle) del self._filteredhandles[filtidx] del self._filtereddispnames[filtidx] except ValueError: pass self.applyFilter() def onContactSave(self): handle, attr = self.getUpdatedContactData() idx = self._contacthandles.index(handle) self._contactdispnames[idx] = attr[0] self._contactcategories[idx] = attr[1] # Now get the sorted list from the server: srvidx = self.model.ListHandles(self.SORTBY).index(handle) if idx != srvidx: def move(list, x, y): temp = list[x]; del list[x] list.insert(y, temp) # and move our saved contact to it's sorted place: move(self._contacthandles, idx, srvidx) move(self._contactdispnames, idx, srvidx) move(self._contactcategories, idx, srvidx) self.buildCategories() self.applyFilter() self.selectContact(handle) def _listboxClick(self): "Event triggered by clicking on the listbox" sel = self.listbox.curselection() if sel and self.selectcommand: self.selectcommand(self._filteredhandles[int(sel[0])]) def _listboxDblClick(self): "Event triggered by double-clicking on the listbox" sel = self.listbox.curselection() if sel and self.dblclickcommand: self.dblclickcommand(self._filteredhandles[int(sel[0])]) currentFilter = "None" def getCurrentCategory(self): """If a card wants to appear in our filtered list it must include the category returned by this function in it's 'categories' field""" ret = self.currentFilter if ret == "All" or ret == "No Category": ret = "" return ret def applyFilter(self, filter=None): "If called with no arguments the filter will be unchanged" if filter is None: filter = self.currentFilter else: self.currentFilter = filter if filter == "All": self._filteredhandles[:] = self._contacthandles self._filtereddispnames[:] = self._contactdispnames else: self._filteredhandles = [] self._filtereddispnames = [] for str, dispname, handle in zip(self._contactcategories, self._contactdispnames, self._contacthandles): if filter == "No Category": if str.strip() == "": self._filteredhandles.append(handle) self._filtereddispnames.append(dispname) else: if str.find(filter) != -1: self._filteredhandles.append(handle) self._filtereddispnames.append(dispname) self.listbox.setlist(self._filtereddispnames) self.selFilter.set(filter) _filteredhandles = [] _filtereddispnames = [] _contacthandles = [] _contactdispnames = [] def updateList(self): "Completely update our list" self._contacthandles[:] = self.model.ListHandles(self.SORTBY) attrlist = self.model.QueryAttributes(self._contacthandles, (self.DISPLAYFIELD,'Categories')) self._contactdispnames = [dispname for dispname, cats in attrlist] self._contactcategories = [cats for dispname, cats in attrlist] self.buildCategories() self.applyFilter("All") broadcaster.Broadcast('Notification', 'Status', {'message':'%d Contacts loaded.' % (len(self._contacthandles[:]),)}) def buildCategories(self): "Update our categories set" for str in self._contactcategories: cats = filter(None, map(string.strip, str.split(','))) for cat in cats: self.categories.add(cat) self.selFilter.setlist(zip(self.categories.items(),self.categories.items())) def selectContact(self, handle): "Set the listbox selection" try: idx = self._filteredhandles.index(handle) self.listbox.selection_clear() self.listbox.selection_set(idx) except ValueError: # The Contact does not appear in our filtered list! idx = 0 self.listbox.selection_clear() self.listbox.see(idx)