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