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