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