MYAddressField.m
changeset 29 8874aff14cc9
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/MYAddressField.m	Sun May 10 18:57:43 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