* 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 "AIContactInfoWindowController.h" #import "AIContactInfoImageViewWithImagePicker.h" #import <Adium/AIInterfaceControllerProtocol.h> #import <Adium/AIAccountControllerProtocol.h> #import <Adium/AIContactAlertsControllerProtocol.h> #import <Adium/AIListGroup.h> #import <Adium/AIMetaContact.h> #import <Adium/AIModularPaneCategoryView.h> #import <Adium/AIListObject.h> #import <Adium/AIListOutlineView.h> #import <Adium/AIService.h> #import <AIUtilities/AIDictionaryAdditions.h> #import <AIUtilities/AIImageAdditions.h> #import <AIUtilities/AIImageViewWithImagePicker.h> #import <AIUtilities/AIOutlineViewAdditions.h> #import <AIUtilities/AIStringAdditions.h> #import <AIUtilities/AITabViewAdditions.h> #import <QuartzCore/QuartzCore.h> #define CONTACT_INFO_NIB @"ContactInfoInspector" //Filename of the contact info nib #define KEY_INFO_WINDOW_FRAME @"Contact Info Inspector Frame" // #define KEY_INFO_SELECTED_CATEGORY @"Selected Info Category" // #define CONTACT_INFO_THEME @"Contact Info List Theme" #define CONTACT_INFO_LAYOUT @"Contact Info List Layout" //Defines for the image files used by the toolbar segments #define INFO_SEGMENT_IMAGE (@"Personal.tiff") #define ADDRESS_BOOK_SEGMENT_IMAGE (@"get-info-address-book.tiff") #define EVENTS_SEGMENT_IMAGE (@"get-info-events.tiff") #define ADVANCED_SEGMENT_IMAGE (@"get-info-advanced.tiff") CONTACT_INFO_SEGMENT = 0 , CONTACT_ADDRESSBOOK_SEGMENT = 1 , CONTACT_EVENTS_SEGMENT = 2 , CONTACT_ADVANCED_SEGMENT = 3 , CONTACT_PLUGINS_SEGMENT = 4 @interface AIContactInfoWindowController (PRIVATE) - ( void ) configureForDisplayedObject ; -( void ) segmentSelected: ( id ) sender animate: ( BOOL ) shouldAnimate ; - ( void ) selectionChanged: ( NSNotification * ) notification ; - ( void ) setupToolbarSegments ; - ( void ) configureToolbarForListObject: ( AIListObject * ) inObject ; - ( void ) contactInfoListControllerSelectionDidChangeToListObject: ( AIListObject * ) listObject ; -( void ) addInspectorPanel: ( NSInteger ) newSegment animate: ( BOOL ) doAnimate ; -( void ) animateViewIn: ( NSView * ) aView ; -( void ) animateViewOut: ( NSView * ) aView ; @interface NSWindow (FakeLeopardAdditions) - ( void ) setAutorecalculatesContentBorderThickness: ( BOOL ) autorecalculateContentBorderThickness forEdge: ( NSRectEdge ) edge ; - ( float ) contentBorderThicknessForEdge: ( NSRectEdge ) edge ; - ( void ) setContentBorderThickness: ( float ) borderThickness forEdge: ( NSRectEdge ) edge ; @implementation AIContactInfoWindowController static AIContactInfoWindowController * sharedContactInfoInstance = nil ; - ( IBAction ) segmentSelected: ( id ) sender [ self segmentSelected : sender animate : YES ]; - ( void ) segmentSelected: ( id ) sender animate: ( BOOL ) shouldAnimate //Action method for the Segmented Control, which is actually the toolbar. NSInteger currentSegment = [ sender selectedColumn ]; //Take focus away from any textual controls to ensure that they register changes and save if ([[[ self window ] firstResponder ] isKindOfClass : [ NSText class ]]) { [[ self window ] makeFirstResponder : nil ]; if ( currentSegment != lastSegment ) { [ inspectorToolbar deselectAllCells ]; [ inspectorToolbar selectCellAtRow : 0 column : currentSegment ]; [ self addInspectorPanel : currentSegment animate : shouldAnimate ]; //Return the shared contact info window + ( AIContactInfoWindowController * ) showInfoWindowForListObject: ( AIListObject * ) listObject if ( ! sharedContactInfoInstance ) { sharedContactInfoInstance = [[ self alloc ] initWithWindowNibName : CONTACT_INFO_NIB ]; [ sharedContactInfoInstance setDisplayedListObject : listObject ]; [[ sharedContactInfoInstance window ] makeKeyAndOrderFront : nil ]; return sharedContactInfoInstance ; if ( sharedContactInfoInstance ) { [ sharedContactInfoInstance closeWindow : nil ]; [ sharedContactInfoInstance release ]; sharedContactInfoInstance = nil ; [ self setDisplayedListObject : nil ]; [ displayedObject release ]; displayedObject = nil ; [ loadedContent release ]; loadedContent = nil ; [ contentController release ]; contentController = nil ; [[ NSNotificationCenter defaultCenter ] removeObserver : self ]; - ( NSString * ) adiumFrameAutosaveName return KEY_INFO_WINDOW_FRAME ; //If we are on Leopard, we want our panel to have a finder-esque look. contentController = [[ AIContactInfoContentController defaultInfoContentController ] retain ]; //Load the content array from the content controller. loadedContent = [[ contentController loadedPanes ] retain ]; //Monitor the selected contact [[ NSNotificationCenter defaultCenter ] addObserver : self selector : @selector ( selectionChanged : ) name : Interface_ContactSelectionChanged //Setup the window before it is displayed [ self setupToolbarSegments ]; //Select the previously selected category selectedSegment = [[[ adium preferenceController ] preferenceForKey : KEY_INFO_SELECTED_CATEGORY group : PREF_GROUP_WINDOW_POSITIONS ] intValue ]; if ( selectedSegment < 0 || selectedSegment > [ inspectorToolbar numberOfColumns ]) [ inspectorToolbar selectCellAtRow : 0 column : selectedSegment ]; [ self segmentSelected : inspectorToolbar ]; - ( void ) windowWillClose: ( NSNotification * ) inNotification [[ adium preferenceController ] setPreference : [ NSNumber numberWithInteger : [ inspectorToolbar selectedColumn ]] forKey : KEY_INFO_SELECTED_CATEGORY group : PREF_GROUP_WINDOW_POSITIONS ]; [ sharedContactInfoInstance autorelease ]; sharedContactInfoInstance = nil ; [ super windowWillClose : inNotification ]; @method setupToolbarSegments @abstract setupToolbarSegments loads the localized tooltips and images for each toolbar segment @discussion Since we don't want to enumerate over all of the segments twice, we've combined the localization and image loading steps into this method. - ( void ) setupToolbarSegments for ( i = 0 ; i < [ inspectorToolbar numberOfColumns ]; i ++ ) { NSString * segmentLabel = nil ; NSImage * segmentImage = nil ; case CONTACT_INFO_SEGMENT : segmentLabel = AILocalizedString ( @"Status and Profile" , "This segment displays the status and profile information for the selected contact." ); segmentImage = [ NSImage imageNamed : INFO_SEGMENT_IMAGE ]; case CONTACT_ADDRESSBOOK_SEGMENT : segmentLabel = AILocalizedString ( @"Contact Information" , "This segment displays contact and alias information for the selected contact." ); segmentImage = [ NSImage imageNamed : ADDRESS_BOOK_SEGMENT_IMAGE ]; case CONTACT_EVENTS_SEGMENT : segmentLabel = AILocalizedString ( @"Events" , "This segment displays controls for a user to set up events for this contact." ); segmentImage = [ NSImage imageNamed : EVENTS_SEGMENT_IMAGE ]; case CONTACT_ADVANCED_SEGMENT : segmentLabel = AILocalizedString ( @"Advanced Settings" , "This segment displays the advanced settings for a contact, including encryption details and account information." ); segmentImage = [ NSImage imageNamed : ADVANCED_SEGMENT_IMAGE ]; [ inspectorToolbar setToolTip : segmentLabel forCell : [ inspectorToolbar cellAtRow : 0 column : i ]]; [ segmentImage setDataRetained : YES ]; [[ inspectorToolbar cellAtRow : 0 column : i ] setImage : segmentImage ]; //When the contact list selection changes, then configure the window for the new contact - ( void ) selectionChanged: ( NSNotification * ) notification AIListObject * object = [[ adium interfaceController ] selectedListObject ]; [ self setDisplayedListObject : object ]; - ( void ) setDisplayedListObject: ( AIListObject * ) inObject if ([ inObject isKindOfClass : [ AIListContact class ]]) { inObject = [( AIListContact * ) inObject parentContact ]; if ( inObject != displayedObject ) { NSMutableDictionary * notificationUserInfo = [ NSMutableDictionary dictionary ];; [ notificationUserInfo setObject : displayedObject forKey : KEY_PREVIOUS_INSPECTED_OBJECT ]; [ notificationUserInfo setObject : inObject forKey : KEY_NEW_INSPECTED_OBJECT ]; [ displayedObject release ]; displayedObject = [ inObject retain ]; //Ensure our window is loaded //Configure for the new object [ self configureForDisplayedObject ]; [[ NSNotificationCenter defaultCenter ] postNotificationName : AIContactInfoInspectorDidChangeInspectedObject userInfo : notificationUserInfo ]; - ( void ) configureForDisplayedObject //Set the title of the window. [[ self window ] setTitle : [ NSString stringWithFormat : AILocalizedString ( @"%@'s Info" , nil ), displayedObject . displayName ]]; [[ self window ] setTitle : AILocalizedString ( @"Contact Info" , nil )]; //Configure each pane for this contact. for ( id < AIContentInspectorPane > pane in loadedContent ) { [ pane updateForListObject : displayedObject ]; #pragma mark View Management and Animation -( void ) addInspectorPanel: ( NSInteger ) newSegment animate: ( BOOL ) doAnimate NSView * newPane = [[ loadedContent objectAtIndex : newSegment ] inspectorContentView ]; if ( currentPane == newPane ) { [ self animateViewOut : currentPane ]; [ currentPane removeFromSuperview ]; lastSegment = newSegment ; NSRect paneFrame = [ newPane frame ], contentBounds = [ inspectorContent bounds ]; paneFrame . size . width = contentBounds . size . width ; paneFrame . size . height = contentBounds . size . height ; [ newPane setFrame : paneFrame ]; [ inspectorContent addSubview : newPane ]; [ self animateViewIn : currentPane ]; -( void ) animateViewIn: ( NSView * ) aView ; NSMutableDictionary * animationDict = [ NSMutableDictionary dictionaryWithCapacity : 4 ]; [ animationDict setObject : aView forKey : NSViewAnimationTargetKey ]; [ animationDict setObject : NSViewAnimationFadeInEffect forKey : NSViewAnimationEffectKey ]; NSViewAnimation * viewAnim = [[ NSViewAnimation alloc ] initWithViewAnimations : [ NSArray arrayWithObjects : animationDict , nil ]]; [ viewAnim setDuration : 0.1 ]; [ viewAnim setAnimationCurve : NSAnimationEaseInOut ]; [ viewAnim setAnimationBlockingMode : NSAnimationBlocking ]; [ viewAnim startAnimation ]; - ( void ) animateViewOut : ( NSView * ) aView ; NSMutableDictionary * animationDict = [ NSMutableDictionary dictionaryWithCapacity : 3 ]; [ animationDict setObject : aView forKey : NSViewAnimationTargetKey ]; [ animationDict setObject : NSViewAnimationFadeOutEffect forKey : NSViewAnimationEffectKey ]; NSViewAnimation * viewAnim = [[ NSViewAnimation alloc ] initWithViewAnimations : [ NSArray arrayWithObjects : animationDict , nil ]]; [ viewAnim setDuration : 0.1 ]; [ viewAnim setAnimationCurve : NSAnimationEaseInOut ]; [ viewAnim setAnimationBlockingMode : NSAnimationBlocking ]; [ viewAnim startAnimation ];