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