Logo Search packages:      
Sourcecode: addresses-for-gnustep version File versions  Download package

Controller.m

#include <AppKit/AppKit.h>
#include "AddressView/ADPersonView.h"
#include "Controller.h"
#include "DragDropMatrix.h"

@interface ADAddressBook (AddressManagerAdditions)
- (ADPerson*) personWithFirstName: (NSString*) first
                   lastName: (NSString*) last;
@end

@implementation ADAddressBook (AddressManagerAdditions)
- (ADPerson*) personWithFirstName: (NSString*) first
                   lastName: (NSString*) last
{
  NSEnumerator *e; ADPerson *p;
  e = [[self people] objectEnumerator];
  while((p = [e nextObject]))
    if([[p valueForProperty: ADFirstNameProperty]
       isEqualToString: first] &&
       [[p valueForProperty: ADLastNameProperty]
       isEqualToString: last])
      return p;
  return nil;
}
@end

@interface Controller (Private)
- (void) browserAction: (id) sender;
@end

@implementation Controller
- (void) applicationDidFinishLaunching: (NSNotification*) note
{
  NSUserDefaults *def;
  NSString *uid;
  
  [NSApp registerServicesMenuSendTypes: [NSArray arrayWithObjects:
                                       NSStringPboardType,
                                     nil]
       returnTypes: nil];
  
  servicesMenu = [[[NSApp mainMenu]
                itemWithTitle: @"Services"]
               submenu];
  [NSApp setServicesMenu: servicesMenu];

  def = [NSUserDefaults standardUserDefaults];
  uid = [def stringForKey: @"SelectedGroup"];
  if(uid && ![uid isEqualToString: @"None"])
    [self selectGroup: (ADGroup*) [_book recordForUniqueId: uid]];
  else
    [self selectGroup: nil];

  uid = [def stringForKey: @"SelectedPerson"];
  if(uid && ![uid isEqualToString: @"None"])
    [self selectPerson: (ADPerson*)[_book recordForUniqueId: uid]];
  else
    [groupsBrowser selectRow: 0 inColumn: 1];

  [[NSNotificationCenter defaultCenter]
    addObserver: self
    selector: @selector(handleDatabaseChanged:)
    name: ADDatabaseChangedNotification
    object: nil];
  [[NSNotificationCenter defaultCenter]
    addObserver: self
    selector: @selector(handleDatabaseChangedExternally:)
    name: ADDatabaseChangedExternallyNotification
    object: nil];
  [[NSNotificationCenter defaultCenter]
    addObserver:self
    selector: @selector(handleNameChanged:)
    name: ADPersonNameChangedNotification
    object: nil];
}

- (void) awakeFromNib
{
  NSString *filename;
  
  _fm = [NSFileManager defaultManager];
  _book = [ADAddressBook sharedAddressBook];
  _selfChanging = NO;
  _selectedByDrop = NO;

  if(!_book)
    {
      NSRunAlertPanel(_(@"No Address Book"),
                  _(@"[ADAddressBook sharedAddressBook] returned nil.\n"
                  @"Configuration broken?"),
                  _(@"OK"), nil, nil, nil);
      exit(-1);
    }

  [[groupsBrowser window] setFrameAutosaveName: @"Addresses"];
  [[groupsBrowser window] setFrameUsingName: @"Addresses"];
  [prefsPanel setFrameAutosaveName: @"Preferences"];
  [prefsPanel setFrameUsingName: @"Preferences"];
  
  [groupsBrowser setAllowsEmptySelection: NO];
  [groupsBrowser setAllowsMultipleSelection: YES];
  [groupsBrowser setMaxVisibleColumns: 2];
  [groupsBrowser setDelegate: self];
  [groupsBrowser setTitle: _(@"Group") ofColumn: 0];
  [groupsBrowser setTitle: _(@"Name") ofColumn: 1];
  [groupsBrowser setMatrixClass: [DragDropMatrix class]];
  [groupsBrowser setTarget: self];
  [groupsBrowser setAction: @selector(browserAction:)];

  clipView = [[NSClipView alloc] initWithFrame: [scrollView frame]];
  [clipView setAutoresizesSubviews: YES];
  personView = [[ADPersonView alloc] initWithFrame: NSZeroRect];
  [clipView setDocumentView: personView];
  [personView setFillsSuperview: YES];
  [personView setForceImage: YES];
            
  [scrollView setContentView: clipView];
  [scrollView setHasVerticalScroller: YES];
  [scrollView setHasHorizontalScroller: YES];
  [scrollView setBorderType: NSBezelBorder];

  [personView setDelegate: self];

  filename = [[NSBundle mainBundle] pathForResource: @"ISOCountryCodes"
                            ofType: @"dict"];
  _countryCodeDict = [[[NSString stringWithContentsOfFile: filename]
                  propertyList] retain];
  NSAssert(_countryCodeDict, @"ISOCountryCodes.dict could not be loaded.");

  [self createCache];
  if([_peopleCache count])
    [self selectPerson: [_peopleCache objectAtIndex: 0]];
  
  [self initPrefsPanel];
}

