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