* Adium is the legal property of its developers, whose names are listed in the copyright file included * with this source distribution. * This program is free software; you can redistribute it and/or modify it under the terms of the GNU * General Public License as published by the Free Software Foundation; either version 2 of the License, * or (at your option) any later version. * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General * Public License for more details. * You should have received a copy of the GNU General Public License along with this program; if not, * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #import "ESAddressBookIntegrationAdvancedPreferences.h" #import <Adium/AIAddressBookController.h> #import <Adium/AIContactControllerProtocol.h> #import <Adium/AILocalizationTextField.h> #import <AIUtilities/AIDictionaryAdditions.h> #import <AIUtilities/AIImageAdditions.h> #import <AIUtilities/AIMenuAdditions.h> @interface NSTokenField (NSTokenFieldAdditions) @implementation NSTokenField (NSTokenFieldAdditions) NSRange selectionRange = [[[ self window ] fieldEditor : YES forObject : self ] selectedRange ]; // XXX - Reassign objectValue to let NSTokenField know it has changed. id objectValue = [ self objectValue ]; [ self setObjectValue : nil ]; [ self setObjectValue : objectValue ]; [[[ self window ] fieldEditor : YES forObject : self ] setSelectedRange : selectionRange ]; @interface ESAddressBookIntegrationAdvancedPreferences () - ( IBAction ) changeFormat : ( id ) sender ; - ( void ) sheetDidEnd: ( NSWindow * ) sheet returnCode: ( NSInteger ) returnCode contextInfo: ( void * ) contextInfo ; - ( NSArray * ) separateStringIntoTokens: ( NSString * ) string ; - ( void ) changeFormatToFullName: ( id ) representedObject ; - ( void ) changeFormatToInitialCharacter: ( id ) representedObject ; * @class ESAddressBookIntegrationAdvancedPreferences * @brief Provide advanced preferences for the address book integration @implementation ESAddressBookIntegrationAdvancedPreferences return AILocalizedString ( @"Address Book" , nil ); return @"AddressBookPrefs" ; * @brief Image for advanced preferences return [ NSImage imageNamed : @"AddressBook" forClass : [ self class ]]; * @brief Configure the preference view [ label_instructions setLocalizedString : AILocalizedString ( @"Type text and drag name elements to create a custom name format." , nil )]; [ label_names setLocalizedString : AILocalizedString ( @"Names" , nil )]; [ label_images setLocalizedString : AILocalizedString ( @"Images" , nil )]; [ label_contacts setLocalizedString : AILocalizedString ( @"Contacts" , nil )]; [ box_nameElements setTitle : AILocalizedString ( @"Name elements" , "Contains name format tokens" )]; [ label_firstToken setLocalizedString : AILocalizedString ( @"First" , "First name token" )]; [ label_middleToken setLocalizedString : AILocalizedString ( @"Middle" , "Middle name token" )]; [ label_lastToken setLocalizedString : AILocalizedString ( @"Last" , "Last name token" )]; [ label_nickToken setLocalizedString : AILocalizedString ( @"Nick" , "Nickname token" )]; NSString * displayFormat = [ adium . preferenceController preferenceForKey : KEY_AB_DISPLAYFORMAT group : PREF_GROUP_ADDRESSBOOK ]; [ tokenField_format setDelegate : self ]; [ tokenField_format setTokenizingCharacterSet : [ NSCharacterSet characterSetWithCharactersInString : @"" ]]; [ tokenField_format setObjectValue : [ self separateStringIntoTokens : displayFormat ]]; [ tokenField_firstToken setDelegate : self ]; [ tokenField_firstToken setStringValue : FORMAT_FIRST_FULL ]; [ tokenField_middleToken setDelegate : self ]; [ tokenField_middleToken setStringValue : FORMAT_MIDDLE_FULL ]; [ tokenField_lastToken setDelegate : self ]; [ tokenField_lastToken setStringValue : FORMAT_LAST_FULL ]; [ tokenField_nickToken setDelegate : self ]; [ tokenField_nickToken setStringValue : FORMAT_NICK_FULL ]; [ checkBox_enableImport setLocalizedString : AILocalizedString ( @"Import my contacts' names from the Address Book" , nil )]; [ checkBox_useFirstName setLocalizedString : AILocalizedString ( @"Replace Nick with First if not available" , nil )]; [ checkBox_useNickName setLocalizedString : AILocalizedString ( @"Use Nick exclusively if available" , nil )]; [ checkBox_useABImages setLocalizedString : AILocalizedString ( @"Use Address Book images as contacts' icons" , nil )]; [ checkBox_preferABImages setLocalizedString : AILocalizedString ( @"Even if the contact already has a contact icon" , nil )]; [ checkBox_syncAutomatic setLocalizedString : AILocalizedString ( @"Overwrite Address Book images with contacts' icons" , nil )]; [ checkBox_metaContacts setLocalizedString : AILocalizedString ( @"Combine contacts listed on a single card" , nil )]; [ checkBox_enableImport setState : [[ adium . preferenceController preferenceForKey : KEY_AB_ENABLE_IMPORT group : PREF_GROUP_ADDRESSBOOK ] boolValue ]]; [ checkBox_useFirstName setState : [[ adium . preferenceController preferenceForKey : KEY_AB_USE_FIRSTNAME group : PREF_GROUP_ADDRESSBOOK ] boolValue ]]; [ checkBox_useNickName setState : [[ adium . preferenceController preferenceForKey : KEY_AB_USE_NICKNAME group : PREF_GROUP_ADDRESSBOOK ] boolValue ]]; [ checkBox_syncAutomatic setState : [[ adium . preferenceController preferenceForKey : KEY_AB_IMAGE_SYNC group : PREF_GROUP_ADDRESSBOOK ] boolValue ]]; [ checkBox_useABImages setState : [[ adium . preferenceController preferenceForKey : KEY_AB_USE_IMAGES group : PREF_GROUP_ADDRESSBOOK ] boolValue ]]; [ checkBox_enableNoteSync setState : [[ adium . preferenceController preferenceForKey : KEY_AB_NOTE_SYNC group : PREF_GROUP_ADDRESSBOOK ] boolValue ]]; [ checkBox_preferABImages setState : [[ adium . preferenceController preferenceForKey : KEY_AB_PREFER_ADDRESS_BOOK_IMAGES group : PREF_GROUP_ADDRESSBOOK ] boolValue ]]; [ checkBox_metaContacts setState : [[ adium . preferenceController preferenceForKey : KEY_AB_CREATE_METACONTACTS group : PREF_GROUP_ADDRESSBOOK ] boolValue ]]; [ self configureControlDimming ]; * @brief Configure control dimming - ( void ) configureControlDimming BOOL enableImport = [[ adium . preferenceController preferenceForKey : KEY_AB_ENABLE_IMPORT group : PREF_GROUP_ADDRESSBOOK ] boolValue ]; BOOL useImages = [[ adium . preferenceController preferenceForKey : KEY_AB_USE_IMAGES group : PREF_GROUP_ADDRESSBOOK ] boolValue ]; [ label_instructions setTextColor : (( enableImport ) ? [ NSColor controlTextColor ] : [ NSColor disabledControlTextColor ])]; //Use Nick Name and the format menu are irrelevent if importing of names is not enabled [ checkBox_useFirstName setEnabled : enableImport ]; [ checkBox_useNickName setEnabled : enableImport ]; [ tokenField_format setEnabled : enableImport ]; [ tokenField_firstToken setEnabled : enableImport ]; [ tokenField_middleToken setEnabled : enableImport ]; [ tokenField_lastToken setEnabled : enableImport ]; [ tokenField_nickToken setEnabled : enableImport ]; //We will not allow image syncing if AB images are preferred //so disable the control and uncheck the box to indicate this to the user //dchoby98: why are image import and export linked? //[checkBox_syncAutomatic setEnabled:!preferABImages]; // [checkBox_syncAutomatic setState:NSOffState]; //Disable the image priority checkbox if we aren't using images [ checkBox_preferABImages setEnabled : useImages ]; * @brief Save changed name format preference - ( IBAction ) changeFormat: ( id ) sender [ adium . preferenceController setPreference : [[ sender objectValue ] componentsJoinedByString : @"" ] forKey : KEY_AB_DISPLAYFORMAT group : PREF_GROUP_ADDRESSBOOK ]; * @brief Save changed preference - ( IBAction ) changePreference: ( id ) sender if ( sender == checkBox_syncAutomatic ) { [ adium . preferenceController setPreference : [ NSNumber numberWithBool : ([ sender state ] == NSOnState )] group : PREF_GROUP_ADDRESSBOOK ]; } else if ( sender == checkBox_useABImages ) { [ adium . preferenceController setPreference : [ NSNumber numberWithBool : ([ sender state ] == NSOnState )] group : PREF_GROUP_ADDRESSBOOK ]; } else if ( sender == checkBox_useFirstName ) { [ adium . preferenceController setPreference : [ NSNumber numberWithBool : ([ sender state ] == NSOnState )] forKey : KEY_AB_USE_FIRSTNAME group : PREF_GROUP_ADDRESSBOOK ]; } else if ( sender == checkBox_useNickName ) { [ adium . preferenceController setPreference : [ NSNumber numberWithBool : ([ sender state ] == NSOnState )] forKey : KEY_AB_USE_NICKNAME group : PREF_GROUP_ADDRESSBOOK ]; } else if ( sender == checkBox_enableImport ) { [ adium . preferenceController setPreference : [ NSNumber numberWithBool : ([ sender state ] == NSOnState )] forKey : KEY_AB_ENABLE_IMPORT group : PREF_GROUP_ADDRESSBOOK ]; } else if ( sender == checkBox_preferABImages ) { [ adium . preferenceController setPreference : [ NSNumber numberWithBool : ([ sender state ] == NSOnState )] forKey : KEY_AB_PREFER_ADDRESS_BOOK_IMAGES group : PREF_GROUP_ADDRESSBOOK ]; } else if ( sender == checkBox_enableNoteSync ) { [ adium . preferenceController setPreference : [ NSNumber numberWithBool : ([ sender state ] == NSOnState )] group : PREF_GROUP_ADDRESSBOOK ]; } else if ( sender == checkBox_metaContacts ) { BOOL shouldCreateMetaContacts = ([ sender state ] == NSOnState ); if ( shouldCreateMetaContacts ) { [ adium . preferenceController setPreference : [ NSNumber numberWithBool : YES ] forKey : KEY_AB_CREATE_METACONTACTS group : PREF_GROUP_ADDRESSBOOK ]; AILocalizedString ( @"Unconsolidate all metacontacts" , nil ), AILocalizedString ( @"Cancel" , nil ), nil , [[ self view ] window ], self , @selector ( sheetDidEnd : returnCode : contextInfo : ), NULL , AILocalizedString ( @"Disabling automatic contact consolidation will also unconsolidate all existing metacontacts, including any created manually. You will need to recreate any manually-created metacontacts if you proceed." , nil )); [ self configureControlDimming ]; - ( void ) sheetDidEnd: ( NSWindow * ) sheet returnCode: ( NSInteger ) returnCode contextInfo: ( void * ) contextInfo if ( returnCode == NSAlertDefaultReturn ) { //If we now shouldn't create metaContacts, clear 'em all... not pretty, but effective. //Delay to the next run loop to give better UI responsiveness [ adium . contactController performSelector : @selector ( clearAllMetaContactData ) [ adium . preferenceController setPreference : [ NSNumber numberWithBool : NO ] forKey : KEY_AB_CREATE_METACONTACTS group : PREF_GROUP_ADDRESSBOOK ]; [ checkBox_metaContacts setState :! [ checkBox_metaContacts state ]]; #pragma mark Token Field Delegate - ( NSArray * ) tokenField: ( NSTokenField * ) tokenField shouldAddObjects: ( NSArray * ) tokens atIndex: ( NSUInteger ) index NSString * tokenStrings = [ tokens componentsJoinedByString : @"" ]; return [ self separateStringIntoTokens : tokenStrings ]; - ( BOOL ) tokenField: ( NSTokenField * ) tokenField writeRepresentedObjects: ( NSArray * ) objects toPasteboard: ( NSPasteboard * ) pboard [ pboard setString : [ objects componentsJoinedByString : @"" ] forType : NSStringPboardType ]; - ( NSArray * ) tokenField: ( NSTokenField * ) tokenField readFromPasteboard: ( NSPasteboard * ) pboard return [ self separateStringIntoTokens : [ pboard stringForType : NSStringPboardType ]]; - ( NSTokenStyle ) tokenField: ( NSTokenField * ) tokenField styleForRepresentedObject: ( id ) representedObject if ([ representedObject hasPrefix : @"%[" ] && [ representedObject hasSuffix : @"]" ]) { return NSRoundedTokenStyle ; return NSPlainTextTokenStyle ; - ( NSString * ) tokenField: ( NSTokenField * ) tokenField displayStringForRepresentedObject: ( id ) representedObject if ([ representedObject isEqualToString : FORMAT_FIRST_FULL ]) { } else if ([ representedObject isEqualToString : FORMAT_FIRST_INITIAL ]) { } else if ([ representedObject isEqualToString : FORMAT_MIDDLE_FULL ]) { } else if ([ representedObject isEqualToString : FORMAT_MIDDLE_INITIAL ]) { } else if ([ representedObject isEqualToString : FORMAT_LAST_FULL ]) { } else if ([ representedObject isEqualToString : FORMAT_LAST_INITIAL ]) { } else if ([ representedObject isEqualToString : FORMAT_NICK_FULL ]) { } else if ([ representedObject isEqualToString : FORMAT_NICK_INITIAL ]) { - ( NSString * ) tokenField: ( NSTokenField * ) tokenField editingStringForRepresentedObject: ( id ) representedObject if ([ representedObject hasPrefix : @"%[" ] && [ representedObject hasSuffix : @"]" ]) { return representedObject ; - ( id ) tokenField: ( NSTokenField * ) tokenField representedObjectForEditingString: ( NSString * ) editingString if ([ editingString hasPrefix : @"%[" ] && [ editingString hasSuffix : @"]" ]) { // Return mutable string as formats should be modifiable return [ NSMutableString stringWithString : editingString ]; - ( BOOL ) tokenField: ( NSTokenField * ) tokenField hasMenuForRepresentedObject: ( id ) representedObject if ( tokenField == tokenField_format ) { // Only tokens in Format should have menus - ( NSMenu * ) tokenField: ( NSTokenField * ) tokenField menuForRepresentedObject: ( id ) representedObject NSMenu * menu = [[[ NSMenu alloc ] init ] autorelease ]; NSString * fullName = [ self tokenField : tokenField displayStringForRepresentedObject :[ representedObject stringByReplacingOccurrencesOfString : @"INITIAL" [ menu addItemWithTitle : fullName action : @selector ( changeFormatToFullName : ) representedObject : representedObject ]; NSString * initialCharacter = [ self tokenField : tokenField displayStringForRepresentedObject :[ representedObject stringByReplacingOccurrencesOfString : @"FULL" [ menu addItemWithTitle : initialCharacter action : @selector ( changeFormatToInitialCharacter : ) representedObject : representedObject ]; - ( void ) changeFormatToInitialCharacter: ( id ) sender [[ sender representedObject ] replaceOccurrencesOfString : FORMAT_FULL withString : FORMAT_INITIAL range : NSMakeRange ( 0 , [[ sender representedObject ] length ])]; [ tokenField_format updateDisplay ]; [ self changeFormat : tokenField_format ]; - ( void ) changeFormatToFullName: ( id ) sender [[ sender representedObject ] replaceOccurrencesOfString : FORMAT_INITIAL range : NSMakeRange ( 0 , [[ sender representedObject ] length ])]; [ tokenField_format updateDisplay ]; [ self changeFormat : tokenField_format ]; - ( NSArray * ) separateStringIntoTokens: ( NSString * ) string NSMutableArray * tokens = [ NSMutableArray array ]; while ( i < [ string length ]) { // Search for end of current token if ([[ string substringFromIndex : i ] hasPrefix : @"%[" ]) { for (; i < [ string length ]; i ++ ) { if ([[ string substringFromIndex : i ] hasPrefix : @"]" ]) { // Search for start of next token for (; i < [ string length ]; i ++ ) { if ([[ string substringFromIndex : ( i + 1 )] hasPrefix : @"%[" ]) { [ tokens addObject : [[[ string substringWithRange : NSMakeRange ( start , i - start )] mutableCopy ] autorelease ]];