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