1#import <AppKit/AppKit.h> 2 3#import "AddressView/ADPersonView.h" 4#import "Controller.h" 5#import "DragDropMatrix.h" 6 7@interface ADAddressBook (AddressManagerAdditions) 8- (ADPerson*) personWithFirstName: (NSString*) first 9 lastName: (NSString*) last; 10@end 11 12@implementation ADAddressBook (AddressManagerAdditions) 13- (ADPerson*) personWithFirstName: (NSString*) first 14 lastName: (NSString*) last 15{ 16 NSEnumerator *e; ADPerson *p; 17 e = [[self people] objectEnumerator]; 18 while((p = [e nextObject])) 19 if([[p valueForProperty: ADFirstNameProperty] 20 isEqualToString: first] && 21 [[p valueForProperty: ADLastNameProperty] 22 isEqualToString: last]) 23 return p; 24 return nil; 25} 26@end 27 28@interface Controller (Private) 29- (void) browserAction: (id) sender; 30@end 31 32@implementation Controller 33- (void) applicationDidFinishLaunching: (NSNotification*) note 34{ 35 NSUserDefaults *def; 36 NSString *uid; 37 38 [NSApp registerServicesMenuSendTypes: [NSArray arrayWithObjects: 39 NSStringPboardType, 40 nil] 41 returnTypes: nil]; 42 43 servicesMenu = [[[NSApp mainMenu] 44 itemWithTitle: @"Services"] 45 submenu]; 46 [NSApp setServicesMenu: servicesMenu]; 47 48 def = [NSUserDefaults standardUserDefaults]; 49 uid = [def stringForKey: @"SelectedGroup"]; 50 if(uid && ![uid isEqualToString: @"None"]) 51 [self selectGroup: (ADGroup*) [_book recordForUniqueId: uid]]; 52 else 53 [self selectGroup: nil]; 54 55 uid = [def stringForKey: @"SelectedPerson"]; 56 if(uid && ![uid isEqualToString: @"None"]) 57 [self selectPerson: (ADPerson*)[_book recordForUniqueId: uid]]; 58 else 59 [groupsBrowser selectRow: 0 inColumn: 1]; 60 61 [[NSNotificationCenter defaultCenter] 62 addObserver: self 63 selector: @selector(handleDatabaseChanged:) 64 name: ADDatabaseChangedNotification 65 object: nil]; 66 [[NSNotificationCenter defaultCenter] 67 addObserver: self 68 selector: @selector(handleDatabaseChangedExternally:) 69 name: ADDatabaseChangedExternallyNotification 70 object: nil]; 71 [[NSNotificationCenter defaultCenter] 72 addObserver:self 73 selector: @selector(handleNameChanged:) 74 name: ADPersonNameChangedNotification 75 object: nil]; 76} 77 78- (void) awakeFromNib 79{ 80 NSString *filename; 81 82 _fm = [NSFileManager defaultManager]; 83 _book = [ADAddressBook sharedAddressBook]; 84 _selfChanging = NO; 85 _selectedByDrop = NO; 86 87 if(!_book) 88 { 89 NSRunAlertPanel(_(@"No Address Book"), 90 _(@"[ADAddressBook sharedAddressBook] returned nil.\n" 91 @"Configuration broken?"), 92 _(@"OK"), nil, nil, nil); 93 exit(-1); 94 } 95 96 [[groupsBrowser window] setFrameAutosaveName: @"Addresses"]; 97 [[groupsBrowser window] setFrameUsingName: @"Addresses"]; 98 [prefsPanel setFrameAutosaveName: @"Preferences"]; 99 [prefsPanel setFrameUsingName: @"Preferences"]; 100 101 [groupsBrowser setAllowsEmptySelection: NO]; 102 [groupsBrowser setAllowsMultipleSelection: YES]; 103 [groupsBrowser setMaxVisibleColumns: 2]; 104 [groupsBrowser setDelegate: self]; 105 [groupsBrowser setTitle: _(@"Group") ofColumn: 0]; 106 [groupsBrowser setTitle: _(@"Name") ofColumn: 1]; 107 [groupsBrowser setMatrixClass: [DragDropMatrix class]]; 108 [groupsBrowser setTarget: self]; 109 [groupsBrowser setAction: @selector(browserAction:)]; 110 111 clipView = [[NSClipView alloc] initWithFrame: [scrollView frame]]; 112 [clipView setAutoresizesSubviews: YES]; 113 personView = [[ADPersonView alloc] initWithFrame: NSZeroRect]; 114 [clipView setDocumentView: personView]; 115 [personView setFillsSuperview: YES]; 116 [personView setForceImage: YES]; 117 118 [scrollView setContentView: clipView]; 119 [scrollView setHasVerticalScroller: YES]; 120 [scrollView setHasHorizontalScroller: YES]; 121 [scrollView setBorderType: NSBezelBorder]; 122 123 [personView setDelegate: self]; 124 125 filename = [[NSBundle mainBundle] pathForResource: @"ISOCountryCodes" 126 ofType: @"dict"]; 127 _countryCodeDict = [[[NSString stringWithContentsOfFile: filename] 128 propertyList] retain]; 129 NSAssert(_countryCodeDict, @"ISOCountryCodes.dict could not be loaded."); 130 131 [self createCache]; 132 if([_peopleCache count]) 133 [self selectPerson: [_peopleCache objectAtIndex: 0]]; 134 135 [self initPrefsPanel]; 136} 137 138- (void) initPrefsPanel 139{ 140 NSUserDefaults *ud; 141 NSEnumerator *e; NSString *key; 142 143 ud = [NSUserDefaults standardUserDefaults]; 144 if(![ud objectForKey: @"Autosave"] || 145 ![[ud objectForKey: @"Autosave"] boolValue]) 146 [prefsAutosaveButton setState: NSOffState]; 147 else 148 [prefsAutosaveButton setState: NSOnState]; 149 150 [prefsAddressLayoutPopup removeAllItems]; 151 e = [[[_countryCodeDict allKeys] 152 sortedArrayUsingSelector: @selector(compare:)] 153 objectEnumerator]; 154 while((key = [e nextObject])) 155 { 156 [prefsAddressLayoutPopup 157 addItemWithTitle: [_countryCodeDict objectForKey: key]]; 158 [[prefsAddressLayoutPopup 159 itemWithTitle: [_countryCodeDict objectForKey: key]] 160 setRepresentedObject: key]; 161 } 162 [prefsAddressLayoutPopup addItemWithTitle: _(@"Everything")]; 163 [[prefsAddressLayoutPopup itemWithTitle: _(@"Everything")] 164 setRepresentedObject: @"Default"]; 165 [prefsAddressLayoutPopup sizeToFit]; 166 167 if([ud objectForKey: @"DefaultISOCountryCode"]) 168 { 169 NSString *def; 170 NSInteger index; 171 172 def = [ud objectForKey: @"DefaultISOCountryCode"]; 173 index = [prefsAddressLayoutPopup indexOfItemWithRepresentedObject: def]; 174 if(index != NSNotFound) 175 [prefsAddressLayoutPopup selectItemAtIndex: index]; 176 [[personView class] setDefaultISOCountryCode: def]; 177 } 178 179 if([[ADPerson class] screenNameFormat] == ADScreenNameFirstNameFirst) 180 [prefsScreenNameLayoutMatrix selectCellWithTag: 1]; 181 else 182 [prefsScreenNameLayoutMatrix selectCellWithTag: 0]; 183} 184 185- (void) createCache 186{ 187 SEL sortingSelector; 188 189 sortingSelector = 190 GSSelectorFromNameAndTypes("compareByScreenName:", "i12@0:4@8"); 191 192 [_peopleCache release]; 193 if(_currentGroup) 194 _peopleCache = [_currentGroup members]; 195 else 196 _peopleCache = [_book people]; 197 _peopleCache = 198 [[_peopleCache sortedArrayUsingSelector: sortingSelector] 199 retain]; 200} 201 202- (NSArray*) groupNames 203{ 204 NSArray *groups; 205 NSMutableArray *retval; 206 int i; 207 208 groups = [_book groups]; 209 retval = [NSMutableArray arrayWithCapacity: [groups count]]; 210 211 for(i=0; i<[groups count]; i++) 212 { 213 NSString *name; 214 215 name = [[groups objectAtIndex: i] 216 valueForProperty: ADGroupNameProperty]; 217 if(!name) NSLog(@"Group at %d has no name!\n", i); 218 else [retval addObject: name]; 219 } 220 return retval; 221} 222 223- (void) selectGroup: (ADGroup*) group 224{ 225 int num, i; 226 227 if([personView isEditable]) 228 [self finishEditingPerson]; 229 230 if(!group) 231 { 232 [[groupsBrowser matrixInColumn: 0] deselectAllCells]; 233 [groupsBrowser selectRow: 0 inColumn: 0]; 234 [_currentGroup autorelease]; 235 _currentGroup = nil; 236 [self createCache]; 237 238 [groupsBrowser reloadColumn: 1]; 239 240 if([_peopleCache count]) 241 [self selectPerson: [_peopleCache objectAtIndex: 0]]; 242 else 243 [self selectPerson: nil]; 244 245 return; 246 } 247 248 num = [[groupsBrowser matrixInColumn: 0] numberOfRows]; 249 for(i=0; i<num; i++) 250 { 251 ADRecord *r; 252 253 r = [[groupsBrowser loadedCellAtRow: i column: 0] representedObject]; 254 if([[r uniqueId] isEqualToString: [group uniqueId]]) 255 { 256 [[groupsBrowser matrixInColumn: 0] deselectAllCells]; 257 [groupsBrowser selectRow: i inColumn: 0]; 258 259 [_currentGroup autorelease]; 260 _currentGroup = [group retain]; 261 [self createCache]; 262 263 [groupsBrowser reloadColumn: 1]; 264 265 if([_peopleCache count]) 266 [self selectPerson: [_peopleCache objectAtIndex: 0]]; 267 else 268 [self selectPerson: nil]; 269 return; 270 } 271 } 272 273 NSLog(@"Group %@ not found in column 0!\n", [group uniqueId]); 274} 275 276 277- (void) selectPerson: (ADPerson*) person 278{ 279 int i; 280 281 if([personView isEditable]) 282 [self finishEditingPerson]; 283 284 if(!person) 285 { 286 [personView setPerson: nil]; 287 return; 288 } 289 290 for(i=0; i<[_peopleCache count]; i++) 291 if([[[_peopleCache objectAtIndex: i] uniqueId] 292 isEqualToString: [person uniqueId]]) 293 break; 294 295 if(i==[_peopleCache count]) // Not found? Select "all" and try again 296 { 297 NSArray *all; 298 SEL sortingSelector; 299 300 sortingSelector = 301 GSSelectorFromNameAndTypes("compareByScreenName:", "i12@0:4@8"); 302 303 304 all = [[_book people] sortedArrayUsingSelector: sortingSelector]; 305 for(i=0; i<[all count]; i++) 306 { 307 if([[[all objectAtIndex: i] uniqueId] 308 isEqualToString: [person uniqueId]]) 309 { 310 [self selectGroup: nil]; 311 break; 312 } 313 } 314 } 315 316 if(i==[_peopleCache count]) // Still not found? WEIRD. 317 { 318 NSLog(@"Person %@ not found!\n", [person uniqueId]); 319 return; 320 } 321 322 // HACK to avoid NSBrowser to keep its old selection; we want only 323 // the new one. 324 [groupsBrowser setAllowsMultipleSelection: NO]; 325 [groupsBrowser selectRow: i inColumn: 1]; 326 [groupsBrowser setAllowsMultipleSelection: YES]; 327 328 [personView setPerson: [_peopleCache objectAtIndex: i]]; 329 [clipView scrollToPoint: NSZeroPoint]; 330 331 if([person readOnly]) 332 { 333 [editButton setEnabled: NO]; 334 [editItem setEnabled: NO]; 335 } 336 else 337 { 338 [editButton setEnabled: YES]; 339 [editItem setEnabled: YES]; 340 } 341} 342 343- (void) deletePersonAndSelectNext: (ADPerson*) person 344{ 345 int row = [groupsBrowser selectedRowInColumn: 1]; 346 347 if(_currentGroup) 348 [_currentGroup removeMember: person]; 349 else 350 [_book removeRecord: person]; 351 352 [self createCache]; 353 [groupsBrowser reloadColumn: 1]; 354 355 if(![_peopleCache count]) 356 [personView setPerson: nil]; 357 else if(row >= [_peopleCache count]) 358 [self selectPerson: [_peopleCache objectAtIndex: [_peopleCache count]-1]]; 359 else 360 [self selectPerson: [_peopleCache objectAtIndex: row]]; 361} 362 363- (void) beginEditingPerson: (ADPerson*) person 364{ 365 if([person readOnly]) 366 { 367 NSRunAlertPanel(_(@"Read-Only Person"), 368 [NSString stringWithFormat: 369 _(@"'%@' cannot be edited because\n" 370 @"the record is marked as read-only."), [person screenName]], 371 _(@"OK"), nil, nil, nil); 372 [editButton setState: NSOffState]; 373 [editItem setTitle: _(@"Edit person")]; 374 return; 375 } 376 [self selectPerson: person]; 377 [personView setEditable: YES]; 378 [editItem setTitle: _(@"End editing")]; 379 [editButton setState: NSOnState]; 380 [clipView scrollToPoint: NSZeroPoint]; 381}; 382 383- (void) finishEditingPerson 384{ 385 ADPerson *p; 386 387 p = [personView person]; 388 if(!p || ![personView isEditable]) return; 389 390 if(![p valueForProperty: ADLastNameProperty] || 391 ![p valueForProperty: ADFirstNameProperty]) 392 { 393 if(NSRunAlertPanel(_(@"Discard Person?"), 394 _(@"The person you have edited has no first or last\n" 395 @"names. Would you like to discard this person?"), 396 _(@"Yes"), _(@"No"), nil, nil)) 397 { 398 [personView setEditable: NO]; 399 [self deletePersonAndSelectNext: p]; 400 p = nil; 401 } 402 } 403 [personView setEditable: NO]; 404 405 [editItem setTitle: _(@"Edit Person")]; 406 [editButton setState: NSOffState]; 407 408 if(p) 409 { 410 [groupsBrowser reloadColumn: 1]; 411 [self selectPerson: p]; 412 } 413} 414 415- (NSArray*) selectedPersons 416{ 417 NSMutableArray *arr; 418 NSEnumerator *e; 419 NSCell *c; 420 421 e = [[groupsBrowser selectedCells] objectEnumerator]; 422 arr = [NSMutableArray arrayWithCapacity: [[groupsBrowser selectedCells] 423 count]]; 424 while((c = [e nextObject])) 425 if([c representedObject]) 426 [arr addObject: [c representedObject]]; 427 428 return [NSArray arrayWithArray: arr]; 429} 430 431/* 432 Actions 433*/ 434 435- (void) doEditPerson: (id) sender 436{ 437 ADPerson *p; 438 439 p = [personView person]; 440 if(!p) return; // nothing to do 441 if([personView isEditable]) 442 [self finishEditingPerson]; 443 else 444 [self beginEditingPerson: p]; 445 [clipView scrollToPoint: NSZeroPoint]; 446} 447 448- (void) doTogglePersonEditable: (id) sender 449{ 450 ADPerson *p; 451 452 p = [personView person]; 453 if(!p && [sender state] == NSOnState) 454 [self doCreatePerson: sender]; 455 else if([sender state] == NSOnState) 456 [self beginEditingPerson: p]; 457 else 458 [self finishEditingPerson]; 459 [clipView scrollToPoint: NSZeroPoint]; 460} 461 462- (void) doCreatePerson: (id) sender 463{ 464 BOOL ok; 465 ADPerson *p; 466 467 p = [[ADPerson alloc] init]; 468 469 ok = [_book addRecord: p]; 470 if(!ok) 471 { 472 NSRunAlertPanel(_(@"Couldn't create person"), 473 _(@"A new person could not be created."), 474 _(@"OK"), nil, nil, nil); 475 return; 476 } 477 478 if(_currentGroup) 479 { 480 ok = [_currentGroup addMember: p]; 481 if(!ok) 482 { 483 NSRunAlertPanel(_(@"Couldn't add person"), 484 _(@"The newly created person could not be\n" 485 @"added to this group."), 486 _(@"OK"), nil, nil, nil); 487 return; 488 } 489 } 490 491 p = (ADPerson*)[_book recordForUniqueId: [p uniqueId]]; 492 493 [self createCache]; 494 [groupsBrowser reloadColumn: 1]; 495 [self beginEditingPerson: p]; 496 [personView beginEditingInFirstCell]; 497} 498 499- (IBAction) doDeletePerson: (id) sender 500{ 501 NSArray *a; 502 NSEnumerator *e; 503 ADPerson *p; 504 505 a = [self selectedPersons]; 506 if(![a count]) return; // nothing to do. 507 508 // deleting from "All"? Ask. 509 if(!_currentGroup) 510 { 511 NSString *msg, *cpt; 512 if([a count] == 1) 513 { 514 msg = [NSString stringWithFormat: _(@"Do you really want to delete %@ " 515 @"from \"All\" and all groups?"), 516 [[a objectAtIndex: 0] screenName]]; 517 cpt = _(@"Delete Person?"); 518 } 519 else 520 { 521 msg = [NSString stringWithFormat: _(@"Do you really want to delete " 522 @"the %d selected persons " 523 @"from \"All\" and all groups?"), 524 [a count]]; 525 cpt = _(@"Delete Persons?"); 526 } 527 if(!NSRunAlertPanel(cpt, msg, 528 _(@"Yes"), _(@"No"), nil, nil)) 529 { 530 NSLog(@"Not deleting.\n"); 531 return; 532 } 533 } 534 535 e = [a objectEnumerator]; 536 while((p = [e nextObject])) 537 [self deletePersonAndSelectNext: p]; 538} 539 540- (void) doImportPerson: (id) sender 541{ 542 int retval; 543 id obj; 544 int loaded = 0; 545 NSString *fname; 546 id<ADInputConverting> conv; 547 ADConverterManager *man; 548 NSOpenPanel *p; 549 550 man = [ADConverterManager sharedManager]; 551 552 p = [NSOpenPanel openPanel]; 553 554 [p setDirectory: [[NSUserDefaults standardUserDefaults] 555 objectForKey: @"ImportDirectory"]]; 556 557 [p setCanChooseDirectories: NO]; 558 [p setAllowsMultipleSelection: NO]; 559 [p setTitle: _(@"Import...")]; 560 retval = [p runModalForTypes: [man inputConvertableFileTypes]]; 561 if(!retval) 562 return; 563 564 [[NSUserDefaults standardUserDefaults] 565 setObject: [p directory] forKey: @"ImportDirectory"]; 566 567 fname = [[p filenames] objectAtIndex: 0]; 568 conv = [man inputConverterWithFile: fname]; 569 NSAssert(conv, @"No converter for this file!"); 570 571 while((obj = [conv nextRecord])) 572 { 573 NSEnumerator *e = [_peopleCache objectEnumerator]; 574 id other; 575 int retval; 576 577 loaded++; 578 579 retval = 0; // insert anyway 580 while((other = [e nextObject])) 581 { 582 if([[other screenName] isEqualToString: [obj screenName]]) 583 { 584 NSString *fmt; 585 fmt = 586 [NSString stringWithFormat: 587 _(@"Trying to import person named '%@',\n" 588 @"which already exists in the database."), 589 [obj screenName]]; 590 retval = NSRunAlertPanel(_(@"Existing person?"), 591 fmt, _(@"Replace"), _(@"Insert anyway"), 592 _(@"Don't insert"), nil); 593 break; 594 } 595 } 596 if(retval == 1) // replace 597 { 598 [_book removeRecord: other]; 599 [_book addRecord: obj]; 600 } 601 else if(retval == 0) // insert anyway 602 [_book addRecord: obj]; 603 else if(retval == -1) // don't insert; continue reading 604 continue; 605 } 606 [groupsBrowser reloadColumn: 1]; 607} 608 609- (void) doExportPerson: (id) sender 610{ 611 ADConverterManager *man; 612 NSSavePanel *panel; 613 NSString *fname; 614 int retval; 615 NSArray *a; 616 NSEnumerator *e; ADPerson *person; 617 id<ADOutputConverting> conv; 618 619 if([personView isEditable]) 620 [self finishEditingPerson]; 621 622 a = [self selectedPersons]; 623 if(![a count]) return; 624 625 man = [ADConverterManager sharedManager]; 626 panel = [NSSavePanel savePanel]; 627 628 [panel setDirectory: [[NSUserDefaults standardUserDefaults] 629 objectForKey: @"ExportDirectory"]]; 630 [panel setRequiredFileType: @"vcf"]; 631 if([a count] > 1) 632 [panel 633 setTitle: [NSString stringWithFormat: _(@"Export %d records to..."), 634 [a count]]]; 635 else 636 [panel 637 setTitle: [NSString stringWithFormat: _(@"Export '%@' to..."), 638 [[a objectAtIndex: 0] screenName]]]; 639 640 641 retval = [panel runModal]; 642 if(!retval) 643 return; 644 645 [[NSUserDefaults standardUserDefaults] 646 setObject: [panel directory] 647 forKey: @"ExportDirectory"]; 648 649 fname = [panel filename]; 650 conv = [man outputConverterForType: [[fname pathExtension] lowercaseString]]; 651 if(!conv) 652 { 653 NSString *msg = 654 [NSString stringWithFormat: _(@"Cannot export files of type %@"), 655 [fname pathExtension]]; 656 NSRunAlertPanel(_(@"Invalid File Type"), msg, _(@"OK"), nil, nil, nil); 657 return; 658 } 659 else if([a count]>1 && ![conv canStoreMultipleRecords]) 660 { 661 NSString *msg = 662 [NSString stringWithFormat: _(@"Can only store a single person\n" 663 @"in files of type %@"), 664 [fname pathExtension]]; 665 NSRunAlertPanel(_(@"Invalid File Type"), msg, _(@"OK"), nil, nil, nil); 666 return; 667 } 668 669 e = [a objectEnumerator]; 670 while((person = [e nextObject])) 671 [conv storeRecord: person]; 672 673 retval = [[conv string] writeToFile: fname atomically: NO]; 674 if(!retval) 675 { 676 NSString *msg = 677 [NSString stringWithFormat: _(@"Could not write file %@.\n" 678 @"Permissions error?"), 679 fname]; 680 NSRunAlertPanel(_(@"Write Failed"), msg, _(@"OK"), nil, nil, nil); 681 } 682} 683 684- (void) doSetMe: (id) sender 685{ 686 if(![personView person]) return; 687 [_book setMe: [personView person]]; 688 [personView setNeedsDisplay: YES]; 689 [groupsBrowser reloadColumn: 1]; 690 [self selectPerson: [personView person]]; 691} 692 693- (void) doShowMe: (id) sender 694{ 695 if(![_book me]) return; 696 [self selectPerson: [_book me]]; 697} 698 699- (void) doSelectAllPersons: (id) sender 700{ 701 [groupsBrowser selectAll: self]; 702 if([[groupsBrowser selectedCells] count] == 1) 703 [personView setPerson: [[groupsBrowser selectedCellInColumn: 1] 704 representedObject]]; 705 else [personView setPerson: nil]; 706} 707 708- (void) doToggleShared: (id) sender 709{ 710 NSEnumerator *e; 711 BOOL share; 712 ADPerson *person; 713 714 share = YES; // default yes, but if one selected person is shared, 715 // set to no 716 e = [[self selectedPersons] objectEnumerator]; 717 while((person = [e nextObject])) 718 if([person shared]) 719 { 720 share = NO; 721 break; 722 } 723 724 e = [[self selectedPersons] objectEnumerator]; 725 while((person = [e nextObject])) 726 [person setShared: share]; 727 728 if(share) 729 { 730 if([[self selectedPersons] count] > 1) 731 [shareItem setTitle: _(@"Do not share these people")]; 732 else 733 [shareItem setTitle: _(@"Do not share this person")]; 734 } 735 else 736 { 737 if([[self selectedPersons] count] > 1) 738 [shareItem setTitle: _(@"Share these people")]; 739 else 740 [shareItem setTitle: _(@"Share this person")]; 741 } 742 743 [personView setNeedsDisplay: YES]; 744} 745 746- (void) doDuplicatePerson: (id) sender 747{ 748 ADPerson *newPerson, *oldPerson; 749 BOOL ok; 750 751 oldPerson = [personView person]; 752 if(!oldPerson) return; 753 754 newPerson = [oldPerson copy]; 755 [newPerson removeValueForProperty: ADFirstNameProperty]; 756 [newPerson removeValueForProperty: ADLastNameProperty]; 757 758 ok = [_book addRecord: newPerson]; 759 if(!ok) 760 { 761 NSRunAlertPanel(_(@"Couldn't create person"), 762 _(@"A new person could not be created."), 763 _(@"OK"), nil, nil, nil); 764 return; 765 } 766 if(_currentGroup) 767 { 768 ok = [_currentGroup addMember: newPerson]; 769 if(!ok) 770 { 771 NSRunAlertPanel(_(@"Couldn't add person"), 772 _(@"The newly created person could not be\n" 773 @"added to this group."), 774 _(@"OK"), nil, nil, nil); 775 return; 776 } 777 } 778 779 [self createCache]; 780 [groupsBrowser reloadColumn: 1]; 781 [self beginEditingPerson: newPerson]; 782 [personView beginEditingInFirstCell]; 783} 784 785- (void) doMergePersons: (id) sender 786{ 787 // FIXME: Unimplemented! 788} 789 790- (void) doCreateGroup: (id)sender 791{ 792 ADGroup *group; 793 794 group = [[[ADGroup alloc] init] autorelease]; 795 [group setValue: _(@"New Group") forProperty: ADGroupNameProperty]; 796 if(![_book addRecord: group]) 797 NSRunAlertPanel(_(@"Error"), _(@"Could not create group!"), 798 _(@"OK"), nil, nil, nil); 799 [groupsBrowser reloadColumn: 0]; 800 801 [self selectGroup: group]; 802} 803 804- (void) doDeleteGroup: (id) sender 805{ 806 ADGroup *group; 807 NSString *name; 808 NSString *msg; 809 int retval; 810 int row = [groupsBrowser selectedRowInColumn: 0]; 811 812 if(row == 0) return; // Can't delete this 813 814 group = [[_book groups] objectAtIndex: row-1]; 815 name = [group valueForProperty: ADGroupNameProperty]; 816 817 msg = 818 [NSString stringWithFormat: _(@"Do you really want to delete " 819 @"the group '%@'?"), 820 name]; 821 retval = NSRunAlertPanel(_(@"Delete Group?"), msg, 822 _(@"Yes"), _(@"No"), nil, nil); 823 if(!retval) return; 824 825 if(![_book removeRecord: group]) 826 { 827 msg = 828 [NSString stringWithFormat: _(@"The group '%@` could not be deleted."), 829 name]; 830 NSRunAlertPanel(_(@"Error"), msg, _(@"OK"), nil, nil, nil); 831 } 832 833 lastCell = nil; 834 [groupsBrowser reloadColumn: 0]; 835 [groupsBrowser selectRow: row-1 inColumn: 0]; // OK -- we caught 0 above 836 [self browserAction: groupsBrowser]; 837 [self createCache]; 838 if([_peopleCache count]) 839 [self selectPerson: [_peopleCache objectAtIndex: 0]]; 840} 841 842- (void) doSaveDatabase: (id) sender 843{ 844 if([personView isEditable]) 845 [self finishEditingPerson]; 846 if([_book hasUnsavedChanges]) 847 { 848 _selfChanging = YES; 849 850 if(![_book save]) 851 NSRunAlertPanel(_(@"Couldn't save"), 852 _(@"The database could not be saved!"), 853 _(@"OK"), nil, nil, nil); 854 else 855 [[personView window] setTitle: _(@"Addresses")]; 856 857 _selfChanging = NO; 858 } 859} 860 861- (void) doShowPrefsPanel: (id) sender 862{ 863 [prefsPanel makeKeyAndOrderFront: self]; 864} 865 866- (void) prefsToggleAutosave: (id) sender 867{ 868 if([sender state] == NSOnState) 869 [[NSUserDefaults standardUserDefaults] 870 setObject: @"YES" forKey: @"Autosave"]; 871 else 872 [[NSUserDefaults standardUserDefaults] 873 setObject: @"NO" forKey: @"Autosave"]; 874} 875 876- (void) prefsChangeAddressLayout: (id) sender 877{ 878 NSString *code, *title; NSEnumerator *e; 879 880 title = [[sender selectedItem] title]; 881 e = [_countryCodeDict keyEnumerator]; 882 while((code = [e nextObject])) 883 if([[_countryCodeDict objectForKey: code] isEqualToString: title]) 884 break; 885 if(!code && [title isEqualToString: _(@"Everything")]) 886 code = @"Default"; 887 if(!code) return; 888 889 [[NSUserDefaults standardUserDefaults] 890 setObject: code forKey: @"DefaultISOCountryCode"]; 891 [[personView class] setDefaultISOCountryCode: code]; 892 [personView layout]; 893} 894 895- (void) prefsChangeScreenNameLayout: (id) sender 896{ 897 int tag; 898 ADPerson *p; 899 900 tag = [sender selectedTag]; 901 [[ADPerson class] setScreenNameFormat: tag]; 902 // FIXME: Doesn't change in remote address books 903 904 p = [personView person]; 905 [self createCache]; 906 [groupsBrowser reloadColumn: 1]; 907 if(p) [self selectPerson: p]; 908} 909 910/* 911 * Browser delegate methods 912 */ 913 914- (int) browser: (NSBrowser*) sender 915numberOfRowsInColumn: (int) column 916{ 917 NSArray *groupnames = [self groupNames]; 918 919 if(column == 0) 920 return [groupnames count]+1; 921 else 922 { 923 NSCell *cell; 924 NSString *oldName = @""; 925 NSString *newName = @""; 926 ADGroup *group = nil; 927 928 cell = [sender selectedCellInColumn: 0]; 929 930 if([sender selectedRowInColumn: 0] != 0) 931 { 932 group = 933 [[_book groups] objectAtIndex: [sender selectedRowInColumn: 0]-1]; 934 oldName = [group valueForProperty: ADGroupNameProperty]; 935 newName = [cell stringValue]; 936 } 937 938 // stop editing; rename if necessary 939 [lastCell setEditable: NO]; 940 if(cell == lastCell && ![oldName isEqualToString: newName]) 941 { 942 if([newName isEqualToString: _(@"All")]) 943 { 944 [cell setStringValue: oldName]; 945 NSRunAlertPanel(_(@"Disallowed"), 946 _(@"You cannot rename a group to \"All\",\n" 947 @"since that name is reserved by the system."), 948 _(@"OK"), nil, nil, nil); 949 } 950 else if([groupnames containsObject: newName]) 951 { 952 [cell setStringValue: oldName]; 953 NSRunAlertPanel(_(@"Disallowed"), 954 [NSString stringWithFormat: 955 _(@"You cannot rename this group " 956 @"to \"%@\",\n" 957 @"since a group of that name " 958 @"already exists."), newName], 959 _(@"OK"), nil, nil, nil); 960 } 961 else 962 [group setValue: newName forProperty: ADGroupNameProperty]; 963 [lastCell setEditable: NO]; 964 } 965 966 if(![newName isEqualToString: _(@"All")] && 967 ![oldName isEqualToString: _(@"All")] && 968 ![[cell stringValue] isEqualToString: _(@"All")]) 969 { 970 if([cell isEditable] || _selectedByDrop) 971 [cell setEditable: NO]; 972 else 973 [cell setEditable: YES]; 974 975 _selectedByDrop = NO; 976 977 [lastCell release]; 978 lastCell = [cell retain]; 979 } 980 981 return [_peopleCache count]; 982 } 983 984 return 0; 985} 986 987- (void) browser: (NSBrowser*) sender 988 willDisplayCell: (id) cell 989 atRow: (int) row 990 column: (int) column 991{ 992 [cell setFont: [NSFont systemFontOfSize: [NSFont systemFontSize]]]; 993 if(column == 0) 994 { 995 if(row == 0) 996 [cell setStringValue: _(@"All")]; 997 else 998 { 999 [cell setStringValue: [[self groupNames] objectAtIndex: row-1]]; 1000 [cell setRepresentedObject: [[_book groups] objectAtIndex: row-1]]; 1001 [cell setEditable: NO]; 1002 } 1003 } 1004 else 1005 { 1006 ADPerson *p = [_peopleCache objectAtIndex: row]; 1007 if(p == [_book me]) 1008 [cell setStringValue: [[p screenName] 1009 stringByAppendingString: _(@" (Me)")]]; 1010 else 1011 [cell setStringValue: [p screenName]]; 1012 [cell setRepresentedObject: p]; 1013 [cell setLeaf: YES]; 1014 } 1015} 1016 1017- (void) browserAction: (id) sender 1018{ 1019 if([personView isEditable]) 1020 { 1021 [personView setPerson: nil]; 1022 [personView setEditable: NO]; 1023 [editButton setState: NSOffState]; 1024 } 1025 1026 if([sender selectedColumn] == 0) 1027 { 1028 int row; 1029 ADGroup *group = nil; 1030 [_currentGroup release]; 1031 _currentGroup = nil; 1032 1033 row = [sender selectedRowInColumn: 0]; 1034 if(row != 0) 1035 group = [[_book groups] objectAtIndex: row-1]; 1036 if(![[group uniqueId] isEqualToString: [_currentGroup uniqueId]]) 1037 { 1038 _currentGroup = [group retain]; 1039 [self createCache]; 1040 [sender reloadColumn: 1]; 1041 if([_peopleCache count]) 1042 [self selectPerson: [_peopleCache objectAtIndex: 0]]; 1043 else 1044 [self selectPerson: nil]; 1045 } 1046 } 1047 else 1048 { 1049 NSEnumerator *e; ADPerson *p; BOOL shared; 1050 1051 if([[sender selectedCells] count] == 1) 1052 [personView setPerson: [[sender selectedCellInColumn: 1] 1053 representedObject]]; 1054 else [personView setPerson: nil]; 1055 1056 shared = NO; 1057 e = [[self selectedPersons] objectEnumerator]; 1058 while((p = [e nextObject])) 1059 if([p shared]) 1060 { 1061 shared = YES; 1062 break; 1063 } 1064 1065 if(shared) 1066 { 1067 if([[self selectedPersons] count] > 1) 1068 [shareItem setTitle: _(@"Do not share these people")]; 1069 else 1070 [shareItem setTitle: _(@"Do not share this person")]; 1071 } 1072 else 1073 { 1074 if([[self selectedPersons] count] > 1) 1075 [shareItem setTitle: _(@"Share these people")]; 1076 else 1077 [shareItem setTitle: _(@"Share this person")]; 1078 } 1079 } 1080} 1081 1082- (void) handleDatabaseChanged: (NSNotification*) note 1083{ 1084 if([_book hasUnsavedChanges]) 1085 { 1086 if([prefsAutosaveButton state] == NSOnState) 1087 { 1088 _selfChanging = YES; 1089 [_book save]; 1090 _selfChanging = NO; 1091 [[personView window] setDocumentEdited: NO]; 1092 [[personView window] setTitle: _(@"Addresses")]; 1093 } 1094 else 1095 { 1096 [[personView window] setDocumentEdited: YES]; 1097 [[personView window] setTitle: _(@"Addresses*")]; 1098 } 1099 } 1100 else 1101 { 1102 [[personView window] setDocumentEdited: NO]; 1103 [[personView window] setTitle: _(@"Addresses")]; 1104 } 1105 [self createCache]; 1106} 1107 1108- (void) handleDatabaseChangedExternally: (NSNotification*) note 1109{ 1110 NSString *guid = nil, *uid = nil; 1111 1112 if(_selfChanging) return; 1113 1114 if(_currentGroup) 1115 guid = [[_currentGroup uniqueId] retain]; 1116 if([personView person]) 1117 uid = [[[personView person] uniqueId] retain]; 1118 1119 [self createCache]; 1120 [groupsBrowser reloadColumn: 0]; 1121 [groupsBrowser reloadColumn: 1]; 1122 1123 [self selectGroup: (ADGroup*)[_book recordForUniqueId: guid]]; 1124 if(uid) 1125 { 1126 ADPerson *p = (ADPerson*)[_book recordForUniqueId: uid]; 1127 if(p) [self selectPerson: p]; 1128 } 1129} 1130 1131- (void) handleNameChanged: (NSNotification*) note 1132{ 1133 NSDictionary *userInfo; ADPerson *p; 1134 NSString *first, *last, *prop, *val, *scrName; 1135 ADScreenNameFormat fmt; 1136 1137 if([note object] != [personView person]) 1138 return; 1139 1140 userInfo = [note userInfo]; 1141 p = [note object]; 1142 1143 prop = [userInfo objectForKey: @"Property"]; 1144 val = [userInfo objectForKey: @"Value"]; 1145 if([prop isEqualToString: ADFirstNameProperty]) 1146 { 1147 first = val; 1148 last = [p valueForProperty: ADLastNameProperty]; 1149 } 1150 else 1151 { 1152 first = [p valueForProperty: ADFirstNameProperty]; 1153 last = val; 1154 } 1155 1156 if([first isEmptyString]) first = nil; 1157 if([last isEmptyString]) last = nil; 1158 1159 fmt = [[p class] screenNameFormat]; 1160 1161 if(!last && !first) scrName = _(@"New Person"); 1162 else if(!first) scrName = last; 1163 else if(!last) scrName = first; 1164 else if(fmt == ADScreenNameFirstNameFirst) 1165 scrName = [NSString stringWithFormat: @"%@ %@", first, last]; 1166 else 1167 scrName = [NSString stringWithFormat: @"%@, %@", last, first]; 1168 1169 [[groupsBrowser selectedCellInColumn: 1] setStringValue: scrName]; 1170 [groupsBrowser setNeedsDisplay: YES]; 1171} 1172 1173- (BOOL) application: (NSApplication*) app 1174 openFile: (NSString*) filename 1175{ 1176 id conv; 1177 ADRecord *r, *r1; 1178 1179 conv = [[ADConverterManager sharedManager] inputConverterWithFile: filename]; 1180 if(!conv) return NO; 1181 1182 r1 = nil; 1183 while((r = [conv nextRecord])) 1184 { 1185 if(!r1) r1 = r; 1186 [_book addRecord: r]; 1187 } 1188 if(!r1) return NO; 1189 1190 [self createCache]; 1191 [groupsBrowser reloadColumn: 0]; 1192 [groupsBrowser reloadColumn: 1]; 1193 if([r1 isKindOfClass: [ADPerson class]]) 1194 { 1195 [self selectGroup: _currentGroup]; 1196 [self selectPerson: (ADPerson*)r1]; 1197 } 1198 else 1199 { 1200 [self selectGroup: (ADGroup*)r1]; 1201 if(![_peopleCache count]) [self selectPerson: nil]; 1202 else [self selectPerson: [_peopleCache objectAtIndex: 0]]; 1203 } 1204 1205 return YES; 1206} 1207 1208- (NSApplicationTerminateReply) applicationShouldTerminate: (NSApplication*) app 1209{ 1210 NSUserDefaults *def; 1211 1212 if([personView isEditable]) 1213 [self finishEditingPerson]; 1214 1215 // store current group and person in defaults 1216 def = [NSUserDefaults standardUserDefaults]; 1217 if(_currentGroup) 1218 [def setObject: [_currentGroup uniqueId] 1219 forKey: @"SelectedGroup"]; 1220 else 1221 [def setObject: @"None" forKey: @"SelectedGroup"]; 1222 1223 if([personView person]) 1224 [def setObject: [[personView person] uniqueId] 1225 forKey: @"SelectedPerson"]; 1226 else 1227 [def setObject: @"None" forKey: @"SelectedPerson"]; 1228 1229 1230 if([_book hasUnsavedChanges]) 1231 { 1232 int retval = 1233 NSRunAlertPanel(_(@"Save Changes?"), 1234 _(@"You have made changes to the database.\n" 1235 @"Should these changes be saved?"), 1236 _(@"Save and Quit"), _(@"Quit without saving"), 1237 _(@"Don't quit"), nil); 1238 switch(retval) 1239 { 1240 case 1: 1241 if(![_book save]) 1242 { 1243 NSRunAlertPanel(_(@"Couldn't save"), 1244 _(@"The database could not be saved!"), 1245 _(@"OK"), nil, nil, nil); 1246 return NSTerminateCancel; 1247 } 1248 return NSTerminateNow; 1249 case 0: 1250 return NSTerminateNow; 1251 default: 1252 return NSTerminateCancel; 1253 } 1254 } 1255 else 1256 return NSTerminateNow; 1257} 1258 1259- (BOOL) validateMenuItem: (NSMenuItem*) anItem 1260{ 1261 int count; 1262 1263 count = [[self selectedPersons] count]; 1264 1265 if(anItem == editItem || anItem == duplicatePersonItem || 1266 anItem == thisIsMeItem) 1267 { 1268 if(count != 1) return NO; 1269 return YES; 1270 } 1271 1272 if(anItem == shareItem) 1273 { 1274 if(count < 1) return NO; 1275 return YES; 1276 } 1277 1278 if(anItem == mergePersonsItem) 1279 { 1280 if(count <= 1) return NO; 1281 return YES; 1282 } 1283 1284 return YES; 1285} 1286 1287- (BOOL) personView: (ADPersonView*) aView 1288 shouldAcceptDrop: (id<NSDraggingInfo>) info 1289{ 1290 return YES; 1291} 1292 1293- (BOOL) personView: (ADPersonView*) aView 1294receivedDroppedPersons: (NSArray*) persons 1295{ 1296 int i; 1297 1298 if(![persons count]) return NO; 1299 1300 for(i=0; i<[persons count]; i++) 1301 { 1302 ADPerson *p, *o; 1303 1304 p = [persons objectAtIndex: i]; 1305 o = [_book personWithFirstName: [p valueForProperty: ADFirstNameProperty] 1306 lastName: [p valueForProperty: ADLastNameProperty]]; 1307 if(o) 1308 { 1309 NSString *fmt; 1310 int retval; 1311 1312 fmt = 1313 [NSString stringWithFormat: 1314 _(@"Trying to import person named '%@',\n" 1315 @"which already exists in the database."), 1316 [p screenName]]; 1317 retval = NSRunAlertPanel(_(@"Existing person?"), 1318 fmt, _(@"Replace"), _(@"Insert anyway"), 1319 _(@"Don't insert"), nil); 1320 if(retval == 1) // replace 1321 { 1322 [_book removeRecord: o]; 1323 [_book addRecord: p]; 1324 } 1325 else if(retval == 0) // insert anyway 1326 [_book addRecord: p]; 1327 else if(retval == -1) // don't insert; continue reading 1328 return NO; 1329 } 1330 else 1331 if(![_book addRecord: p]) return NO; 1332 1333 if(_currentGroup) 1334 [_currentGroup addMember: p]; 1335 } 1336 1337 [groupsBrowser reloadColumn: 1]; 1338 [self selectPerson: [persons objectAtIndex: 0]]; 1339 1340 return YES; 1341} 1342 1343- (BOOL) personView: (ADPersonView*) aView 1344 willDragPerson: (ADPerson*) person 1345{ 1346 return YES; 1347} 1348 1349- (BOOL) personView: (ADPersonView*) aView 1350 willDragProperty: (NSString*) property 1351{ 1352 return YES; 1353} 1354 1355- (NSDragOperation) dragDropMatrix: (DragDropMatrix*) matrix 1356 shouldAcceptDropFromSender: (id<NSDraggingInfo>) sender 1357 onCell: (NSCell*) cell 1358{ 1359 ADGroup *g; 1360 1361 g = [cell representedObject]; 1362 if(g && ![g isKindOfClass: [ADGroup class]]) 1363 return NSDragOperationNone; 1364 1365 if([[[sender draggingPasteboard] types] 1366 containsObject: ADPeoplePboardType]) 1367 { 1368 NSDictionary *d; NSEnumerator *e; 1369 BOOL _pureLocal, _didSomeWork; 1370 1371 if(![cell representedObject] || 1372 ![[cell representedObject] isKindOfClass: [ADGroup class]] || 1373 [[[cell representedObject] uniqueId] 1374 isEqualToString: [_currentGroup uniqueId]]) 1375 goto useVCard; 1376 1377 _pureLocal = YES; 1378 _didSomeWork = NO; 1379 e = [[[sender draggingPasteboard] 1380 propertyListForType: ADPeoplePboardType] objectEnumerator]; 1381 while((d = [e nextObject])) 1382 { 1383 int pid; NSString *uid; NSArray *adDescr; 1384 ADPerson *p; 1385 1386 if(![d objectForKey: @"UID"] || 1387 ![d objectForKey: @"AB"] || 1388 ![d objectForKey: @"PID"]) 1389 continue; 1390 1391 pid = [[d objectForKey: @"PID"] intValue]; 1392 uid = [d objectForKey: @"UID"]; 1393 adDescr = [d objectForKey: @"AD"]; 1394 1395 p = (ADPerson*)[_book recordForUniqueId: uid]; 1396 1397 if(pid != [[NSProcessInfo processInfo] processIdentifier]) 1398 _pureLocal = NO; 1399 else if(!p || ![p isKindOfClass: [ADPerson class]]) 1400 continue; 1401 1402 if(g) 1403 { 1404 ADPerson *p2; NSEnumerator *e; 1405 BOOL isIn; 1406 1407 isIn = NO; 1408 e = [[g members] objectEnumerator]; 1409 while((p2 = [e nextObject])) 1410 if([[p2 uniqueId] isEqualToString: [p uniqueId]]) 1411 { 1412 isIn = YES; 1413 break; 1414 } 1415 1416 if(isIn) continue; 1417 } 1418 1419 _didSomeWork = YES; 1420 } 1421 1422 if(_didSomeWork) 1423 { 1424 if(_pureLocal) 1425 return NSDragOperationLink; 1426 return NSDragOperationCopy; 1427 } 1428 } 1429 1430 useVCard: 1431 if([[[sender draggingPasteboard] types] 1432 containsObject: @"NSVCardPboardType"]) 1433 return NSDragOperationCopy; 1434 1435 return NSDragOperationNone; 1436} 1437 1438- (BOOL) dragDropMatrix: (DragDropMatrix*) matrix 1439didAcceptDropFromSender: (id<NSDraggingInfo>) sender 1440 onCell: (NSCell*) cell 1441{ 1442 NSPasteboard *pb; 1443 ADGroup *g; 1444 1445 g = [cell representedObject]; 1446 if(g && ![g isKindOfClass: [ADGroup class]]) 1447 return NO; 1448 1449 pb = [sender draggingPasteboard]; 1450 1451 if([[pb types] containsObject: ADPeoplePboardType]) 1452 { 1453 NSDictionary *d; NSEnumerator *e; 1454 BOOL _didSomeWork; 1455 1456 if(!g) goto useVCard; 1457 1458 _didSomeWork = NO; 1459 e = [[pb propertyListForType: ADPeoplePboardType] objectEnumerator]; 1460 while((d = [e nextObject])) 1461 { 1462 int pid; NSString *uid; NSArray *adDescr; 1463 ADPerson *p; 1464 1465 if(![d objectForKey: @"UID"] || 1466 ![d objectForKey: @"AB"] || 1467 ![d objectForKey: @"PID"]) 1468 continue; 1469 1470 pid = [[d objectForKey: @"PID"] intValue]; 1471 uid = [d objectForKey: @"UID"]; 1472 adDescr = [d objectForKey: @"AD"]; 1473 1474 if(pid != [[NSProcessInfo processInfo] processIdentifier]) 1475 continue; 1476 1477 p = (ADPerson*)[_book recordForUniqueId: uid]; 1478 if(!p || ![p isKindOfClass: [ADPerson class]]) 1479 continue; 1480 1481 if([g addMember: p]) 1482 _didSomeWork = YES; 1483 } 1484 if(_didSomeWork) 1485 return YES; 1486 } 1487 1488 useVCard: 1489 if([[pb types] containsObject: @"NSVCardPboardType"]) 1490 { 1491 ADPerson *p, *o; 1492 NSData *vcard; 1493 1494 vcard = [pb dataForType: @"NSVCardPboardType"]; 1495 p = [[[ADPerson alloc] initWithVCardRepresentation: vcard] autorelease]; 1496 if(!p) 1497 return NO; 1498 1499 o = [_book personWithFirstName: [p valueForProperty: ADFirstNameProperty] 1500 lastName: [p valueForProperty: ADLastNameProperty]]; 1501 if(o) 1502 { 1503 NSString *fmt; 1504 int retval; 1505 1506 fmt = 1507 [NSString stringWithFormat: 1508 _(@"Trying to import person named '%@',\n" 1509 @"which already exists in the database."), 1510 [p screenName]]; 1511 retval = NSRunAlertPanel(_(@"Existing person?"), 1512 fmt, _(@"Replace"), _(@"Insert anyway"), 1513 _(@"Don't insert"), nil); 1514 if(retval == 1) // replace 1515 { 1516 [_book removeRecord: o]; 1517 [_book addRecord: p]; 1518 } 1519 else if(retval == 0) // insert anyway 1520 [_book addRecord: p]; 1521 else if(retval == -1) // don't insert; continue reading 1522 return NO; 1523 } 1524 else 1525 if(![_book addRecord: p]) return NO; 1526 1527 p = (ADPerson*)[_book recordForUniqueId: [p uniqueId]]; 1528 if(!p || ![p isKindOfClass: [ADPerson class]]) return NO; 1529 1530 if(g) 1531 [g addMember: p]; 1532 1533 [groupsBrowser reloadColumn: 1]; 1534 if(g) 1535 { 1536 [self selectGroup: g]; 1537 _selectedByDrop = YES; 1538 } 1539 [self selectPerson: p]; 1540 1541 return YES; 1542 } 1543 1544 return NO; 1545} 1546@end 1547 1548