1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/MYAddressField.m Wed Apr 08 16:31:19 2009 -0700
1.3 @@ -0,0 +1,223 @@
1.4 +//
1.5 +// MYAddressField.m
1.6 +// YourMove
1.7 +//
1.8 +// Created by Jens Alfke on 7/16/08.
1.9 +// Copyright 2008 Jens Alfke. All rights reserved.
1.10 +//
1.11 +
1.12 +#import "MYAddressField.h"
1.13 +#import "RegexKitLite.h"
1.14 +#import <AddressBook/AddressBook.h>
1.15 +
1.16 +
1.17 +@interface MYAddressField ()
1.18 +@property (retain) MYAddressItem *selectedAddress;
1.19 +@end
1.20 +
1.21 +
1.22 +@implementation MYAddressField
1.23 +
1.24 +
1.25 +@synthesize defaultAddresses=_defaultAddresses, addressProperty=_property, selectedAddress=_selectedAddress;
1.26 +
1.27 +
1.28 +- (void) _computeAddresses
1.29 +{
1.30 + NSMutableArray *newAddresses = $marray();
1.31 + if( _property ) {
1.32 + if( _prefix.length ) {
1.33 + // Find all the people in the address book matching _prefix:
1.34 + ABAddressBook *ab = [ABAddressBook sharedAddressBook];
1.35 + ABSearchElement *search = [ABPerson searchElementForProperty: _property
1.36 + label: nil
1.37 + key: nil
1.38 + value: nil
1.39 + comparison: kABNotEqual];
1.40 + for( ABPerson *person in [ab recordsMatchingSearchElement: search] ) {
1.41 + ABMultiValue *values = [person valueForProperty: _property];
1.42 + NSString *first = [person valueForProperty: kABFirstNameProperty];
1.43 + NSString *last = [person valueForProperty: kABLastNameProperty];
1.44 + BOOL nameMatches = _prefix==nil || ([first.lowercaseString hasPrefix: _prefix]
1.45 + || [last.lowercaseString hasPrefix: _prefix]);
1.46 + for( int i=0; i<values.count; i++ ) {
1.47 + NSString *address = [values valueAtIndex: i];
1.48 + if( nameMatches || [address.lowercaseString hasPrefix: _prefix] ) {
1.49 + MYAddressItem *item = [[MYAddressItem alloc] initWithPerson: person
1.50 + addressType: _property
1.51 + address: address];
1.52 + [newAddresses addObject: item];
1.53 + [item release];
1.54 + }
1.55 + }
1.56 + }
1.57 + } else if( _defaultAddresses ) {
1.58 + [newAddresses addObjectsFromArray: _defaultAddresses];
1.59 + }
1.60 + }
1.61 +
1.62 + [newAddresses sortUsingSelector: @selector(compare:)];
1.63 +
1.64 + if( ifSetObj(&_addresses,newAddresses) )
1.65 + [self reloadData];
1.66 +}
1.67 +
1.68 +- (NSArray*) addresses
1.69 +{
1.70 + if( ! _addresses )
1.71 + [self _computeAddresses];
1.72 + return _addresses;
1.73 +}
1.74 +
1.75 +
1.76 +- (void) awakeFromNib
1.77 +{
1.78 + if( ! _addresses )
1.79 + _addresses = [[NSMutableArray alloc] init];
1.80 + _property = [kABEmailProperty retain];
1.81 + self.completes = NO;
1.82 + self.usesDataSource = YES;
1.83 + self.dataSource = self;
1.84 + self.delegate = self;
1.85 +}
1.86 +
1.87 +- (void) dealloc
1.88 +{
1.89 + [_addresses release];
1.90 + [_property release];
1.91 + [_prefix release];
1.92 + [_selectedAddress release];
1.93 + [super dealloc];
1.94 +}
1.95 +
1.96 +
1.97 +- (BOOL) isExpanded
1.98 +{
1.99 + id ax = NSAccessibilityUnignoredDescendant(self);
1.100 + return [[ax accessibilityAttributeValue: NSAccessibilityExpandedAttribute] boolValue];
1.101 +}
1.102 +
1.103 +
1.104 +- (void) setExpanded: (BOOL)expanded
1.105 +{
1.106 + id ax = NSAccessibilityUnignoredDescendant(self);
1.107 + [ax accessibilitySetValue: $object(expanded) forAttribute: NSAccessibilityExpandedAttribute];
1.108 +}
1.109 +
1.110 +
1.111 +- (void) controlTextDidChange: (NSNotification*)n
1.112 +{
1.113 + if( _prefix.length == 0 )
1.114 + self.expanded = YES;
1.115 +
1.116 + if( ifSetObj(&_prefix, self.stringValue.lowercaseString) )
1.117 + [self _computeAddresses];
1.118 + MYAddressItem *item = [[MYAddressItem alloc] initWithString: self.stringValue
1.119 + addressType: _property];
1.120 + self.selectedAddress = item;
1.121 + [item release];
1.122 +
1.123 + if( _prefix.length == 0 )
1.124 + self.expanded = NO;
1.125 +
1.126 + //Log(@"Address selection = %@",self.selectedAddress);
1.127 +}
1.128 +
1.129 +- (void)comboBoxSelectionDidChange:(NSNotification *)notification
1.130 +{
1.131 + int sel = self.indexOfSelectedItem;
1.132 + self.selectedAddress = sel>=0 ?[self.addresses objectAtIndex: sel] :nil;
1.133 + //Log(@"Address selection = %@",self.selectedAddress);
1.134 +}
1.135 +
1.136 +
1.137 +#pragma mark -
1.138 +#pragma mark DATA SOURCE:
1.139 +
1.140 +
1.141 +- (NSInteger)numberOfItemsInComboBox:(NSComboBox *)aComboBox
1.142 +{
1.143 + return self.addresses.count;
1.144 +}
1.145 +
1.146 +- (id)comboBox:(NSComboBox *)aComboBox objectValueForItemAtIndex:(NSInteger)index
1.147 +{
1.148 + return [[self.addresses objectAtIndex: index] description];
1.149 +}
1.150 +
1.151 +
1.152 +@end
1.153 +
1.154 +
1.155 +
1.156 +
1.157 +@implementation MYAddressItem
1.158 +
1.159 +- (id) initWithName: (NSString*)name
1.160 + addressType: (NSString*)addressType address: (NSString*)address
1.161 +{
1.162 + self = [super init];
1.163 + if( self ) {
1.164 + _name = name.length ?[name copy] :nil;
1.165 + _addressType = [addressType copy];
1.166 + _address = [address copy];
1.167 + }
1.168 + return self;
1.169 +}
1.170 +
1.171 +- (id) initWithPerson: (ABPerson*)person
1.172 + addressType: (NSString*)addressType address: (NSString*)address
1.173 +{
1.174 + NSString *first = [person valueForProperty: kABFirstNameProperty] ?: @"";
1.175 + NSString *last = [person valueForProperty: kABLastNameProperty] ?: @"";
1.176 + NSString *name = $sprintf(@"%@ %@", first,last);
1.177 +
1.178 + self = [self initWithName: name addressType: addressType address: address];
1.179 + if( self )
1.180 + _uuid = person.uniqueId.copy;
1.181 + return self;
1.182 +}
1.183 +
1.184 +- (id) initWithString: (NSString*)str addressType: (NSString*)addressType
1.185 +{
1.186 + #define kJustAddrRegex "[-a-zA-Z0-9%_+.]+(?:@[-a-zA-Z0-9.]+)"
1.187 + static NSString* const kNameAndAddrRegex = @"^\\s*(\\S+)?\\s*<("kJustAddrRegex")>\\s*$";
1.188 + static NSString* const kAddrRegex = @"^\\s*("kJustAddrRegex")\\s*$";
1.189 +
1.190 + NSString *name = nil;
1.191 + NSString *address = [str stringByMatching: kNameAndAddrRegex capture: 2];
1.192 + if( address ) {
1.193 + name = [str stringByMatching: kNameAndAddrRegex capture: 1];
1.194 + } else {
1.195 + address = [str stringByMatching: kAddrRegex];
1.196 + }
1.197 + if( ! address ) {
1.198 + [self release];
1.199 + return nil;
1.200 + }
1.201 + return [self initWithName: name addressType: addressType address: address];
1.202 +}
1.203 +
1.204 +@synthesize name=_name, addressType=_addressType, address=_address;
1.205 +
1.206 +- (ABPerson*) person
1.207 +{
1.208 + if( _uuid )
1.209 + return (ABPerson*) [[ABAddressBook sharedAddressBook] recordForUniqueId: _uuid];
1.210 + else
1.211 + return nil;
1.212 +}
1.213 +
1.214 +- (NSString*) description
1.215 +{
1.216 + return $sprintf(@"%@%@<%@>", _name,(_name ?@" ":@""),_address);
1.217 +}
1.218 +
1.219 +- (NSComparisonResult) compare: (MYAddressItem*)other
1.220 +{
1.221 + NSString *str1 = _name ?:_address;
1.222 + NSString *str2 = other->_name ?: other->_address;
1.223 + return [str1 localizedCaseInsensitiveCompare: str2];
1.224 +}
1.225 +
1.226 +@end