- (void) initPrefsPanel
{
  NSUserDefaults *ud;
  NSEnumerator *e; NSString *key;
  
  ud = [NSUserDefaults standardUserDefaults];
  if(![ud objectForKey: @"Autosave"] ||
     ![[ud objectForKey: @"Autosave"] boolValue])
    [prefsAutosaveButton setState: NSOffState];
  else
    [prefsAutosaveButton setState: NSOnState];

  [prefsAddressLayoutPopup removeAllItems];
  e = [[[_countryCodeDict allKeys]
       sortedArrayUsingSelector: @selector(compare:)]
      objectEnumerator];
  while((key = [e nextObject]))
    {
      [prefsAddressLayoutPopup
      addItemWithTitle: [_countryCodeDict objectForKey: key]];
      [[prefsAddressLayoutPopup
       itemWithTitle: [_countryCodeDict objectForKey: key]]
      setRepresentedObject: key];
    }
  [prefsAddressLayoutPopup addItemWithTitle: _(@"Everything")];
  [[prefsAddressLayoutPopup itemWithTitle: _(@"Everything")]
    setRepresentedObject: @"Default"];
  [prefsAddressLayoutPopup sizeToFit];

  if([ud objectForKey: @"DefaultISOCountryCode"])
    {
      NSString *def;
      int index;

      def = [ud objectForKey: @"DefaultISOCountryCode"];
      index = [prefsAddressLayoutPopup indexOfItemWithRepresentedObject: def];
      if(index != NSNotFound)
      [prefsAddressLayoutPopup selectItemAtIndex: index];
      [[personView class] setDefaultISOCountryCode: def];
    }

  if([[ADPerson class] screenNameFormat] == ADScreenNameFirstNameFirst)
    [prefsScreenNameLayoutMatrix selectCellWithTag: 1];
  else
    [prefsScreenNameLayoutMatrix selectCellWithTag: 0];
}

- (void) createCache
{
  SEL sortingSelector;
    
  sortingSelector =
    GSSelectorFromNameAndTypes("compareByScreenName:", "i12@0:4@8");

  [_peopleCache release];
  if(_currentGroup)
    _peopleCache = [_currentGroup members];
  else
    _peopleCache = [_book people];
  _peopleCache =
    [[_peopleCache sortedArrayUsingSelector: sortingSelector]
      retain];
}

- (NSArray*) groupNames
{
  NSArray *groups;
  NSMutableArray *retval;
  int i;

  groups = [_book groups];
  retval = [NSMutableArray arrayWithCapacity: [groups count]];

  for(i=0; i<[groups count]; i++)
    {
      NSString *name;

      name = [[groups objectAtIndex: i]
             valueForProperty: ADGroupNameProperty];
      if(!name) NSLog(@"Group at %d has no name!\n", i);
      else [retval addObject: name];
    }
  return retval;
}

- (void) selectGroup: (ADGroup*) group
{
  int num, i;

  if([personView isEditable])
    [self finishEditingPerson];
  
  if(!group)
    {
      [[groupsBrowser matrixInColumn: 0] deselectAllCells];
      [groupsBrowser selectRow: 0 inColumn: 0];
      [_currentGroup autorelease];
      _currentGroup = nil;
      [self createCache];

      [groupsBrowser reloadColumn: 1];
      
      if([_peopleCache count])
      [self selectPerson: [_peopleCache objectAtIndex: 0]];
      else
      [self selectPerson: nil];

      return;
    }

  num = [[groupsBrowser matrixInColumn: 0] numberOfRows];
  for(i=0; i<num; i++)
    {
      ADRecord *r;
      
      r = [[groupsBrowser loadedCellAtRow: i column: 0] representedObject];
      if([[r uniqueId] isEqualToString: [group uniqueId]])
      {
        [[groupsBrowser matrixInColumn: 0] deselectAllCells];
        [groupsBrowser selectRow: i inColumn: 0];

        [_currentGroup autorelease];
        _currentGroup = [group retain];
        [self createCache];

        [groupsBrowser reloadColumn: 1];
        
        if([_peopleCache count])
          [self selectPerson: [_peopleCache objectAtIndex: 0]];
        else
          [self selectPerson: nil];
        return;
      }
    }

  NSLog(@"Group %@ not found in column 0!\n", [group uniqueId]);
}

  
- (void) selectPerson: (ADPerson*) person
{
  int i;

  if([personView isEditable])
    [self finishEditingPerson];

  if(!person)
    {
      [personView setPerson: nil];
      return;
    }

  for(i=0; i<[_peopleCache count]; i++)
    if([[[_peopleCache objectAtIndex: i] uniqueId]
       isEqualToString: [person uniqueId]])
      break;
  
  if(i==[_peopleCache count]) // Not found? Select "all" and try again
    {
      NSArray *all;
      SEL sortingSelector;
    
      sortingSelector =
      GSSelectorFromNameAndTypes("compareByScreenName:", "i12@0:4@8");


      all = [[_book people] sortedArrayUsingSelector: sortingSelector];
      for(i=0; i<[all count]; i++)
      {
        if([[[all objectAtIndex: i] uniqueId]
             isEqualToString: [person uniqueId]])
          {
            [self selectGroup: nil];
            break;
          }
      }
    }

  if(i==[_peopleCache count]) // Still not found? WEIRD.
    {
      NSLog(@"Person %@ not found!\n", [person uniqueId]);
      return;
    }

  // HACK to avoid NSBrowser to keep its old selection; we want only
  // the new one.
  [groupsBrowser setAllowsMultipleSelection: NO];
  [groupsBrowser selectRow: i inColumn: 1];
  [groupsBrowser setAllowsMultipleSelection: YES];

  [personView setPerson: [_peopleCache objectAtIndex: i]];
  [clipView scrollToPoint: NSZeroPoint];

  if([person readOnly])
    {
      [editButton setEnabled: NO];
      [editItem setEnabled: NO];
    }
  else
    {
      [editButton setEnabled: YES];
      [editItem setEnabled: YES];
    }
}

