Moved iTunesRemote to the AETools from AECenter.
[MenuTunes.git] / MainController.m
1 #import "MainController.h"
2 #import "MenuController.h"
3 #import "PreferencesController.h"
4 #import "NetworkController.h"
5 #import "NetworkObject.h"
6 #import <ITKit/ITHotKeyCenter.h>
7 #import <ITKit/ITHotKey.h>
8 #import <ITKit/ITKeyCombo.h>
9 #import <ITKit/ITCategory-NSMenu.h>
10 #import "StatusWindow.h"
11 #import "StatusWindowController.h"
12 #import "StatusItemHack.h"
13
14 @interface NSCarbonMenuImpl:NSObject
15 {
16     NSMenu *_menu;
17 }
18
19 + (void)initialize;
20 + (void)setupForNoMenuBar;
21 - (void)dealloc;
22 - (void)setMenu:fp8;
23 - menu;
24 - (void)itemChanged:fp8;
25 - (void)itemAdded:fp8;
26 - (void)itemRemoved:fp8;
27 - (void)performActionWithHighlightingForItemAtIndex:(int)fp8;
28 - (void)performMenuAction:(SEL)fp8 withTarget:fp12;
29 - (void)setupCarbonMenuBar;
30 - (void)setAsMainCarbonMenuBar;
31 - (void)clearAsMainCarbonMenuBar;
32 - (void)popUpMenu:fp8 atLocation:(NSPoint)fp12 width:(float)fp20 forView:fp24 withSelectedItem:(int)fp28 withFont:fp32;
33 - (void)_popUpContextMenu:fp8 withEvent:fp12 forView:fp16 withFont:fp20;
34 - (void)_popUpContextMenu:fp8 withEvent:fp12 forView:fp16;
35 - window;
36 @end
37
38 @implementation NSImage (SmoothAdditions)
39
40 - (NSImage *)imageScaledSmoothlyToSize:(NSSize)scaledSize
41 {
42     NSImage *newImage;
43     NSImageRep *rep = [self bestRepresentationForDevice:nil];
44     
45     newImage = [[NSImage alloc] initWithSize:scaledSize];
46     [newImage lockFocus];
47     {
48         [[NSGraphicsContext currentContext] setImageInterpolation:NSImageInterpolationHigh];
49         [[NSGraphicsContext currentContext] setShouldAntialias:YES];
50         [rep drawInRect:NSMakeRect(3, 3, scaledSize.width - 6, scaledSize.height - 6)];
51     }
52     [newImage unlockFocus];
53     return [newImage autorelease];
54 }
55
56 @end
57
58 @interface MainController(Private)
59 - (ITMTRemote *)loadRemote;
60 - (void)setLatestSongIdentifier:(NSString *)newIdentifier;
61 - (void)applicationLaunched:(NSNotification *)note;
62 - (void)applicationTerminated:(NSNotification *)note;
63 @end
64
65 static MainController *sharedController;
66
67 @implementation MainController
68
69 + (MainController *)sharedController
70 {
71     return sharedController;
72 }
73
74 /*************************************************************************/
75 #pragma mark -
76 #pragma mark INITIALIZATION/DEALLOCATION METHODS
77 /*************************************************************************/
78
79 - (id)init
80 {
81     if ( ( self = [super init] ) ) {
82         sharedController = self;
83         
84         remoteArray = [[NSMutableArray alloc] initWithCapacity:1];
85         [[PreferencesController sharedPrefs] setController:self];
86         statusWindowController = [StatusWindowController sharedController];
87         menuController = [[MenuController alloc] init];
88         df = [[NSUserDefaults standardUserDefaults] retain];
89         timerUpdating = NO;
90         blinged = NO;
91     }
92     return self;
93 }
94
95 - (void)applicationDidFinishLaunching:(NSNotification *)note
96 {
97     //Turn on debug mode if needed
98     if ([df boolForKey:@"ITDebugMode"]) {
99         SetITDebugMode(YES);
100     }
101     
102     if (([df integerForKey:@"appVersion"] < 1200) && ([df integerForKey:@"SongsInAdvance"] > 0)) {
103         [df removePersistentDomainForName:@"com.ithinksw.menutunes"];
104         [df synchronize];
105         [[PreferencesController sharedPrefs] registerDefaults];
106         [[StatusWindowController sharedController] showPreferencesUpdateWindow];
107     }
108     
109     currentRemote = [self loadRemote];
110     [[self currentRemote] begin];
111     
112     //Turn on network stuff if needed
113     networkController = [[NetworkController alloc] init];
114     if ([df boolForKey:@"enableSharing"]) {
115         [self setServerStatus:YES];
116     } else if ([df boolForKey:@"useSharedPlayer"]) {
117         [self checkForRemoteServerAndConnectImmediately:YES];
118     }
119     
120     //Setup for notification of the remote player launching or quitting
121     [[[NSWorkspace sharedWorkspace] notificationCenter]
122             addObserver:self
123             selector:@selector(applicationTerminated:)
124             name:NSWorkspaceDidTerminateApplicationNotification
125             object:nil];
126     
127     [[[NSWorkspace sharedWorkspace] notificationCenter]
128             addObserver:self
129             selector:@selector(applicationLaunched:)
130             name:NSWorkspaceDidLaunchApplicationNotification
131             object:nil];
132     
133     if (![df objectForKey:@"menu"]) {  // If this is nil, defaults have never been registered.
134         [[PreferencesController sharedPrefs] registerDefaults];
135     }
136     
137     if ([df boolForKey:@"ITMTNoStatusItem"]) {
138         statusItem = nil;
139     } else {
140         [StatusItemHack install];
141         statusItem = [[ITStatusItem alloc]
142                 initWithStatusBar:[NSStatusBar systemStatusBar]
143                 withLength:NSSquareStatusItemLength];
144     }
145     
146     bling = [[MTBlingController alloc] init];
147     [self blingTime];
148     registerTimer = [[NSTimer scheduledTimerWithTimeInterval:10.0
149                              target:self
150                              selector:@selector(blingTime)
151                              userInfo:nil
152                              repeats:YES] retain];
153     
154     NS_DURING
155         if ([[self currentRemote] playerRunningState] == ITMTRemotePlayerRunning) {
156             [self applicationLaunched:nil];
157         } else {
158             if ([df boolForKey:@"LaunchPlayerWithMT"])
159                 [self showPlayer];
160             else
161                 [self applicationTerminated:nil];
162         }
163     NS_HANDLER
164         [self networkError:localException];
165     NS_ENDHANDLER
166     
167     [statusItem setImage:[NSImage imageNamed:@"MenuNormal"]];
168     [statusItem setAlternateImage:[NSImage imageNamed:@"MenuInverted"]];
169
170     [networkController startRemoteServerSearch];
171     [NSApp deactivate];
172 }
173
174 - (ITMTRemote *)loadRemote
175 {
176     NSString *folderPath = [[NSBundle mainBundle] builtInPlugInsPath];
177     ITDebugLog(@"Gathering remotes.");
178     if (folderPath) {
179         NSArray      *bundlePathList = [NSBundle pathsForResourcesOfType:@"remote" inDirectory:folderPath];
180         NSEnumerator *enumerator     = [bundlePathList objectEnumerator];
181         NSString     *bundlePath;
182
183         while ( (bundlePath = [enumerator nextObject]) ) {
184             NSBundle* remoteBundle = [NSBundle bundleWithPath:bundlePath];
185
186             if (remoteBundle) {
187                 Class remoteClass = [remoteBundle principalClass];
188
189                 if ([remoteClass conformsToProtocol:@protocol(ITMTRemote)] &&
190                     [(NSObject *)remoteClass isKindOfClass:[NSObject class]]) {
191                     id remote = [remoteClass remote];
192                     ITDebugLog(@"Adding remote at path %@", bundlePath);
193                     [remoteArray addObject:remote];
194                 }
195             }
196         }
197
198 //      if ( [remoteArray count] > 0 ) {  // UNCOMMENT WHEN WE HAVE > 1 PLUGIN
199 //          if ( [remoteArray count] > 1 ) {
200 //              [remoteArray sortUsingSelector:@selector(sortAlpha:)];
201 //          }
202 //          [self loadModuleAccessUI]; //Comment out this line to disable remote visibility
203 //      }
204     }
205 //  NSLog(@"%@", [remoteArray objectAtIndex:0]);  //DEBUG
206     return [remoteArray objectAtIndex:0];
207 }
208
209 /*************************************************************************/
210 #pragma mark -
211 #pragma mark INSTANCE METHODS
212 /*************************************************************************/
213
214 /*- (void)startTimerInNewThread
215 {
216     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
217     NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
218     refreshTimer = [[NSTimer scheduledTimerWithTimeInterval:0.5
219                              target:self
220                              selector:@selector(timerUpdate)
221                              userInfo:nil
222                              repeats:YES] retain];
223     [runLoop run];
224     ITDebugLog(@"Timer started.");
225     [pool release];
226 }*/
227
228 - (void)setBlingTime:(NSDate*)date
229 {
230     NSMutableDictionary *globalPrefs;
231     [df synchronize];
232     globalPrefs = [[df persistentDomainForName:@".GlobalPreferences"] mutableCopy];
233     if (date) {
234         [globalPrefs setObject:date forKey:@"ITMTTrialStart"];
235         [globalPrefs setObject:[NSNumber numberWithInt:MT_CURRENT_VERSION] forKey:@"ITMTTrialVers"];
236     } else {
237         [globalPrefs removeObjectForKey:@"ITMTTrialStart"];
238         [globalPrefs removeObjectForKey:@"ITMTTrialVers"];
239     }
240     [df setPersistentDomain:globalPrefs forName:@".GlobalPreferences"];
241     [df synchronize];
242     [globalPrefs release];
243 }
244
245 - (NSDate*)getBlingTime
246 {
247     [df synchronize];
248     return [[df persistentDomainForName:@".GlobalPreferences"] objectForKey:@"ITMTTrialStart"];
249 }
250
251 - (void)blingTime
252 {
253     NSDate *now = [NSDate date];
254     if (![self blingBling]) {
255         if ( (! [self getBlingTime] ) || ([now timeIntervalSinceDate:[self getBlingTime]] < 0) ) {
256             [self setBlingTime:now];
257         } else if ([[[df persistentDomainForName:@".GlobalPreferences"] objectForKey:@"ITMTTrialVers"] intValue] < MT_CURRENT_VERSION) {
258             if ([now timeIntervalSinceDate:[self getBlingTime]] >= 345600) {
259                 [self setBlingTime:[now addTimeInterval:-259200]];
260             } else {
261                 NSMutableDictionary *globalPrefs;
262                 [df synchronize];
263                 globalPrefs = [[df persistentDomainForName:@".GlobalPreferences"] mutableCopy];
264                 [globalPrefs setObject:[NSNumber numberWithInt:MT_CURRENT_VERSION] forKey:@"ITMTTrialVers"];
265                 [df setPersistentDomain:globalPrefs forName:@".GlobalPreferences"];
266                 [df synchronize];
267                 [globalPrefs release];
268             }
269         }
270         
271         if ( ([now timeIntervalSinceDate:[self getBlingTime]] >= 604800) && (blinged != YES) ) {
272             blinged = YES;
273             [statusItem setEnabled:NO];
274             [self clearHotKeys];
275             if ([refreshTimer isValid]) {
276                 [refreshTimer invalidate];
277             }
278             [statusWindowController showRegistrationQueryWindow];
279         }
280     } else {
281         if (blinged) {
282             [statusItem setEnabled:YES];
283             [self setupHotKeys];
284             if (![refreshTimer isValid]) {
285                 [refreshTimer release];
286                 refreshTimer = [[NSTimer scheduledTimerWithTimeInterval:([networkController isConnectedToServer] ? 10.0 : 0.5)
287                              target:self
288                              selector:@selector(timerUpdate)
289                              userInfo:nil
290                              repeats:YES] retain];
291             }
292             blinged = NO;
293         }
294         [self setBlingTime:nil];
295     }
296 }
297
298 - (void)blingNow
299 {
300     [bling showPanel];
301 }
302
303 - (BOOL)blingBling
304 {
305     if ( ! ([bling checkDone] == 2475) ) {
306         return NO;
307     } else {
308         return YES;
309     }
310 }
311
312 - (BOOL)songIsPlaying
313 {
314     NSString *identifier = nil;
315     NS_DURING
316         identifier = [[self currentRemote] playerStateUniqueIdentifier];
317     NS_HANDLER
318         [self networkError:localException];
319     NS_ENDHANDLER
320     return ( ! ([identifier isEqualToString:@"0-0"]) );
321 }
322
323 - (BOOL)radioIsPlaying
324 {
325     ITMTRemotePlayerPlaylistClass class = nil;
326     NS_DURING
327         class = [[self currentRemote] currentPlaylistClass];
328     NS_HANDLER
329         [self networkError:localException];
330     NS_ENDHANDLER
331     return (class  == ITMTRemotePlayerRadioPlaylist );
332 }
333
334 - (BOOL)songChanged
335 {
336     NSString *identifier = nil;
337     NS_DURING
338         identifier = [[self currentRemote] playerStateUniqueIdentifier];
339     NS_HANDLER
340         [self networkError:localException];
341     NS_ENDHANDLER
342     return ( ! [identifier isEqualToString:_latestSongIdentifier] );
343 }
344
345 - (NSString *)latestSongIdentifier
346 {
347     return _latestSongIdentifier;
348 }
349
350 - (void)setLatestSongIdentifier:(NSString *)newIdentifier
351 {
352     ITDebugLog(@"Setting latest song identifier:");
353     ITDebugLog(@"   - Identifier: %@", newIdentifier);
354     [_latestSongIdentifier autorelease];
355     _latestSongIdentifier = [newIdentifier retain];
356 }
357
358 - (void)timerUpdate
359 {
360     if ( [self songChanged] && (timerUpdating != YES) && (playerRunningState == ITMTRemotePlayerRunning) ) {
361         ITDebugLog(@"The song changed.");
362         if ([df boolForKey:@"runScripts"]) {
363             NSArray *scripts = [[NSFileManager defaultManager] directoryContentsAtPath:[NSHomeDirectory() stringByAppendingPathComponent:@"Library/Application Support/MenuTunes/Scripts"]];
364             NSEnumerator *scriptsEnum = [scripts objectEnumerator];
365             NSString *nextScript;
366             ITDebugLog(@"Running AppleScripts for song change.");
367             while ( (nextScript = [scriptsEnum nextObject]) ) {
368                 NSDictionary *error;
369                 NSAppleScript *currentScript = [[NSAppleScript alloc] initWithContentsOfURL:[NSURL fileURLWithPath:[[NSHomeDirectory() stringByAppendingPathComponent:@"Library/Application Support/MenuTunes/Scripts"] stringByAppendingPathComponent:nextScript]] error:&error];
370                 ITDebugLog(@"Running script: %@", nextScript);
371                 if (!currentScript || ![currentScript executeAndReturnError:nil]) {
372                     ITDebugLog(@"Error running script %@.", nextScript);
373                 }
374                 [currentScript release];
375             }
376         }
377         
378         timerUpdating = YES;
379         [statusItem setEnabled:NO];
380         
381         NS_DURING
382             latestPlaylistClass = [[self currentRemote] currentPlaylistClass];
383             [menuController rebuildSubmenus];
384     
385             if ( [df boolForKey:@"showSongInfoOnChange"] ) {
386                 [self performSelector:@selector(showCurrentTrackInfo) withObject:nil afterDelay:0.0];
387             }
388             
389             [self setLatestSongIdentifier:[[self currentRemote] playerStateUniqueIdentifier]];
390             
391             //Create the tooltip for the status item
392             if ( [df boolForKey:@"showToolTip"] ) {
393                 NSString *artist = [[self currentRemote] currentSongArtist];
394                 NSString *title = [[self currentRemote] currentSongTitle];
395                 NSString *toolTip;
396                 ITDebugLog(@"Creating status item tooltip.");
397                 if (artist) {
398                     toolTip = [NSString stringWithFormat:@"%@ - %@", artist, title];
399                 } else if (title) {
400                     toolTip = title;
401                 } else {
402                     toolTip = @"No Song Playing";
403                 }
404                 [statusItem setToolTip:toolTip];
405             } else {
406                 [statusItem setToolTip:nil];
407             }
408         NS_HANDLER
409             [self networkError:localException];
410         NS_ENDHANDLER
411         
412         timerUpdating = NO;
413         [statusItem setEnabled:YES];
414     }
415     
416     if ([networkController isConnectedToServer]) {
417         [statusItem setMenu:([[self currentRemote] playerRunningState] == ITMTRemotePlayerRunning) ? [menuController menu] : [menuController menuForNoPlayer]];
418     }
419 }
420
421 - (void)menuClicked
422 {
423     ITDebugLog(@"Menu clicked.");
424     if ([networkController isConnectedToServer]) {
425         //Used the cached version
426         return;
427     }
428     
429     NS_DURING
430         if ([[self currentRemote] playerRunningState] == ITMTRemotePlayerRunning) {
431             [statusItem setMenu:[menuController menu]];
432         } else {
433             [statusItem setMenu:[menuController menuForNoPlayer]];
434         }
435     NS_HANDLER
436         [self networkError:localException];
437     NS_ENDHANDLER
438 }
439
440 //
441 //
442 // Menu Selectors
443 //
444 //
445
446 - (void)playPause
447 {
448     NS_DURING
449         ITMTRemotePlayerPlayingState state = [[self currentRemote] playerPlayingState];
450         ITDebugLog(@"Play/Pause toggled");
451         if (state == ITMTRemotePlayerPlaying) {
452             [[self currentRemote] pause];
453         } else if ((state == ITMTRemotePlayerForwarding) || (state == ITMTRemotePlayerRewinding)) {
454             [[self currentRemote] pause];
455             [[self currentRemote] play];
456         } else {
457             [[self currentRemote] play];
458         }
459     NS_HANDLER
460         [self networkError:localException];
461     NS_ENDHANDLER
462     
463     [self timerUpdate];
464 }
465
466 - (void)nextSong
467 {
468     ITDebugLog(@"Going to next song.");
469     NS_DURING
470         [[self currentRemote] goToNextSong];
471     NS_HANDLER
472         [self networkError:localException];
473     NS_ENDHANDLER
474     [self timerUpdate];
475 }
476
477 - (void)prevSong
478 {
479     ITDebugLog(@"Going to previous song.");
480     NS_DURING
481         [[self currentRemote] goToPreviousSong];
482     NS_HANDLER
483         [self networkError:localException];
484     NS_ENDHANDLER
485     [self timerUpdate];
486 }
487
488 - (void)fastForward
489 {
490     ITDebugLog(@"Fast forwarding.");
491     NS_DURING
492         [[self currentRemote] forward];
493     NS_HANDLER
494         [self networkError:localException];
495     NS_ENDHANDLER
496     [self timerUpdate];
497 }
498
499 - (void)rewind
500 {
501     ITDebugLog(@"Rewinding.");
502     NS_DURING
503         [[self currentRemote] rewind];
504     NS_HANDLER
505         [self networkError:localException];
506     NS_ENDHANDLER
507     [self timerUpdate];
508 }
509
510 - (void)selectPlaylistAtIndex:(int)index
511 {
512     ITDebugLog(@"Selecting playlist %i", index);
513     NS_DURING
514         [[self currentRemote] switchToPlaylistAtIndex:(index % 1000) ofSourceAtIndex:(index / 1000)];
515         //[[self currentRemote] switchToPlaylistAtIndex:index];
516     NS_HANDLER
517         [self networkError:localException];
518     NS_ENDHANDLER
519     [self timerUpdate];
520 }
521
522 - (void)selectSongAtIndex:(int)index
523 {
524     ITDebugLog(@"Selecting song %i", index);
525     NS_DURING
526         [[self currentRemote] switchToSongAtIndex:index];
527     NS_HANDLER
528         [self networkError:localException];
529     NS_ENDHANDLER
530     [self timerUpdate];
531 }
532
533 - (void)selectSongRating:(int)rating
534 {
535     ITDebugLog(@"Selecting song rating %i", rating);
536     NS_DURING
537         [[self currentRemote] setCurrentSongRating:(float)rating / 100.0];
538     NS_HANDLER
539         [self networkError:localException];
540     NS_ENDHANDLER
541     [self timerUpdate];
542 }
543
544 - (void)selectEQPresetAtIndex:(int)index
545 {
546     ITDebugLog(@"Selecting EQ preset %i", index);
547     NS_DURING
548         if (index == -1) {
549             [[self currentRemote] setEqualizerEnabled:![[self currentRemote] equalizerEnabled]];
550         } else {
551             [[self currentRemote] switchToEQAtIndex:index];
552         }
553     NS_HANDLER
554         [self networkError:localException];
555     NS_ENDHANDLER
556     [self timerUpdate];
557 }
558
559 - (void)makePlaylistWithTerm:(NSString *)term ofType:(int)type
560 {
561     ITDebugLog(@"Making playlist with term %@, type %i", term, type);
562     NS_DURING
563         [[self currentRemote] makePlaylistWithTerm:term ofType:type];
564     NS_HANDLER
565         [self networkError:localException];
566     NS_ENDHANDLER
567     ITDebugLog(@"Done making playlist");
568 }
569
570 - (void)showPlayer
571 {
572     ITDebugLog(@"Beginning show player.");
573     //if ( ( playerRunningState == ITMTRemotePlayerRunning) ) {
574         ITDebugLog(@"Showing player interface.");
575         NS_DURING
576             [[self currentRemote] showPrimaryInterface];
577         NS_HANDLER
578             [self networkError:localException];
579         NS_ENDHANDLER
580     /*} else {
581         ITDebugLog(@"Launching player.");
582         NS_DURING
583             NSString *path;
584             if ( (path = [df stringForKey:@"CustomPlayerPath"]) ) {
585             } else {
586                 pathITDebugLog(@"Showing player interface."); = [[self currentRemote] playerFullName];
587             }
588             if (![[NSWorkspace sharedWorkspace] launchApplication:path]) {
589                 ITDebugLog(@"Error Launching Player");
590             }
591         NS_HANDLER
592             [self networkError:localException];
593         NS_ENDHANDLER
594     }*/
595     ITDebugLog(@"Finished show player.");
596 }
597
598 - (void)showPreferences
599 {
600     ITDebugLog(@"Show preferences.");
601     [[PreferencesController sharedPrefs] showPrefsWindow:self];
602 }
603
604 - (void)showPreferencesAndClose
605 {
606     ITDebugLog(@"Show preferences.");
607     [[PreferencesController sharedPrefs] showPrefsWindow:self];
608     [[StatusWindow sharedWindow] setLocked:NO];
609     [[StatusWindow sharedWindow] vanish:self];
610     [[StatusWindow sharedWindow] setIgnoresMouseEvents:YES];
611 }
612
613 - (void)showTestWindow
614 {
615     [self showCurrentTrackInfo];
616 }
617
618 - (void)quitMenuTunes
619 {
620     ITDebugLog(@"Quitting MenuTunes.");
621     [NSApp terminate:self];
622 }
623
624 //
625 //
626
627 - (MenuController *)menuController
628 {
629     return menuController;
630 }
631
632 - (void)closePreferences
633 {
634     ITDebugLog(@"Preferences closed.");
635     if ( ( playerRunningState == ITMTRemotePlayerRunning) ) {
636         [self setupHotKeys];
637     }
638 }
639
640 - (ITMTRemote *)currentRemote
641 {
642     if ([networkController isConnectedToServer] && ![[networkController networkObject] isValid]) {
643         [self networkError:nil];
644         return nil;
645     }
646     return currentRemote;
647 }
648
649 //
650 //
651 // Hot key setup
652 //
653 //
654
655 - (void)clearHotKeys
656 {
657     NSEnumerator *hotKeyEnumerator = [[[ITHotKeyCenter sharedCenter] allHotKeys] objectEnumerator];
658     ITHotKey *nextHotKey;
659     ITDebugLog(@"Clearing hot keys.");
660     while ( (nextHotKey = [hotKeyEnumerator nextObject]) ) {
661         [[ITHotKeyCenter sharedCenter] unregisterHotKey:nextHotKey];
662     }
663     ITDebugLog(@"Done clearing hot keys.");
664 }
665
666 - (void)setupHotKeys
667 {
668     ITHotKey *hotKey;
669     ITDebugLog(@"Setting up hot keys.");
670     
671     if (playerRunningState == ITMTRemotePlayerNotRunning && ![[NetworkController sharedController] isConnectedToServer]) {
672         return;
673     }
674     
675     if ([df objectForKey:@"PlayPause"] != nil) {
676         ITDebugLog(@"Setting up play pause hot key.");
677         hotKey = [[ITHotKey alloc] init];
678         [hotKey setName:@"PlayPause"];
679         [hotKey setKeyCombo:[ITKeyCombo keyComboWithPlistRepresentation:[df objectForKey:@"PlayPause"]]];
680         [hotKey setTarget:self];
681         [hotKey setAction:@selector(playPause)];
682         [[ITHotKeyCenter sharedCenter] registerHotKey:[hotKey autorelease]];
683     }
684     
685     if ([df objectForKey:@"NextTrack"] != nil) {
686         ITDebugLog(@"Setting up next track hot key.");
687         hotKey = [[ITHotKey alloc] init];
688         [hotKey setName:@"NextTrack"];
689         [hotKey setKeyCombo:[ITKeyCombo keyComboWithPlistRepresentation:[df objectForKey:@"NextTrack"]]];
690         [hotKey setTarget:self];
691         [hotKey setAction:@selector(nextSong)];
692         [[ITHotKeyCenter sharedCenter] registerHotKey:[hotKey autorelease]];
693     }
694     
695     if ([df objectForKey:@"PrevTrack"] != nil) {
696         ITDebugLog(@"Setting up previous track hot key.");
697         hotKey = [[ITHotKey alloc] init];
698         [hotKey setName:@"PrevTrack"];
699         [hotKey setKeyCombo:[ITKeyCombo keyComboWithPlistRepresentation:[df objectForKey:@"PrevTrack"]]];
700         [hotKey setTarget:self];
701         [hotKey setAction:@selector(prevSong)];
702         [[ITHotKeyCenter sharedCenter] registerHotKey:[hotKey autorelease]];
703     }
704     
705     if ([df objectForKey:@"FastForward"] != nil) {
706         ITDebugLog(@"Setting up fast forward hot key.");
707         hotKey = [[ITHotKey alloc] init];
708         [hotKey setName:@"FastForward"];
709         [hotKey setKeyCombo:[ITKeyCombo keyComboWithPlistRepresentation:[df objectForKey:@"FastForward"]]];
710         [hotKey setTarget:self];
711         [hotKey setAction:@selector(fastForward)];
712         [[ITHotKeyCenter sharedCenter] registerHotKey:[hotKey autorelease]];
713     }
714     
715     if ([df objectForKey:@"Rewind"] != nil) {
716         ITDebugLog(@"Setting up rewind hot key.");
717         hotKey = [[ITHotKey alloc] init];
718         [hotKey setName:@"Rewind"];
719         [hotKey setKeyCombo:[ITKeyCombo keyComboWithPlistRepresentation:[df objectForKey:@"Rewind"]]];
720         [hotKey setTarget:self];
721         [hotKey setAction:@selector(rewind)];
722         [[ITHotKeyCenter sharedCenter] registerHotKey:[hotKey autorelease]];
723     }
724     
725     if ([df objectForKey:@"ShowPlayer"] != nil) {
726         ITDebugLog(@"Setting up show player hot key.");
727         hotKey = [[ITHotKey alloc] init];
728         [hotKey setName:@"ShowPlayer"];
729         [hotKey setKeyCombo:[ITKeyCombo keyComboWithPlistRepresentation:[df objectForKey:@"ShowPlayer"]]];
730         [hotKey setTarget:self];
731         [hotKey setAction:@selector(showPlayer)];
732         [[ITHotKeyCenter sharedCenter] registerHotKey:[hotKey autorelease]];
733     }
734     
735     if ([df objectForKey:@"TrackInfo"] != nil) {
736         ITDebugLog(@"Setting up track info hot key.");
737         hotKey = [[ITHotKey alloc] init];
738         [hotKey setName:@"TrackInfo"];
739         [hotKey setKeyCombo:[ITKeyCombo keyComboWithPlistRepresentation:[df objectForKey:@"TrackInfo"]]];
740         [hotKey setTarget:self];
741         [hotKey setAction:@selector(showCurrentTrackInfo)];
742         [[ITHotKeyCenter sharedCenter] registerHotKey:[hotKey autorelease]];
743     }
744     
745     if ([df objectForKey:@"UpcomingSongs"] != nil) {
746         ITDebugLog(@"Setting up upcoming songs hot key.");
747         hotKey = [[ITHotKey alloc] init];
748         [hotKey setName:@"UpcomingSongs"];
749         [hotKey setKeyCombo:[ITKeyCombo keyComboWithPlistRepresentation:[df objectForKey:@"UpcomingSongs"]]];
750         [hotKey setTarget:self];
751         [hotKey setAction:@selector(showUpcomingSongs)];
752         [[ITHotKeyCenter sharedCenter] registerHotKey:[hotKey autorelease]];
753     }
754     
755     if ([df objectForKey:@"ToggleLoop"] != nil) {
756         ITDebugLog(@"Setting up toggle loop hot key.");
757         hotKey = [[ITHotKey alloc] init];
758         [hotKey setName:@"ToggleLoop"];
759         [hotKey setKeyCombo:[ITKeyCombo keyComboWithPlistRepresentation:[df objectForKey:@"ToggleLoop"]]];
760         [hotKey setTarget:self];
761         [hotKey setAction:@selector(toggleLoop)];
762         [[ITHotKeyCenter sharedCenter] registerHotKey:[hotKey autorelease]];
763     }
764     
765     if ([df objectForKey:@"ToggleShuffle"] != nil) {
766         ITDebugLog(@"Setting up toggle shuffle hot key.");
767         hotKey = [[ITHotKey alloc] init];
768         [hotKey setName:@"ToggleShuffle"];
769         [hotKey setKeyCombo:[ITKeyCombo keyComboWithPlistRepresentation:[df objectForKey:@"ToggleShuffle"]]];
770         [hotKey setTarget:self];
771         [hotKey setAction:@selector(toggleShuffle)];
772         [[ITHotKeyCenter sharedCenter] registerHotKey:[hotKey autorelease]];
773     }
774     
775     if ([df objectForKey:@"IncrementVolume"] != nil) {
776         ITDebugLog(@"Setting up increment volume hot key.");
777         hotKey = [[ITHotKey alloc] init];
778         [hotKey setName:@"IncrementVolume"];
779         [hotKey setKeyCombo:[ITKeyCombo keyComboWithPlistRepresentation:[df objectForKey:@"IncrementVolume"]]];
780         [hotKey setTarget:self];
781         [hotKey setAction:@selector(incrementVolume)];
782         [[ITHotKeyCenter sharedCenter] registerHotKey:[hotKey autorelease]];
783     }
784     
785     if ([df objectForKey:@"DecrementVolume"] != nil) {
786         ITDebugLog(@"Setting up decrement volume hot key.");
787         hotKey = [[ITHotKey alloc] init];
788         [hotKey setName:@"DecrementVolume"];
789         [hotKey setKeyCombo:[ITKeyCombo keyComboWithPlistRepresentation:[df objectForKey:@"DecrementVolume"]]];
790         [hotKey setTarget:self];
791         [hotKey setAction:@selector(decrementVolume)];
792         [[ITHotKeyCenter sharedCenter] registerHotKey:[hotKey autorelease]];
793     }
794     
795     if ([df objectForKey:@"IncrementRating"] != nil) {
796         ITDebugLog(@"Setting up increment rating hot key.");
797         hotKey = [[ITHotKey alloc] init];
798         [hotKey setName:@"IncrementRating"];
799         [hotKey setKeyCombo:[ITKeyCombo keyComboWithPlistRepresentation:[df objectForKey:@"IncrementRating"]]];
800         [hotKey setTarget:self];
801         [hotKey setAction:@selector(incrementRating)];
802         [[ITHotKeyCenter sharedCenter] registerHotKey:[hotKey autorelease]];
803     }
804     
805     if ([df objectForKey:@"DecrementRating"] != nil) {
806         ITDebugLog(@"Setting up decrement rating hot key.");
807         hotKey = [[ITHotKey alloc] init];
808         [hotKey setName:@"DecrementRating"];
809         [hotKey setKeyCombo:[ITKeyCombo keyComboWithPlistRepresentation:[df objectForKey:@"DecrementRating"]]];
810         [hotKey setTarget:self];
811         [hotKey setAction:@selector(decrementRating)];
812         [[ITHotKeyCenter sharedCenter] registerHotKey:[hotKey autorelease]];
813     }
814     
815     if ([df objectForKey:@"PopupMenu"] != nil) {
816         ITDebugLog(@"Setting up popup menu hot key.");
817         hotKey = [[ITHotKey alloc] init];
818         [hotKey setName:@"PopupMenu"];
819         [hotKey setKeyCombo:[ITKeyCombo keyComboWithPlistRepresentation:[df objectForKey:@"PopupMenu"]]];
820         [hotKey setTarget:self];
821         [hotKey setAction:@selector(popupMenu)];
822         [[ITHotKeyCenter sharedCenter] registerHotKey:[hotKey autorelease]];
823     }
824     
825     int i;
826     for (i = 0; i <= 5; i++) {
827         NSString *curName = [NSString stringWithFormat:@"SetRating%i", i];
828         if ([df objectForKey:curName] != nil) {
829             ITDebugLog(@"Setting up set rating %i hot key.", i);
830             hotKey = [[ITHotKey alloc] init];
831             [hotKey setName:curName];
832             [hotKey setKeyCombo:[ITKeyCombo keyComboWithPlistRepresentation:[df objectForKey:curName]]];
833             [hotKey setTarget:self];
834             [hotKey setAction:@selector(setRating:)];
835             [[ITHotKeyCenter sharedCenter] registerHotKey:[hotKey autorelease]];
836         }
837     }
838     ITDebugLog(@"Finished setting up hot keys.");
839 }
840
841 - (void)showCurrentTrackInfo
842 {
843     ITMTRemotePlayerSource  source      = 0;
844     NSString               *title       = nil;
845     NSString               *album       = nil;
846     NSString               *artist      = nil;
847     NSString               *composer    = nil;
848     NSString               *time        = nil;
849     NSString               *track       = nil;
850     NSImage                *art         = nil;
851     int                     rating      = -1;
852     int                     playCount   = -1;
853     
854     ITDebugLog(@"Showing track info status window.");
855     
856     NS_DURING
857         source      = [[self currentRemote] currentSource];
858         title       = [[self currentRemote] currentSongTitle];
859     NS_HANDLER
860         [self networkError:localException];
861     NS_ENDHANDLER
862     
863     if ( title ) {
864         if ( [df boolForKey:@"showAlbumArtwork"] ) {
865             NSSize oldSize, newSize;
866              NS_DURING
867                  art = [[self currentRemote] currentSongAlbumArt];
868                  oldSize = [art size];
869                  if (oldSize.width > oldSize.height) newSize = NSMakeSize(110,oldSize.height * (110.0f / oldSize.width));
870                  else newSize = NSMakeSize(oldSize.width * (110.0f / oldSize.height),110);
871                 art = [[[[NSImage alloc] initWithData:[art TIFFRepresentation]] autorelease] imageScaledSmoothlyToSize:newSize];
872             NS_HANDLER
873                 [self networkError:localException];
874             NS_ENDHANDLER
875         }
876         
877         if ( [df boolForKey:@"showAlbum"] ) {
878             NS_DURING
879                 album = [[self currentRemote] currentSongAlbum];
880             NS_HANDLER
881                 [self networkError:localException];
882             NS_ENDHANDLER
883         }
884
885         if ( [df boolForKey:@"showArtist"] ) {
886             NS_DURING
887                 artist = [[self currentRemote] currentSongArtist];
888             NS_HANDLER
889                 [self networkError:localException];
890             NS_ENDHANDLER
891         }
892
893         if ( [df boolForKey:@"showComposer"] ) {
894             NS_DURING
895                 composer = [[self currentRemote] currentSongComposer];
896             NS_HANDLER
897                 [self networkError:localException];
898             NS_ENDHANDLER
899         }
900
901         if ( [df boolForKey:@"showTime"] ) {
902             NS_DURING
903                 time = [NSString stringWithFormat:@"%@: %@ / %@",
904                 NSLocalizedString(@"time", @"Time"),
905                 [[self currentRemote] currentSongElapsed],
906                 [[self currentRemote] currentSongLength]];
907             NS_HANDLER
908                 [self networkError:localException];
909             NS_ENDHANDLER
910         }
911
912         if ( [df boolForKey:@"showTrackNumber"] ) {
913             int trackNo    = 0;
914             int trackCount = 0;
915             
916             NS_DURING
917                 trackNo    = [[self currentRemote] currentSongTrack];
918                 trackCount = [[self currentRemote] currentAlbumTrackCount];
919             NS_HANDLER
920                 [self networkError:localException];
921             NS_ENDHANDLER
922             
923             if ( (trackNo > 0) || (trackCount > 0) ) {
924                 track = [NSString stringWithFormat:@"%@: %i %@ %i",
925                     @"Track", trackNo, @"of", trackCount];
926             }
927         }
928
929         if ( [df boolForKey:@"showTrackRating"] ) {
930             float currentRating = 0;
931             
932             NS_DURING
933                 currentRating = [[self currentRemote] currentSongRating];
934             NS_HANDLER
935                 [self networkError:localException];
936             NS_ENDHANDLER
937             
938             if (currentRating >= 0.0) {
939                 rating = ( currentRating * 5 );
940             }
941         }
942         
943         if ( [df boolForKey:@"showPlayCount"] && ![self radioIsPlaying] && [[self currentRemote] currentSource] == ITMTRemoteLibrarySource ) {
944             NS_DURING
945                 playCount = [[self currentRemote] currentSongPlayCount];
946             NS_HANDLER
947                 [self networkError:localException];
948             NS_ENDHANDLER
949         }
950     } else {
951         title = NSLocalizedString(@"noSongPlaying", @"No song is playing.");
952     }
953     ITDebugLog(@"Showing current track info status window.");
954     [statusWindowController showSongInfoWindowWithSource:source
955                                                    title:title
956                                                    album:album
957                                                   artist:artist
958                                                 composer:composer
959                                                     time:time
960                                                    track:track
961                                                   rating:rating
962                                                playCount:playCount
963                                                    image:art];
964 }
965
966 - (void)showUpcomingSongs
967 {
968     int numSongs = 0;
969     NS_DURING
970         numSongs = [[self currentRemote] numberOfSongsInPlaylistAtIndex:[[self currentRemote] currentPlaylistIndex]];
971     NS_HANDLER
972         [self networkError:localException];
973     NS_ENDHANDLER
974     
975     ITDebugLog(@"Showing upcoming songs status window.");
976     NS_DURING
977         if (numSongs > 0) {
978             int numSongsInAdvance = [df integerForKey:@"SongsInAdvance"];
979             NSMutableArray *songList = [NSMutableArray arrayWithCapacity:numSongsInAdvance];
980             int curTrack = [[self currentRemote] currentSongIndex];
981             int i;
982     
983             for (i = curTrack + 1; i <= curTrack + numSongsInAdvance; i++) {
984                 if (i <= numSongs) {
985                     [songList addObject:[[self currentRemote] songTitleAtIndex:i]];
986                 }
987             }
988             
989             if ([songList count] == 0) {
990                 [songList addObject:NSLocalizedString(@"noUpcomingSongs", @"No upcoming songs.")];
991             }
992             
993             [statusWindowController showUpcomingSongsWindowWithTitles:songList];
994         } else {
995             [statusWindowController showUpcomingSongsWindowWithTitles:[NSArray arrayWithObject:NSLocalizedString(@"noUpcomingSongs", @"No upcoming songs.")]];
996         }
997     NS_HANDLER
998         [self networkError:localException];
999     NS_ENDHANDLER
1000 }
1001
1002 - (void)popupMenu
1003 {
1004     if (!_popped) {
1005         _popped = YES;
1006         [self menuClicked];
1007         NSMenu *menu = [statusItem menu];
1008         [(NSCarbonMenuImpl *)[menu _menuImpl] popUpMenu:menu atLocation:[NSEvent mouseLocation] width:1 forView:nil withSelectedItem:-30 withFont:[NSFont menuFontOfSize:32]];
1009         _popped = NO;
1010     }
1011 }
1012
1013 - (void)incrementVolume
1014 {
1015     NS_DURING
1016         float volume  = [[self currentRemote] volume];
1017         float dispVol = volume;
1018         ITDebugLog(@"Incrementing volume.");
1019         volume  += 0.110;
1020         dispVol += 0.100;
1021         
1022         if (volume > 1.0) {
1023             volume  = 1.0;
1024             dispVol = 1.0;
1025         }
1026     
1027         ITDebugLog(@"Setting volume to %f", volume);
1028         [[self currentRemote] setVolume:volume];
1029     
1030         // Show volume status window
1031         [statusWindowController showVolumeWindowWithLevel:dispVol];
1032     NS_HANDLER
1033         [self networkError:localException];
1034     NS_ENDHANDLER
1035 }
1036
1037 - (void)decrementVolume
1038 {
1039     NS_DURING
1040         float volume  = [[self currentRemote] volume];
1041         float dispVol = volume;
1042         ITDebugLog(@"Decrementing volume.");
1043         volume  -= 0.090;
1044         dispVol -= 0.100;
1045     
1046         if (volume < 0.0) {
1047             volume  = 0.0;
1048             dispVol = 0.0;
1049         }
1050         
1051         ITDebugLog(@"Setting volume to %f", volume);
1052         [[self currentRemote] setVolume:volume];
1053         
1054         //Show volume status window
1055         [statusWindowController showVolumeWindowWithLevel:dispVol];
1056     NS_HANDLER
1057         [self networkError:localException];
1058     NS_ENDHANDLER
1059 }
1060
1061 - (void)incrementRating
1062 {
1063     NS_DURING
1064         float rating = [[self currentRemote] currentSongRating];
1065         ITDebugLog(@"Incrementing rating.");
1066         
1067         if ([[self currentRemote] currentPlaylistIndex] == 0) {
1068             ITDebugLog(@"No song playing, rating change aborted.");
1069             return;
1070         }
1071         
1072         rating += 0.2;
1073         if (rating > 1.0) {
1074             rating = 1.0;
1075         }
1076         ITDebugLog(@"Setting rating to %f", rating);
1077         [[self currentRemote] setCurrentSongRating:rating];
1078         
1079         //Show rating status window
1080         [statusWindowController showRatingWindowWithRating:rating];
1081     NS_HANDLER
1082         [self networkError:localException];
1083     NS_ENDHANDLER
1084 }
1085
1086 - (void)decrementRating
1087 {
1088     NS_DURING
1089         float rating = [[self currentRemote] currentSongRating];
1090         ITDebugLog(@"Decrementing rating.");
1091         
1092         if ([[self currentRemote] currentPlaylistIndex] == 0) {
1093             ITDebugLog(@"No song playing, rating change aborted.");
1094             return;
1095         }
1096         
1097         rating -= 0.2;
1098         if (rating < 0.0) {
1099             rating = 0.0;
1100         }
1101         ITDebugLog(@"Setting rating to %f", rating);
1102         [[self currentRemote] setCurrentSongRating:rating];
1103         
1104         //Show rating status window
1105         [statusWindowController showRatingWindowWithRating:rating];
1106     NS_HANDLER
1107         [self networkError:localException];
1108     NS_ENDHANDLER
1109 }
1110
1111 - (void)setRating:(ITHotKey *)sender
1112 {
1113     int stars = [[sender name] characterAtIndex:9] - 48;
1114     [self selectSongRating:stars * 20];
1115     [statusWindowController showRatingWindowWithRating:(float)stars / 5.0];
1116 }
1117
1118 - (void)toggleLoop
1119 {
1120     NS_DURING
1121         ITMTRemotePlayerRepeatMode repeatMode = [[self currentRemote] repeatMode];
1122         ITDebugLog(@"Toggling repeat mode.");
1123         switch (repeatMode) {
1124             case ITMTRemotePlayerRepeatOff:
1125                 repeatMode = ITMTRemotePlayerRepeatAll;
1126             break;
1127             case ITMTRemotePlayerRepeatAll:
1128                 repeatMode = ITMTRemotePlayerRepeatOne;
1129             break;
1130             case ITMTRemotePlayerRepeatOne:
1131                 repeatMode = ITMTRemotePlayerRepeatOff;
1132             break;
1133         }
1134         ITDebugLog(@"Setting repeat mode to %i", repeatMode);
1135         [[self currentRemote] setRepeatMode:repeatMode];
1136         
1137         //Show loop status window
1138         [statusWindowController showRepeatWindowWithMode:repeatMode];
1139     NS_HANDLER
1140         [self networkError:localException];
1141     NS_ENDHANDLER
1142 }
1143
1144 - (void)toggleShuffle
1145 {
1146     NS_DURING
1147         BOOL newShuffleEnabled = ( ! [[self currentRemote] shuffleEnabled] );
1148         ITDebugLog(@"Toggling shuffle mode.");
1149         [[self currentRemote] setShuffleEnabled:newShuffleEnabled];
1150         //Show shuffle status window
1151         ITDebugLog(@"Setting shuffle mode to %i", newShuffleEnabled);
1152         [statusWindowController showShuffleWindow:newShuffleEnabled];
1153     NS_HANDLER
1154         [self networkError:localException];
1155     NS_ENDHANDLER
1156 }
1157
1158 - (void)registerNowOK
1159 {
1160     [[StatusWindow sharedWindow] setLocked:NO];
1161     [[StatusWindow sharedWindow] vanish:self];
1162     [[StatusWindow sharedWindow] setIgnoresMouseEvents:YES];
1163
1164     [self blingNow];
1165 }
1166
1167 - (void)registerNowCancel
1168 {
1169     [[StatusWindow sharedWindow] setLocked:NO];
1170     [[StatusWindow sharedWindow] vanish:self];
1171     [[StatusWindow sharedWindow] setIgnoresMouseEvents:YES];
1172
1173     [NSApp terminate:self];
1174 }
1175
1176 /*************************************************************************/
1177 #pragma mark -
1178 #pragma mark NETWORK HANDLERS
1179 /*************************************************************************/
1180
1181 - (void)setServerStatus:(BOOL)newStatus
1182 {
1183     if (newStatus) {
1184         //Turn on
1185         [networkController setServerStatus:YES];
1186     } else {
1187         //Tear down
1188         [networkController setServerStatus:NO];
1189     }
1190 }
1191
1192 - (int)connectToServer
1193 {
1194     int result;
1195     ITDebugLog(@"Attempting to connect to shared remote.");
1196     result = [networkController connectToHost:[df stringForKey:@"sharedPlayerHost"]];
1197     //Connect
1198     if (result == 1) {
1199         [[PreferencesController sharedPrefs] resetRemotePlayerTextFields];
1200         currentRemote = [[[networkController networkObject] remote] retain];
1201         
1202         [self setupHotKeys];
1203         //playerRunningState = ITMTRemotePlayerRunning;
1204         playerRunningState = [[self currentRemote] playerRunningState];
1205         
1206         [refreshTimer invalidate];
1207         refreshTimer = [[NSTimer scheduledTimerWithTimeInterval:([networkController isConnectedToServer] ? 10.0 : 0.5)
1208                                 target:self
1209                                 selector:@selector(timerUpdate)
1210                                 userInfo:nil
1211                                 repeats:YES] retain];
1212         [self timerUpdate];
1213         ITDebugLog(@"Connection successful.");
1214         return 1;
1215     } else if (result == 0) {
1216         ITDebugLog(@"Connection failed.");
1217         currentRemote = [remoteArray objectAtIndex:0];
1218         return 0;
1219     } else {
1220         //Do something about the password being invalid
1221         ITDebugLog(@"Connection failed.");
1222         currentRemote = [remoteArray objectAtIndex:0];
1223         return -1;
1224     }
1225 }
1226
1227 - (BOOL)disconnectFromServer
1228 {
1229     ITDebugLog(@"Disconnecting from shared remote.");
1230     //Disconnect
1231     [currentRemote release];
1232     currentRemote = [remoteArray objectAtIndex:0];
1233     [networkController disconnect];
1234     
1235     if ([[self currentRemote] playerRunningState] == ITMTRemotePlayerRunning) {
1236         [self applicationLaunched:nil];
1237     } else {
1238         [self applicationTerminated:nil];
1239     }
1240     [self timerUpdate];
1241     return YES;
1242 }
1243
1244 - (void)checkForRemoteServer
1245 {
1246     [self checkForRemoteServerAndConnectImmediately:NO];
1247 }
1248
1249 - (void)checkForRemoteServerAndConnectImmediately:(BOOL)connectImmediately
1250 {
1251     ITDebugLog(@"Checking for remote server.");
1252     if (!_checkingForServer) {
1253         if (!_serverCheckLock) {
1254             _serverCheckLock = [[NSLock alloc] init];
1255         }
1256         [_serverCheckLock lock];
1257         _checkingForServer = YES;
1258         [_serverCheckLock unlock];
1259         [NSThread detachNewThreadSelector:@selector(runRemoteServerCheck:) toTarget:self withObject:[NSNumber numberWithBool:connectImmediately]];
1260     }
1261 }
1262
1263 - (void)runRemoteServerCheck:(id)sender
1264 {
1265     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1266     ITDebugLog(@"Remote server check running.");
1267     if ([networkController checkForServerAtHost:[df stringForKey:@"sharedPlayerHost"]]) {
1268         ITDebugLog(@"Remote server found.");
1269         if ([sender boolValue]) {
1270             [self performSelectorOnMainThread:@selector(connectToServer) withObject:nil waitUntilDone:NO];
1271         } else {
1272             [self performSelectorOnMainThread:@selector(remoteServerFound:) withObject:nil waitUntilDone:NO];
1273         }
1274     } else {
1275         ITDebugLog(@"Remote server not found.");
1276         [self performSelectorOnMainThread:@selector(remoteServerNotFound:) withObject:nil waitUntilDone:NO];
1277     }
1278     [_serverCheckLock lock];
1279     _checkingForServer = NO;
1280     [_serverCheckLock unlock];
1281     [pool release];
1282 }
1283
1284 - (void)remoteServerFound:(id)sender
1285 {
1286     if (![networkController isServerOn] && ![networkController isConnectedToServer]) {
1287         [[StatusWindowController sharedController] showReconnectQueryWindow];
1288     }
1289 }
1290
1291 - (void)remoteServerNotFound:(id)sender
1292 {
1293     if (![[NetworkController sharedController] isConnectedToServer]) {
1294         [NSTimer scheduledTimerWithTimeInterval:90.0 target:self selector:@selector(checkForRemoteServer) userInfo:nil repeats:NO];
1295     }
1296 }
1297
1298 - (void)networkError:(NSException *)exception
1299 {
1300     ITDebugLog(@"Remote exception thrown: %@: %@", [exception name], [exception reason]);
1301     if ( ((exception == nil) || [[exception name] isEqualToString:NSPortTimeoutException]) && [networkController isConnectedToServer]) {
1302         //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);
1303         [[StatusWindowController sharedController] showNetworkErrorQueryWindow];
1304         if ([self disconnectFromServer]) {
1305             [[PreferencesController sharedPrefs] resetRemotePlayerTextFields];
1306             [NSTimer scheduledTimerWithTimeInterval:90.0 target:self selector:@selector(checkForRemoteServer) userInfo:nil repeats:NO];
1307         } else {
1308             ITDebugLog(@"CRITICAL ERROR, DISCONNECTING!");
1309         }
1310     }
1311 }
1312
1313 - (void)reconnect
1314 {
1315     /*if ([self connectToServer] == 0) {
1316         [NSTimer scheduledTimerWithTimeInterval:90.0 target:self selector:@selector(checkForRemoteServer) userInfo:nil repeats:NO];
1317     }*/
1318     [self checkForRemoteServerAndConnectImmediately:YES];
1319     [[StatusWindow sharedWindow] setLocked:NO];
1320     [[StatusWindow sharedWindow] vanish:self];
1321     [[StatusWindow sharedWindow] setIgnoresMouseEvents:YES];
1322 }
1323
1324 - (void)cancelReconnect
1325 {
1326     [[StatusWindow sharedWindow] setLocked:NO];
1327     [[StatusWindow sharedWindow] vanish:self];
1328     [[StatusWindow sharedWindow] setIgnoresMouseEvents:YES];
1329 }
1330
1331 /*************************************************************************/
1332 #pragma mark -
1333 #pragma mark WORKSPACE NOTIFICATION HANDLERS
1334 /*************************************************************************/
1335
1336 - (void)applicationLaunched:(NSNotification *)note
1337 {
1338     NS_DURING
1339         if (!note || ([[[note userInfo] objectForKey:@"NSApplicationName"] isEqualToString:[[self currentRemote] playerFullName]] && ![[NetworkController sharedController] isConnectedToServer])) {
1340             ITDebugLog(@"Remote application launched.");
1341             playerRunningState = ITMTRemotePlayerRunning;
1342             [[self currentRemote] begin];
1343             [self setLatestSongIdentifier:@""];
1344             [self timerUpdate];
1345             refreshTimer = [[NSTimer scheduledTimerWithTimeInterval:([networkController isConnectedToServer] ? 10.0 : 0.5)
1346                                 target:self
1347                                 selector:@selector(timerUpdate)
1348                                 userInfo:nil
1349                                 repeats:YES] retain];
1350             //[NSThread detachNewThreadSelector:@selector(startTimerInNewThread) toTarget:self withObject:nil];
1351             [self setupHotKeys];
1352         }
1353     NS_HANDLER
1354         [self networkError:localException];
1355     NS_ENDHANDLER
1356 }
1357
1358  - (void)applicationTerminated:(NSNotification *)note
1359  {
1360     NS_DURING
1361         if (!note || [[[note userInfo] objectForKey:@"NSApplicationName"] isEqualToString:[[self currentRemote] playerFullName]] && ![[NetworkController sharedController] isConnectedToServer]) {
1362             ITDebugLog(@"Remote application terminated.");
1363             playerRunningState = ITMTRemotePlayerNotRunning;
1364             [[self currentRemote] halt];
1365             [refreshTimer invalidate];
1366             [refreshTimer release];
1367             refreshTimer = nil;
1368             [self clearHotKeys];
1369             
1370             if ([df objectForKey:@"ShowPlayer"] != nil) {
1371                 ITHotKey *hotKey;
1372                 ITDebugLog(@"Setting up show player hot key.");
1373                 hotKey = [[ITHotKey alloc] init];
1374                 [hotKey setName:@"ShowPlayer"];
1375                 [hotKey setKeyCombo:[ITKeyCombo keyComboWithPlistRepresentation:[df objectForKey:@"ShowPlayer"]]];
1376                 [hotKey setTarget:self];
1377                 [hotKey setAction:@selector(showPlayer)];
1378                 [[ITHotKeyCenter sharedCenter] registerHotKey:[hotKey autorelease]];
1379             }
1380         }
1381     NS_HANDLER
1382         [self networkError:localException];
1383     NS_ENDHANDLER
1384  }
1385
1386
1387 /*************************************************************************/
1388 #pragma mark -
1389 #pragma mark NSApplication DELEGATE METHODS
1390 /*************************************************************************/
1391
1392 - (void)applicationWillTerminate:(NSNotification *)note
1393 {
1394     [networkController stopRemoteServerSearch];
1395     [self clearHotKeys];
1396     [[NSStatusBar systemStatusBar] removeStatusItem:statusItem];
1397 }
1398
1399
1400 /*************************************************************************/
1401 #pragma mark -
1402 #pragma mark DEALLOCATION METHOD
1403 /*************************************************************************/
1404
1405 - (void)dealloc
1406 {
1407     [self applicationTerminated:nil];
1408     [bling release];
1409     [statusItem release];
1410     [statusWindowController release];
1411     [menuController release];
1412     [networkController release];
1413     [_serverCheckLock release];
1414     [super dealloc];
1415 }
1416
1417 @end