X-Git-Url: http://git.ithinksw.org/MenuTunes.git/blobdiff_plain/f41e707acd7a88f6e5ec68df27ebeca2498293b4..57e47909ea593126a1055a5b077e7531c2fafb30:/PreferencesController.m?ds=sidebyside diff --git a/PreferencesController.m b/PreferencesController.m index 3919848..0a796dc 100755 --- a/PreferencesController.m +++ b/PreferencesController.m @@ -1,33 +1,27 @@ #import "PreferencesController.h" #import "MainController.h" +#import "MenuController.h" #import "NetworkController.h" #import "NetworkObject.h" #import "StatusWindow.h" #import "StatusWindowController.h" #import "CustomMenuTableView.h" +#import "AudioscrobblerController.h" + +#import #import #import #import +#import +#import -#import - -#import -#import -#import -#import -#import - +#import #import -#import -#import -#import -#import -#import -#import - #define SENDER_STATE (([sender state] == NSOnState) ? YES : NO) +#define AUDIOSCROBBLER_KEYCHAIN_SERVICE(user) [[NSString stringWithFormat:@"Audioscrobbler: %@", user] UTF8String] +#define AUDIOSCROBBLER_KEYCHAIN_KIND "application password" /*************************************************************************/ #pragma mark - @@ -35,17 +29,23 @@ /*************************************************************************/ @interface PreferencesController (Private) ++ (SecKeychainItemRef)keychainItemForUser:(NSString *)user; ++ (BOOL)keychainItemExistsForUser:(NSString *)user; ++ (BOOL)createKeychainItemForUser:(NSString *)user andPassword:(NSString *)password; ++ (BOOL)deleteKeychainItemForUser:(NSString *)user; ++ (BOOL)setKeychainItemPassword:(NSString *)password forUser:(NSString *)user; + - (void)setupWindow; - (void)setupCustomizationTables; - (void)setupMenuItems; - (void)setupUI; +- (void)setupScreenPopup; - (void)setStatusWindowEntryEffect:(Class)effectClass; - (void)setStatusWindowExitEffect:(Class)effectClass; - (void)setCustomColor:(NSColor *)color updateWell:(BOOL)update; - (void)repopulateEffectPopupsForVerticalPosition:(ITVerticalWindowPosition)vPos horizontalPosition:(ITHorizontalWindowPosition)hPos; - (BOOL)effect:(Class)effectClass supportsVerticalPosition:(ITVerticalWindowPosition)vPos withHorizontalPosition:(ITHorizontalWindowPosition)hPos; - (IBAction)changeMenus:(id)sender; -- (void)setLaunchesAtLogin:(BOOL)flag; @end @@ -59,6 +59,142 @@ static PreferencesController *prefs = nil; +/*************************************************************************/ +#pragma mark - +#pragma mark STATIC KEYCHAIN SUPPORT METHODS +/*************************************************************************/ + ++ (SecKeychainItemRef)keychainItemForUser:(NSString *)user +{ + SecKeychainSearchRef search; + SecKeychainItemRef item; + OSStatus status; + SecKeychainAttribute attributes[3]; + SecKeychainAttributeList list; + + if ((user == nil) || ([user length] == 0)) { + return nil; + } + + ITDebugLog(@"Audioscrobbler: Searching for keychain item for %@.", user); + attributes[0].tag = kSecAccountItemAttr; + attributes[0].data = (char *)[user UTF8String]; + attributes[0].length = [user length]; + attributes[1].tag = kSecDescriptionItemAttr; + attributes[1].data = AUDIOSCROBBLER_KEYCHAIN_KIND; + attributes[1].length = strlen(AUDIOSCROBBLER_KEYCHAIN_KIND); + attributes[2].tag = kSecLabelItemAttr; + attributes[2].data = (char *)AUDIOSCROBBLER_KEYCHAIN_SERVICE(user); + attributes[2].length = strlen(AUDIOSCROBBLER_KEYCHAIN_SERVICE(user)); + list.count = 3; + list.attr = attributes; + + status = SecKeychainSearchCreateFromAttributes(NULL, kSecGenericPasswordItemClass, &list, &search); + + if (status != noErr) { + ITDebugLog(@"Audioscrobbler: Error searching for existing keychain item: %i", status); + } + + status = SecKeychainSearchCopyNext(search, &item); + + if (status != noErr) { + ITDebugLog(@"Audioscrobbler: Error searching for existing keychain item: %i", status); + item = nil; + } + + CFRelease(search); + return item; +} + ++ (BOOL)keychainItemExistsForUser:(NSString *)user +{ + SecKeychainItemRef item = [PreferencesController keychainItemForUser:user]; + BOOL exists = (item != nil); + if (item) { + CFRelease(item); + } + return exists; +} + ++ (BOOL)createKeychainItemForUser:(NSString *)user andPassword:(NSString *)password +{ + SecKeychainItemRef item; + OSStatus status; + SecKeychainAttribute attributes[3]; + SecKeychainAttributeList list; + + ITDebugLog(@"Audioscrobbler: Creating new keychain item for %@.", user); + attributes[0].tag = kSecAccountItemAttr; + attributes[0].data = (char *)[user UTF8String]; + attributes[0].length = [user length]; + attributes[1].tag = kSecDescriptionItemAttr; + attributes[1].data = AUDIOSCROBBLER_KEYCHAIN_KIND; + attributes[1].length = strlen(AUDIOSCROBBLER_KEYCHAIN_KIND); + attributes[2].tag = kSecLabelItemAttr; + attributes[2].data = (char *)AUDIOSCROBBLER_KEYCHAIN_SERVICE(user); + attributes[2].length = strlen(AUDIOSCROBBLER_KEYCHAIN_SERVICE(user)); + list.count = 3; + list.attr = attributes; + + status = SecKeychainItemCreateFromContent(kSecGenericPasswordItemClass, &list, [password length], [password UTF8String], NULL, NULL, &item); + if (status != noErr) { + ITDebugLog(@"Audioscrobbler: Error creating keychain item: %i", status); + } + return (status == noErr); +} + ++ (BOOL)deleteKeychainItemForUser:(NSString *)user +{ + OSStatus status = errSecNotAvailable; + SecKeychainItemRef item = [PreferencesController keychainItemForUser:user]; + if (item != nil) { + status = SecKeychainItemDelete(item); + if (status != noErr) { + ITDebugLog(@"Audioscrobbler: Error deleting keychain item: %i", status); + } + CFRelease(item); + } + return (status == noErr); +} + ++ (BOOL)setKeychainItemPassword:(NSString *)password forUser:(NSString *)user +{ + OSStatus status = errSecNotAvailable; + SecKeychainItemRef item = [PreferencesController keychainItemForUser:user]; + if (item != nil) { + status = SecKeychainItemModifyContent(item, NULL, [password length], [password UTF8String]); + if (status != noErr) { + ITDebugLog(@"Audioscrobbler: Error deleting keychain item: %i", status); + } + CFRelease(item); + } + return (status == noErr); +} + ++ (NSString *)getKeychainItemPasswordForUser:(NSString *)user +{ + OSStatus status = errSecNotAvailable; + SecKeychainItemRef item = [PreferencesController keychainItemForUser:user]; + NSString *pass = nil; + if (item != nil) { + UInt32 length; + char *buffer; + status = SecKeychainItemCopyContent(item, NULL, NULL, &length, (void **)&buffer); + if (status != noErr) { + ITDebugLog(@"Audioscrobbler: Error getting keychain item password: %i", status); + } else { + NSLog(@"Audioscrobbler: password buffer: \"%s\" \"Length: %i\"", buffer, length); + pass = [[NSString alloc] initWithBytes:buffer length:length encoding:NSUTF8StringEncoding]; + } + if (status != noErr) { + ITDebugLog(@"Audioscrobbler: Error deleting keychain item: %i", status); + } + SecKeychainItemFreeContent(NULL, buffer); + CFRelease(item); + } + NSLog(@"Audioscrobbler: Retrieved password: \"%@\"", pass); + return [pass autorelease]; +} /*************************************************************************/ #pragma mark - @@ -84,8 +220,11 @@ static PreferencesController *prefs = nil; hotKeysArray = [[NSArray alloc] initWithObjects:@"PlayPause", @"NextTrack", @"PrevTrack", + @"FastForward", + @"Rewind", @"ShowPlayer", @"TrackInfo", + @"AlbumArt", @"UpcomingSongs", @"IncrementVolume", @"DecrementVolume", @@ -93,13 +232,24 @@ static PreferencesController *prefs = nil; @"DecrementRating", @"ToggleShuffle", @"ToggleLoop", + @"ToggleShufflability", + @"PopupMenu", + @"SetRating0", + @"SetRating1", + @"SetRating2", + @"SetRating3", + @"SetRating4", + @"SetRating5", nil]; hotKeyNamesArray = [[NSArray alloc] initWithObjects:@"Play/Pause", @"Next Track", @"Previous Track", + @"Fast Forward", + @"Rewind", @"Show Player", @"Track Info", + @"Album Art", @"Upcoming Songs", @"Increment Volume", @"Decrement Volume", @@ -107,6 +257,14 @@ static PreferencesController *prefs = nil; @"Decrement Rating", @"Toggle Shuffle", @"Toggle Loop", + @"Toggle Song Included In Shuffle", + @"Pop-up status menu", + [NSString stringWithUTF8String:"Set Rating: ☆☆☆☆☆"], + [NSString stringWithUTF8String:"Set Rating: ★☆☆☆☆"], + [NSString stringWithUTF8String:"Set Rating: ★★☆☆☆"], + [NSString stringWithUTF8String:"Set Rating: ★★★☆☆"], + [NSString stringWithUTF8String:"Set Rating: ★★★★☆"], + [NSString stringWithUTF8String:"Set Rating: ★★★★"], nil]; hotKeysDictionary = [[NSMutableDictionary alloc] init]; controller = nil; @@ -146,9 +304,11 @@ static PreferencesController *prefs = nil; [passwordPanelOKButton setTitle:@"Connect"]; [passwordPanelTitle setStringValue:@"Password Required"]; [passwordPanelMessage setStringValue:[NSString stringWithFormat:@"Please enter a password for access to the MenuTunes player named %@ at %@.", [[[NetworkController sharedController] networkObject] serverName], [[NetworkController sharedController] remoteHost]]]; - [passwordPanel center]; [passwordPanel setLevel:NSStatusWindowLevel]; - [passwordPanel makeKeyAndOrderFront:nil]; + [NSApp activateIgnoringOtherApps:YES]; + [passwordPanel center]; + [passwordPanel orderFrontRegardless]; + [passwordPanel makeKeyWindow]; if ([NSApp runModalForWindow:passwordPanel]) { return YES; } else { @@ -162,9 +322,11 @@ static PreferencesController *prefs = nil; [passwordPanelOKButton setTitle:@"Retry"]; [passwordPanelTitle setStringValue:@"Invalid Password"]; [passwordPanelMessage setStringValue:[NSString stringWithFormat:@"The password entered for access to the MenuTunes player named %@ at %@ is invalid. Please provide a new password.", [[[NetworkController sharedController] networkObject] serverName], [[NetworkController sharedController] remoteHost]]]; - [passwordPanel center]; [passwordPanel setLevel:NSStatusWindowLevel]; - [passwordPanel makeKeyAndOrderFront:nil]; + [NSApp activateIgnoringOtherApps:YES]; + [passwordPanel center]; + [passwordPanel orderFrontRegardless]; + [passwordPanel makeKeyWindow]; if ([NSApp runModalForWindow:passwordPanel]) { return YES; } else { @@ -182,6 +344,7 @@ static PreferencesController *prefs = nil; [self setupUI]; // Sets up additional UI [window setDelegate:self]; [menuTableView reloadData]; + [hotKeysTableView reloadData]; [hotKeysTableView setDoubleAction:@selector(hotKeysTableViewDoubleClicked:)]; //Change the launch player checkbox to the proper name @@ -192,40 +355,96 @@ static PreferencesController *prefs = nil; NS_ENDHANDLER } - [window center]; + [self resetRemotePlayerTextFields]; + [launchAtLoginCheckbox becomeFirstResponder]; [NSApp activateIgnoringOtherApps:YES]; - [window performSelector:@selector(makeKeyAndOrderFront:) withObject:self afterDelay:0.0]; -} - -- (IBAction)showTestWindow:(id)sender -{ - [controller showTestWindow]; + if (![window isVisible]) { + [window center]; + } + [window orderFrontRegardless]; + [window makeKeyWindow]; } - (IBAction)changeGeneralSetting:(id)sender { ITDebugLog(@"Changing general setting of tag %i.", [sender tag]); if ( [sender tag] == 1010) { - [self setLaunchesAtLogin:SENDER_STATE]; + ITSetApplicationLaunchOnLogin([[NSBundle mainBundle] bundlePath], SENDER_STATE); } else if ( [sender tag] == 1020) { [df setBool:SENDER_STATE forKey:@"LaunchPlayerWithMT"]; } else if ( [sender tag] == 1030) { [df setInteger:[sender intValue] forKey:@"SongsInAdvance"]; - + if ([[controller currentRemote] playerRunningState] == ITMTRemotePlayerRunning) { + [[controller menuController] performSelector:@selector(rebuildSubmenus) withObject:nil afterDelay:0]; + } } else if ( [sender tag] == 1040) { // This will not be executed. Song info always shows the title of the song. // [df setBool:SENDER_STATE forKey:@"showName"]; } else if ( [sender tag] == 1050) { [df setBool:SENDER_STATE forKey:@"showArtist"]; + } else if ( [sender tag] == 1055) { + [df setBool:SENDER_STATE forKey:@"showComposer"]; } else if ( [sender tag] == 1060) { [df setBool:SENDER_STATE forKey:@"showAlbum"]; } else if ( [sender tag] == 1070) { [df setBool:SENDER_STATE forKey:@"showTime"]; } else if ( [sender tag] == 1080) { [df setBool:SENDER_STATE forKey:@"showTrackNumber"]; + } else if ( [sender tag] == 1085) { + [df setBool:SENDER_STATE forKey:@"showPlayCount"]; } else if ( [sender tag] == 1090) { [df setBool:SENDER_STATE forKey:@"showTrackRating"]; - } + } else if ( [sender tag] == 1100) { + [df setBool:SENDER_STATE forKey:@"showAlbumArtwork"]; + } else if ( [sender tag] == 1110) { + [df setBool:SENDER_STATE forKey:@"runScripts"]; + if (SENDER_STATE) { + [runScriptsCheckbox setState:NSOnState]; + [showScriptsButton setEnabled:YES]; + } else { + [showScriptsButton setEnabled:NO]; + } + } else if ( [sender tag] == 1120) { + mkdir([[NSHomeDirectory() stringByAppendingPathComponent:@"Library/Application Support/MenuTunes/Scripts"] UTF8String], 0744); + [[NSWorkspace sharedWorkspace] openFile:[NSHomeDirectory() stringByAppendingPathComponent:@"Library/Application Support/MenuTunes/Scripts"]]; + } else if ( [sender tag] == 6010) { + //Toggle the other Audioscrobbler options + [df setBool:SENDER_STATE forKey:@"audioscrobblerEnabled"]; + [audioscrobblerUseCacheCheckbox setEnabled:SENDER_STATE]; + [audioscrobblerUserTextField setEnabled:SENDER_STATE]; + [audioscrobblerPasswordTextField setEnabled:SENDER_STATE]; + if (SENDER_STATE) { + [[AudioscrobblerController sharedController] attemptHandshake:NO]; + } + } else if ( [sender tag ] == 6015) { + //Here we create a new keychain item if needed and deletes the keychain item if the field is cleared. + NSString *currentAccount = [df stringForKey:@"audioscrobblerUser"], *newAccount = [sender stringValue]; + if ([newAccount length] == 0) { + [PreferencesController deleteKeychainItemForUser:currentAccount]; + } else if (![currentAccount isEqualToString:newAccount] && [[audioscrobblerPasswordTextField stringValue] length] > 0) { + [df setObject:newAccount forKey:@"audioscrobblerUser"]; + if ([PreferencesController keychainItemExistsForUser:currentAccount]) { + //Delete the current keychain item if there is one + [PreferencesController deleteKeychainItemForUser:currentAccount]; + } + [PreferencesController createKeychainItemForUser:newAccount andPassword:[audioscrobblerPasswordTextField stringValue]]; + [[AudioscrobblerController sharedController] attemptHandshake:YES]; + } + } else if ( [sender tag ] == 6030) { + //Here we set the password for an existing keychain item or we create a new keychain item. + if ([[audioscrobblerUserTextField stringValue] length] > 0) { + NSString *account = [df stringForKey:@"audioscrobblerUser"]; + if ([PreferencesController keychainItemExistsForUser:account]) { + //Update the current keychain item + [PreferencesController setKeychainItemPassword:[sender stringValue] forUser:account]; + } else if ([[sender stringValue] length] > 0 && [[audioscrobblerUserTextField stringValue] length]) { + //Create a new keychain item + [PreferencesController createKeychainItemForUser:account andPassword:[sender stringValue]]; + } + } + } else if ( [sender tag] == 6045) { + [df setBool:SENDER_STATE forKey:@"audioscrobblerCacheSubmissions"]; + } [df synchronize]; } @@ -237,35 +456,36 @@ static PreferencesController *prefs = nil; [df setBool:state forKey:@"enableSharing"]; //Disable/enable the use of shared player options [useSharedMenuTunesCheckbox setEnabled:!state]; - [usePasswordCheckbox setEnabled:state]; [passwordTextField setEnabled:state]; [nameTextField setEnabled:state]; [selectSharedPlayerButton setEnabled:NO]; [controller setServerStatus:state]; //Set server status } else if ( [sender tag] == 5015 ) { [df setObject:[sender stringValue] forKey:@"sharedPlayerName"]; - } else if ( [sender tag] == 5020 ) { - [df setBool:SENDER_STATE forKey:@"enableSharingPassword"]; + [[NetworkController sharedController] resetServerName]; } else if ( [sender tag] == 5030 ) { //Set the server password const char *instring = [[sender stringValue] UTF8String]; - const char *password = "password"; - unsigned char *result; + const char *password = "p4s5w0rdMT1.2"; + char *result; NSData *hashedPass, *passwordStringHash; - result = SHA1(instring, strlen(instring), NULL); + if ([[sender stringValue] length] == 0) { + [df setObject:[NSData data] forKey:@"sharedPlayerPassword"]; + return; + } + result = (char *)SHA1((unsigned char *)instring, strlen(instring), NULL); hashedPass = [NSData dataWithBytes:result length:strlen(result)]; - result = SHA1(password, strlen(password), NULL); + result = (char *)SHA1((unsigned char *)password, strlen(password), NULL); passwordStringHash = [NSData dataWithBytes:result length:strlen(result)]; if (![hashedPass isEqualToData:passwordStringHash]) { [df setObject:hashedPass forKey:@"sharedPlayerPassword"]; - [sender setStringValue:@"password"]; + [sender setStringValue:@"p4s5w0rdMT1.2"]; } } else if ( [sender tag] == 5040 ) { BOOL state = SENDER_STATE; [df setBool:state forKey:@"useSharedPlayer"]; //Disable/enable the use of sharing options [shareMenuTunesCheckbox setEnabled:!state]; - [usePasswordCheckbox setEnabled:NO]; [passwordTextField setEnabled:NO]; [nameTextField setEnabled:NO]; [selectSharedPlayerButton setEnabled:state]; @@ -326,8 +546,8 @@ static PreferencesController *prefs = nil; } } else if ( [sender tag] == 5150 ) { const char *instring = [[sender stringValue] UTF8String]; - unsigned char *result; - result = SHA1(instring, strlen(instring), NULL); + char *result; + result = (char *)SHA1((unsigned char *)instring, strlen(instring), NULL); [df setObject:[NSData dataWithBytes:result length:strlen(result)] forKey:@"connectPassword"]; } else if ( [sender tag] == 5110 ) { //Cancel @@ -366,8 +586,8 @@ static PreferencesController *prefs = nil; } else if ( [sender tag] == 6020 ) { //OK password entry, retry connect const char *instring = [[passwordPanelTextField stringValue] UTF8String]; - unsigned char *result; - result = SHA1(instring, strlen(instring), NULL); + char *result; + result = (char *)SHA1((unsigned char *)instring, strlen(instring), NULL); [df setObject:[NSData dataWithBytes:result length:strlen(result)] forKey:@"connectPassword"]; [passwordPanel orderOut:nil]; [NSApp stopModalWithCode:1]; @@ -377,14 +597,14 @@ static PreferencesController *prefs = nil; - (IBAction)changeStatusWindowSetting:(id)sender { - StatusWindow *sw = [StatusWindow sharedWindow]; + StatusWindow *sw = (StatusWindow *)[StatusWindow sharedWindow]; ITDebugLog(@"Changing status window setting of tag %i", [sender tag]); if ( [sender tag] == 2010) { BOOL entryEffectValid = YES; BOOL exitEffectValid = YES; - + [df setInteger:[sender selectedRow] forKey:@"statusWindowVerticalPosition"]; [df setInteger:[sender selectedColumn] forKey:@"statusWindowHorizontalPosition"]; [sw setVerticalPosition:[sender selectedRow]]; @@ -416,20 +636,24 @@ static PreferencesController *prefs = nil; [vanishEffectPopup selectItemAtIndex:[[vanishEffectPopup menu] indexOfItemWithRepresentedObject:[[sw exitEffect] class]]]; } - // Update the window's position. - // Yeah, do that. + [(MainController *)controller showCurrentTrackInfo]; } else if ( [sender tag] == 2020) { // Update screen selection. - + [[StatusWindow sharedWindow] setScreen:[[NSScreen screens] objectAtIndex:[sender indexOfSelectedItem]]]; + [df setInteger:[sender indexOfSelectedItem] forKey:@"statusWindowScreenIndex"]; + [(MainController *)controller showCurrentTrackInfo]; + } else if ( [sender tag] == 2030) { [self setStatusWindowEntryEffect:[[sender selectedItem] representedObject]]; + [(MainController *)controller showCurrentTrackInfo]; } else if ( [sender tag] == 2040) { [self setStatusWindowExitEffect:[[sender selectedItem] representedObject]]; + [(MainController *)controller showCurrentTrackInfo]; } else if ( [sender tag] == 2050) { float newTime = ( -([sender floatValue]) ); @@ -463,9 +687,11 @@ static PreferencesController *prefs = nil; } [df setInteger:setting forKey:@"statusWindowBackgroundMode"]; + [(MainController *)controller showCurrentTrackInfo]; } else if ( [sender tag] == 2091) { [self setCustomColor:[sender color] updateWell:NO]; + [(MainController *)controller showCurrentTrackInfo]; } else if ( [sender tag] == 2092) { int selectedItem = [sender indexOfSelectedItem]; @@ -483,10 +709,11 @@ static PreferencesController *prefs = nil; } else { [self setCustomColor:[NSColor colorWithCalibratedWhite:0.15 alpha:0.70] updateWell:YES]; } + [(MainController *)controller showCurrentTrackInfo]; } else if ( [sender tag] == 2095) { - [sw vanish:self]; [df setInteger:[sender indexOfSelectedItem] forKey:@"statusWindowSizing"]; + [(MainController *)controller showCurrentTrackInfo]; } [df synchronize]; @@ -494,11 +721,6 @@ static PreferencesController *prefs = nil; - (void)registerDefaults { - BOOL found = NO; - NSMutableDictionary *loginWindow; - NSMutableArray *loginArray; - NSEnumerator *loginEnum; - id anItem; ITDebugLog(@"Registering defaults."); [df setObject:[NSArray arrayWithObjects: @"trackInfo", @@ -511,14 +733,19 @@ static PreferencesController *prefs = nil; @"upcomingSongs", @"separator", @"preferences", + @"about", @"quit", nil] forKey:@"menu"]; + [df setInteger:MT_CURRENT_VERSION forKey:@"appVersion"]; [df setInteger:5 forKey:@"SongsInAdvance"]; // [df setBool:YES forKey:@"showName"]; // Song info will always show song title. [df setBool:YES forKey:@"showArtist"]; + [df setBool:YES forKey:@"showAlbumArtwork"]; [df setBool:NO forKey:@"showAlbum"]; + [df setBool:NO forKey:@"showComposer"]; [df setBool:NO forKey:@"showTime"]; + [df setBool:NO forKey:@"showToolTip"]; [df setObject:@"ITCutWindowEffect" forKey:@"statusWindowAppearanceEffect"]; [df setObject:@"ITDissolveWindowEffect" forKey:@"statusWindowVanishEffect"]; @@ -527,40 +754,32 @@ static PreferencesController *prefs = nil; [df setFloat:4.0 forKey:@"statusWindowVanishDelay"]; [df setInteger:(int)ITWindowPositionBottom forKey:@"statusWindowVerticalPosition"]; [df setInteger:(int)ITWindowPositionLeft forKey:@"statusWindowHorizontalPosition"]; + [df setInteger:0 forKey:@"statusWindowScreenIndex"]; + [[StatusWindow sharedWindow] setVerticalPosition:(int)ITWindowPositionBottom]; + [[StatusWindow sharedWindow] setHorizontalPosition:(int)ITWindowPositionLeft]; [df setBool:YES forKey:@"showSongInfoOnChange"]; [df setObject:[NSArchiver archivedDataWithRootObject:[NSColor blueColor]] forKey:@"statusWindowBackgroundColor"]; [df synchronize]; - loginWindow = [[df persistentDomainForName:@"loginwindow"] mutableCopy]; - loginArray = [loginWindow objectForKey:@"AutoLaunchedApplicationDictionary"]; - loginEnum = [loginArray objectEnumerator]; - - while ( (anItem = [loginEnum nextObject]) ) { - if ( [[[anItem objectForKey:@"Path"] lastPathComponent] isEqualToString:[[[NSBundle mainBundle] bundlePath] lastPathComponent]] ) { - found = YES; - } - } - [loginWindow release]; - - if (!found) { + if (ITDoesApplicationLaunchOnLogin([[NSBundle mainBundle] bundlePath])) { [[StatusWindowController sharedController] showSetupQueryWindow]; } } - (void)autoLaunchOK { - [[StatusWindow sharedWindow] setLocked:NO]; + [(StatusWindow *)[StatusWindow sharedWindow] setLocked:NO]; [[StatusWindow sharedWindow] vanish:self]; [[StatusWindow sharedWindow] setIgnoresMouseEvents:YES]; - [self setLaunchesAtLogin:YES]; + ITSetApplicationLaunchOnLogin([[NSBundle mainBundle] bundlePath], YES); } - (void)autoLaunchCancel { - [[StatusWindow sharedWindow] setLocked:NO]; + [(StatusWindow *)[StatusWindow sharedWindow] setLocked:NO]; [[StatusWindow sharedWindow] vanish:self]; [[StatusWindow sharedWindow] setIgnoresMouseEvents:YES]; } @@ -661,6 +880,11 @@ static PreferencesController *prefs = nil; #pragma mark PRIVATE METHOD IMPLEMENTATIONS /*************************************************************************/ +- (void)audioscrobblerStatusChanged:(NSNotification *)note +{ + [audioscrobblerStatusTextField setStringValue:[[note userInfo] objectForKey:@"StatusString"]]; +} + - (void)setupWindow { ITDebugLog(@"Loading Preferences.nib."); @@ -702,6 +926,8 @@ static PreferencesController *prefs = nil; @"trackInfo", @"upcomingSongs", @"playlists", + @"artists", + @"albums", @"eqPresets", @"songRating", @"playPause", @@ -710,6 +936,7 @@ static PreferencesController *prefs = nil; @"fastForward", @"rewind", @"showPlayer", + @"about", @"quit", nil]; @@ -728,6 +955,8 @@ static PreferencesController *prefs = nil; submenuItems = [[NSArray alloc] initWithObjects: @"upcomingSongs", @"playlists", + @"artists", + @"albums", @"eqPresets", @"songRating", nil]; @@ -735,14 +964,19 @@ static PreferencesController *prefs = nil; - (void)setupUI { - NSMutableDictionary *loginwindow; - NSMutableArray *loginarray; - NSEnumerator *loginEnum; NSEnumerator *keyArrayEnum; NSString *serverName; NSData *colorData; int selectedBGStyle; id anItem; + + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(audioscrobblerStatusChanged:) name:@"AudioscrobblerStatusChanged" object:nil]; + if ([df boolForKey:@"audioscrobblerEnabled"]) { + NSString *status = [[AudioscrobblerController sharedController] lastStatus]; + [audioscrobblerStatusTextField setStringValue:(status == nil) ? @"Idle" : status]; + } + + [df setInteger:MT_CURRENT_VERSION forKey:@"appVersion"]; ITDebugLog(@"Setting up preferences UI."); // Fill in the number of songs in advance to show field @@ -760,27 +994,33 @@ static PreferencesController *prefs = nil; } } + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(setupScreenPopup) name:NSApplicationDidChangeScreenParametersNotification object:nil]; + [self setupScreenPopup]; + ITDebugLog(@"Setting up track info checkboxes."); // Check current track info buttons [albumCheckbox setState:[df boolForKey:@"showAlbum"] ? NSOnState : NSOffState]; [nameCheckbox setState:NSOnState]; // Song info will ALWAYS show song title. [nameCheckbox setEnabled:NO]; // Song info will ALWAYS show song title. [artistCheckbox setState:[df boolForKey:@"showArtist"] ? NSOnState : NSOffState]; + [composerCheckbox setState:[df boolForKey:@"showComposer"] ? NSOnState : NSOffState]; [trackTimeCheckbox setState:[df boolForKey:@"showTime"] ? NSOnState : NSOffState]; [trackNumberCheckbox setState:[df boolForKey:@"showTrackNumber"] ? NSOnState : NSOffState]; + [playCountCheckbox setState:[df boolForKey:@"showPlayCount"] ? NSOnState : NSOffState]; [ratingCheckbox setState:[df boolForKey:@"showTrackRating"] ? NSOnState : NSOffState]; + [albumArtworkCheckbox setState:[df boolForKey:@"showAlbumArtwork"] ? NSOnState : NSOffState]; + + if ([df boolForKey:@"runScripts"]) { + [runScriptsCheckbox setState:NSOnState]; + [showScriptsButton setEnabled:YES]; + } else { + [showScriptsButton setEnabled:NO]; + } // Set the launch at login checkbox state ITDebugLog(@"Setting launch at login state."); - [df synchronize]; - loginwindow = [[df persistentDomainForName:@"loginwindow"] mutableCopy]; - loginarray = [loginwindow objectForKey:@"AutoLaunchedApplicationDictionary"]; - - loginEnum = [loginarray objectEnumerator]; - while ( (anItem = [loginEnum nextObject]) ) { - if ([[[anItem objectForKey:@"Path"] lastPathComponent] isEqualToString:[[[NSBundle mainBundle] bundlePath] lastPathComponent]]) { - [launchAtLoginCheckbox setState:NSOnState]; - } + if (ITDoesApplicationLaunchOnLogin([[NSBundle mainBundle] bundlePath])) { + [launchAtLoginCheckbox setState:NSOnState]; } // Set the launch player checkbox state @@ -846,7 +1086,6 @@ static PreferencesController *prefs = nil; [useSharedMenuTunesCheckbox setEnabled:NO]; [selectSharedPlayerButton setEnabled:NO]; [passwordTextField setEnabled:YES]; - [usePasswordCheckbox setEnabled:YES]; [nameTextField setEnabled:YES]; } else if ([df boolForKey:@"useSharedPlayer"]) { [useSharedMenuTunesCheckbox setState:NSOnState]; @@ -854,6 +1093,27 @@ static PreferencesController *prefs = nil; [selectSharedPlayerButton setEnabled:YES]; } + //Setup the Audioscrobbler controls + if ([df boolForKey:@"audioscrobblerEnabled"]) { + [audioscrobblerEnabledCheckbox setState:NSOnState]; + [audioscrobblerUserTextField setEnabled:YES]; + [audioscrobblerPasswordTextField setEnabled:YES]; + [audioscrobblerUseCacheCheckbox setEnabled:YES]; + } else { + [audioscrobblerUserTextField setEnabled:NO]; + [audioscrobblerPasswordTextField setEnabled:NO]; + [audioscrobblerUseCacheCheckbox setEnabled:NO]; + } + NSString *audioscrobblerUser = [df stringForKey:@"audioscrobblerUser"]; + if (audioscrobblerUser != nil && [audioscrobblerUser length] > 0 && [PreferencesController keychainItemExistsForUser:audioscrobblerUser]) { + NSString *password = [PreferencesController getKeychainItemPasswordForUser:audioscrobblerUser]; + [audioscrobblerUserTextField setStringValue:audioscrobblerUser]; + if (password != nil) { + [audioscrobblerPasswordTextField setStringValue:password]; + } + } + [audioscrobblerUseCacheCheckbox setState:[df boolForKey:@"audioscrobblerCacheSubmissions"]]; + [[NSNotificationCenter defaultCenter] addObserver:sharingTableView selector:@selector(reloadData) name:@"ITMTFoundNetService" object:nil]; serverName = [df stringForKey:@"sharedPlayerName"]; @@ -863,9 +1123,10 @@ static PreferencesController *prefs = nil; [nameTextField setStringValue:serverName]; [selectPlayerBox setContentView:zeroConfView]; - [usePasswordCheckbox setState:([df boolForKey:@"enableSharingPassword"] ? NSOnState : NSOffState)]; - if ([df dataForKey:@"sharedPlayerPassword"]) { - [passwordTextField setStringValue:@"password"]; + if ([[df dataForKey:@"sharedPlayerPassword"] length] > 0) { + [passwordTextField setStringValue:@"p4s5w0rdMT1.2"]; + } else { + [passwordTextField setStringValue:@""]; } if ([df stringForKey:@"sharedPlayerHost"]) { [hostTextField setStringValue:[df stringForKey:@"sharedPlayerHost"]]; @@ -880,9 +1141,33 @@ static PreferencesController *prefs = nil; } } +- (void)setupScreenPopup +{ + ITDebugLog(@"Setting up screen popup"); + NSArray *screens = [NSScreen screens]; + if ([screens count] > 1) { + int i, index = [df integerForKey:@"statusWindowScreenIndex"]; + [screenPopup setEnabled:YES]; + for (i = 0; i < [screens count]; i++) { + NSScreen *screen = [screens objectAtIndex:i]; + if (![screen isEqual:[NSScreen mainScreen]]) { + [screenPopup addItemWithTitle:[NSString stringWithFormat:@"Screen %i", i + 1]]; + } + } + [screenPopup selectItemAtIndex:index]; + [[StatusWindow sharedWindow] setScreen:[[NSScreen screens] objectAtIndex:index]]; + } else { + while ([screenPopup numberOfItems] > 1) { + [screenPopup removeItemAtIndex:1]; + } + [screenPopup setEnabled:NO]; + [[StatusWindow sharedWindow] setScreen:[NSScreen mainScreen]]; + } +} + - (void)setStatusWindowEntryEffect:(Class)effectClass { - StatusWindow *sw = [StatusWindow sharedWindow]; + StatusWindow *sw = (StatusWindow *)[StatusWindow sharedWindow]; float time = ([df floatForKey:@"statusWindowAppearanceSpeed"] ? [df floatForKey:@"statusWindowAppearanceSpeed"] : 0.8); [df setObject:NSStringFromClass(effectClass) forKey:@"statusWindowAppearanceEffect"]; @@ -893,7 +1178,7 @@ static PreferencesController *prefs = nil; - (void)setStatusWindowExitEffect:(Class)effectClass { - StatusWindow *sw = [StatusWindow sharedWindow]; + StatusWindow *sw = (StatusWindow *)[StatusWindow sharedWindow]; float time = ([df floatForKey:@"statusWindowVanishSpeed"] ? [df floatForKey:@"statusWindowVanishSpeed"] : 0.8); [df setObject:NSStringFromClass(effectClass) forKey:@"statusWindowVanishEffect"]; @@ -976,36 +1261,13 @@ static PreferencesController *prefs = nil; ITDebugLog(@"Synchronizing menus"); [df setObject:myItems forKey:@"menu"]; [df synchronize]; -} - -- (void)setLaunchesAtLogin:(BOOL)flag -{ - NSMutableDictionary *loginwindow; - NSMutableArray *loginarray; - ITDebugLog(@"Setting launches at login: %i", flag); - [df synchronize]; - loginwindow = [[df persistentDomainForName:@"loginwindow"] mutableCopy]; - loginarray = [loginwindow objectForKey:@"AutoLaunchedApplicationDictionary"]; - if (flag) { - NSDictionary *itemDict = [NSDictionary dictionaryWithObjectsAndKeys: - [[NSBundle mainBundle] bundlePath], @"Path", - [NSNumber numberWithInt:0], @"Hide", nil]; - [loginarray addObject:itemDict]; - } else { - int i; - for (i = 0; i < [loginarray count]; i++) { - NSDictionary *tempDict = [loginarray objectAtIndex:i]; - if ([[[tempDict objectForKey:@"Path"] lastPathComponent] isEqualToString:[[[NSBundle mainBundle] bundlePath] lastPathComponent]]) { - [loginarray removeObjectAtIndex:i]; - break; - } - } + [[controller menuController] performSelector:@selector(rebuildSubmenus) withObject:nil afterDelay:0.0]; + + //If we're connected over a network, refresh the menu immediately + if ([[NetworkController sharedController] isConnectedToServer]) { + [controller timerUpdate]; } - [df setPersistentDomain:loginwindow forName:@"loginwindow"]; - [df synchronize]; - [loginwindow release]; - ITDebugLog(@"Finished setting launches at login."); } @@ -1200,6 +1462,7 @@ static PreferencesController *prefs = nil; - (void)dealloc { + [[NSNotificationCenter defaultCenter] removeObserver:self]; [hotKeysArray release]; [hotKeysDictionary release]; [effectClasses release]; @@ -1210,6 +1473,7 @@ static PreferencesController *prefs = nil; [submenuItems release]; [myItems release]; [df release]; + [super dealloc]; } @end