- (void) deletePersonAndSelectNext: (ADPerson*) person
{
  int row = [groupsBrowser selectedRowInColumn: 1];

  if(_currentGroup)
    [_currentGroup removeMember: person];
  else
    [_book removeRecord: person];

  [self createCache];
  [groupsBrowser reloadColumn: 1];

  if(![_peopleCache count])
    [personView setPerson: nil];
  else if(row >= [_peopleCache count])
    [self selectPerson: [_peopleCache objectAtIndex: [_peopleCache count]-1]];
  else
    [self selectPerson: [_peopleCache objectAtIndex: row]];
}

- (void) beginEditingPerson: (ADPerson*) person
{
  if([person readOnly])
    {
      NSRunAlertPanel(_(@"Read-Only Person"),
                  [NSString stringWithFormat:
                  _(@"'%@' cannot be edited because\n"
                    @"the record is marked as read-only."), [person screenName]],
                  _(@"OK"), nil, nil, nil);
      [editButton setState: NSOffState];
      [editItem setTitle: _(@"Edit person")];
      return;
    }
  [self selectPerson: person];
  [personView setEditable: YES];
  [editItem setTitle: _(@"End editing")];
  [editButton setState: NSOnState];
  [clipView scrollToPoint: NSZeroPoint];
};

- (void) finishEditingPerson
{
  ADPerson *p;

  p = [personView person];
  if(!p || ![personView isEditable]) return;

  if(![p valueForProperty: ADLastNameProperty] ||
     ![p valueForProperty: ADFirstNameProperty])
    {
      if(NSRunAlertPanel(_(@"Discard Person?"),
                   _(@"The person you have edited has no first or last\n"
                     @"names. Would you like to discard this person?"),
                   _(@"Yes"), _(@"No"), nil, nil))
      {
          [personView setEditable: NO];
          [self deletePersonAndSelectNext: p];
          p = nil;
      }
    }
  [personView setEditable: NO];
  
  [editItem setTitle: _(@"Edit Person")];
  [editButton setState: NSOffState];

  if(p)
    {
        [groupsBrowser reloadColumn: 1];
      [self selectPerson: p];
    }
}

- (NSArray*) selectedPersons
{
  NSMutableArray *arr;
  NSEnumerator *e;
  NSCell *c;

  e = [[groupsBrowser selectedCells] objectEnumerator];
  arr = [NSMutableArray arrayWithCapacity: [[groupsBrowser selectedCells]
                                   count]];
  while((c = [e nextObject]))
    if([c representedObject])
      [arr addObject: [c representedObject]];

  return [NSArray arrayWithArray: arr];
}
  
/*
  Actions
*/

- (void) doEditPerson: (id) sender
{
  ADPerson *p;

  p = [personView person];
  if(!p) return; // nothing to do
  if([personView isEditable])
    [self finishEditingPerson];
  else
    [self beginEditingPerson: p];
  [clipView scrollToPoint: NSZeroPoint];
}

- (void) doTogglePersonEditable: (id) sender
{
  ADPerson *p;

  p = [personView person];
  if(!p && [sender state] == NSOnState)
    [self doCreatePerson: sender];
  else if([sender state] == NSOnState)
    [self beginEditingPerson: p];
  else
    [self finishEditingPerson];
  [clipView scrollToPoint: NSZeroPoint];
}

- (void) doCreatePerson: (id) sender
{
  BOOL ok;
  ADPerson *p;

  p = [[ADPerson alloc] init];

  ok = [_book addRecord: p];
  if(!ok)
    {
      NSRunAlertPanel(_(@"Couldn't create person"),
                  _(@"A new person could not be created."),
                  _(@"OK"), nil, nil, nil);
      return;
    }

  if(_currentGroup)
    {
      ok = [_currentGroup addMember: p];
      if(!ok)
      {
        NSRunAlertPanel(_(@"Couldn't add person"),
                    _(@"The newly created person could not be\n"
                      @"added to this group."),
                    _(@"OK"), nil, nil, nil);
        return;
      }
    }

  p = (ADPerson*)[_book recordForUniqueId: [p uniqueId]];
    
  [self createCache];
  [groupsBrowser reloadColumn: 1];
  [self beginEditingPerson: p];
  [personView beginEditingInFirstCell];
}

- (void) doDeletePerson: (id) sender
{
  NSArray *a;
  NSEnumerator *e;
  ADPerson *p;

  a = [self selectedPersons];
  if(![a count]) return; // nothing to do.

  // deleting from "All"? Ask.
  if(!_currentGroup)
    {
      NSString *msg, *cpt;
      if([a count] == 1)
      {
        msg = [NSString stringWithFormat: _(@"Do you really want to delete %@ "
                                    @"from \"All\" and all groups?"),
                    [[a objectAtIndex: 0] screenName]];
        cpt = _(@"Delete Person?");
      }
      else
      {
        msg = [NSString stringWithFormat: _(@"Do you really want to delete "
                                    @"the %d selected persons "
                                    @"from \"All\" and all groups?"),
                    [a count]];
        cpt = _(@"Delete Persons?");
      }
      if(!NSRunAlertPanel(cpt, msg,
                    _(@"Yes"), _(@"No"), nil, nil))
      {
        NSLog(@"Not deleting.\n");
        return;
      }
    }

  e = [a objectEnumerator];
  while((p = [e nextObject]))
    [self deletePersonAndSelectNext: p];
}

