MYAddressField.m
author Jens Alfke <jens@mooseyard.com>
Wed Sep 02 08:41:25 2009 -0700 (2009-09-02)
changeset 35 5cab3034d3a1
permissions -rw-r--r--
10.6 compatibility: Fix some new compiler warnings, and work around apparent regressions in NSTask and -stringByStandardizingPath.
     1 //
     2 //  MYAddressField.m
     3 //  YourMove
     4 //
     5 //  Created by Jens Alfke on 7/16/08.
     6 //  Copyright 2008 Jens Alfke. All rights reserved.
     7 //
     8 
     9 #import "MYAddressField.h"
    10 #import "RegexKitLite.h"
    11 #import <AddressBook/AddressBook.h>
    12 
    13 
    14 @interface MYAddressField ()
    15 @property (retain) MYAddressItem *selectedAddress;
    16 @end
    17 
    18 
    19 @implementation MYAddressField
    20 
    21 
    22 @synthesize defaultAddresses=_defaultAddresses, addressProperty=_property, selectedAddress=_selectedAddress;
    23 
    24 
    25 - (void) _computeAddresses
    26 {
    27     NSMutableArray *newAddresses = $marray();
    28     if( _property ) {
    29         if( _prefix.length ) {
    30             // Find all the people in the address book matching _prefix:
    31             ABAddressBook *ab = [ABAddressBook sharedAddressBook];
    32             ABSearchElement *search = [ABPerson searchElementForProperty: _property
    33                                                                    label: nil
    34                                                                      key: nil
    35                                                                    value: nil
    36                                                               comparison: kABNotEqual];
    37             for( ABPerson *person in [ab recordsMatchingSearchElement: search] ) {
    38                 ABMultiValue *values = [person valueForProperty: _property];
    39                 NSString *first = [person valueForProperty: kABFirstNameProperty];
    40                 NSString *last = [person valueForProperty: kABLastNameProperty];
    41                 BOOL nameMatches = _prefix==nil || ([first.lowercaseString hasPrefix: _prefix] 
    42                                                     || [last.lowercaseString hasPrefix: _prefix]);
    43                 for( int i=0; i<values.count; i++ ) {
    44                     NSString *address = [values valueAtIndex: i];
    45                     if( nameMatches || [address.lowercaseString hasPrefix: _prefix] ) {
    46                         MYAddressItem *item = [[MYAddressItem alloc] initWithPerson: person
    47                                                                         addressType: _property
    48                                                                             address: address];
    49                         [newAddresses addObject: item];
    50                         [item release];
    51                     }
    52                 }
    53             }
    54         } else if( _defaultAddresses ) {
    55             [newAddresses addObjectsFromArray: _defaultAddresses];
    56         }
    57     }
    58     
    59     [newAddresses sortUsingSelector: @selector(compare:)];
    60     
    61     if( ifSetObj(&_addresses,newAddresses) )
    62         [self reloadData];
    63 }
    64 
    65 - (NSArray*) addresses
    66 {
    67     if( ! _addresses )
    68         [self _computeAddresses];
    69     return _addresses;
    70 }
    71 
    72 
    73 - (void) awakeFromNib
    74 {
    75     if( ! _addresses )
    76         _addresses = [[NSMutableArray alloc] init];
    77     _property = [kABEmailProperty retain];
    78     self.completes = NO;
    79     self.usesDataSource = YES;
    80     self.dataSource = self;
    81     self.delegate = self;
    82 }
    83 
    84 - (void) dealloc
    85 {
    86     [_addresses release];
    87     [_property release];
    88     [_prefix release];
    89     [_selectedAddress release];
    90     [super dealloc];
    91 }
    92 
    93 
    94 - (BOOL) isExpanded
    95 {
    96     id ax = NSAccessibilityUnignoredDescendant(self);
    97     return [[ax accessibilityAttributeValue: NSAccessibilityExpandedAttribute] boolValue];
    98 }
    99 
   100 
   101 - (void) setExpanded: (BOOL)expanded
   102 {
   103     id ax = NSAccessibilityUnignoredDescendant(self);
   104     [ax accessibilitySetValue: $object(expanded) forAttribute: NSAccessibilityExpandedAttribute];
   105 }
   106 
   107 
   108 - (void) controlTextDidChange: (NSNotification*)n
   109 {
   110     if( _prefix.length == 0 )
   111         self.expanded = YES;
   112     
   113     if( ifSetObj(&_prefix, self.stringValue.lowercaseString) )
   114         [self _computeAddresses];
   115     MYAddressItem *item = [[MYAddressItem alloc] initWithString: self.stringValue 
   116                                                     addressType: _property];
   117     self.selectedAddress = item;
   118     [item release];
   119 
   120     if( _prefix.length == 0 )
   121         self.expanded = NO;
   122 
   123     //Log(@"Address selection = %@",self.selectedAddress);
   124 }
   125 
   126 - (void)comboBoxSelectionDidChange:(NSNotification *)notification
   127 {
   128     int sel = self.indexOfSelectedItem;
   129     self.selectedAddress = sel>=0 ?[self.addresses objectAtIndex: sel] :nil;
   130     //Log(@"Address selection = %@",self.selectedAddress);
   131 }
   132 
   133 
   134 #pragma mark -
   135 #pragma mark DATA SOURCE:
   136 
   137 
   138 - (NSInteger)numberOfItemsInComboBox:(NSComboBox *)aComboBox
   139 {
   140     return self.addresses.count;
   141 }
   142 
   143 - (id)comboBox:(NSComboBox *)aComboBox objectValueForItemAtIndex:(NSInteger)index
   144 {
   145     return [[self.addresses objectAtIndex: index] description];
   146 }
   147 
   148 
   149 @end
   150 
   151 
   152 
   153 
   154 @implementation MYAddressItem
   155 
   156 - (id) initWithName: (NSString*)name
   157         addressType: (NSString*)addressType address: (NSString*)address
   158 {
   159     self = [super init];
   160     if( self ) {
   161         _name = name.length ?[name copy] :nil;
   162         _addressType = [addressType copy];
   163         _address = [address copy];
   164     }
   165     return self;
   166 }
   167     
   168 - (id) initWithPerson: (ABPerson*)person
   169           addressType: (NSString*)addressType address: (NSString*)address
   170 {
   171     NSString *first = [person valueForProperty: kABFirstNameProperty] ?: @"";
   172     NSString *last = [person valueForProperty: kABLastNameProperty] ?: @"";
   173     NSString *name = $sprintf(@"%@ %@", first,last);
   174     
   175     self = [self initWithName: name addressType: addressType address: address];
   176     if( self )
   177         _uuid = person.uniqueId.copy;
   178     return self;
   179 }
   180 
   181 - (id) initWithString: (NSString*)str addressType: (NSString*)addressType
   182 {
   183     #define kJustAddrRegex "[-a-zA-Z0-9%_+.]+(?:@[-a-zA-Z0-9.]+)"
   184     static NSString* const kNameAndAddrRegex = @"^\\s*(\\S+)?\\s*<("kJustAddrRegex")>\\s*$";
   185     static NSString* const kAddrRegex = @"^\\s*("kJustAddrRegex")\\s*$";
   186 
   187     NSString *name = nil;
   188     NSString *address = [str stringByMatching: kNameAndAddrRegex capture: 2];
   189     if( address ) {
   190         name = [str stringByMatching: kNameAndAddrRegex capture: 1];
   191     } else {
   192         address = [str stringByMatching: kAddrRegex];
   193     }
   194     if( ! address ) {
   195         [self release];
   196         return nil;
   197     }
   198     return [self initWithName: name addressType: addressType address: address];
   199 }
   200 
   201 @synthesize name=_name, addressType=_addressType, address=_address;
   202 
   203 - (ABPerson*) person
   204 {
   205     if( _uuid )
   206         return (ABPerson*) [[ABAddressBook sharedAddressBook] recordForUniqueId: _uuid];
   207     else
   208         return nil;
   209 }
   210 
   211 - (NSString*) description
   212 {
   213     return $sprintf(@"%@%@<%@>", _name,(_name ?@" ":@""),_address);
   214 }
   215 
   216 - (NSComparisonResult) compare: (MYAddressItem*)other
   217 {
   218     NSString *str1 = _name ?:_address;
   219     NSString *str2 = other->_name ?: other->_address;
   220     return [str1 localizedCaseInsensitiveCompare: str2];
   221 }
   222 
   223 @end