* 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 "AIDockIconSelectionSheet.h" #import "AIDockController.h" #import "AIAppearancePreferencesPlugin.h" #import <AIUtilities/AIFileManagerAdditions.h> #import <Adium/AIIconState.h> #import <Adium/AIDockControllerProtocol.h> #define PREF_GROUP_DOCK_ICON @"Dock Icon" #define DEFAULT_DOCK_ICON_NAME @"Adiumy Green" @interface AIDockIconSelectionSheet () - (void)selectIconWithName:(NSString *)selectName; - (void)xtrasChanged:(NSNotification *)notification; - (void)sheetDidEnd:(NSWindow *)sheet returnCode:(NSInteger)returnCode contextInfo:(void *)contextInfo; - (void)trashConfirmSheetDidEnd:(NSWindow *)sheet returnCode:(NSInteger)returnCode contextInfo:(NSString *)selectedIconPath; @implementation AIDockIconSelectionSheet @synthesize imageCollectionView, okButton; @synthesize icons, iconsData, animatedIconState, animatedIndex, animationTimer, previousIndex; if (self = [super initWithWindowNibName:@"DockIconSelectionSheet"]) { - (void)openOnWindow:(NSWindow *)parentWindow [NSApp beginSheet:self.window modalForWindow:parentWindow didEndSelector:@selector(sheetDidEnd:returnCode:contextInfo:) [self.window makeKeyAndOrderFront:nil]; [NSApp activateIgnoringOtherApps:YES]; [[NSNotificationCenter defaultCenter] removeObserver:self]; [icons release], icons = nil; [iconsData release], iconsData = nil; [animatedIconState release], animatedIconState = nil; [animationTimer release], animationTimer = nil; // Setup our preference view [self setAnimatedIndex:NSNotFound]; [self setPreviousIndex:NSNotFound]; // Set-up collection view [[self imageCollectionView] setMaxNumberOfColumns:7]; [[self imageCollectionView] setMaxItemSize:NSMakeSize(64.0f, 64.0f)]; [[self imageCollectionView] setMinItemSize:NSMakeSize(64.0f, 64.0f)]; [[self imageCollectionView] setHighlightStyle:AIImageCollectionViewHighlightBackgroundStyle]; [[self imageCollectionView] setHighlightCornerRadius:4.0f]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(xtrasChanged:) name:AIXtrasDidChangeNotification [[self okButton] setLocalizedString:AILocalizedStringFromTable(@"Close", @"Buttons", nil)]; // Preference view is closing - (void)windowWillClose:(id)sender [super windowWillClose:sender]; [self setAnimatedDockIconAtIndex:NSNotFound]; // Invoked as the sheet closes, dismiss the sheet - (void)sheetDidEnd:(NSWindow *)sheet returnCode:(NSInteger)returnCode contextInfo:(void *)contextInfo [self setAnimatedDockIconAtIndex:NSNotFound]; // When the xtras are changed, update our icons - (void)xtrasChanged:(NSNotification *)notification if (!notification || [[notification object] caseInsensitiveCompare:@"AdiumIcon"] == NSOrderedSame) { [self setIconsData:[NSMutableArray array]]; NSMutableArray *dockIcons = [[NSMutableArray alloc] init]; // Fetch the pack previews for (NSString *path in [adium.dockController availableDockIconPacks]) { AIIconState *previewState = [adium.dockController previewStateForIconPackAtPath:path]; [[self iconsData] addObject:[NSMutableDictionary dictionaryWithObjectsAndKeys:path, @"Path", previewState, @"State", nil]]; [dockIcons addObject:[previewState image]]; [self setIcons:dockIcons]; [self selectIconWithName:[adium.preferenceController preferenceForKey:KEY_ACTIVE_DOCK_ICON group:PREF_GROUP_APPEARANCE]]; // Set the selected icon by name - (void)selectIconWithName:(NSString *)selectName NSDictionary *iconDictionary; if (![[[self imageCollectionView] selectionIndexes] isEqualToIndexSet:[NSIndexSet indexSet]]) { [self setPreviousIndex:[[[self imageCollectionView] selectionIndexes] firstIndex]]; for (iconDictionary in [self iconsData]) { NSString *iconName = [[[iconDictionary objectForKey:@"Path"] lastPathComponent] stringByDeletingPathExtension]; if ([iconName isEqualToString:selectName]) { [[self imageCollectionView] setSelectionIndexes:[NSIndexSet indexSetWithIndex:anIndex]]; break; // we can exit early // Set previous index - in case it wasn't set first time if ([self previousIndex] == NSNotFound && ![[[self imageCollectionView] selectionIndexes] isEqualToIndexSet:[NSIndexSet indexSet]]) { [self setPreviousIndex:[[[self imageCollectionView] selectionIndexes] firstIndex]]; // Start animating an icon in our collection by index (pass NSNotFound to stop animation) - (void)setAnimatedDockIconAtIndex:(NSInteger)anIndex // Stop the current animation if ([self animationTimer]) { [[self animationTimer] invalidate]; [self setAnimationTimer:nil]; if ([self animatedIndex] != NSNotFound) { [[self imageCollectionView] setImage:[[[[self iconsData] objectAtIndex:[self animatedIndex]] objectForKey:@"State"] image] forItemAtIndex:[self animatedIndex]]; [self setAnimatedIconState:nil]; [self setAnimatedIndex:NSNotFound]; // Start the new animation if (anIndex != NSNotFound) { NSString *path = [[[self iconsData] objectAtIndex:anIndex] objectForKey:@"Path"]; [self setAnimatedIconState:[self animatedStateForDockIconAtPath:path]]; [self setAnimatedIndex:anIndex]; [self setAnimationTimer:[NSTimer scheduledTimerWithTimeInterval:[[self animatedIconState] animationDelay] selector:@selector(animate:) // Returns an animated AIIconState for the dock icon pack at the specified path - (AIIconState *)animatedStateForDockIconAtPath:(NSString *)path NSDictionary *iconPackDict = [adium.dockController iconPackAtPath:path]; NSDictionary *stateDict = [iconPackDict objectForKey:@"State"]; return [[[AIIconState alloc] initByCompositingStates:[NSArray arrayWithObjects:[stateDict objectForKey:@"Base"], [stateDict objectForKey:@"Online"], [stateDict objectForKey:@"Alert"], // Animate the hovered icon - (void)animate:(NSTimer *)timer [animatedIconState nextFrame]; [[self imageCollectionView] setImage:animatedIconState.image forItemAtIndex:animatedIndex]; #pragma mark - AIImageCollectionViewDelegate - (BOOL)imageCollectionView:(AIImageCollectionView *)collectionView shouldHighlightItemAtIndex:(NSUInteger)anIndex if (anIndex == NSNotFound) { [self setAnimatedDockIconAtIndex:NSNotFound]; return (anIndex < [[self icons] count]); - (BOOL)imageCollectionView:(AIImageCollectionView *)imageCollectionView shouldSelectItemAtIndex:(NSUInteger)anIndex // Prevent empty selection if (anIndex == NSNotFound) { if ([self previousIndex] == [[self icons] count] || [self previousIndex] == NSNotFound) { [self selectIconWithName:[adium.preferenceController preferenceForKey:KEY_ACTIVE_DOCK_ICON group:PREF_GROUP_APPEARANCE]]; [[self imageCollectionView] setSelectionIndexes:[NSIndexSet indexSetWithIndex:previousIndex]]; return (anIndex < [[self icons] count]); - (BOOL)imageCollectionView:(AIImageCollectionView *)imageCollectionView shouldDeleteItemsAtIndexes:(NSIndexSet *)indexes return ([indexes firstIndex] < [[self icons] count]); - (void)imageCollectionView:(AIImageCollectionView *)imageCollectionView didHighlightItemAtIndex:(NSUInteger)anIndex [self setAnimatedDockIconAtIndex:anIndex]; - (void)imageCollectionView:(AIImageCollectionView *)imageCollectionView didSelectItemAtIndex:(NSUInteger)anIndex NSString *iconName = [[[[[self iconsData] objectAtIndex:anIndex] objectForKey:@"Path"] lastPathComponent] stringByDeletingPathExtension]; if (![[adium.preferenceController preferenceForKey:KEY_ACTIVE_DOCK_ICON group:PREF_GROUP_APPEARANCE] isEqualToString:iconName]) { [adium.preferenceController setPreference:iconName forKey:KEY_ACTIVE_DOCK_ICON group:PREF_GROUP_APPEARANCE]; [self setPreviousIndex:anIndex]; #pragma mark - Deleting dock xtras // Delete the selected dock icon - (void)imageCollectionView:(AIImageCollectionView *)imageCollectionView didDeleteItemsAtIndexes:(NSIndexSet *)indexes NSString *selectedIconPath = [[iconsData objectAtIndex:[[[self imageCollectionView] selectionIndexes] firstIndex]] valueForKey:@"Path"]; NSString *name = [[selectedIconPath lastPathComponent] stringByDeletingPathExtension]; // We need at least one icon installed, so prevent the user from deleting the default icon if (![name isEqualToString:DEFAULT_DOCK_ICON_NAME]) { NSBeginAlertSheet(AILocalizedString(@"Delete Dock Icon",nil), AILocalizedString(@"Delete",nil), AILocalizedString(@"Cancel",nil), @selector(trashConfirmSheetDidEnd:returnCode:contextInfo:), AILocalizedString(@"Are you sure you want to delete the %@ Dock Icon? It will be moved to the Trash.", nil), name); - (void)trashConfirmSheetDidEnd:(NSWindow *)sheet returnCode:(NSInteger)returnCode contextInfo:(NSString *)selectedIconPath if (returnCode == NSOKButton) { NSInteger deletedIndex = [[[self imageCollectionView] selectionIndexes] firstIndex]; // Deselect and stop animating [self setAnimatedDockIconAtIndex:NSNotFound]; [[self imageCollectionView] setSelectionIndexes:[NSIndexSet indexSet]]; // Trash the file & Rebuild our icons [[NSFileManager defaultManager] trashFileAtPath:selectedIconPath]; // Select the next available icon (prevent empty selection) NSUInteger newIndex = (deletedIndex == [[self icons] count]) ? --deletedIndex : deletedIndex; [[self imageCollectionView] setSelectionIndexes:[NSIndexSet indexSetWithIndex:newIndex]];