- (void) doImportPerson: (id) sender
{
  int retval;
  id obj;
  int loaded = 0;
  NSString *fname;
  id<ADInputConverting> conv;
  ADConverterManager *man;
  NSOpenPanel *p;
  
  man = [ADConverterManager sharedManager];
  
  p = [NSOpenPanel openPanel];

  [p setDirectory: [[NSUserDefaults standardUserDefaults]
                 objectForKey: @"ImportDirectory"]];

  [p setCanChooseDirectories: NO];
  [p setAllowsMultipleSelection: NO];
  [p setTitle: _(@"Import...")];
  retval = [p runModalForTypes: [man inputConvertableFileTypes]];
  if(!retval)
    return;

  [[NSUserDefaults standardUserDefaults]
    setObject: [p directory] forKey: @"ImportDirectory"];

  fname = [[p filenames] objectAtIndex: 0];
  conv = [man inputConverterWithFile: fname];
  NSAssert(conv, @"No converter for this file!");

  while((obj = [conv nextRecord]))
    {
      NSEnumerator *e = [_peopleCache objectEnumerator];
      id other;
      int retval;

      loaded++;
      
      retval = 0; // insert anyway
      while((other = [e nextObject]))
      {
        if([[other screenName] isEqualToString: [obj screenName]])
          {
            NSString *fmt; 
            fmt =
            [NSString stringWithFormat:
                      _(@"Trying to import person named '%@',\n"
                        @"which already exists in the database."),
                    [obj screenName]];
            retval = NSRunAlertPanel(_(@"Existing person?"),
                               fmt, _(@"Replace"), _(@"Insert anyway"),
                               _(@"Don't insert"), nil);
            break;
          }
      }
      if(retval == 1) // replace
      {
        [_book removeRecord: other];
        [_book addRecord: obj];
      }
      else if(retval == 0) // insert anyway
      [_book addRecord: obj];
      else if(retval == -1) // don't insert; continue reading
      continue;
    }
  [groupsBrowser reloadColumn: 1];
}

- (void) doExportPerson: (id) sender
{
  ADConverterManager *man;
  NSSavePanel *panel;
  NSString *fname;
  int retval;
  NSArray *a;
  NSEnumerator *e; ADPerson *person;
  id<ADOutputConverting> conv;

  if([personView isEditable])
    [self finishEditingPerson];
  
  a = [self selectedPersons];
  if(![a count]) return;
  
  man = [ADConverterManager sharedManager];
  panel = [NSSavePanel savePanel];

  [panel setDirectory: [[NSUserDefaults standardUserDefaults]
                   objectForKey: @"ExportDirectory"]];
  [panel setRequiredFileType: @"vcf"];
  if([a count] > 1)
    [panel
      setTitle: [NSString stringWithFormat: _(@"Export %d records to..."),
                    [a count]]];
  else
    [panel
      setTitle: [NSString stringWithFormat: _(@"Export '%@' to..."),
                    [[a objectAtIndex: 0] screenName]]];
      
  
  retval = [panel runModal];
  if(!retval)
    return;

  [[NSUserDefaults standardUserDefaults]
    setObject: [panel directory]
    forKey: @"ExportDirectory"];
  
  fname = [panel filename];
  conv = [man outputConverterForType: [[fname pathExtension] lowercaseString]];
  if(!conv)
    {
      NSString *msg =
      [NSString stringWithFormat: _(@"Cannot export files of type %@"),
              [fname pathExtension]];
      NSRunAlertPanel(_(@"Invalid File Type"), msg, _(@"OK"), nil, nil, nil);
      return;
    }
  else if([a count]>1 && ![conv canStoreMultipleRecords])
    {
      NSString *msg =
      [NSString stringWithFormat: _(@"Can only store a single person\n"
                              @"in files of type %@"),
              [fname pathExtension]];
      NSRunAlertPanel(_(@"Invalid File Type"), msg, _(@"OK"), nil, nil, nil);
      return;
    }      

  e = [a objectEnumerator];
  while((person = [e nextObject]))
    [conv storeRecord: person];

  retval = [[conv string] writeToFile: fname atomically: NO];
  if(!retval)
    {
      NSString *msg =
      [NSString stringWithFormat: _(@"Could not write file %@.\n"
                              @"Permissions error?"),
              fname];
      NSRunAlertPanel(_(@"Write Failed"), msg, _(@"OK"), nil, nil, nil);
    }      
}

- (void) doSetMe: (id) sender
{
  if(![personView person]) return;
  [_book setMe: [personView person]];
  [personView setNeedsDisplay: YES];
  [groupsBrowser reloadColumn: 1];
  [self selectPerson: [personView person]];
}

- (void) doShowMe: (id) sender
{
  if(![_book me]) return;
  [self selectPerson: [_book me]];
}

- (void) doSelectAllPersons: (id) sender
{
  [groupsBrowser selectAll: self];
  if([[groupsBrowser selectedCells] count] == 1)
    [personView setPerson: [[groupsBrowser selectedCellInColumn: 1]
                       representedObject]];
  else [personView setPerson: nil];
}

- (void) doToggleShared: (id) sender
{
  NSEnumerator *e;
  BOOL share;
  ADPerson *person;

  share = YES; // default yes, but if one selected person is shared,
             // set to no
  e = [[self selectedPersons] objectEnumerator];
  while((person = [e nextObject]))
    if([person shared])
      {
      share = NO;
      break;
      }

  e = [[self selectedPersons] objectEnumerator];
  while((person = [e nextObject]))
    [person setShared: share];

  if(share)
    {
      if([[self selectedPersons] count] > 1)
      [shareItem setTitle: _(@"Do not share these people")];
      else
      [shareItem setTitle: _(@"Do not share this person")];
    }
  else
    {    
      if([[self selectedPersons] count] > 1)
      [shareItem setTitle: _(@"Share these people")];
      else
      [shareItem setTitle: _(@"Share this person")];
    }

  [personView setNeedsDisplay: YES];
}

