1from Cocoa import * 2from AddressBook import * 3from InstantMessage import * 4 5from ServiceWatcher import * 6 7class PeopleDataSource (NSObject): 8 _abPeople = objc.ivar() 9 _imPersonStatus = objc.ivar() # Parallel array to abPeople 10 _table = objc.IBOutlet() 11 12 def awakeFromNib(self): 13 self._imPersonStatus = NSMutableArray.alloc().init() 14 15 # We don't need to query the staus of everyone, we will wait for 16 # notifications of their status to arrive, so we just set them all up 17 # as offline. 18 self.setupABPeople() 19 20 NSNotificationCenter.defaultCenter().addObserver_selector_name_object_( 21 self, 'abDatabaseChangedExternallyNotification:', 22 kABDatabaseChangedExternallyNotification, None) 23 24 NSNotificationCenter.defaultCenter().addObserver_selector_name_object_( 25 self, 'addressBookPersonStatusChanged:', 26 kAddressBookPersonStatusChanged, None) 27 28 NSNotificationCenter.defaultCenter().addObserver_selector_name_object_( 29 self, 'statusImagesChanged:', 30 kStatusImagesChanged, None) 31 32 # This dumps all the status information and rebuilds the array against 33 # the current _abPeople 34 # Fairly expensive, so this is only done when necessary 35 def rebuildStatusInformation(self): 36 # Now scan through all the people, adding their status to the status 37 # cache array 38 for i, person in enumerate(self._abPeople): 39 # Let's assume they're offline to start 40 bestStatus = IMPersonStatusOffline 41 42 for service in IMService.allServices(): 43 screenNames = service.screenNamesForPerson_(person) 44 45 for screenName in screenNames: 46 dictionary = service.infoForScreenName_( 47 screenName) 48 status = dictionary.get(IMPersonStatusKey) 49 if status is not None: 50 thisStatus = status 51 if IMComparePersonStatus(bestStatus, thisStatus) != NSOrderedAscending: 52 bestStatus = thisStatus; 53 54 self._imPersonStatus[i] = bestStatus 55 56 self._table.reloadData() 57 58 # Rebuild status information for a given person, much faster than a full 59 # rebuild 60 def rebuildStatusInformationForPerson_(self, forPerson): 61 for i, person in enumerate(self._abPeople): 62 if person is forPerson: 63 bestStatus = IMPersonStatusOffline 64 65 # Scan through all the services, taking the 'best' status we 66 # can find 67 for service in IMService.allServices(): 68 screenNames = service.screenNamesForPerson_(person) 69 70 # Ask for the status on each of their screen names 71 for screenName in screenNames: 72 dictionary = service.infoForScreenName_(screenName) 73 status = dictionary.get(IMPersonStatusKey) 74 if status is not None: 75 thisStatus = status 76 if IMComparePersonStatus(bestStatus, thisStatus) != NSOrderedAscending: 77 bestStatus = thisStatus 78 79 self._imPersonStatus[i] = bestStatus 80 self._table.reloadData() 81 break 82 83 # Sets up all our internal data 84 def setupABPeople(self): 85 # Keep around a copy of all the people in the AB now 86 self._abPeople = ABAddressBook.sharedAddressBook().people().mutableCopy() 87 88 # Sort them by display name 89 self._abPeople.sortUsingSelector_('compareDisplayNames:') 90 91 # Assume everyone is offline. 92 self._imPersonStatus.removeAllObjects() 93 offlineNumber = IMPersonStatusOffline 94 for i in range(len(self._abPeople)): 95 self._imPersonStatus.append(offlineNumber) 96 97 # This will do a full flush of people in our AB Cache, along with 98 # rebuilding their status */ 99 def reloadABPeople(self): 100 self.setupABPeople() 101 102 # Now recache all the status info, this will spawn a reload of the table 103 self.rebuildStatusInformation() 104 105 def numberOfRowsInTableView_(self, tableView): 106 if self._abPeople is None: 107 return 0 108 return len(self._abPeople) 109 110 def tableView_objectValueForTableColumn_row_(self, tableView, tableColumn, row): 111 identifier = tableColumn.identifier() 112 if identifier == u"image": 113 status = self._imPersonStatus[row] 114 return NSImage.imageNamed_(IMService.imageNameForStatus_(status)) 115 116 elif identifier == u"name": 117 return self._abPeople[row].displayName() 118 119 return None 120 121 122 # Posted from ServiceWatcher 123 # The object of this notification is an ABPerson who's status has 124 # Changed 125 def addressBookPersonStatusChanged_(self, notification): 126 self.rebuildStatusInformationForPerson_(notification.object()) 127 128 # Posted from ServiceWatcher 129 # We should reload the tableview, because the user has changed the 130 # status images that Messages is using. 131 def statusImagesChanged_(self, notification): 132 self._table.reloadData() 133 134 # If the AB database changes, force a reload of everyone 135 # We could look in the notification to catch differential updates, but 136 # for now this is fine. 137 def abDatabaseChangedExternallyNotification_(self, notification): 138 self.reloadABPeople() 139