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