- (void) doDuplicatePerson: (id) sender
{
  ADPerson *newPerson, *oldPerson;
  BOOL ok;

  oldPerson = [personView person];
  if(!oldPerson) return;

  newPerson = [oldPerson copy];
  [newPerson removeValueForProperty: ADFirstNameProperty];
  [newPerson removeValueForProperty: ADLastNameProperty];

  ok = [_book addRecord: newPerson];
  if(!ok)
    {
      NSRunAlertPanel(_(@"Couldn't create person"),
                  _(@"A new person could not be created."),
                  _(@"OK"), nil, nil, nil);
      return;
    }
  if(_currentGroup)
    {
      ok = [_currentGroup addMember: newPerson];
      if(!ok)
      {
        NSRunAlertPanel(_(@"Couldn't add person"),
                    _(@"The newly created person could not be\n"
                      @"added to this group."),
                    _(@"OK"), nil, nil, nil);
        return;
      }
    }

  [self createCache];
  [groupsBrowser reloadColumn: 1];
  [self beginEditingPerson: newPerson];
  [personView beginEditingInFirstCell];
}

- (void) doMergePersons: (id) sender
{
  // FIXME: Unimplemented!
}

- (void) doCreateGroup: (id)sender
{
  ADGroup *group;
  
  group = [[[ADGroup alloc] init] autorelease];
  [group setValue: _(@"New Group") forProperty: ADGroupNameProperty];
  if(![_book addRecord: group])
    NSRunAlertPanel(_(@"Error"), _(@"Could not create group!"),
                _(@"OK"), nil, nil, nil);
  [groupsBrowser reloadColumn: 0];

  [self selectGroup: group];
}

- (void) doDeleteGroup: (id) sender
{
  ADGroup *group;
  NSString *name;
  NSString *msg;
  int retval;
  int row = [groupsBrowser selectedRowInColumn: 0];

  if(row == 0) return; // Can't delete this

  group = [[_book groups] objectAtIndex: row-1];
  name = [group valueForProperty: ADGroupNameProperty];

  msg =
    [NSString stringWithFormat: _(@"Do you really want to delete "
                          @"the group '%@'?"),
            name];
  retval = NSRunAlertPanel(_(@"Delete Group?"), msg,
                         _(@"Yes"), _(@"No"), nil, nil);
  if(!retval) return;

  if(![_book removeRecord: group])
    {
      msg = 
      [NSString stringWithFormat: _(@"The group '%@` could not be deleted."),
              name];
      NSRunAlertPanel(_(@"Error"), msg, _(@"OK"), nil, nil, nil);
    }

  lastCell = nil;
  [groupsBrowser reloadColumn: 0];
  [groupsBrowser selectRow: row-1 inColumn: 0]; // OK -- we caught 0 above
  [self browserAction: groupsBrowser];
  [self createCache];
  if([_peopleCache count])
    [self selectPerson: [_peopleCache objectAtIndex: 0]];
}

- (void) doSaveDatabase: (id) sender
{
  if([personView isEditable])
    [self finishEditingPerson];
  if([_book hasUnsavedChanges])
    {
      _selfChanging = YES;

      if(![_book save])
      NSRunAlertPanel(_(@"Couldn't save"),
                  _(@"The database could not be saved!"),
                  _(@"OK"), nil, nil, nil);
      else
      [[personView window] setTitle: _(@"Addresses")];

      _selfChanging = NO;
    }
}

- (void) doShowPrefsPanel: (id) sender
{
  [prefsPanel makeKeyAndOrderFront: self];
}

- (void) prefsToggleAutosave: (id) sender
{
  if([sender state] == NSOnState)
    [[NSUserDefaults standardUserDefaults]
      setObject: @"YES" forKey: @"Autosave"];
  else
    [[NSUserDefaults standardUserDefaults]
      setObject: @"NO" forKey: @"Autosave"];
}

- (void) prefsChangeAddressLayout: (id) sender
{
  NSString *code, *title; NSEnumerator *e;

  title = [[sender selectedItem] title];
  e = [_countryCodeDict keyEnumerator];
  while((code = [e nextObject]))
    if([[_countryCodeDict objectForKey: code] isEqualToString: title])
      break;
  if(!code && [title isEqualToString: _(@"Everything")])
    code = @"Default";
  if(!code) return;
  
  [[NSUserDefaults standardUserDefaults]
    setObject: code forKey: @"DefaultISOCountryCode"];
  [[personView class] setDefaultISOCountryCode: code];
  [personView layout];
}

- (void) prefsChangeScreenNameLayout: (id) sender
{
  int tag;
  ADPerson *p;

  tag = [sender selectedTag];
  [[ADPerson class] setScreenNameFormat: tag];
  // FIXME: Doesn't change in remote address books

  p = [personView person];
  [self createCache];
  [groupsBrowser reloadColumn: 1];
  if(p) [self selectPerson: p];
}

/*
 *  Browser delegate methods
 */

