8784dd192d045b445f57b84629903aceba61c35d
[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 currentRemote] playerStateUniqueIdentifier] == nil) {
361                 [statusItem setEnabled:NO];
362                 [statusItem setToolTip:@"iTunes not responding."];
363                 return;
364         } else if (![statusItem isEnabled]) {
365                 [statusItem setEnabled:YES];
366                 [statusItem setToolTip:_toolTip];
367                 return;
368         }
369         
370         if ( [self songChanged] && (timerUpdating != YES) && (playerRunningState == ITMTRemotePlayerRunning) ) {
371         ITDebugLog(@"The song changed.");
372         if ([df boolForKey:@"runScripts"]) {
373             NSArray *scripts = [[NSFileManager defaultManager] directoryContentsAtPath:[NSHomeDirectory() stringByAppendingPathComponent:@"Library/Application Support/MenuTunes/Scripts"]];
374             NSEnumerator *scriptsEnum = [scripts objectEnumerator];
375             NSString *nextScript;
376             ITDebugLog(@"Running AppleScripts for song change.");
377             while ( (nextScript = [scriptsEnum nextObject]) ) {
378                 NSDictionary *error;
379                 NSAppleScript *currentScript = [[NSAppleScript alloc] initWithContentsOfURL:[NSURL fileURLWithPath:[[NSHomeDirectory() stringByAppendingPathComponent:@"Library/Application Support/MenuTunes/Scripts"] stringByAppendingPathComponent:nextScript]] error:&error];
380                 ITDebugLog(@"Running script: %@", nextScript);
381                 if (!currentScript || ![currentScript executeAndReturnError:nil]) {
382                     ITDebugLog(@"Error running script %@.", nextScript);
383                 }
384                 [currentScript release];
385             }
386         }
387         
388         timerUpdating = YES;
389         [statusItem setEnabled:NO];
390         
391         NS_DURING
392             latestPlaylistClass = [[self currentRemote] currentPlaylistClass];
393             [menuController rebuildSubmenus];
394     
395             if ( [df boolForKey:@"showSongInfoOnChange"] ) {
396                 [self performSelector:@selector(showCurrentTrackInfo) withObject:nil afterDelay:0.0];
397             }
398             
399             [self setLatestSongIdentifier:[[self currentRemote] playerStateUniqueIdentifier]];
400             
401             //Create the tooltip for the status item
402             if ( [df boolForKey:@"showToolTip"] ) {
403                 NSString *artist = [[self currentRemote] currentSongArtist];
404                 NSString *title = [[self currentRemote] currentSongTitle];
405                 ITDebugLog(@"Creating status item tooltip.");
406                 if (artist) {
407                     _toolTip = [NSString stringWithFormat:@"%@ - %@", artist, title];
408                 } else if (title) {
409                     _toolTip = title;
410                 } else {
411                     _toolTip = @"No Song Playing";
412                 }
413                 [statusItem setToolTip:_toolTip];
414             } else {
415                 [statusItem setToolTip:nil];
416             }
417         NS_HANDLER
418             [self networkError:localException];
419         NS_ENDHANDLER
420         
421         timerUpdating = NO;
422         [statusItem setEnabled:YES];
423     }
424     
425     if ([networkController isConnectedToServer]) {
426         [statusItem setMenu:([[self currentRemote] playerRunningState] == ITMTRemotePlayerRunning) ? [menuController menu] : [menuController menuForNoPlayer]];
427     }
428 }
429
430 - (void)menuClicked
431 {
432     ITDebugLog(@"Menu clicked.");
433     if ([networkController isConnectedToServer]) {
434         //Used the cached version
435         return;
436     }
437     
438     NS_DURING
439         if ([[self currentRemote] playerRunningState] == ITMTRemotePlayerRunning) {
440             [statusItem setMenu:[menuController menu]];
441         } else {
442             [statusItem setMenu:[menuController menuForNoPlayer]];
443         }
444     NS_HANDLER
445         [self networkError:localException];
446     NS_ENDHANDLER
447 }
448
449 //
450 //
451 // Menu Selectors
452 //
453 //
454
455 - (void)playPause
456 {
457     NS_DURING
458         ITMTRemotePlayerPlayingState state = [[self currentRemote] playerPlayingState];
459         ITDebugLog(@"Play/Pause toggled");
460         if (state == ITMTRemotePlayerPlaying) {
461             [[self currentRemote] pause];
462         } else if ((state == ITMTRemotePlayerForwarding) || (state == ITMTRemotePlayerRewinding)) {
463             [[self currentRemote] pause];
464             [[self currentRemote] play];
465         } else {
466             [[self currentRemote] play];
467         }
468     NS_HANDLER
469         [self networkError:localException];
470     NS_ENDHANDLER
471     
472     [self timerUpdate];
473 }
474
475 - (void)nextSong
476 {
477     ITDebugLog(@"Going to next song.");
478     NS_DURING
479         [[self currentRemote] goToNextSong];
480     NS_HANDLER
481         [self networkError:localException];
482     NS_ENDHANDLER
483     [self timerUpdate];
484 }
485
486 - (void)prevSong
487 {
488     ITDebugLog(@"Going to previous song.");
489     NS_DURING
490         [[self currentRemote] goToPreviousSong];
491     NS_HANDLER
492         [self networkError:localException];
493     NS_ENDHANDLER
494     [self timerUpdate];
495 }
496
497 - (void)fastForward
498 {
499     ITDebugLog(@"Fast forwarding.");
500     NS_DURING
501         [[self currentRemote] forward];
502     NS_HANDLER
503         [self networkError:localException];
504     NS_ENDHANDLER
505     [self timerUpdate];
506 }
507
508 - (void)rewind
509 {
510     ITDebugLog(@"Rewinding.");
511     NS_DURING
512         [[self currentRemote] rewind];
513     NS_HANDLER
514         [self networkError:localException];
515     NS_ENDHANDLER
516     [self timerUpdate];
517 }
518
519 - (void)selectPlaylistAtIndex:(int)index
520 {
521     ITDebugLog(@"Selecting playlist %i", index);
522     NS_DURING
523         [[self currentRemote] switchToPlaylistAtIndex:(index % 1000) ofSourceAtIndex:(index / 1000)];
524         //[[self currentRemote] switchToPlaylistAtIndex:index];
525     NS_HANDLER
526         [self networkError:localException];
527     NS_ENDHANDLER
528     [self timerUpdate];
529 }
530
531 - (void)selectSongAtIndex:(int)index
532 {
533     ITDebugLog(@"Selecting song %i", index);
534     NS_DURING
535         [[self currentRemote] switchToSongAtIndex:index];
536     NS_HANDLER
537         [self networkError:localException];
538     NS_ENDHANDLER
539     [self timerUpdate];
540 }
541
542 - (void)selectSongRating:(int)rating
543 {
544     ITDebugLog(@"Selecting song rating %i", rating);
545     NS_DURING
546         [[self currentRemote] setCurrentSongRating:(float)rating / 100.0];
547     NS_HANDLER
548         [self networkError:localException];
549     NS_ENDHANDLER
550     [self timerUpdate];
551 }
552
553 - (void)selectEQPresetAtIndex:(int)index
554 {
555     ITDebugLog(@"Selecting EQ preset %i", index);
556     NS_DURING
557         if (index == -1) {
558             [[self currentRemote] setEqualizerEnabled:![[self currentRemote] equalizerEnabled]];
559         } else {
560             [[self currentRemote] switchToEQAtIndex:index];
561         }
562     NS_HANDLER
563         [self networkError:localException];
564     NS_ENDHANDLER
565     [self timerUpdate];
566 }
567
568 - (void)makePlaylistWithTerm:(NSString *)term ofType:(int)type
569 {
570     ITDebugLog(@"Making playlist with term %@, type %i", term, type);
571     NS_DURING
572         [[self currentRemote] makePlaylistWithTerm:term ofType:type];
573     NS_HANDLER
574         [self networkError:localException];
575     NS_ENDHANDLER
576     ITDebugLog(@"Done making playlist");
577 }
578
579 - (void)showPlayer
580 {
581     ITDebugLog(@"Beginning show player.");
582     //if ( ( playerRunningState == ITMTRemotePlayerRunning) ) {
583         ITDebugLog(@"Showing player interface.");
584         NS_DURING
585             [[self currentRemote] showPrimaryInterface];
586         NS_HANDLER
587             [self networkError:localException];
588         NS_ENDHANDLER
589     /*} else {
590         ITDebugLog(@"Launching player.");
591         NS_DURING
592             NSString *path;
593             if ( (path = [df stringForKey:@"CustomPlayerPath"]) ) {
594             } else {
595                 pathITDebugLog(@"Showing player interface."); = [[self currentRemote] playerFullName];
596             }
597             if (![[NSWorkspace sharedWorkspace] launchApplication:path]) {
598                 ITDebugLog(@"Error Launching Player");
599             }
600         NS_HANDLER
601             [self networkError:localException];
602         NS_ENDHANDLER
603     }*/
604     ITDebugLog(@"Finished show player.");
605 }
606
607 - (void)showPreferences
608 {
609     ITDebugLog(@"Show preferences.");
610     [[PreferencesController sharedPrefs] showPrefsWindow:self];
611 }
612
613 - (void)showPreferencesAndClose
614 {
615     ITDebugLog(@"Show preferences.");
616     [[PreferencesController sharedPrefs] showPrefsWindow:self];
617     [[StatusWindow sharedWindow] setLocked:NO];
618     [[StatusWindow sharedWindow] vanish:self];
619     [[StatusWindow sharedWindow] setIgnoresMouseEvents:YES];
620 }
621
622 - (void)showTestWindow
623 {
624     [self showCurrentTrackInfo];
625 }
626
627 - (void)quitMenuTunes
628 {
629     ITDebugLog(@"Quitting MenuTunes.");
630     [NSApp terminate:self];
631 }
632
633 //
634 //
635
636 - (MenuController *)menuController
637 {
638     return menuController;
639 }
640
641 - (void)closePreferences
642 {
643     ITDebugLog(@"Preferences closed.");
644     if ( ( playerRunningState == ITMTRemotePlayerRunning) ) {
645         [self setupHotKeys];
646     }
647 }
648
649 - (ITMTRemote *)currentRemote
650 {
651     if ([networkController isConnectedToServer] && ![[networkController networkObject] isValid]) {
652         [self networkError:nil];
653         return nil;
654     }
655     return currentRemote;
656 }
657
658 //
659 //
660 // Hot key setup
661 //
662 //
663
664 - (void)clearHotKeys
665 {
666     NSEnumerator *hotKeyEnumerator = [[[ITHotKeyCenter sharedCenter] allHotKeys] objectEnumerator];
667     ITHotKey *nextHotKey;
668     ITDebugLog(@"Clearing hot keys.");
669     while ( (nextHotKey = [hotKeyEnumerator nextObject]) ) {
670         [[ITHotKeyCenter sharedCenter] unregisterHotKey:nextHotKey];
671     }
672     ITDebugLog(@"Done clearing hot keys.");
673 }
674
675 - (void)setupHotKeys
676 {
677     ITHotKey *hotKey;
678     ITDebugLog(@"Setting up hot keys.");
679     
680     if (playerRunningState == ITMTRemotePlayerNotRunning && ![[NetworkController sharedController] isConnectedToServer]) {
681         return;
682     }
683     
684     if ([df objectForKey:@"PlayPause"] != nil) {
685         ITDebugLog(@"Setting up play pause hot key.");
686         hotKey = [[ITHotKey alloc] init];
687         [hotKey setName:@"PlayPause"];
688         [hotKey setKeyCombo:[ITKeyCombo keyComboWithPlistRepresentation:[df objectForKey:@"PlayPause"]]];
689         [hotKey setTarget:self];
690         [hotKey setAction:@selector(playPause)];
691         [[ITHotKeyCenter sharedCenter] registerHotKey:[hotKey autorelease]];
692     }
693     
694     if ([df objectForKey:@"NextTrack"] != nil) {
695         ITDebugLog(@"Setting up next track hot key.");
696         hotKey = [[ITHotKey alloc] init];
697         [hotKey setName:@"NextTrack"];
698         [hotKey setKeyCombo:[ITKeyCombo keyComboWithPlistRepresentation:[df objectForKey:@"NextTrack"]]];
699         [hotKey setTarget:self];
700         [hotKey setAction:@selector(nextSong)];
701         [[ITHotKeyCenter sharedCenter] registerHotKey:[hotKey autorelease]];
702     }
703     
704     if ([df objectForKey:@"PrevTrack"] != nil) {
705         ITDebugLog(@"Setting up previous track hot key.");
706         hotKey = [[ITHotKey alloc] init];
707         [hotKey setName:@"PrevTrack"];
708         [hotKey setKeyCombo:[ITKeyCombo keyComboWithPlistRepresentation:[df objectForKey:@"PrevTrack"]]];
709         [hotKey setTarget:self];
710         [hotKey setAction:@selector(prevSong)];
711         [[ITHotKeyCenter sharedCenter] registerHotKey:[hotKey autorelease]];
712     }
713     
714     if ([df objectForKey:@"FastForward"] != nil) {
715         ITDebugLog(@"Setting up fast forward hot key.");
716         hotKey = [[ITHotKey alloc] init];
717         [hotKey setName:@"FastForward"];
718         [hotKey setKeyCombo:[ITKeyCombo keyComboWithPlistRepresentation:[df objectForKey:@"FastForward"]]];
719         [hotKey setTarget:self];
720         [hotKey setAction:@selector(fastForward)];
721         [[ITHotKeyCenter sharedCenter] registerHotKey:[hotKey autorelease]];
722     }
723     
724     if ([df objectForKey:@"Rewind"] != nil) {
725         ITDebugLog(@"Setting up rewind hot key.");
726         hotKey = [[ITHotKey alloc] init];
727         [hotKey setName:@"Rewind"];
728         [hotKey setKeyCombo:[ITKeyCombo keyComboWithPlistRepresentation:[df objectForKey:@"Rewind"]]];
729         [hotKey setTarget:self];
730         [hotKey setAction:@selector(rewind)];
731         [[ITHotKeyCenter sharedCenter] registerHotKey:[hotKey autorelease]];
732     }
733     
734     if ([df objectForKey:@"ShowPlayer"] != nil) {
735         ITDebugLog(@"Setting up show player hot key.");
736         hotKey = [[ITHotKey alloc] init];
737         [hotKey setName:@"ShowPlayer"];
738         [hotKey setKeyCombo:[ITKeyCombo keyComboWithPlistRepresentation:[df objectForKey:@"ShowPlayer"]]];
739         [hotKey setTarget:self];
740         [hotKey setAction:@selector(showPlayer)];
741         [[ITHotKeyCenter sharedCenter] registerHotKey:[hotKey autorelease]];
742     }
743     
744     if ([df objectForKey:@"TrackInfo"] != nil) {
745         ITDebugLog(@"Setting up track info hot key.");
746         hotKey = [[ITHotKey alloc] init];
747         [hotKey setName:@"TrackInfo"];
748         [hotKey setKeyCombo:[ITKeyCombo keyComboWithPlistRepresentation:[df objectForKey:@"TrackInfo"]]];
749         [hotKey setTarget:self];
750         [hotKey setAction:@selector(showCurrentTrackInfo)];
751         [[ITHotKeyCenter sharedCenter] registerHotKey:[hotKey autorelease]];
752     }
753     
754     if ([df objectForKey:@"UpcomingSongs"] != nil) {
755         ITDebugLog(@"Setting up upcoming songs hot key.");
756         hotKey = [[ITHotKey alloc] init];
757         [hotKey setName:@"UpcomingSongs"];
758         [hotKey setKeyCombo:[ITKeyCombo keyComboWithPlistRepresentation:[df objectForKey:@"UpcomingSongs"]]];
759         [hotKey setTarget:self];
760         [hotKey setAction:@selector(showUpcomingSongs)];
761         [[ITHotKeyCenter sharedCenter] registerHotKey:[hotKey autorelease]];
762     }
763     
764     if ([df objectForKey:@"ToggleLoop"] != nil) {
765         ITDebugLog(@"Setting up toggle loop hot key.");
766         hotKey = [[ITHotKey alloc] init];
767         [hotKey setName:@"ToggleLoop"];
768         [hotKey setKeyCombo:[ITKeyCombo keyComboWithPlistRepresentation:[df objectForKey:@"ToggleLoop"]]];
769         [hotKey setTarget:self];
770         [hotKey setAction:@selector(toggleLoop)];
771         [[ITHotKeyCenter sharedCenter] registerHotKey:[hotKey autorelease]];
772     }
773     
774     if ([df objectForKey:@"ToggleShuffle"] != nil) {
775         ITDebugLog(@"Setting up toggle shuffle hot key.");
776         hotKey = [[ITHotKey alloc] init];
777         [hotKey setName:@"ToggleShuffle"];
778         [hotKey setKeyCombo:[ITKeyCombo keyComboWithPlistRepresentation:[df objectForKey:@"ToggleShuffle"]]];
779         [hotKey setTarget:self];
780         [hotKey setAction:@selector(toggleShuffle)];
781         [[ITHotKeyCenter sharedCenter] registerHotKey:[hotKey autorelease]];
782     }
783     
784     if ([df objectForKey:@"IncrementVolume"] != nil) {
785         ITDebugLog(@"Setting up increment volume hot key.");
786         hotKey = [[ITHotKey alloc] init];
787         [hotKey setName:@"IncrementVolume"];
788         [hotKey setKeyCombo:[ITKeyCombo keyComboWithPlistRepresentation:[df objectForKey:@"IncrementVolume"]]];
789         [hotKey setTarget:self];
790         [hotKey setAction:@selector(incrementVolume)];
791         [[ITHotKeyCenter sharedCenter] registerHotKey:[hotKey autorelease]];
792     }
793     
794     if ([df objectForKey:@"DecrementVolume"] != nil) {
795         ITDebugLog(@"Setting up decrement volume hot key.");
796         hotKey = [[ITHotKey alloc] init];
797         [hotKey setName:@"DecrementVolume"];
798         [hotKey setKeyCombo:[ITKeyCombo keyComboWithPlistRepresentation:[df objectForKey:@"DecrementVolume"]]];
799         [hotKey setTarget:self];
800         [hotKey setAction:@selector(decrementVolume)];
801         [[ITHotKeyCenter sharedCenter] registerHotKey:[hotKey autorelease]];
802     }
803     
804     if ([df objectForKey:@"IncrementRating"] != nil) {
805         ITDebugLog(@"Setting up increment rating hot key.");
806         hotKey = [[ITHotKey alloc] init];
807         [hotKey setName:@"IncrementRating"];
808         [hotKey setKeyCombo:[ITKeyCombo keyComboWithPlistRepresentation:[df objectForKey:@"IncrementRating"]]];
809         [hotKey setTarget:self];
810         [hotKey setAction:@selector(incrementRating)];
811         [[ITHotKeyCenter sharedCenter] registerHotKey:[hotKey autorelease]];
812     }
813     
814     if ([df objectForKey:@"DecrementRating"] != nil) {
815         ITDebugLog(@"Setting up decrement rating hot key.");
816         hotKey = [[ITHotKey alloc] init];
817         [hotKey setName:@"DecrementRating"];
818         [hotKey setKeyCombo:[ITKeyCombo keyComboWithPlistRepresentation:[df objectForKey:@"DecrementRating"]]];
819         [hotKey setTarget:self];
820         [hotKey setAction:@selector(decrementRating)];
821         [[ITHotKeyCenter sharedCenter] registerHotKey:[hotKey autorelease]];
822     }
823     
824     if ([df objectForKey:@"PopupMenu"] != nil) {
825         ITDebugLog(@"Setting up popup menu hot key.");
826         hotKey = [[ITHotKey alloc] init];
827         [hotKey setName:@"PopupMenu"];
828         [hotKey setKeyCombo:[ITKeyCombo keyComboWithPlistRepresentation:[df objectForKey:@"PopupMenu"]]];
829         [hotKey setTarget:self];
830         [hotKey setAction:@selector(popupMenu)];
831         [[ITHotKeyCenter sharedCenter] registerHotKey:[hotKey autorelease]];
832     }
833     
834     int i;
835     for (i = 0; i <= 5; i++) {
836         NSString *curName = [NSString stringWithFormat:@"SetRating%i", i];
837         if ([df objectForKey:curName] != nil) {
838             ITDebugLog(@"Setting up set rating %i hot key.", i);
839             hotKey = [[ITHotKey alloc] init];
840             [hotKey setName:curName];
841             [hotKey setKeyCombo:[ITKeyCombo keyComboWithPlistRepresentation:[df objectForKey:curName]]];
842             [hotKey setTarget:self];
843             [hotKey setAction:@selector(setRating:)];
844             [[ITHotKeyCenter sharedCenter] registerHotKey:[hotKey autorelease]];
845         }
846     }
847     ITDebugLog(@"Finished setting up hot keys.");
848 }
849
850 - (void)showCurrentTrackInfo
851 {
852     ITMTRemotePlayerSource  source      = 0;
853     NSString               *title       = nil;
854     NSString               *album       = nil;
855     NSString               *artist      = nil;
856     NSString               *composer    = nil;
857     NSString               *time        = nil;
858     NSString               *track       = nil;
859     NSImage                *art         = nil;
860     int                     rating      = -1;
861     int                     playCount   = -1;
862     
863     ITDebugLog(@"Showing track info status window.");
864     
865     NS_DURING
866         source      = [[self currentRemote] currentSource];
867         title       = [[self currentRemote] currentSongTitle];
868     NS_HANDLER
869         [self networkError:localException];
870     NS_ENDHANDLER
871     
872     if ( title ) {
873         if ( [df boolForKey:@"showAlbumArtwork"] ) {
874             NSSize oldSize, newSize;
875              NS_DURING
876                  art = [[self currentRemote] currentSongAlbumArt];
877                  oldSize = [art size];
878                  if (oldSize.width > oldSize.height) newSize = NSMakeSize(110,oldSize.height * (110.0f / oldSize.width));
879                  else newSize = NSMakeSize(oldSize.width * (110.0f / oldSize.height),110);
880                 art = [[[[NSImage alloc] initWithData:[art TIFFRepresentation]] autorelease] imageScaledSmoothlyToSize:newSize];
881             NS_HANDLER
882                 [self networkError:localException];
883             NS_ENDHANDLER
884         }
885         
886         if ( [df boolForKey:@"showAlbum"] ) {
887             NS_DURING
888                 album = [[self currentRemote] currentSongAlbum];
889             NS_HANDLER
890                 [self networkError:localException];
891             NS_ENDHANDLER
892         }
893
894         if ( [df boolForKey:@"showArtist"] ) {
895             NS_DURING
896                 artist = [[self currentRemote] currentSongArtist];
897             NS_HANDLER
898                 [self networkError:localException];
899             NS_ENDHANDLER
900         }
901
902         if ( [df boolForKey:@"showComposer"] ) {
903             NS_DURING
904                 composer = [[self currentRemote] currentSongComposer];
905             NS_HANDLER
906                 [self networkError:localException];
907             NS_ENDHANDLER
908         }
909
910         if ( [df boolForKey:@"showTime"] ) {
911             NS_DURING
912                 time = [NSString stringWithFormat:@"%@: %@ / %@",
913                 NSLocalizedString(@"time", @"Time"),
914                 [[self currentRemote] currentSongElapsed],
915                 [[self currentRemote] currentSongLength]];
916             NS_HANDLER
917                 [self networkError:localException];
918             NS_ENDHANDLER
919         }
920
921         if ( [df boolForKey:@"showTrackNumber"] ) {
922             int trackNo    = 0;
923             int trackCount = 0;
924             
925             NS_DURING
926                 trackNo    = [[self currentRemote] currentSongTrack];
927                 trackCount = [[self currentRemote] currentAlbumTrackCount];
928             NS_HANDLER
929                 [self networkError:localException];
930             NS_ENDHANDLER
931             
932             if ( (trackNo > 0) || (trackCount > 0) ) {
933                 track = [NSString stringWithFormat:@"%@: %i %@ %i",
934                     @"Track", trackNo, @"of", trackCount];
935             }
936         }
937
938         if ( [df boolForKey:@"showTrackRating"] ) {
939             float currentRating = 0;
940             
941             NS_DURING
942                 currentRating = [[self currentRemote] currentSongRating];
943             NS_HANDLER
944                 [self networkError:localException];
945             NS_ENDHANDLER
946             
947             if (currentRating >= 0.0) {
948                 rating = ( currentRating * 5 );
949             }
950         }
951         
952         if ( [df boolForKey:@"showPlayCount"] && ![self radioIsPlaying] && [[self currentRemote] currentSource] == ITMTRemoteLibrarySource ) {
953             NS_DURING
954                 playCount = [[self currentRemote] currentSongPlayCount];
955             NS_HANDLER
956                 [self networkError:localException];
957             NS_ENDHANDLER
958         }
959     } else {
960         title = NSLocalizedString(@"noSongPlaying", @"No song is playing.");
961     }
962     ITDebugLog(@"Showing current track info status window.");
963     [statusWindowController showSongInfoWindowWithSource:source
964                                                    title:title
965                                                    album:album
966                                                   artist:artist
967                                                 composer:composer
968                                                     time:time
969                                                    track:track
970                                                   rating:rating
971                                                playCount:playCount
972                                                    image:art];
973 }
974
975 - (void)showUpcomingSongs
976 {
977     int numSongs = 0;
978     NS_DURING
979         numSongs = [[self currentRemote] numberOfSongsInPlaylistAtIndex:[[self currentRemote] currentPlaylistIndex]];
980     NS_HANDLER
981         [self networkError:localException];
982     NS_ENDHANDLER
983     
984     ITDebugLog(@"Showing upcoming songs status window.");
985     NS_DURING
986         if (numSongs > 0) {
987             int numSongsInAdvance = [df integerForKey:@"SongsInAdvance"];
988             NSMutableArray *songList = [NSMutableArray arrayWithCapacity:numSongsInAdvance];
989             int curTrack = [[self currentRemote] currentSongIndex];
990             int i;
991     
992             for (i = curTrack + 1; i <= curTrack + numSongsInAdvance; i++) {
993                 if (i <= numSongs) {
994                     [songList addObject:[[self currentRemote] songTitleAtIndex:i]];
995                 }
996             }
997             
998             if ([songList count] == 0) {
999                 [songList addObject:NSLocalizedString(@"noUpcomingSongs", @"No upcoming songs.")];
1000             }
1001             
1002             [statusWindowController showUpcomingSongsWindowWithTitles:songList];
1003         } else {
1004             [statusWindowController showUpcomingSongsWindowWithTitles:[NSArray arrayWithObject:NSLocalizedString(@"noUpcomingSongs", @"No upcoming songs.")]];
1005         }
1006     NS_HANDLER
1007         [self networkError:localException];
1008     NS_ENDHANDLER
1009 }
1010
1011 - (void)popupMenu
1012 {
1013     if (!_popped) {
1014         _popped = YES;
1015         [self menuClicked];
1016         NSMenu *menu = [statusItem menu];
1017         [(NSCarbonMenuImpl *)[menu _menuImpl] popUpMenu:menu atLocation:[NSEvent mouseLocation] width:1 forView:nil withSelectedItem:-30 withFont:[NSFont menuFontOfSize:32]];
1018         _popped = NO;
1019     }
1020 }
1021
1022 - (void)incrementVolume
1023 {
1024     NS_DURING
1025         float volume  = [[self currentRemote] volume];
1026         float dispVol = volume;
1027         ITDebugLog(@"Incrementing volume.");
1028         volume  += 0.110;
1029         dispVol += 0.100;
1030         
1031         if (volume > 1.0) {
1032             volume  = 1.0;
1033             dispVol = 1.0;
1034         }
1035     
1036         ITDebugLog(@"Setting volume to %f", volume);
1037         [[self currentRemote] setVolume:volume];
1038     
1039         // Show volume status window
1040         [statusWindowController showVolumeWindowWithLevel:dispVol];
1041     NS_HANDLER
1042         [self networkError:localException];
1043     NS_ENDHANDLER
1044 }
1045
1046 - (void)decrementVolume
1047 {
1048     NS_DURING
1049         float volume  = [[self currentRemote] volume];
1050         float dispVol = volume;
1051         ITDebugLog(@"Decrementing volume.");
1052         volume  -= 0.090;
1053         dispVol -= 0.100;
1054     
1055         if (volume < 0.0) {
1056             volume  = 0.0;
1057             dispVol = 0.0;
1058         }
1059         
1060         ITDebugLog(@"Setting volume to %f", volume);
1061         [[self currentRemote] setVolume:volume];
1062         
1063         //Show volume status window
1064         [statusWindowController showVolumeWindowWithLevel:dispVol];
1065     NS_HANDLER
1066         [self networkError:localException];
1067     NS_ENDHANDLER
1068 }
1069
1070 - (void)incrementRating
1071 {
1072     NS_DURING
1073         float rating = [[self currentRemote] currentSongRating];
1074         ITDebugLog(@"Incrementing rating.");
1075         
1076         if ([[self currentRemote] currentPlaylistIndex] == 0) {
1077             ITDebugLog(@"No song playing, rating change aborted.");
1078             return;
1079         }
1080         
1081         rating += 0.2;
1082         if (rating > 1.0) {
1083             rating = 1.0;
1084         }
1085         ITDebugLog(@"Setting rating to %f", rating);
1086         [[self currentRemote] setCurrentSongRating:rating];
1087         
1088         //Show rating status window
1089         [statusWindowController showRatingWindowWithRating:rating];
1090     NS_HANDLER
1091         [self networkError:localException];
1092     NS_ENDHANDLER
1093 }
1094
1095 - (void)decrementRating
1096 {
1097     NS_DURING
1098         float rating = [[self currentRemote] currentSongRating];
1099         ITDebugLog(@"Decrementing rating.");
1100         
1101         if ([[self currentRemote] currentPlaylistIndex] == 0) {
1102             ITDebugLog(@"No song playing, rating change aborted.");
1103             return;
1104         }
1105         
1106         rating -= 0.2;
1107         if (rating < 0.0) {
1108             rating = 0.0;
1109         }
1110         ITDebugLog(@"Setting rating to %f", rating);
1111         [[self currentRemote] setCurrentSongRating:rating];
1112         
1113         //Show rating status window
1114         [statusWindowController showRatingWindowWithRating:rating];
1115     NS_HANDLER
1116         [self networkError:localException];
1117     NS_ENDHANDLER
1118 }
1119
1120 - (void)setRating:(ITHotKey *)sender
1121 {
1122     int stars = [[sender name] characterAtIndex:9] - 48;
1123     [self selectSongRating:stars * 20];
1124     [statusWindowController showRatingWindowWithRating:(float)stars / 5.0];
1125 }
1126
1127 - (void)toggleLoop
1128 {
1129     NS_DURING
1130         ITMTRemotePlayerRepeatMode repeatMode = [[self currentRemote] repeatMode];
1131         ITDebugLog(@"Toggling repeat mode.");
1132         switch (repeatMode) {
1133             case ITMTRemotePlayerRepeatOff:
1134                 repeatMode = ITMTRemotePlayerRepeatAll;
1135             break;
1136             case ITMTRemotePlayerRepeatAll:
1137                 repeatMode = ITMTRemotePlayerRepeatOne;
1138             break;
1139             case ITMTRemotePlayerRepeatOne:
1140                 repeatMode = ITMTRemotePlayerRepeatOff;
1141             break;
1142         }
1143         ITDebugLog(@"Setting repeat mode to %i", repeatMode);
1144         [[self currentRemote] setRepeatMode:repeatMode];
1145         
1146         //Show loop status window
1147         [statusWindowController showRepeatWindowWithMode:repeatMode];
1148     NS_HANDLER
1149         [self networkError:localException];
1150     NS_ENDHANDLER
1151 }
1152
1153 - (void)toggleShuffle
1154 {
1155     NS_DURING
1156         BOOL newShuffleEnabled = ( ! [[self currentRemote] shuffleEnabled] );
1157         ITDebugLog(@"Toggling shuffle mode.");
1158         [[self currentRemote] setShuffleEnabled:newShuffleEnabled];
1159         //Show shuffle status window
1160         ITDebugLog(@"Setting shuffle mode to %i", newShuffleEnabled);
1161         [statusWindowController showShuffleWindow:newShuffleEnabled];
1162     NS_HANDLER
1163         [self networkError:localException];
1164     NS_ENDHANDLER
1165 }
1166
1167 - (void)registerNowOK
1168 {
1169     [[StatusWindow sharedWindow] setLocked:NO];
1170     [[StatusWindow sharedWindow] vanish:self];
1171     [[StatusWindow sharedWindow] setIgnoresMouseEvents:YES];
1172
1173     [self blingNow];
1174 }
1175
1176 - (void)registerNowCancel
1177 {
1178     [[StatusWindow sharedWindow] setLocked:NO];
1179     [[StatusWindow sharedWindow] vanish:self];
1180     [[StatusWindow sharedWindow] setIgnoresMouseEvents:YES];
1181
1182     [NSApp terminate:self];
1183 }
1184
1185 /*************************************************************************/
1186 #pragma mark -
1187 #pragma mark NETWORK HANDLERS
1188 /*************************************************************************/
1189
1190 - (void)setServerStatus:(BOOL)newStatus
1191 {
1192     if (newStatus) {
1193         //Turn on
1194         [networkController setServerStatus:YES];
1195     } else {
1196         //Tear down
1197         [networkController setServerStatus:NO];
1198     }
1199 }
1200
1201 - (int)connectToServer
1202 {
1203     int result;
1204     ITDebugLog(@"Attempting to connect to shared remote.");
1205     result = [networkController connectToHost:[df stringForKey:@"sharedPlayerHost"]];
1206     //Connect
1207     if (result == 1) {
1208         [[PreferencesController sharedPrefs] resetRemotePlayerTextFields];
1209         currentRemote = [[[networkController networkObject] remote] retain];
1210         
1211         [self setupHotKeys];
1212         //playerRunningState = ITMTRemotePlayerRunning;
1213         playerRunningState = [[self currentRemote] playerRunningState];
1214         
1215         [refreshTimer invalidate];
1216         refreshTimer = [[NSTimer scheduledTimerWithTimeInterval:([networkController isConnectedToServer] ? 10.0 : 0.5)
1217                                 target:self
1218                                 selector:@selector(timerUpdate)
1219                                 userInfo:nil
1220                                 repeats:YES] retain];
1221         [self timerUpdate];
1222         ITDebugLog(@"Connection successful.");
1223         return 1;
1224     } else if (result == 0) {
1225         ITDebugLog(@"Connection failed.");
1226         currentRemote = [remoteArray objectAtIndex:0];
1227         return 0;
1228     } else {
1229         //Do something about the password being invalid
1230         ITDebugLog(@"Connection failed.");
1231         currentRemote = [remoteArray objectAtIndex:0];
1232         return -1;
1233     }
1234 }
1235
1236 - (BOOL)disconnectFromServer
1237 {
1238     ITDebugLog(@"Disconnecting from shared remote.");
1239     //Disconnect
1240     [currentRemote release];
1241     currentRemote = [remoteArray objectAtIndex:0];
1242     [networkController disconnect];
1243     
1244     if ([[self currentRemote] playerRunningState] == ITMTRemotePlayerRunning) {
1245         [self applicationLaunched:nil];
1246     } else {
1247         [self applicationTerminated:nil];
1248     }
1249     [self timerUpdate];
1250     return YES;
1251 }
1252
1253 - (void)checkForRemoteServer
1254 {
1255     [self checkForRemoteServerAndConnectImmediately:NO];
1256 }
1257
1258 - (void)checkForRemoteServerAndConnectImmediately:(BOOL)connectImmediately
1259 {
1260     ITDebugLog(@"Checking for remote server.");
1261     if (!_checkingForServer) {
1262         if (!_serverCheckLock) {
1263             _serverCheckLock = [[NSLock alloc] init];
1264         }
1265         [_serverCheckLock lock];
1266         _checkingForServer = YES;
1267         [_serverCheckLock unlock];
1268         [NSThread detachNewThreadSelector:@selector(runRemoteServerCheck:) toTarget:self withObject:[NSNumber numberWithBool:connectImmediately]];
1269     }
1270 }
1271
1272 - (void)runRemoteServerCheck:(id)sender
1273 {
1274     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1275     ITDebugLog(@"Remote server check running.");
1276     if ([networkController checkForServerAtHost:[df stringForKey:@"sharedPlayerHost"]]) {
1277         ITDebugLog(@"Remote server found.");
1278         if ([sender boolValue]) {
1279             [self performSelectorOnMainThread:@selector(connectToServer) withObject:nil waitUntilDone:NO];
1280         } else {
1281             [self performSelectorOnMainThread:@selector(remoteServerFound:) withObject:nil waitUntilDone:NO];
1282         }
1283     } else {
1284         ITDebugLog(@"Remote server not found.");
1285         [self performSelectorOnMainThread:@selector(remoteServerNotFound:) withObject:nil waitUntilDone:NO];
1286     }
1287     [_serverCheckLock lock];
1288     _checkingForServer = NO;
1289     [_serverCheckLock unlock];
1290     [pool release];
1291 }
1292
1293 - (void)remoteServerFound:(id)sender
1294 {
1295     if (![networkController isServerOn] && ![networkController isConnectedToServer]) {
1296         [[StatusWindowController sharedController] showReconnectQueryWindow];
1297     }
1298 }
1299
1300 - (void)remoteServerNotFound:(id)sender
1301 {
1302     if (![[NetworkController sharedController] isConnectedToServer]) {
1303         [NSTimer scheduledTimerWithTimeInterval:90.0 target:self selector:@selector(checkForRemoteServer) userInfo:nil repeats:NO];
1304     }
1305 }
1306
1307 - (void)networkError:(NSException *)exception
1308 {
1309     ITDebugLog(@"Remote exception thrown: %@: %@", [exception name], [exception reason]);
1310     if ( ((exception == nil) || [[exception name] isEqualToString:NSPortTimeoutException]) && [networkController isConnectedToServer]) {
1311         //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);
1312         [[StatusWindowController sharedController] showNetworkErrorQueryWindow];
1313         if ([self disconnectFromServer]) {
1314             [[PreferencesController sharedPrefs] resetRemotePlayerTextFields];
1315             [NSTimer scheduledTimerWithTimeInterval:90.0 target:self selector:@selector(checkForRemoteServer) userInfo:nil repeats:NO];
1316         } else {
1317             ITDebugLog(@"CRITICAL ERROR, DISCONNECTING!");
1318         }
1319     }
1320 }
1321
1322 - (void)reconnect
1323 {
1324     /*if ([self connectToServer] == 0) {
1325         [NSTimer scheduledTimerWithTimeInterval:90.0 target:self selector:@selector(checkForRemoteServer) userInfo:nil repeats:NO];
1326     }*/
1327     [self checkForRemoteServerAndConnectImmediately:YES];
1328     [[StatusWindow sharedWindow] setLocked:NO];
1329     [[StatusWindow sharedWindow] vanish:self];
1330     [[StatusWindow sharedWindow] setIgnoresMouseEvents:YES];
1331 }
1332
1333 - (void)cancelReconnect
1334 {
1335     [[StatusWindow sharedWindow] setLocked:NO];
1336     [[StatusWindow sharedWindow] vanish:self];
1337     [[StatusWindow sharedWindow] setIgnoresMouseEvents:YES];
1338 }
1339
1340 /*************************************************************************/
1341 #pragma mark -
1342 #pragma mark WORKSPACE NOTIFICATION HANDLERS
1343 /*************************************************************************/
1344
1345 - (void)applicationLaunched:(NSNotification *)note
1346 {
1347     NS_DURING
1348         if (!note || ([[[note userInfo] objectForKey:@"NSApplicationName"] isEqualToString:[[self currentRemote] playerFullName]] && ![[NetworkController sharedController] isConnectedToServer])) {
1349             ITDebugLog(@"Remote application launched.");
1350             playerRunningState = ITMTRemotePlayerRunning;
1351             [[self currentRemote] begin];
1352             [self setLatestSongIdentifier:@""];
1353             [self timerUpdate];
1354             refreshTimer = [[NSTimer scheduledTimerWithTimeInterval:([networkController isConnectedToServer] ? 10.0 : 0.5)
1355                                 target:self
1356                                 selector:@selector(timerUpdate)
1357                                 userInfo:nil
1358                                 repeats:YES] retain];
1359             //[NSThread detachNewThreadSelector:@selector(startTimerInNewThread) toTarget:self withObject:nil];
1360             [self setupHotKeys];
1361         }
1362     NS_HANDLER
1363         [self networkError:localException];
1364     NS_ENDHANDLER
1365 }
1366
1367  - (void)applicationTerminated:(NSNotification *)note
1368  {
1369     NS_DURING
1370         if (!note || [[[note userInfo] objectForKey:@"NSApplicationName"] isEqualToString:[[self currentRemote] playerFullName]] && ![[NetworkController sharedController] isConnectedToServer]) {
1371             ITDebugLog(@"Remote application terminated.");
1372             playerRunningState = ITMTRemotePlayerNotRunning;
1373             [[self currentRemote] halt];
1374             [refreshTimer invalidate];
1375             [refreshTimer release];
1376             refreshTimer = nil;
1377             [self clearHotKeys];
1378             
1379             if ([df objectForKey:@"ShowPlayer"] != nil) {
1380                 ITHotKey *hotKey;
1381                 ITDebugLog(@"Setting up show player hot key.");
1382                 hotKey = [[ITHotKey alloc] init];
1383                 [hotKey setName:@"ShowPlayer"];
1384                 [hotKey setKeyCombo:[ITKeyCombo keyComboWithPlistRepresentation:[df objectForKey:@"ShowPlayer"]]];
1385                 [hotKey setTarget:self];
1386                 [hotKey setAction:@selector(showPlayer)];
1387                 [[ITHotKeyCenter sharedCenter] registerHotKey:[hotKey autorelease]];
1388             }
1389         }
1390     NS_HANDLER
1391         [self networkError:localException];
1392     NS_ENDHANDLER
1393  }
1394
1395
1396 /*************************************************************************/
1397 #pragma mark -
1398 #pragma mark NSApplication DELEGATE METHODS
1399 /*************************************************************************/
1400
1401 - (void)applicationWillTerminate:(NSNotification *)note
1402 {
1403     [networkController stopRemoteServerSearch];
1404     [self clearHotKeys];
1405     [[NSStatusBar systemStatusBar] removeStatusItem:statusItem];
1406 }
1407
1408
1409 /*************************************************************************/
1410 #pragma mark -
1411 #pragma mark DEALLOCATION METHOD
1412 /*************************************************************************/
1413
1414 - (void)dealloc
1415 {
1416     [self applicationTerminated:nil];
1417     [bling release];
1418     [statusItem release];
1419     [statusWindowController release];
1420     [menuController release];
1421     [networkController release];
1422     [_serverCheckLock release];
1423     [super dealloc];
1424 }
1425
1426 @end