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