- (int) browser: (NSBrowser*) sender
numberOfRowsInColumn: (int) column
{
  NSArray *groupnames = [self groupNames];

  if(column == 0)
    return [groupnames count]+1;
  else
    {
      NSCell *cell;
      NSString *oldName = @"";
      NSString *newName = @"";
      ADGroup *group = nil;

      cell = [sender selectedCellInColumn: 0];

      if([sender selectedRowInColumn: 0] != 0)
      {
        group =
          [[_book groups] objectAtIndex: [sender selectedRowInColumn: 0]-1];
        oldName = [group valueForProperty: ADGroupNameProperty];
        newName = [cell stringValue];
      }
      
      // stop editing; rename if necessary
      [lastCell setEditable: NO];
      if(cell == lastCell && ![oldName isEqualToString: newName])
      {
        if([newName isEqualToString: _(@"All")])
          {
            [cell setStringValue: oldName];
            NSRunAlertPanel(_(@"Disallowed"),
                        _(@"You cannot rename a group to \"All\",\n"
                        @"since that name is reserved by the system."),
                        _(@"OK"), nil, nil, nil);
          }
        else if([groupnames containsObject: newName])
          {
            [cell setStringValue: oldName];
            NSRunAlertPanel(_(@"Disallowed"),
                        [NSString stringWithFormat:
                                _(@"You cannot rename this group "
                                  @"to \"%@\",\n"
                                  @"since a group of that name "
                                  @"already exists."), newName],
                        _(@"OK"), nil, nil, nil);
          }
        else
          [group setValue: newName forProperty: ADGroupNameProperty];
        [lastCell setEditable: NO];
      }

      if(![newName isEqualToString: _(@"All")] &&
       ![oldName isEqualToString: _(@"All")] &&
       ![[cell stringValue] isEqualToString: _(@"All")])
      {
        if([cell isEditable] || _selectedByDrop)
          [cell setEditable: NO];
        else
          [cell setEditable: YES];

        _selectedByDrop = NO;
        
        [lastCell release];
        lastCell = [cell retain];
      }
      
      return [_peopleCache count];
    }

  return 0;
}

- (void) browser: (NSBrowser*) sender
 willDisplayCell: (id) cell
         atRow: (int) row
        column: (int) column
{
  [cell setFont: [NSFont systemFontOfSize: [NSFont systemFontSize]]];
  if(column == 0)
    {
      if(row == 0)
      [cell setStringValue: _(@"All")];
      else
      {
        [cell setStringValue: [[self groupNames] objectAtIndex: row-1]];
        [cell setRepresentedObject: [[_book groups] objectAtIndex: row-1]];
        [cell setEditable: NO];
      }
    }
  else
    {
      ADPerson *p = [_peopleCache objectAtIndex: row];
      if(p == [_book me])
      [cell setStringValue: [[p screenName]
                        stringByAppendingString: _(@" (Me)")]];
      else
      [cell setStringValue: [p screenName]];
      [cell setRepresentedObject: p];
      [cell setLeaf: YES];
    }      
}

- (void) browserAction: (id) sender
{
  if([personView isEditable])
    {
      [personView setPerson: nil];
      [personView setEditable: NO];
      [editButton setState: NSOffState];
    }
  
  if([sender selectedColumn] == 0)
    {
      int row;
      ADGroup *group = nil;
      [_currentGroup release];
      _currentGroup = nil;

      row = [sender selectedRowInColumn: 0];
      if(row != 0)
      group = [[_book groups] objectAtIndex: row-1];
      if(![[group uniqueId] isEqualToString: [_currentGroup uniqueId]])
      {
        _currentGroup = [group retain];
        [self createCache];
        [sender reloadColumn: 1];
        if([_peopleCache count])
          [self selectPerson: [_peopleCache objectAtIndex: 0]];
        else
          [self selectPerson: nil];
      }
    }
  else
    {
      NSEnumerator *e; ADPerson *p; BOOL shared;
      
      if([[sender selectedCells] count] == 1)
      [personView setPerson: [[sender selectedCellInColumn: 1]
                         representedObject]];
      else [personView setPerson: nil];

      shared = NO;
      e = [[self selectedPersons] objectEnumerator];
      while((p = [e nextObject]))
      if([p shared])
        {
          shared = YES;
          break;
        }

      if(shared)
      {
        if([[self selectedPersons] count] > 1)
          [shareItem setTitle: _(@"Do not share these people")];
        else
          [shareItem setTitle: _(@"Do not share this person")];
      }
      else
      {    
        if([[self selectedPersons] count] > 1)
          [shareItem setTitle: _(@"Share these people")];
        else
          [shareItem setTitle: _(@"Share this person")];
      }
    }
}

- (void) handleDatabaseChanged: (NSNotification*) note
{
  if([_book hasUnsavedChanges])
    {
      if([prefsAutosaveButton state] == NSOnState)
      {
        _selfChanging = YES;
        [_book save];
        _selfChanging = NO;
        [[personView window] setDocumentEdited: NO];
        [[personView window] setTitle: _(@"Addresses")];
      }
      else
      {
        [[personView window] setDocumentEdited: YES];
        [[personView window] setTitle: _(@"Addresses*")];
      }
    }
  else
    {
      [[personView window] setDocumentEdited: NO];
      [[personView window] setTitle: _(@"Addresses")];
    }
  [self createCache];
}

- (void) handleDatabaseChangedExternally: (NSNotification*) note
{
  NSString *guid = nil, *uid = nil;

  if(_selfChanging) return;

  if(_currentGroup)
    guid = [[_currentGroup uniqueId] retain];
  if([personView person])
    uid = [[[personView person] uniqueId] retain];

  [self createCache];
  [groupsBrowser reloadColumn: 0];
  [groupsBrowser reloadColumn: 1];

  [self selectGroup: (ADGroup*)[_book recordForUniqueId: guid]];
  if(uid)
    {
      ADPerson *p = (ADPerson*)[_book recordForUniqueId: uid];
      if(p) [self selectPerson: p];
    }
}

