#import "MenuController.h"
#import "PreferencesController.h"
#import "NetworkController.h"
+#import "NetworkObject.h"
#import <ITKit/ITHotKeyCenter.h>
#import <ITKit/ITHotKey.h>
#import <ITKit/ITKeyCombo.h>
#import "StatusWindowController.h"
#import "StatusItemHack.h"
+@implementation NSImage (SmoothAdditions)
+
+- (NSImage *)imageScaledSmoothlyToSize:(NSSize)scaledSize
+{
+ NSImage *newImage;
+ NSImageRep *rep = [self bestRepresentationForDevice:nil];
+
+ newImage = [[NSImage alloc] initWithSize:scaledSize];
+ [newImage lockFocus];
+ {
+ [[NSGraphicsContext currentContext] setImageInterpolation:NSImageInterpolationHigh];
+ [[NSGraphicsContext currentContext] setShouldAntialias:YES];
+ [rep drawInRect:NSMakeRect(3, 3, scaledSize.width - 6, scaledSize.height - 6)];
+ }
+ [newImage unlockFocus];
+ return [newImage autorelease];
+}
+
+@end
+
@interface MainController(Private)
- (ITMTRemote *)loadRemote;
-- (void)timerUpdate;
- (void)setLatestSongIdentifier:(NSString *)newIdentifier;
-- (void)showCurrentTrackInfo;
- (void)applicationLaunched:(NSNotification *)note;
- (void)applicationTerminated:(NSNotification *)note;
@end
sharedController = self;
remoteArray = [[NSMutableArray alloc] initWithCapacity:1];
+ [[PreferencesController sharedPrefs] setController:self];
statusWindowController = [StatusWindowController sharedController];
menuController = [[MenuController alloc] init];
df = [[NSUserDefaults standardUserDefaults] retain];
SetITDebugMode(YES);
}
+ if (([df integerForKey:@"appVersion"] < 1200) && ([df integerForKey:@"SongsInAdvance"] > 0)) {
+ [df removePersistentDomainForName:@"com.ithinksw.menutunes"];
+ [df synchronize];
+ [[PreferencesController sharedPrefs] registerDefaults];
+ [[StatusWindowController sharedController] showPreferencesUpdateWindow];
+ }
+
currentRemote = [self loadRemote];
[[self currentRemote] begin];
networkController = [[NetworkController alloc] init];
if ([df boolForKey:@"enableSharing"]) {
[self setServerStatus:YES];
- } else if ([df boolForKey:@"useSharedPlayer"] && [df boolForKey:@"alwaysUseSharedPlayer"]) {
- [self connectToServer];
+ } else if ([df boolForKey:@"useSharedPlayer"]) {
+ if ([self connectToServer] == 0) {
+ [NSTimer scheduledTimerWithTimeInterval:45 target:self selector:@selector(checkForRemoteServer:) userInfo:nil repeats:YES];
+ }
}
//Setup for notification of the remote player launching or quitting
[[PreferencesController sharedPrefs] registerDefaults];
}
- [StatusItemHack install];
- statusItem = [[ITStatusItem alloc]
- initWithStatusBar:[NSStatusBar systemStatusBar]
- withLength:NSSquareStatusItemLength];
+ if ([df boolForKey:@"ITMTNoStatusItem"]) {
+ statusItem = nil;
+ } else {
+ [StatusItemHack install];
+ statusItem = [[ITStatusItem alloc]
+ initWithStatusBar:[NSStatusBar systemStatusBar]
+ withLength:NSSquareStatusItemLength];
+ }
bling = [[MTBlingController alloc] init];
[self blingTime];
globalPrefs = [[df persistentDomainForName:@".GlobalPreferences"] mutableCopy];
if (date) {
[globalPrefs setObject:date forKey:@"ITMTTrialStart"];
+ [globalPrefs setObject:[NSNumber numberWithInt:MT_CURRENT_VERSION] forKey:@"ITMTTrialVers"];
} else {
[globalPrefs removeObjectForKey:@"ITMTTrialStart"];
+ [globalPrefs removeObjectForKey:@"ITMTTrialVers"];
}
[df setPersistentDomain:globalPrefs forName:@".GlobalPreferences"];
[df synchronize];
if (![self blingBling]) {
if ( (! [self getBlingTime] ) || ([now timeIntervalSinceDate:[self getBlingTime]] < 0) ) {
[self setBlingTime:now];
+ } else if ([[[df persistentDomainForName:@".GlobalPreferences"] objectForKey:@"ITMTTrialVers"] intValue] < MT_CURRENT_VERSION) {
+ if ([now timeIntervalSinceDate:[self getBlingTime]] >= 345600) {
+ [self setBlingTime:[now addTimeInterval:-259200]];
+ } else {
+ NSMutableDictionary *globalPrefs;
+ [df synchronize];
+ globalPrefs = [[df persistentDomainForName:@".GlobalPreferences"] mutableCopy];
+ [globalPrefs setObject:[NSNumber numberWithInt:MT_CURRENT_VERSION] forKey:@"ITMTTrialVers"];
+ [df setPersistentDomain:globalPrefs forName:@".GlobalPreferences"];
+ [df synchronize];
+ [globalPrefs release];
+ }
}
+
if ( ([now timeIntervalSinceDate:[self getBlingTime]] >= 604800) && (blinged != YES) ) {
blinged = YES;
[statusItem setEnabled:NO];
[self setupHotKeys];
if (![refreshTimer isValid]) {
[refreshTimer release];
- refreshTimer = [[NSTimer scheduledTimerWithTimeInterval:0.5
+ refreshTimer = [[NSTimer scheduledTimerWithTimeInterval:([networkController isConnectedToServer] ? 10.0 : 0.5)
target:self
selector:@selector(timerUpdate)
userInfo:nil
- (BOOL)songIsPlaying
{
- NSString *identifier;
+ NSString *identifier = nil;
NS_DURING
identifier = [[self currentRemote] playerStateUniqueIdentifier];
NS_HANDLER
- (BOOL)radioIsPlaying
{
- ITMTRemotePlayerPlaylistClass class;
+ ITMTRemotePlayerPlaylistClass class = nil;
NS_DURING
class = [[self currentRemote] currentPlaylistClass];
NS_HANDLER
- (BOOL)songChanged
{
- NSString *identifier;
+ NSString *identifier = nil;
NS_DURING
identifier = [[self currentRemote] playerStateUniqueIdentifier];
NS_HANDLER
- (void)setLatestSongIdentifier:(NSString *)newIdentifier
{
- ITDebugLog(@"Setting latest song identifier to %@", newIdentifier);
+ ITDebugLog(@"Setting latest song identifier:");
+ ITDebugLog(@" - Identifier: %@", newIdentifier);
[_latestSongIdentifier autorelease];
- _latestSongIdentifier = [newIdentifier copy];
+ _latestSongIdentifier = [newIdentifier retain];
}
- (void)timerUpdate
[statusItem setMenu:[menuController menu]];
}
- if ( [self songChanged] && (timerUpdating != YES) ) {
+ if ( [self songChanged] && (timerUpdating != YES) && (playerRunningState == ITMTRemotePlayerRunning) ) {
ITDebugLog(@"The song changed.");
+
+ if ([df boolForKey:@"runScripts"]) {
+ NSArray *scripts = [[NSFileManager defaultManager] directoryContentsAtPath:[NSHomeDirectory() stringByAppendingPathComponent:@"Library/Application Support/MenuTunes/Scripts"]];
+ NSEnumerator *scriptsEnum = [scripts objectEnumerator];
+ NSString *nextScript;
+ ITDebugLog(@"Running AppleScripts for song change.");
+ while ( (nextScript = [scriptsEnum nextObject]) ) {
+ NSDictionary *error;
+ NSAppleScript *currentScript = [[NSAppleScript alloc] initWithContentsOfURL:[NSURL fileURLWithPath:[[NSHomeDirectory() stringByAppendingPathComponent:@"Library/Application Support/MenuTunes/Scripts"] stringByAppendingPathComponent:nextScript]] error:&error];
+ ITDebugLog(@"Running script: %@", nextScript);
+ if (!currentScript || ![currentScript executeAndReturnError:nil]) {
+ ITDebugLog(@"Error running script %@.", nextScript);
+ }
+ [currentScript release];
+ }
+ }
+
timerUpdating = YES;
+ [_statusItem setEnabled:NO];
NS_DURING
latestPlaylistClass = [[self currentRemote] currentPlaylistClass];
}
[self setLatestSongIdentifier:[[self currentRemote] playerStateUniqueIdentifier]];
+
+ //Create the tooltip for the status item
+ if ( [df boolForKey:@"showToolTip"] ) {
+ NSString *artist = [[self currentRemote] currentSongArtist];
+ NSString *title = [[self currentRemote] currentSongTitle];
+ NSString *toolTip;
+ ITDebugLog(@"Creating status item tooltip.");
+ if (artist) {
+ toolTip = [NSString stringWithFormat:@"%@ - %@", artist, title];
+ } else if (title) {
+ toolTip = title;
+ } else {
+ toolTip = @"No Song Playing";
+ }
+ [statusItem setToolTip:toolTip];
+ } else {
+ [statusItem setToolTip:nil];
+ }
NS_HANDLER
[self networkError:localException];
NS_ENDHANDLER
timerUpdating = NO;
+ [_statusItem setEnabled:YES];
}
}
{
ITDebugLog(@"Selecting playlist %i", index);
NS_DURING
- [[self currentRemote] switchToPlaylistAtIndex:index];
+ [[self currentRemote] switchToPlaylistAtIndex:(index % 1000) ofSourceAtIndex:(index / 1000)];
+ //[[self currentRemote] switchToPlaylistAtIndex:index];
NS_HANDLER
[self networkError:localException];
NS_ENDHANDLER
} else {
ITDebugLog(@"Launching player.");
NS_DURING
- if (![[NSWorkspace sharedWorkspace] launchApplication:[[self currentRemote] playerFullName]]) {
+ NSString *path;
+ if ( (path = [df stringForKey:@"CustomPlayerPath"]) ) {
+ } else {
+ path = [[self currentRemote] playerFullName];
+ }
+ if (![[NSWorkspace sharedWorkspace] launchApplication:path]) {
ITDebugLog(@"Error Launching Player");
}
NS_HANDLER
- (void)showPreferences
{
ITDebugLog(@"Show preferences.");
- [[PreferencesController sharedPrefs] setController:self];
[[PreferencesController sharedPrefs] showPrefsWindow:self];
}
+- (void)showPreferencesAndClose
+{
+ ITDebugLog(@"Show preferences.");
+ [[PreferencesController sharedPrefs] showPrefsWindow:self];
+ [[StatusWindow sharedWindow] setLocked:NO];
+ [[StatusWindow sharedWindow] vanish:self];
+ [[StatusWindow sharedWindow] setIgnoresMouseEvents:YES];
+}
+
+- (void)showTestWindow
+{
+ [self showCurrentTrackInfo];
+}
+
- (void)quitMenuTunes
{
ITDebugLog(@"Quitting MenuTunes.");
//
//
+- (MenuController *)menuController
+{
+ return menuController;
+}
+
- (void)closePreferences
{
ITDebugLog(@"Preferences closed.");
- (ITMTRemote *)currentRemote
{
+ if ([networkController isConnectedToServer] && ![[networkController networkObject] isValid]) {
+ [self networkError:nil];
+ return nil;
+ }
return currentRemote;
}
ITHotKey *hotKey;
ITDebugLog(@"Setting up hot keys.");
- if (playerRunningState == ITMTRemotePlayerNotRunning) {
+ if (playerRunningState == ITMTRemotePlayerNotRunning && ![[NetworkController sharedController] isConnectedToServer]) {
return;
}
- (void)showCurrentTrackInfo
{
- ITMTRemotePlayerSource source;
- NSString *title;
+ ITMTRemotePlayerSource source = 0;
+ NSString *title = nil;
NSString *album = nil;
NSString *artist = nil;
+ NSString *composer = nil;
NSString *time = nil;
NSString *track = nil;
+ NSImage *art = nil;
int rating = -1;
+ ITDebugLog(@"Showing track info status window.");
+
NS_DURING
source = [[self currentRemote] currentSource];
title = [[self currentRemote] currentSongTitle];
[self networkError:localException];
NS_ENDHANDLER
- ITDebugLog(@"Showing track info status window.");
-
if ( title ) {
if ( [df boolForKey:@"showAlbum"] ) {
NS_ENDHANDLER
}
+ if ( [df boolForKey:@"showComposer"] ) {
+ NS_DURING
+ composer = [[self currentRemote] currentSongComposer];
+ NS_HANDLER
+ [self networkError:localException];
+ NS_ENDHANDLER
+ }
+
if ( [df boolForKey:@"showTime"] ) {
NS_DURING
time = [NSString stringWithFormat:@"%@: %@ / %@",
}
if ( [df boolForKey:@"showTrackNumber"] ) {
- int trackNo;
- int trackCount;
+ int trackNo = 0;
+ int trackCount = 0;
NS_DURING
trackNo = [[self currentRemote] currentSongTrack];
}
if ( [df boolForKey:@"showTrackRating"] ) {
- float currentRating;
+ float currentRating = 0;
NS_DURING
currentRating = [[self currentRemote] currentSongRating];
}
}
+ if ( [df boolForKey:@"showAlbumArtwork"] ) {
+ NSSize oldSize, newSize;
+ NS_DURING
+ art = [[self currentRemote] currentSongAlbumArt];
+ oldSize = [art size];
+ if (oldSize.width > oldSize.height) newSize = NSMakeSize(110,oldSize.height * (110.0f / oldSize.width));
+ else newSize = NSMakeSize(oldSize.width * (110.0f / oldSize.height),110);
+ art = [[[[NSImage alloc] initWithData:[art TIFFRepresentation]] autorelease] imageScaledSmoothlyToSize:newSize];
+ NS_HANDLER
+ [self networkError:localException];
+ NS_ENDHANDLER
+ }
+
} else {
title = NSLocalizedString(@"noSongPlaying", @"No song is playing.");
}
-
+ ITDebugLog(@"Showing current track info status window.");
[statusWindowController showSongInfoWindowWithSource:source
title:title
album:album
artist:artist
+ composer:composer
time:time
track:track
- rating:rating];
+ rating:rating
+ image:art];
}
- (void)showUpcomingSongs
{
- int numSongs;
-
+ int numSongs = 0;
NS_DURING
numSongs = [[self currentRemote] numberOfSongsInPlaylistAtIndex:[[self currentRemote] currentPlaylistIndex]];
NS_HANDLER
ITDebugLog(@"Showing upcoming songs status window.");
NS_DURING
if (numSongs > 0) {
- NSMutableArray *songList = [NSMutableArray arrayWithCapacity:5];
int numSongsInAdvance = [df integerForKey:@"SongsInAdvance"];
+ NSMutableArray *songList = [NSMutableArray arrayWithCapacity:numSongsInAdvance];
int curTrack = [[self currentRemote] currentSongIndex];
int i;
}
}
+ if ([songList count] == 0) {
+ [songList addObject:NSLocalizedString(@"noUpcomingSongs", @"No upcoming songs.")];
+ }
+
[statusWindowController showUpcomingSongsWindowWithTitles:songList];
} else {
[statusWindowController showUpcomingSongsWindowWithTitles:[NSArray arrayWithObject:NSLocalizedString(@"noUpcomingSongs", @"No upcoming songs.")]];
}
}
-- (BOOL)connectToServer
+- (int)connectToServer
{
+ int result;
+ ITDebugLog(@"Attempting to connect to shared remote.");
+ result = [networkController connectToHost:[df stringForKey:@"sharedPlayerHost"]];
//Connect
- if ([networkController connectToHost:[df stringForKey:@"sharedPlayerHost"]]) {
- currentRemote = [networkController sharedRemote];
+ if (result == 1) {
+ [[PreferencesController sharedPrefs] resetRemotePlayerTextFields];
+ currentRemote = [[[networkController networkObject] remote] retain];
[refreshTimer invalidate];
- return YES;
+ refreshTimer = [[NSTimer scheduledTimerWithTimeInterval:([networkController isConnectedToServer] ? 10.0 : 0.5)
+ target:self
+ selector:@selector(timerUpdate)
+ userInfo:nil
+ repeats:YES] retain];
+ [self timerUpdate];
+ ITDebugLog(@"Connection successful.");
+ return 1;
+ } else if (result == 0) {
+ ITDebugLog(@"Connection failed.");
+ currentRemote = [remoteArray objectAtIndex:0];
+ return 0;
} else {
+ //Do something about the password being invalid
+ ITDebugLog(@"Connection failed.");
currentRemote = [remoteArray objectAtIndex:0];
- return NO;
+ return -1;
}
}
- (BOOL)disconnectFromServer
{
+ ITDebugLog(@"Disconnecting from shared remote.");
//Disconnect
+ [currentRemote release];
currentRemote = [remoteArray objectAtIndex:0];
[networkController disconnect];
[self timerUpdate];
return YES;
}
+- (void)checkForRemoteServer:(NSTimer *)timer
+{
+ ITDebugLog(@"Checking for remote server.");
+ if ([networkController checkForServerAtHost:[df stringForKey:@"sharedPlayerHost"]]) {
+ ITDebugLog(@"Remote server found.");
+ [timer invalidate];
+ if (![networkController isServerOn] && ![networkController isConnectedToServer]) {
+ [[StatusWindowController sharedController] showReconnectQueryWindow];
+ }
+ } else {
+ ITDebugLog(@"Remote server not found.");
+ }
+}
+
- (void)networkError:(NSException *)exception
{
ITDebugLog(@"Remote exception thrown: %@: %@", [exception name], [exception reason]);
- NSRunAlertPanel(@"Remote MenuTunes Disconnected", @"The MenuTunes server you were connected to stopped responding or quit. MenuTunes will revert back to the local player.", @"OK", nil, nil);
- if ([networkController isConnectedToServer] && [self disconnectFromServer]) {
- } else {
- ITDebugLog(@"CRITICAL ERROR DISCONNECTING!");
+ if ( ((exception == nil) || [[exception name] isEqualToString:NSPortTimeoutException]) && [networkController isConnectedToServer]) {
+ NSRunCriticalAlertPanel(@"Remote MenuTunes Disconnected", @"The MenuTunes server you were connected to stopped responding or quit. MenuTunes will revert back to the local player.", @"OK", nil, nil);
+ if ([self disconnectFromServer]) {
+ [[PreferencesController sharedPrefs] resetRemotePlayerTextFields];
+ [NSTimer scheduledTimerWithTimeInterval:45 target:self selector:@selector(checkForRemoteServer:) userInfo:nil repeats:YES];
+ } else {
+ ITDebugLog(@"CRITICAL ERROR, DISCONNECTING!");
+ }
+ }
+}
+
+- (void)reconnect
+{
+ if ([self connectToServer] == 0) {
+ [NSTimer scheduledTimerWithTimeInterval:45 target:self selector:@selector(checkForRemoteServer:) userInfo:nil repeats:YES];
}
+ [[StatusWindow sharedWindow] setLocked:NO];
+ [[StatusWindow sharedWindow] vanish:self];
+ [[StatusWindow sharedWindow] setIgnoresMouseEvents:YES];
+}
+
+- (void)cancelReconnect
+{
+ [[StatusWindow sharedWindow] setLocked:NO];
+ [[StatusWindow sharedWindow] vanish:self];
+ [[StatusWindow sharedWindow] setIgnoresMouseEvents:YES];
}
/*************************************************************************/
NS_DURING
if (!note || [[[note userInfo] objectForKey:@"NSApplicationName"] isEqualToString:[[self currentRemote] playerFullName]]) {
ITDebugLog(@"Remote application terminated.");
+ playerRunningState = ITMTRemotePlayerNotRunning;
[[self currentRemote] halt];
[refreshTimer invalidate];
[refreshTimer release];
refreshTimer = nil;
[self clearHotKeys];
- playerRunningState = ITMTRemotePlayerNotRunning;
+
+ if ([df objectForKey:@"ShowPlayer"] != nil) {
+ ITHotKey *hotKey;
+ ITDebugLog(@"Setting up show player hot key.");
+ hotKey = [[ITHotKey alloc] init];
+ [hotKey setName:@"ShowPlayer"];
+ [hotKey setKeyCombo:[ITKeyCombo keyComboWithPlistRepresentation:[df objectForKey:@"ShowPlayer"]]];
+ [hotKey setTarget:self];
+ [hotKey setAction:@selector(showPlayer)];
+ [[ITHotKeyCenter sharedCenter] registerHotKey:[hotKey autorelease]];
+ }
}
NS_HANDLER
[self networkError:localException];
- (void)applicationWillTerminate:(NSNotification *)note
{
- [self clearHotKeys];
[networkController stopRemoteServerSearch];
+ [self clearHotKeys];
[[NSStatusBar systemStatusBar] removeStatusItem:statusItem];
}
[super dealloc];
}
-
-@end
\ No newline at end of file
+@end