Initial checkin of MYAddressField, a combo-box for entering email or IM addresses.
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/MYAddressField.h Thu Jul 17 13:29:34 2008 -0700
1.3 @@ -0,0 +1,43 @@
1.4 +//
1.5 +// MYAddressField.h
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 <Cocoa/Cocoa.h>
1.13 +@class MYAddressItem, ABPerson;
1.14 +
1.15 +
1.16 +@interface MYAddressField : NSComboBox
1.17 +{
1.18 + NSString *_property, *_prefix;
1.19 + NSMutableArray *_addresses;
1.20 + NSArray *_defaultAddresses;
1.21 + MYAddressItem *_selectedAddress;
1.22 +}
1.23 +
1.24 +@property (getter=isExpanded) BOOL expanded;
1.25 +
1.26 +@property (copy) NSString *addressProperty;
1.27 +@property (copy) NSArray *defaultAddresses;
1.28 +
1.29 +@property (readonly,retain) MYAddressItem* selectedAddress;
1.30 +
1.31 +@end
1.32 +
1.33 +
1.34 +
1.35 +@interface MYAddressItem : NSObject
1.36 +{
1.37 + NSString *_name, *_addressType, *_address, *_uuid;
1.38 +}
1.39 +- (id) initWithName: (NSString*)name
1.40 + addressType: (NSString*)addressType address: (NSString*)address;
1.41 +- (id) initWithPerson: (ABPerson*)person
1.42 + addressType: (NSString*)addressType address: (NSString*)address;
1.43 +- (id) initWithString: (NSString*)str addressType: (NSString*)addressType;
1.44 +@property (readonly) NSString *name, *addressType, *address;
1.45 +@property (readonly) ABPerson *person;
1.46 +@end
1.47 \ No newline at end of file
2.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2.2 +++ b/MYAddressField.m Thu Jul 17 13:29:34 2008 -0700
2.3 @@ -0,0 +1,223 @@
2.4 +//
2.5 +// MYAddressField.m
2.6 +// YourMove
2.7 +//
2.8 +// Created by Jens Alfke on 7/16/08.
2.9 +// Copyright 2008 Jens Alfke. All rights reserved.
2.10 +//
2.11 +
2.12 +#import "MYAddressField.h"
2.13 +#import "RegexKitLite.h"
2.14 +#import <AddressBook/AddressBook.h>
2.15 +
2.16 +
2.17 +@interface MYAddressField ()
2.18 +@property (retain) MYAddressItem *selectedAddress;
2.19 +@end
2.20 +
2.21 +
2.22 +@implementation MYAddressField
2.23 +
2.24 +
2.25 +@synthesize defaultAddresses=_defaultAddresses, addressProperty=_property, selectedAddress=_selectedAddress;
2.26 +
2.27 +
2.28 +- (void) _computeAddresses
2.29 +{
2.30 + NSMutableArray *newAddresses = $marray();
2.31 + if( _property ) {
2.32 + if( _prefix.length ) {
2.33 + // Find all the people in the address book matching _prefix:
2.34 + ABAddressBook *ab = [ABAddressBook sharedAddressBook];
2.35 + ABSearchElement *search = [ABPerson searchElementForProperty: _property
2.36 + label: nil
2.37 + key: nil
2.38 + value: nil
2.39 + comparison: kABNotEqual];
2.40 + for( ABPerson *person in [ab recordsMatchingSearchElement: search] ) {
2.41 + ABMultiValue *values = [person valueForProperty: _property];
2.42 + NSString *first = [person valueForProperty: kABFirstNameProperty];
2.43 + NSString *last = [person valueForProperty: kABLastNameProperty];
2.44 + BOOL nameMatches = _prefix==nil || ([first.lowercaseString hasPrefix: _prefix]
2.45 + || [last.lowercaseString hasPrefix: _prefix]);
2.46 + for( int i=0; i<values.count; i++ ) {
2.47 + NSString *address = [values valueAtIndex: i];
2.48 + if( nameMatches || [address.lowercaseString hasPrefix: _prefix] ) {
2.49 + MYAddressItem *item = [[MYAddressItem alloc] initWithPerson: person
2.50 + addressType: _property
2.51 + address: address];
2.52 + [newAddresses addObject: item];
2.53 + [item release];
2.54 + }
2.55 + }
2.56 + }
2.57 + } else if( _defaultAddresses ) {
2.58 + [newAddresses addObjectsFromArray: _defaultAddresses];
2.59 + }
2.60 + }
2.61 +
2.62 + [newAddresses sortUsingSelector: @selector(compare:)];
2.63 +
2.64 + if( ifSetObj(&_addresses,newAddresses) )
2.65 + [self reloadData];
2.66 +}
2.67 +
2.68 +- (NSArray*) addresses
2.69 +{
2.70 + if( ! _addresses )
2.71 + [self _computeAddresses];
2.72 + return _addresses;
2.73 +}
2.74 +
2.75 +
2.76 +- (void) awakeFromNib
2.77 +{
2.78 + if( ! _addresses )
2.79 + _addresses = [[NSMutableArray alloc] init];
2.80 + _property = [kABEmailProperty retain];
2.81 + self.completes = NO;
2.82 + self.usesDataSource = YES;
2.83 + self.dataSource = self;
2.84 + self.delegate = self;
2.85 +}
2.86 +
2.87 +- (void) dealloc
2.88 +{
2.89 + [_addresses release];
2.90 + [_property release];
2.91 + [_prefix release];
2.92 + [_selectedAddress release];
2.93 + [super dealloc];
2.94 +}
2.95 +
2.96 +
2.97 +- (BOOL) isExpanded
2.98 +{
2.99 + id ax = NSAccessibilityUnignoredDescendant(self);
2.100 + return [[ax accessibilityAttributeValue: NSAccessibilityExpandedAttribute] boolValue];
2.101 +}
2.102 +
2.103 +
2.104 +- (void) setExpanded: (BOOL)expanded
2.105 +{
2.106 + id ax = NSAccessibilityUnignoredDescendant(self);
2.107 + [ax accessibilitySetValue: $object(expanded) forAttribute: NSAccessibilityExpandedAttribute];
2.108 +}
2.109 +
2.110 +
2.111 +- (void) controlTextDidChange: (NSNotification*)n
2.112 +{
2.113 + if( _prefix.length == 0 )
2.114 + self.expanded = YES;
2.115 +
2.116 + if( ifSetObj(&_prefix, self.stringValue.lowercaseString) )
2.117 + [self _computeAddresses];
2.118 + MYAddressItem *item = [[MYAddressItem alloc] initWithString: self.stringValue
2.119 + addressType: _property];
2.120 + self.selectedAddress = item;
2.121 + [item release];
2.122 +
2.123 + if( _prefix.length == 0 )
2.124 + self.expanded = NO;
2.125 +
2.126 + //Log(@"Address selection = %@",self.selectedAddress);
2.127 +}
2.128 +
2.129 +- (void)comboBoxSelectionDidChange:(NSNotification *)notification
2.130 +{
2.131 + int sel = self.indexOfSelectedItem;
2.132 + self.selectedAddress = sel>=0 ?[self.addresses objectAtIndex: sel] :nil;
2.133 + //Log(@"Address selection = %@",self.selectedAddress);
2.134 +}
2.135 +
2.136 +
2.137 +#pragma mark -
2.138 +#pragma mark DATA SOURCE:
2.139 +
2.140 +
2.141 +- (NSInteger)numberOfItemsInComboBox:(NSComboBox *)aComboBox
2.142 +{
2.143 + return self.addresses.count;
2.144 +}
2.145 +
2.146 +- (id)comboBox:(NSComboBox *)aComboBox objectValueForItemAtIndex:(NSInteger)index
2.147 +{
2.148 + return [[self.addresses objectAtIndex: index] description];
2.149 +}
2.150 +
2.151 +
2.152 +@end
2.153 +
2.154 +
2.155 +
2.156 +
2.157 +@implementation MYAddressItem
2.158 +
2.159 +- (id) initWithName: (NSString*)name
2.160 + addressType: (NSString*)addressType address: (NSString*)address
2.161 +{
2.162 + self = [super init];
2.163 + if( self ) {
2.164 + _name = name.length ?[name copy] :nil;
2.165 + _addressType = [addressType copy];
2.166 + _address = [address copy];
2.167 + }
2.168 + return self;
2.169 +}
2.170 +
2.171 +- (id) initWithPerson: (ABPerson*)person
2.172 + addressType: (NSString*)addressType address: (NSString*)address
2.173 +{
2.174 + NSString *first = [person valueForProperty: kABFirstNameProperty] ?: @"";
2.175 + NSString *last = [person valueForProperty: kABLastNameProperty] ?: @"";
2.176 + NSString *name = $sprintf(@"%@ %@", first,last);
2.177 +
2.178 + self = [self initWithName: name addressType: addressType address: address];
2.179 + if( self )
2.180 + _uuid = person.uniqueId.copy;
2.181 + return self;
2.182 +}
2.183 +
2.184 +- (id) initWithString: (NSString*)str addressType: (NSString*)addressType
2.185 +{
2.186 + #define kJustAddrRegex "[-a-zA-Z0-9%_+.]+(?:@[-a-zA-Z0-9.]+)"
2.187 + static NSString* const kNameAndAddrRegex = @"^\\s*(\\S+)?\\s*<("kJustAddrRegex")>\\s*$";
2.188 + static NSString* const kAddrRegex = @"^\\s*("kJustAddrRegex")\\s*$";
2.189 +
2.190 + NSString *name = nil;
2.191 + NSString *address = [str stringByMatching: kNameAndAddrRegex capture: 2];
2.192 + if( address ) {
2.193 + name = [str stringByMatching: kNameAndAddrRegex capture: 1];
2.194 + } else {
2.195 + address = [str stringByMatching: kAddrRegex];
2.196 + }
2.197 + if( ! address ) {
2.198 + [self release];
2.199 + return nil;
2.200 + }
2.201 + return [self initWithName: name addressType: addressType address: address];
2.202 +}
2.203 +
2.204 +@synthesize name=_name, addressType=_addressType, address=_address;
2.205 +
2.206 +- (ABPerson*) person
2.207 +{
2.208 + if( _uuid )
2.209 + return (ABPerson*) [[ABAddressBook sharedAddressBook] recordForUniqueId: _uuid];
2.210 + else
2.211 + return nil;
2.212 +}
2.213 +
2.214 +- (NSString*) description
2.215 +{
2.216 + return $sprintf(@"%@%@<%@>", _name,(_name ?@" ":@""),_address);
2.217 +}
2.218 +
2.219 +- (NSComparisonResult) compare: (MYAddressItem*)other
2.220 +{
2.221 + NSString *str1 = _name ?:_address;
2.222 + NSString *str2 = other->_name ?: other->_address;
2.223 + return [str1 localizedCaseInsensitiveCompare: str2];
2.224 +}
2.225 +
2.226 +@end