- (void) handleNameChanged: (NSNotification*) note
{
  NSDictionary *userInfo; ADPerson *p;
  NSString *first, *last, *prop, *val, *scrName;
  ADScreenNameFormat fmt;
  
  if([note object] != [personView person])
    return;

  userInfo = [note userInfo];
  p = [note object];

  prop = [userInfo objectForKey: @"Property"];
  val = [userInfo objectForKey: @"Value"];
  if([prop isEqualToString: ADFirstNameProperty])
    {
      first = val;
      last = [p valueForProperty: ADLastNameProperty];
    }
  else
    {
      first = [p valueForProperty: ADFirstNameProperty];
      last = val;
    }

  if([first isEmptyString]) first = nil;
  if([last isEmptyString]) last = nil;

  fmt = [[p class] screenNameFormat];
  
  if(!last && !first) scrName = _(@"New Person");
  else if(!first) scrName = last;
  else if(!last) scrName = first;
  else if(fmt == ADScreenNameFirstNameFirst)
    scrName = [NSString stringWithFormat: @"%@ %@", first, last];
  else
    scrName = [NSString stringWithFormat: @"%@, %@", last, first];

  [[groupsBrowser selectedCellInColumn: 1] setStringValue: scrName];
  [groupsBrowser setNeedsDisplay: YES];
}

- (BOOL) application: (NSApplication*) app
          openFile: (NSString*) filename
{
  id conv;
  ADRecord *r, *r1;

  conv = [[ADConverterManager sharedManager] inputConverterWithFile: filename];
  if(!conv) return NO;

  r1 = nil;
  while((r = [conv nextRecord]))
    {
      if(!r1) r1 = r;
      [_book addRecord: r];
    }
  if(!r1) return NO;

  [self createCache];
  [groupsBrowser reloadColumn: 0];
  [groupsBrowser reloadColumn: 1];
  if([r1 isKindOfClass: [ADPerson class]])
    {
      [self selectGroup: _currentGroup];
      [self selectPerson: (ADPerson*)r1];
    }
  else
    {
      [self selectGroup: (ADGroup*)r1];
      if(![_peopleCache count]) [self selectPerson: nil];
      else [self selectPerson: [_peopleCache objectAtIndex: 0]];
    }
    
  return YES;
}

- (BOOL) applicationShouldTerminate: (NSApplication*) app
{
  NSUserDefaults *def;
  
  if([personView isEditable])
    [self finishEditingPerson];

  // store current group and person in defaults
  def = [NSUserDefaults standardUserDefaults];
  if(_currentGroup)
    [def setObject: [_currentGroup uniqueId]
       forKey: @"SelectedGroup"];
  else
    [def setObject: @"None" forKey: @"SelectedGroup"];
  
  if([personView person])
    [def setObject: [[personView person] uniqueId]
       forKey: @"SelectedPerson"];
  else
    [def setObject: @"None" forKey: @"SelectedPerson"];
    
      
  if([_book hasUnsavedChanges])
    {
      int retval =
      NSRunAlertPanel(_(@"Save Changes?"),
                  _(@"You have made changes to the database.\n"
                    @"Should these changes be saved?"),
                  _(@"Save and Quit"), _(@"Quit without saving"),
                  _(@"Don't quit"), nil);
      switch(retval)
      {
      case 1:
        if(![_book save])
          {
            NSRunAlertPanel(_(@"Couldn't save"),
                        _(@"The database could not be saved!"),
                        _(@"OK"), nil, nil, nil);
            return NO;
          }
        return YES;
      case 0:
        return YES;
      default:
        return NO;
      }
    }
  else
    return YES;
}

- (BOOL) validateMenuItem: (NSMenuItem*) anItem
{
  int count;

  count = [[self selectedPersons] count];
  
  if(anItem == editItem || anItem == duplicatePersonItem ||
     anItem == thisIsMeItem)
    {
      if(count != 1) return NO;
      return YES;
    }

  if(anItem == shareItem)
    {
      if(count < 1) return NO;
      return YES;
    }

  if(anItem == mergePersonsItem)
    {
      if(count <= 1) return NO;
      return YES;
    }

  return YES;
}

- (BOOL) personView: (ADPersonView*) aView
   shouldAcceptDrop: (id<NSDraggingInfo>) info
{
  return YES;
}

- (BOOL) personView: (ADPersonView*) aView
receivedDroppedPersons: (NSArray*) persons
{
  int i;

  if(![persons count]) return NO;

  for(i=0; i<[persons count]; i++)
    {
      ADPerson *p, *o;

      p = [persons objectAtIndex: i];
      o = [_book personWithFirstName: [p valueForProperty: ADFirstNameProperty]
             lastName: [p valueForProperty: ADLastNameProperty]];
      if(o)
      {
        NSString *fmt;
        int retval;
        
        fmt =
          [NSString stringWithFormat:
                  _(@"Trying to import person named '%@',\n"
                    @"which already exists in the database."),
                  [p screenName]];
        retval = NSRunAlertPanel(_(@"Existing person?"),
                           fmt, _(@"Replace"), _(@"Insert anyway"),
                           _(@"Don't insert"), nil);
        if(retval == 1) // replace
          {
            [_book removeRecord: o];
            [_book addRecord: p];
          }
        else if(retval == 0) // insert anyway
          [_book addRecord: p];
        else if(retval == -1) // don't insert; continue reading
          return NO;
      }
      else
      if(![_book addRecord: p]) return NO;

      if(_currentGroup)
      [_currentGroup addMember: p];
    }

  [groupsBrowser reloadColumn: 1];
  [self selectPerson: [persons objectAtIndex: 0]];
  
  return YES;
}

- (BOOL) personView: (ADPersonView*) aView
     willDragPerson: (ADPerson*) person
{
  return YES;
}

- (BOOL) personView: (ADPersonView*) aView
   willDragProperty: (NSString*) property
{
  return YES;
}

