X-Git-Url: http://git.ithinksw.org/MenuTunes.git/blobdiff_plain/de2bf09451cd859cce4a514ad0fbff93cd9f3b0e..0948b4b6166bbee8caefca1278b6551d264068a8:/PreferencesController.m diff --git a/PreferencesController.m b/PreferencesController.m index 372cce7..8ad7630 100755 --- a/PreferencesController.m +++ b/PreferencesController.m @@ -1,16 +1,28 @@ #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 +#import +#import +#import +#import + +#import + #import #import #import #import #import +#import +#import #import #import #import @@ -30,8 +42,13 @@ - (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 @@ -64,9 +81,14 @@ static PreferencesController *prefs = nil; if ( (self = [super init]) ) { ITDebugLog(@"Preferences initialized."); df = [[NSUserDefaults standardUserDefaults] retain]; + + effectClasses = [[ITWindowEffect effectClasses] retain]; + hotKeysArray = [[NSArray alloc] initWithObjects:@"PlayPause", @"NextTrack", @"PrevTrack", + @"FastForward", + @"Rewind", @"ShowPlayer", @"TrackInfo", @"UpcomingSongs", @@ -76,11 +98,20 @@ static PreferencesController *prefs = nil; @"DecrementRating", @"ToggleShuffle", @"ToggleLoop", + @"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", @"Upcoming Songs", @@ -90,9 +121,19 @@ static PreferencesController *prefs = nil; @"Decrement Rating", @"Toggle Shuffle", @"Toggle Loop", + @"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; + + [self setupWindow]; // Load in the nib, and perform any initial setup. + [[NSColorPanel sharedColorPanel] setShowsAlpha:YES]; } return self; } @@ -120,17 +161,53 @@ static PreferencesController *prefs = nil; #pragma mark INSTANCE METHODS /*************************************************************************/ +- (BOOL)showPasswordPanel +{ + [passwordPanel setLevel:NSStatusWindowLevel]; + [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 setLevel:NSStatusWindowLevel]; + [NSApp activateIgnoringOtherApps:YES]; + [passwordPanel center]; + [passwordPanel orderFrontRegardless]; + [passwordPanel makeKeyWindow]; + if ([NSApp runModalForWindow:passwordPanel]) { + return YES; + } else { + return NO; + } +} + +- (BOOL)showInvalidPasswordPanel +{ + [passwordPanel setLevel:NSStatusWindowLevel]; + [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 setLevel:NSStatusWindowLevel]; + [NSApp activateIgnoringOtherApps:YES]; + [passwordPanel center]; + [passwordPanel orderFrontRegardless]; + [passwordPanel makeKeyWindow]; + if ([NSApp runModalForWindow:passwordPanel]) { + return YES; + } else { + return NO; + } +} + - (IBAction)showPrefsWindow:(id)sender { ITDebugLog(@"Showing preferences window."); - if (! window) { // If window does not exist yet, then the nib hasn't been loaded. + if (!myItems) { // If menu array does not exist yet, then the window hasn't been setup. ITDebugLog(@"Window doesn't exist, initial setup."); - [self setupWindow]; // Load in the nib, and perform any initial setup. [self setupCustomizationTables]; // Setup the DnD manu config tables. [self setupMenuItems]; // Setup the arrays of menu items [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 @@ -141,34 +218,58 @@ static PreferencesController *prefs = nil; NS_ENDHANDLER } - [window center]; + [self resetRemotePlayerTextFields]; + [launchAtLoginCheckbox becomeFirstResponder]; [NSApp activateIgnoringOtherApps:YES]; - [window performSelector:@selector(makeKeyAndOrderFront:) withObject:self afterDelay:0.0]; + 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"] cString], 0744); + [[NSWorkspace sharedWorkspace] openFile:[NSHomeDirectory() stringByAppendingPathComponent:@"Library/Application Support/MenuTunes/Scripts"]]; } [df synchronize]; } @@ -181,32 +282,68 @@ static PreferencesController *prefs = nil; [df setBool:state forKey:@"enableSharing"]; //Disable/enable the use of shared player options [useSharedMenuTunesCheckbox setEnabled:!state]; - [sharePasswordCheckbox setEnabled:!state]; - [sharePasswordTextField setEnabled:!state]; + [passwordTextField setEnabled:state]; + [nameTextField setEnabled:state]; + [selectSharedPlayerButton setEnabled:NO]; [controller setServerStatus:state]; //Set server status - } else if ( [sender tag] == 5020 ) { - [df setBool:SENDER_STATE forKey:@"enableSharingPassword"]; + } else if ( [sender tag] == 5015 ) { + [df setObject:[sender stringValue] forKey:@"sharedPlayerName"]; + [[NetworkController sharedController] resetServerName]; } else if ( [sender tag] == 5030 ) { - [df setObject:[sender stringValue] forKey:@"sharingPassword"]; + //Set the server password + const char *instring = [[sender stringValue] UTF8String]; + const char *password = "p4s5w0rdMT1.2"; + unsigned char *result; + NSData *hashedPass, *passwordStringHash; + if ([[sender stringValue] length] == 0) { + [df setObject:[NSData data] forKey:@"sharedPlayerPassword"]; + return; + } + result = SHA1(instring, strlen(instring), NULL); + hashedPass = [NSData dataWithBytes:result length:strlen(result)]; + result = SHA1(password, strlen(password), NULL); + passwordStringHash = [NSData dataWithBytes:result length:strlen(result)]; + if (![hashedPass isEqualToData:passwordStringHash]) { + [df setObject:hashedPass forKey:@"sharedPlayerPassword"]; + [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]; - [sharePasswordCheckbox setEnabled:!state]; - [sharePasswordTextField setEnabled:!state]; + [passwordTextField setEnabled:NO]; + [nameTextField setEnabled:NO]; + [selectSharedPlayerButton setEnabled:state]; - if (state) { - [controller connectToServer]; + if (state && ([controller connectToServer] == 1)) { + [selectedPlayerTextField setStringValue:[[[NetworkController sharedController] networkObject] serverName]]; + [locationTextField setStringValue:[[NetworkController sharedController] remoteHost]]; } else { - [controller disconnectFromServer]; + [selectedPlayerTextField setStringValue:@"No shared player selected."]; + [locationTextField setStringValue:@"-"]; + if ([[NetworkController sharedController] isConnectedToServer]) { + [controller disconnectFromServer]; + } + } } else if ( [sender tag] == 5050 ) { - if ([sender clickedRow] > -1) { - //Set sharedPlayerHost - //[df setObject:[[[[NetworkController sharedController] remoteServices] objectAtIndex:[sender clickedRow]] objectForKey:@"ip"] forKey:@"sharedPlayerHost"]; + //If no player is selected in the table view, turn off OK button. + if ([sender clickedRow] == -1 ) { + [sharingPanelOKButton setEnabled:NO]; + } else { + [sharingPanelOKButton setEnabled:YES]; } + } else if ( [sender tag] == 5051 ) { + [df setObject:[sender stringValue] forKey:@"sharedPlayerHost"]; } else if ( [sender tag] == 5060 ) { + //Set OK button state + if (([selectPlayerBox contentView] == zeroConfView && [sharingTableView selectedRow] == -1) || + ([selectPlayerBox contentView] == manualView && [[hostTextField stringValue] length] == 0)) { + [sharingPanelOKButton setEnabled:NO]; + } else { + [sharingPanelOKButton setEnabled:YES]; + } //Show selection sheet [NSApp beginSheet:selectPlayerSheet modalForWindow:window modalDelegate:self didEndSelector:NULL contextInfo:nil]; } else if ( [sender tag] == 5100 ) { @@ -215,15 +352,29 @@ static PreferencesController *prefs = nil; NSRect frame = [selectPlayerSheet frame]; frame.origin.y -= 58; frame.size.height = 273; - [selectPlayerSheet setFrame:frame display:YES animate:YES]; + if ([sharingTableView selectedRow] == -1) { + [sharingPanelOKButton setEnabled:NO]; + } [selectPlayerBox setContentView:zeroConfView]; - } else if ([selectPlayerBox contentView] != manualView) { + [selectPlayerSheet setFrame:frame display:YES animate:YES]; + } else if ( ([sender indexOfItem:[sender selectedItem]] == 1) && ([selectPlayerBox contentView] != manualView) ){ NSRect frame = [selectPlayerSheet frame]; frame.origin.y += 58; frame.size.height = 215; - [selectPlayerSheet setFrame:frame display:YES animate:YES]; + if ([[hostTextField stringValue] length] == 0) { + [sharingPanelOKButton setEnabled:NO]; + } else { + [sharingPanelOKButton setEnabled:YES]; + } [selectPlayerBox setContentView:manualView]; + [selectPlayerSheet setFrame:frame display:YES animate:YES]; + [hostTextField selectText:nil]; } + } else if ( [sender tag] == 5150 ) { + const char *instring = [[sender stringValue] UTF8String]; + unsigned char *result; + result = SHA1(instring, strlen(instring), NULL); + [df setObject:[NSData dataWithBytes:result length:strlen(result)] forKey:@"connectPassword"]; } else if ( [sender tag] == 5110 ) { //Cancel [NSApp endSheet:selectPlayerSheet]; @@ -237,19 +388,35 @@ static PreferencesController *prefs = nil; [NSApp endSheet:selectPlayerSheet]; [selectPlayerSheet orderOut:nil]; + [self changeSharingSetting:clientPasswordTextField]; + if ([selectPlayerBox contentView] == manualView) { [df setObject:[hostTextField stringValue] forKey:@"sharedPlayerHost"]; } else { if ([sharingTableView selectedRow] > -1) { - [df setObject:[[[[NetworkController sharedController] remoteServices] objectAtIndex:[sharingTableView selectedRow]] objectForKey:@"ip"] forKey:@"sharedPlayerHost"]; + [df setObject:[NSString stringWithCString:inet_ntoa((*(struct sockaddr_in*)[[[[[[NetworkController sharedController] remoteServices] objectAtIndex:[sharingTableView selectedRow]] addresses] objectAtIndex:0] bytes]).sin_addr)] forKey:@"sharedPlayerHost"]; } } - if (![controller connectToServer]) { - NSRunAlertPanel(@"Connection error.", @"The MenuTunes server you attempted to connect to was not responding. MenuTunes will revert back to the local player.", @"OK", nil, nil); - } else { + if ([controller connectToServer] == 1) { [useSharedMenuTunesCheckbox setState:NSOnState]; + [selectedPlayerTextField setStringValue:[[[NetworkController sharedController] networkObject] serverName]]; + [locationTextField setStringValue:[[NetworkController sharedController] remoteHost]]; + } else { + NSRunAlertPanel(@"Connection error.", @"The MenuTunes server you attempted to connect to was not responding. MenuTunes will revert back to the local player.", @"OK", nil, nil); } + } else if ( [sender tag] == 6010 ) { + //Cancel password entry + [passwordPanel orderOut:nil]; + [NSApp stopModalWithCode:0]; + } 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); + [df setObject:[NSData dataWithBytes:result length:strlen(result)] forKey:@"connectPassword"]; + [passwordPanel orderOut:nil]; + [NSApp stopModalWithCode:1]; } [df synchronize]; } @@ -258,58 +425,68 @@ static PreferencesController *prefs = nil; { StatusWindow *sw = [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"]; - // update the window's position here + [sw setVerticalPosition:[sender selectedRow]]; + [sw setHorizontalPosition:[sender selectedColumn]]; + + // Enable/disable the items in the popups. + [self repopulateEffectPopupsForVerticalPosition:[sw verticalPosition] + horizontalPosition:[sw horizontalPosition]]; + + // Make sure the effects support the new position. + entryEffectValid = ( [self effect:[[sw entryEffect] class] + supportsVerticalPosition:[sw verticalPosition] + withHorizontalPosition:[sw horizontalPosition]] ); + exitEffectValid = ( [self effect:[[sw exitEffect] class] + supportsVerticalPosition:[sw verticalPosition] + withHorizontalPosition:[sw horizontalPosition]] ); + + if ( ! entryEffectValid ) { + [appearanceEffectPopup selectItemAtIndex:[[appearanceEffectPopup menu] indexOfItemWithRepresentedObject:NSClassFromString(@"ITCutWindowEffect")]]; + [self setStatusWindowEntryEffect:NSClassFromString(@"ITCutWindowEffect")]; + } else { + [appearanceEffectPopup selectItemAtIndex:[[appearanceEffectPopup menu] indexOfItemWithRepresentedObject:[[sw entryEffect] class]]]; + } + + if ( ! exitEffectValid ) { + [vanishEffectPopup selectItemAtIndex:[[vanishEffectPopup menu] indexOfItemWithRepresentedObject:NSClassFromString(@"ITDissolveWindowEffect")]]; + [self setStatusWindowExitEffect:NSClassFromString(@"ITDissolveWindowEffect")]; + } else { + [vanishEffectPopup selectItemAtIndex:[[vanishEffectPopup menu] indexOfItemWithRepresentedObject:[[sw exitEffect] class]]]; + } + + [(MainController *)controller showCurrentTrackInfo]; + } else if ( [sender tag] == 2020) { - // update screen selection + + // 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) { - int effectTag = [[sender selectedItem] tag]; - float time = ([df floatForKey:@"statusWindowAppearanceSpeed"] ? [df floatForKey:@"statusWindowAppearanceSpeed"] : 0.8); - [df setInteger:effectTag forKey:@"statusWindowAppearanceEffect"]; - - if ( effectTag == 2100 ) { - [sw setEntryEffect:[[[ITCutWindowEffect alloc] initWithWindow:sw] autorelease]]; - } else if ( effectTag == 2101 ) { - [sw setEntryEffect:[[[ITDissolveWindowEffect alloc] initWithWindow:sw] autorelease]]; - } else if ( effectTag == 2102 ) { - [sw setEntryEffect:[[[ITSlideVerticallyWindowEffect alloc] initWithWindow:sw] autorelease]]; - } else if ( effectTag == 2103 ) { - [sw setEntryEffect:[[[ITSlideHorizontallyWindowEffect alloc] initWithWindow:sw] autorelease]]; - } else if ( effectTag == 2104 ) { - NSLog(@"dflhgldf"); - [sw setEntryEffect:[[[ITPivotWindowEffect alloc] initWithWindow:sw] autorelease]]; - } - - [[sw entryEffect] setEffectTime:time]; + + [self setStatusWindowEntryEffect:[[sender selectedItem] representedObject]]; + [(MainController *)controller showCurrentTrackInfo]; } else if ( [sender tag] == 2040) { - int effectTag = [[sender selectedItem] tag]; - float time = ([df floatForKey:@"statusWindowVanishSpeed"] ? [df floatForKey:@"statusWindowVanishSpeed"] : 0.8); - - [df setInteger:[[sender selectedItem] tag] forKey:@"statusWindowVanishEffect"]; + + [self setStatusWindowExitEffect:[[sender selectedItem] representedObject]]; + [(MainController *)controller showCurrentTrackInfo]; - if ( effectTag == 2100 ) { - [sw setExitEffect:[[[ITCutWindowEffect alloc] initWithWindow:sw] autorelease]]; - } else if ( effectTag == 2101 ) { - [sw setExitEffect:[[[ITDissolveWindowEffect alloc] initWithWindow:sw] autorelease]]; - } else if ( effectTag == 2102 ) { - [sw setExitEffect:[[[ITSlideVerticallyWindowEffect alloc] initWithWindow:sw] autorelease]]; - } else if ( effectTag == 2103 ) { - [sw setExitEffect:[[[ITSlideHorizontallyWindowEffect alloc] initWithWindow:sw] autorelease]]; - } else if ( effectTag == 2104 ) { - [sw setExitEffect:[[[ITPivotWindowEffect alloc] initWithWindow:sw] autorelease]]; - } - - [[sw exitEffect] setEffectTime:time]; - } else if ( [sender tag] == 2050) { - float newTime = (-([sender floatValue])); + float newTime = ( -([sender floatValue]) ); [df setFloat:newTime forKey:@"statusWindowAppearanceSpeed"]; [[sw entryEffect] setEffectTime:newTime]; } else if ( [sender tag] == 2060) { - float newTime = (-([sender floatValue])); + float newTime = ( -([sender floatValue]) ); [df setFloat:newTime forKey:@"statusWindowVanishSpeed"]; [[sw exitEffect] setEffectTime:newTime]; } else if ( [sender tag] == 2070) { @@ -317,17 +494,59 @@ static PreferencesController *prefs = nil; [sw setExitDelay:[sender floatValue]]; } else if ( [sender tag] == 2080) { [df setBool:SENDER_STATE forKey:@"showSongInfoOnChange"]; + } else if ( [sender tag] == 2090) { + + int setting = [sender indexOfSelectedItem]; + + if ( setting == 0 ) { + [(ITTSWBackgroundView *)[sw contentView] setBackgroundMode:ITTSWBackgroundApple]; + [backgroundColorWell setEnabled:NO]; + [backgroundColorPopup setEnabled:NO]; + } else if ( setting == 1 ) { + [(ITTSWBackgroundView *)[sw contentView] setBackgroundMode:ITTSWBackgroundReadable]; + [backgroundColorWell setEnabled:NO]; + [backgroundColorPopup setEnabled:NO]; + } else if ( setting == 2 ) { + [(ITTSWBackgroundView *)[sw contentView] setBackgroundMode:ITTSWBackgroundColored]; + [backgroundColorWell setEnabled:YES]; + [backgroundColorPopup setEnabled:YES]; + } + + [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]; + + if ( selectedItem == 1 ) { // An NSPopUpButton in PullDown mode uses item 0 as its title. Its first selectable item is 1. + [self setCustomColor:[NSColor colorWithCalibratedRed:0.92549 green:0.686275 blue:0.0 alpha:1.0] updateWell:YES]; + } else if ( selectedItem == 2 ) { + [self setCustomColor:[NSColor colorWithCalibratedRed:0.380392 green:0.670588 blue:0.0 alpha:1.0] updateWell:YES]; + } else if ( selectedItem == 3 ) { + [self setCustomColor:[NSColor colorWithCalibratedRed:0.443137 green:0.231373 blue:0.619608 alpha:1.0] updateWell:YES]; + } else if ( selectedItem == 4 ) { + [self setCustomColor:[NSColor colorWithCalibratedRed:0.831373 green:0.12549 blue:0.509804 alpha:1.0] updateWell:YES]; + } else if ( selectedItem == 5 ) { + [self setCustomColor:[NSColor colorWithCalibratedRed:0.00784314 green:0.611765 blue:0.662745 alpha:1.0] updateWell:YES]; + } else { + [self setCustomColor:[NSColor colorWithCalibratedWhite:0.15 alpha:0.70] updateWell:YES]; + } + [(MainController *)controller showCurrentTrackInfo]; + + } else if ( [sender tag] == 2095) { + [df setInteger:[sender indexOfSelectedItem] forKey:@"statusWindowSizing"]; + [(MainController *)controller showCurrentTrackInfo]; } + [df synchronize]; } - (void)registerDefaults { - BOOL found = NO; - NSMutableDictionary *loginWindow; - NSMutableArray *loginArray; - NSEnumerator *loginEnum; - id anItem; ITDebugLog(@"Registering defaults."); [df setObject:[NSArray arrayWithObjects: @"trackInfo", @@ -343,33 +562,33 @@ static PreferencesController *prefs = nil; @"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:@"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 setInteger:2100 forKey:@"statusWindowAppearanceEffect"]; - [df setInteger:2101 forKey:@"statusWindowVanishEffect"]; + [df setObject:@"ITCutWindowEffect" forKey:@"statusWindowAppearanceEffect"]; + [df setObject:@"ITDissolveWindowEffect" forKey:@"statusWindowVanishEffect"]; [df setFloat:0.8 forKey:@"statusWindowAppearanceSpeed"]; [df setFloat:0.8 forKey:@"statusWindowVanishSpeed"]; [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 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]; + [df setObject:[NSArchiver archivedDataWithRootObject:[NSColor blueColor]] forKey:@"statusWindowBackgroundColor"]; - if (!found) { + [df synchronize]; + + if (ITDoesApplicationLaunchOnLogin([[NSBundle mainBundle] bundlePath])) { [[StatusWindowController sharedController] showSetupQueryWindow]; } } @@ -380,7 +599,7 @@ static PreferencesController *prefs = nil; [[StatusWindow sharedWindow] vanish:self]; [[StatusWindow sharedWindow] setIgnoresMouseEvents:YES]; - [self setLaunchesAtLogin:YES]; + ITSetApplicationLaunchOnLogin([[NSBundle mainBundle] bundlePath], YES); } - (void)autoLaunchCancel @@ -414,6 +633,17 @@ static PreferencesController *prefs = nil; } } +- (void)resetRemotePlayerTextFields +{ + if ([[NetworkController sharedController] isConnectedToServer]) { + [selectedPlayerTextField setStringValue:[[[NetworkController sharedController] networkObject] serverName]]; + [locationTextField setStringValue:[[NetworkController sharedController] remoteHost]]; + } else { + [selectedPlayerTextField setStringValue:@"No shared player selected."]; + [locationTextField setStringValue:@"-"]; + } +} + /*************************************************************************/ #pragma mark - #pragma mark HOTKEY SUPPORT METHODS @@ -516,6 +746,8 @@ static PreferencesController *prefs = nil; @"trackInfo", @"upcomingSongs", @"playlists", + @"artists", + @"albums", @"eqPresets", @"songRating", @"playPause", @@ -542,6 +774,8 @@ static PreferencesController *prefs = nil; submenuItems = [[NSArray alloc] initWithObjects: @"upcomingSongs", @"playlists", + @"artists", + @"albums", @"eqPresets", @"songRating", nil]; @@ -549,10 +783,13 @@ static PreferencesController *prefs = nil; - (void)setupUI { - NSMutableDictionary *loginwindow; - NSMutableArray *loginarray; - NSEnumerator *loginEnum, *keyArrayEnum; + NSEnumerator *keyArrayEnum; + NSString *serverName; + NSData *colorData; + int selectedBGStyle; id anItem; + + [df setInteger:MT_CURRENT_VERSION forKey:@"appVersion"]; ITDebugLog(@"Setting up preferences UI."); // Fill in the number of songs in advance to show field @@ -570,27 +807,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 @@ -598,73 +841,225 @@ static PreferencesController *prefs = nil; [launchPlayerAtLaunchCheckbox setState:[df boolForKey:@"LaunchPlayerWithMT"] ? NSOnState : NSOffState]; // Setup the positioning controls + [positionMatrix selectCellAtRow:[df integerForKey:@"statusWindowVerticalPosition"] + column:[df integerForKey:@"statusWindowHorizontalPosition"]]; // Setup effects controls - [appearanceEffectPopup selectItem:[appearanceEffectPopup itemAtIndex:[appearanceEffectPopup indexOfItemWithTag:[df integerForKey:@"statusWindowAppearanceEffect"]]]]; - [vanishEffectPopup selectItem:[vanishEffectPopup itemAtIndex:[vanishEffectPopup indexOfItemWithTag:[df integerForKey:@"statusWindowVanishEffect"]]]]; - [appearanceSpeedSlider setFloatValue:-([df floatForKey:@"statusWindowAppearanceSpeed"])]; - [vanishSpeedSlider setFloatValue:-([df floatForKey:@"statusWindowVanishSpeed"])]; + // Populate the effects popups + [appearanceEffectPopup setAutoenablesItems:NO]; + [vanishEffectPopup setAutoenablesItems:NO]; + [self repopulateEffectPopupsForVerticalPosition:[df integerForKey:@"statusWindowVerticalPosition"] + horizontalPosition:[df integerForKey:@"statusWindowHorizontalPosition"]]; + + // Attempt to find the pref'd effect in the list. + // If it's not there, use cut/dissolve. + if ( [effectClasses containsObject:NSClassFromString([df stringForKey:@"statusWindowAppearanceEffect"])] ) { + [appearanceEffectPopup selectItemAtIndex:[effectClasses indexOfObject:NSClassFromString([df stringForKey:@"statusWindowAppearanceEffect"])]]; + } else { + [appearanceEffectPopup selectItemAtIndex:[effectClasses indexOfObject:NSClassFromString(@"ITCutWindowEffect")]]; + } + + if ( [effectClasses containsObject:NSClassFromString([df stringForKey:@"statusWindowVanishEffect"])] ) { + [vanishEffectPopup selectItemAtIndex:[effectClasses indexOfObject:NSClassFromString([df stringForKey:@"statusWindowVanishEffect"])]]; + } else { + [vanishEffectPopup selectItemAtIndex:[effectClasses indexOfObject:NSClassFromString(@"ITCutWindowEffect")]]; + } + + [appearanceSpeedSlider setFloatValue:( -([df floatForKey:@"statusWindowAppearanceSpeed"]) )]; + [vanishSpeedSlider setFloatValue:( -([df floatForKey:@"statusWindowVanishSpeed"]) )]; [vanishDelaySlider setFloatValue:[df floatForKey:@"statusWindowVanishDelay"]]; - [showOnChangeCheckbox setState:([df boolForKey:@"showSongInfoOnChange"] ? NSOnState : NSOffState)]; + + // Setup General Controls + selectedBGStyle = [df integerForKey:@"statusWindowBackgroundMode"]; + [backgroundStylePopup selectItem:[backgroundStylePopup itemAtIndex:[backgroundStylePopup indexOfItemWithTag:selectedBGStyle]]]; + + if ( selectedBGStyle == ITTSWBackgroundColored ) { + [backgroundColorWell setEnabled:YES]; + [backgroundColorPopup setEnabled:YES]; + } else { + [backgroundColorWell setEnabled:NO]; + [backgroundColorPopup setEnabled:NO]; + } + + colorData = [df dataForKey:@"statusWindowBackgroundColor"]; + + if ( colorData ) { + [backgroundColorWell setColor:(NSColor *)[NSUnarchiver unarchiveObjectWithData:colorData]]; + } else { + [backgroundColorWell setColor:[NSColor blueColor]]; + } + + [showOnChangeCheckbox setState:([df boolForKey:@"showSongInfoOnChange"] ? NSOnState : NSOffState)]; + [windowSizingPopup selectItem:[windowSizingPopup itemAtIndex:[windowSizingPopup indexOfItemWithTag:[df integerForKey:@"statusWindowSizing"]]]]; + // Setup the sharing controls if ([df boolForKey:@"enableSharing"]) { [shareMenuTunesCheckbox setState:NSOnState]; [useSharedMenuTunesCheckbox setEnabled:NO]; [selectSharedPlayerButton setEnabled:NO]; - [hostTextField setEnabled:NO]; + [passwordTextField setEnabled:YES]; + [nameTextField setEnabled:YES]; } else if ([df boolForKey:@"useSharedPlayer"]) { [useSharedMenuTunesCheckbox setState:NSOnState]; [shareMenuTunesCheckbox setEnabled:NO]; - [sharePasswordCheckbox setEnabled:NO]; - [sharePasswordTextField setEnabled:NO]; + [selectSharedPlayerButton setEnabled:YES]; } [[NSNotificationCenter defaultCenter] addObserver:sharingTableView selector:@selector(reloadData) name:@"ITMTFoundNetService" object:nil]; + serverName = [df stringForKey:@"sharedPlayerName"]; + if (!serverName || [serverName length] == 0) { + serverName = @"MenuTunes Shared Player"; + } + [nameTextField setStringValue:serverName]; + [selectPlayerBox setContentView:zeroConfView]; - [sharePasswordCheckbox setState:([df boolForKey:@"enableSharingPassword"] ? NSOnState : NSOffState)]; - //[sharePasswordTextField setStringValue:@""]; //DO THIS LATER + if ([[df dataForKey:@"sharedPlayerPassword"] length] > 0) { + [passwordTextField setStringValue:@"p4s5w0rdMT1.2"]; + } else { + [passwordTextField setStringValue:@""]; + } if ([df stringForKey:@"sharedPlayerHost"]) { [hostTextField setStringValue:[df stringForKey:@"sharedPlayerHost"]]; } + + if ([[NetworkController sharedController] isConnectedToServer]) { + [selectedPlayerTextField setStringValue:[[[NetworkController sharedController] networkObject] serverName]]; + [locationTextField setStringValue:[[NetworkController sharedController] remoteHost]]; + } else { + [selectedPlayerTextField setStringValue:@"No shared player selected."]; + [locationTextField setStringValue:@"-"]; + } } -- (IBAction)changeMenus:(id)sender +- (void)setupScreenPopup { - ITDebugLog(@"Synchronizing menus"); - [df setObject:myItems forKey:@"menu"]; - [df synchronize]; + 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)setLaunchesAtLogin:(BOOL)flag +- (void)setStatusWindowEntryEffect:(Class)effectClass { - 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; - } + StatusWindow *sw = [StatusWindow sharedWindow]; + + float time = ([df floatForKey:@"statusWindowAppearanceSpeed"] ? [df floatForKey:@"statusWindowAppearanceSpeed"] : 0.8); + [df setObject:NSStringFromClass(effectClass) forKey:@"statusWindowAppearanceEffect"]; + + [sw setEntryEffect:[[[effectClass alloc] initWithWindow:sw] autorelease]]; + [[sw entryEffect] setEffectTime:time]; +} + +- (void)setStatusWindowExitEffect:(Class)effectClass +{ + StatusWindow *sw = [StatusWindow sharedWindow]; + + float time = ([df floatForKey:@"statusWindowVanishSpeed"] ? [df floatForKey:@"statusWindowVanishSpeed"] : 0.8); + [df setObject:NSStringFromClass(effectClass) forKey:@"statusWindowVanishEffect"]; + + [sw setExitEffect:[[[effectClass alloc] initWithWindow:sw] autorelease]]; + [[sw exitEffect] setEffectTime:time]; +} + +- (void)setCustomColor:(NSColor *)color updateWell:(BOOL)update +{ + [(ITTSWBackgroundView *)[[StatusWindow sharedWindow] contentView] setBackgroundColor:color]; + [df setObject:[NSArchiver archivedDataWithRootObject:color] forKey:@"statusWindowBackgroundColor"]; + + if ( update ) { + [backgroundColorWell setColor:color]; + } +} + +- (void)repopulateEffectPopupsForVerticalPosition:(ITVerticalWindowPosition)vPos horizontalPosition:(ITHorizontalWindowPosition)hPos +{ + NSEnumerator *effectEnum = [effectClasses objectEnumerator]; + id anItem; + + [appearanceEffectPopup removeAllItems]; + [vanishEffectPopup removeAllItems]; + + while ( (anItem = [effectEnum nextObject]) ) { + [appearanceEffectPopup addItemWithTitle:[anItem effectName]]; + [vanishEffectPopup addItemWithTitle:[anItem effectName]]; + + [[appearanceEffectPopup lastItem] setRepresentedObject:anItem]; + [[vanishEffectPopup lastItem] setRepresentedObject:anItem]; + + if ( [self effect:anItem supportsVerticalPosition:vPos withHorizontalPosition:hPos] ) { + [[appearanceEffectPopup lastItem] setEnabled:YES]; + [[vanishEffectPopup lastItem] setEnabled:YES]; + } else { + [[appearanceEffectPopup lastItem] setEnabled:NO]; + [[vanishEffectPopup lastItem] setEnabled:NO]; + } + } + +} + +- (BOOL)effect:(Class)effectClass supportsVerticalPosition:(ITVerticalWindowPosition)vPos withHorizontalPosition:(ITHorizontalWindowPosition)hPos +{ + BOOL valid = NO; + + if ( vPos == ITWindowPositionTop ) { + if ( hPos == ITWindowPositionLeft ) { + valid = ( [[[[effectClass supportedPositions] objectForKey:@"Top"] objectForKey:@"Left"] boolValue] ) ; + } else if ( hPos == ITWindowPositionCenter ) { + valid = ( [[[[effectClass supportedPositions] objectForKey:@"Top"] objectForKey:@"Center"] boolValue] ); + } else if ( hPos == ITWindowPositionRight ) { + valid = ( [[[[effectClass supportedPositions] objectForKey:@"Top"] objectForKey:@"Right"] boolValue] ); + } + } else if ( vPos == ITWindowPositionMiddle ) { + if ( hPos == ITWindowPositionLeft ) { + valid = ( [[[[effectClass supportedPositions] objectForKey:@"Middle"] objectForKey:@"Left"] boolValue] ); + } else if ( hPos == ITWindowPositionCenter ) { + valid = ( [[[[effectClass supportedPositions] objectForKey:@"Middle"] objectForKey:@"Center"] boolValue] ); + } else if ( hPos == ITWindowPositionRight ) { + valid = ( [[[[effectClass supportedPositions] objectForKey:@"Middle"] objectForKey:@"Right"] boolValue] ); + } + } else if ( vPos == ITWindowPositionBottom ) { + if ( hPos == ITWindowPositionLeft ) { + valid = ( [[[[effectClass supportedPositions] objectForKey:@"Bottom"] objectForKey:@"Left"] boolValue] ); + } else if ( hPos == ITWindowPositionCenter ) { + valid = ( [[[[effectClass supportedPositions] objectForKey:@"Bottom"] objectForKey:@"Center"] boolValue] ); + } else if ( hPos == ITWindowPositionRight ) { + valid = ( [[[[effectClass supportedPositions] objectForKey:@"Bottom"] objectForKey:@"Right"] boolValue] ); } } - [df setPersistentDomain:loginwindow forName:@"loginwindow"]; + + return valid; +} + +- (IBAction)changeMenus:(id)sender +{ + ITDebugLog(@"Synchronizing menus"); + [df setObject:myItems forKey:@"menu"]; [df synchronize]; - [loginwindow release]; - ITDebugLog(@"Finished setting launches at login."); + + [[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]; + } } @@ -678,6 +1073,21 @@ static PreferencesController *prefs = nil; [(MainController *)controller closePreferences]; } +/*************************************************************************/ +#pragma mark - +#pragma mark NSTextField DELEGATE METHODS +/*************************************************************************/ + +- (void)controlTextDidChange:(NSNotification*)note +{ + if ([note object] == hostTextField) { + if ([[hostTextField stringValue] length] == 0) { + [sharingPanelOKButton setEnabled:NO]; + } else { + [sharingPanelOKButton setEnabled:YES]; + } + } +} /*************************************************************************/ #pragma mark - @@ -703,7 +1113,7 @@ static PreferencesController *prefs = nil; NSString *object = [myItems objectAtIndex:rowIndex]; if ([[aTableColumn identifier] isEqualToString:@"name"]) { if ([object isEqualToString:@"showPlayer"]) { - NSString *string; + NSString *string = nil; NS_DURING string = [NSString stringWithFormat:@"%@ %@", NSLocalizedString(@"show", @"Show"), [[controller currentRemote] playerSimpleName]]; NS_HANDLER @@ -724,7 +1134,7 @@ static PreferencesController *prefs = nil; NSString *object = [availableItems objectAtIndex:rowIndex]; if ([[aTableColumn identifier] isEqualToString:@"name"]) { if ([object isEqualToString:@"showPlayer"]) { - NSString *string; + NSString *string = nil; NS_DURING string = [NSString stringWithFormat:@"%@ %@", NSLocalizedString(@"show", @"Show"), [[controller currentRemote] playerSimpleName]]; NS_HANDLER @@ -747,11 +1157,7 @@ static PreferencesController *prefs = nil; return [[hotKeysDictionary objectForKey:[hotKeysArray objectAtIndex:rowIndex]] description]; } } else { - if ([[aTableColumn identifier] isEqualToString:@"name"]) { - return [[[[NetworkController sharedController] remoteServices] objectAtIndex:rowIndex] objectForKey:@"name"]; - } else { - return @"X"; - } + return [[[[NetworkController sharedController] remoteServices] objectAtIndex:rowIndex] name]; } } @@ -848,8 +1254,10 @@ static PreferencesController *prefs = nil; - (void)dealloc { + [[NSNotificationCenter defaultCenter] removeObserver:self]; [hotKeysArray release]; [hotKeysDictionary release]; + [effectClasses release]; [menuTableView setDataSource:nil]; [allTableView setDataSource:nil]; [controller release];