- (NSDragOperation) dragDropMatrix: (DragDropMatrix*) matrix
      shouldAcceptDropFromSender: (id<NSDraggingInfo>) sender
                      onCell: (NSCell*) cell
{
  ADGroup *g;

  g = [cell representedObject];
  if(g && ![g isKindOfClass: [ADGroup class]])
    return NSDragOperationNone;
  
  if([[[sender draggingPasteboard] types]
       containsObject: ADPeoplePboardType])
    {
      NSDictionary *d; NSEnumerator *e; 
      BOOL _pureLocal, _didSomeWork;

      if(![cell representedObject] ||
       ![[cell representedObject] isKindOfClass: [ADGroup class]] ||
       [[[cell representedObject] uniqueId]
         isEqualToString: [_currentGroup uniqueId]])
      goto useVCard;

      _pureLocal = YES;
      _didSomeWork = NO;
      e = [[[sender draggingPasteboard]
           propertyListForType: ADPeoplePboardType] objectEnumerator];
      while((d = [e nextObject]))
      {
        int pid; NSString *uid; NSArray *adDescr;
        ADPerson *p;

        if(![d objectForKey: @"UID"] ||
           ![d objectForKey: @"AB"] ||
           ![d objectForKey: @"PID"])
          continue;

        pid = [[d objectForKey: @"PID"] intValue];
        uid = [d objectForKey: @"UID"];
        adDescr = [d objectForKey: @"AD"];

        p = (ADPerson*)[_book recordForUniqueId: uid];

        if(pid != [[NSProcessInfo processInfo] processIdentifier])
          _pureLocal = NO;
        else if(!p || ![p isKindOfClass: [ADPerson class]])
          continue;

        if(g)
          {
            ADPerson *p2; NSEnumerator *e;
            BOOL isIn;

            isIn = NO;
            e = [[g members] objectEnumerator];
            while((p2 = [e nextObject]))
            if([[p2 uniqueId] isEqualToString: [p uniqueId]])
              {
                isIn = YES;
                break;
              }

            if(isIn) continue;
          }
        
        _didSomeWork = YES;
      }

      if(_didSomeWork)
      {
        if(_pureLocal)
          return NSDragOperationLink;
        return NSDragOperationCopy;
      }
    }

 useVCard:
  if([[[sender draggingPasteboard] types]
       containsObject: @"NSVCardPboardType"])
    return NSDragOperationCopy;

  return NSDragOperationNone;
}
  
- (BOOL) dragDropMatrix: (DragDropMatrix*) matrix
didAcceptDropFromSender: (id<NSDraggingInfo>) sender
             onCell: (NSCell*) cell
{
  NSPasteboard *pb;
  ADGroup *g;

  g = [cell representedObject];
  if(g && ![g isKindOfClass: [ADGroup class]])
    return NO;

  pb = [sender draggingPasteboard];

  if([[pb types] containsObject: ADPeoplePboardType])
    {
      NSDictionary *d; NSEnumerator *e;
      BOOL _didSomeWork;

      if(!g) goto useVCard;

      _didSomeWork = NO;
      e = [[pb propertyListForType: ADPeoplePboardType] objectEnumerator];
      while((d = [e nextObject]))
      {
        int pid; NSString *uid; NSArray *adDescr;
        ADPerson *p;

        if(![d objectForKey: @"UID"] ||
           ![d objectForKey: @"AB"] ||
           ![d objectForKey: @"PID"])
          continue;

        pid = [[d objectForKey: @"PID"] intValue];
        uid = [d objectForKey: @"UID"];
        adDescr = [d objectForKey: @"AD"];

        if(pid != [[NSProcessInfo processInfo] processIdentifier])
          continue;

        p = (ADPerson*)[_book recordForUniqueId: uid];
        if(!p || ![p isKindOfClass: [ADPerson class]])
          continue;

        if([g addMember: p])
          _didSomeWork = YES;
      }
      if(_didSomeWork)
      return YES;
    }

 useVCard:
  if([[pb types] containsObject: @"NSVCardPboardType"])
    {
      ADPerson *p, *o;
      NSData *vcard;

      vcard = [pb dataForType: @"NSVCardPboardType"];
      p = [[[ADPerson alloc] initWithVCardRepresentation: vcard] autorelease];
      if(!p)
      return NO;

      o = [_book personWithFirstName: [p valueForProperty: ADFirstNameProperty]
             lastName: [p valueForProperty: ADLastNameProperty]];
      if(o)
      {
        NSString *fmt;
        int retval;
        
        fmt =
          [NSString stringWithFormat:
                  _(@"Trying to import person named '%@',\n"
                    @"which already exists in the database."),
                  [p screenName]];
        retval = NSRunAlertPanel(_(@"Existing person?"),
                           fmt, _(@"Replace"), _(@"Insert anyway"),
                           _(@"Don't insert"), nil);
        if(retval == 1) // replace
          {
            [_book removeRecord: o];
            [_book addRecord: p];
          }
        else if(retval == 0) // insert anyway
          [_book addRecord: p];
        else if(retval == -1) // don't insert; continue reading
          return NO;
      }
      else
      if(![_book addRecord: p]) return NO;

      p = (ADPerson*)[_book recordForUniqueId: [p uniqueId]];
      if(!p || ![p isKindOfClass: [ADPerson class]]) return NO;

      if(g)
      [g addMember: p];

      [groupsBrowser reloadColumn: 1];
      if(g)
      {
        [self selectGroup: g];
        _selectedByDrop = YES;
      }
      [self selectPerson: p];
      
      return YES;
    }

  return NO;
}
@end


Generated by  Doxygen 1.6.0   Back to index