Added some more debug loggins to Audioscrobbler method. Improved it so it should...
[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:@"UpcomingSongs"] != nil) {
987         ITDebugLog(@"Setting up upcoming songs hot key.");
988         hotKey = [[ITHotKey alloc] init];
989         [hotKey setName:@"UpcomingSongs"];
990         [hotKey setKeyCombo:[ITKeyCombo keyComboWithPlistRepresentation:[df objectForKey:@"UpcomingSongs"]]];
991         [hotKey setTarget:self];
992         [hotKey setAction:@selector(showUpcomingSongs)];
993         [[ITHotKeyCenter sharedCenter] registerHotKey:[hotKey autorelease]];
994     }
995     
996     if ([df objectForKey:@"ToggleLoop"] != nil) {
997         ITDebugLog(@"Setting up toggle loop hot key.");
998         hotKey = [[ITHotKey alloc] init];
999         [hotKey setName:@"ToggleLoop"];
1000         [hotKey setKeyCombo:[ITKeyCombo keyComboWithPlistRepresentation:[df objectForKey:@"ToggleLoop"]]];
1001         [hotKey setTarget:self];
1002         [hotKey setAction:@selector(toggleLoop)];
1003         [[ITHotKeyCenter sharedCenter] registerHotKey:[hotKey autorelease]];
1004     }
1005     
1006     if ([df objectForKey:@"ToggleShuffle"] != nil) {
1007         ITDebugLog(@"Setting up toggle shuffle hot key.");
1008         hotKey = [[ITHotKey alloc] init];
1009         [hotKey setName:@"ToggleShuffle"];
1010         [hotKey setKeyCombo:[ITKeyCombo keyComboWithPlistRepresentation:[df objectForKey:@"ToggleShuffle"]]];
1011         [hotKey setTarget:self];
1012         [hotKey setAction:@selector(toggleShuffle)];
1013         [[ITHotKeyCenter sharedCenter] registerHotKey:[hotKey autorelease]];
1014     }
1015     
1016     if ([df objectForKey:@"IncrementVolume"] != nil) {
1017         ITDebugLog(@"Setting up increment volume hot key.");
1018         hotKey = [[ITHotKey alloc] init];
1019         [hotKey setName:@"IncrementVolume"];
1020         [hotKey setKeyCombo:[ITKeyCombo keyComboWithPlistRepresentation:[df objectForKey:@"IncrementVolume"]]];
1021         [hotKey setTarget:self];
1022         [hotKey setAction:@selector(incrementVolume)];
1023         [[ITHotKeyCenter sharedCenter] registerHotKey:[hotKey autorelease]];
1024     }
1025     
1026     if ([df objectForKey:@"DecrementVolume"] != nil) {
1027         ITDebugLog(@"Setting up decrement volume hot key.");
1028         hotKey = [[ITHotKey alloc] init];
1029         [hotKey setName:@"DecrementVolume"];
1030         [hotKey setKeyCombo:[ITKeyCombo keyComboWithPlistRepresentation:[df objectForKey:@"DecrementVolume"]]];
1031         [hotKey setTarget:self];
1032         [hotKey setAction:@selector(decrementVolume)];
1033         [[ITHotKeyCenter sharedCenter] registerHotKey:[hotKey autorelease]];
1034     }
1035     
1036     if ([df objectForKey:@"IncrementRating"] != nil) {
1037         ITDebugLog(@"Setting up increment rating hot key.");
1038         hotKey = [[ITHotKey alloc] init];
1039         [hotKey setName:@"IncrementRating"];
1040         [hotKey setKeyCombo:[ITKeyCombo keyComboWithPlistRepresentation:[df objectForKey:@"IncrementRating"]]];
1041         [hotKey setTarget:self];
1042         [hotKey setAction:@selector(incrementRating)];
1043         [[ITHotKeyCenter sharedCenter] registerHotKey:[hotKey autorelease]];
1044     }
1045     
1046     if ([df objectForKey:@"DecrementRating"] != nil) {
1047         ITDebugLog(@"Setting up decrement rating hot key.");
1048         hotKey = [[ITHotKey alloc] init];
1049         [hotKey setName:@"DecrementRating"];
1050         [hotKey setKeyCombo:[ITKeyCombo keyComboWithPlistRepresentation:[df objectForKey:@"DecrementRating"]]];
1051         [hotKey setTarget:self];
1052         [hotKey setAction:@selector(decrementRating)];
1053         [[ITHotKeyCenter sharedCenter] registerHotKey:[hotKey autorelease]];
1054     }
1055     
1056         if ([df objectForKey:@"ToggleShufflability"] != nil) {
1057         ITDebugLog(@"Setting up toggle song shufflability hot key.");
1058         hotKey = [[ITHotKey alloc] init];
1059         [hotKey setName:@"ToggleShufflability"];
1060         [hotKey setKeyCombo:[ITKeyCombo keyComboWithPlistRepresentation:[df objectForKey:@"ToggleShufflability"]]];
1061         [hotKey setTarget:self];
1062         [hotKey setAction:@selector(toggleSongShufflable)];
1063         [[ITHotKeyCenter sharedCenter] registerHotKey:[hotKey autorelease]];
1064     }
1065         
1066     if ([df objectForKey:@"PopupMenu"] != nil) {
1067         ITDebugLog(@"Setting up popup menu hot key.");
1068         hotKey = [[ITHotKey alloc] init];
1069         [hotKey setName:@"PopupMenu"];
1070         [hotKey setKeyCombo:[ITKeyCombo keyComboWithPlistRepresentation:[df objectForKey:@"PopupMenu"]]];
1071         [hotKey setTarget:self];
1072         [hotKey setAction:@selector(popupMenu)];
1073         [[ITHotKeyCenter sharedCenter] registerHotKey:[hotKey autorelease]];
1074     }
1075     
1076     int i;
1077     for (i = 0; i <= 5; i++) {
1078         NSString *curName = [NSString stringWithFormat:@"SetRating%i", i];
1079         if ([df objectForKey:curName] != nil) {
1080             ITDebugLog(@"Setting up set rating %i hot key.", i);
1081             hotKey = [[ITHotKey alloc] init];
1082             [hotKey setName:curName];
1083             [hotKey setKeyCombo:[ITKeyCombo keyComboWithPlistRepresentation:[df objectForKey:curName]]];
1084             [hotKey setTarget:self];
1085             [hotKey setAction:@selector(setRating:)];
1086             [[ITHotKeyCenter sharedCenter] registerHotKey:[hotKey autorelease]];
1087         }
1088     }
1089     ITDebugLog(@"Finished setting up hot keys.");
1090 }
1091
1092 - (void)showCurrentTrackInfoHotKey
1093 {
1094         //If we're already visible and the setting says so, vanish instead of displaying again.
1095         if ([df boolForKey:@"ToggleTrackInfoWithHotKey"] && [statusWindowController currentStatusWindowType] == StatusWindowTrackInfoType && [[StatusWindow sharedWindow] visibilityState] == ITWindowVisibleState) {
1096                 ITDebugLog(@"Track window is already visible, hiding track window.");
1097                 [self invalidateStatusWindowUpdateTimer];
1098                 [[StatusWindow sharedWindow] vanish:nil];
1099                 return;
1100         }
1101         [self showCurrentTrackInfo];
1102 }
1103
1104 - (void)showCurrentTrackInfo
1105 {
1106     ITMTRemotePlayerSource  source      = 0;
1107     NSString               *title       = nil;
1108     NSString               *album       = nil;
1109     NSString               *artist      = nil;
1110     NSString               *composer    = nil;
1111     NSString               *time        = nil;
1112     NSString               *track       = nil;
1113     NSImage                *art         = nil;
1114     int                     rating      = -1;
1115     int                     playCount   = -1;
1116         
1117     ITDebugLog(@"Showing track info status window.");
1118     
1119     NS_DURING
1120         source      = [[self currentRemote] currentSource];
1121         title       = [[self currentRemote] currentSongTitle];
1122     NS_HANDLER
1123         [self networkError:localException];
1124     NS_ENDHANDLER
1125     
1126     if ( title ) {
1127         if ( [df boolForKey:@"showAlbumArtwork"] ) {
1128                         NSSize oldSize, newSize;
1129                         NS_DURING
1130                                 art = [[self currentRemote] currentSongAlbumArt];
1131                                 oldSize = [art size];
1132                                 if (oldSize.width > oldSize.height) {
1133                                         newSize = NSMakeSize(110,oldSize.height * (110.0f / oldSize.width));
1134                                 }
1135                                 else newSize = NSMakeSize(oldSize.width * (110.0f / oldSize.height),110);
1136                                 art = [[[[NSImage alloc] initWithData:[art TIFFRepresentation]] autorelease] imageScaledSmoothlyToSize:newSize];
1137                         NS_HANDLER
1138                                 [self networkError:localException];
1139                         NS_ENDHANDLER
1140         }
1141         
1142         if ( [df boolForKey:@"showAlbum"] ) {
1143             NS_DURING
1144                 album = [[self currentRemote] currentSongAlbum];
1145             NS_HANDLER
1146                 [self networkError:localException];
1147             NS_ENDHANDLER
1148         }
1149
1150         if ( [df boolForKey:@"showArtist"] ) {
1151             NS_DURING
1152                 artist = [[self currentRemote] currentSongArtist];
1153             NS_HANDLER
1154                 [self networkError:localException];
1155             NS_ENDHANDLER
1156         }
1157
1158         if ( [df boolForKey:@"showComposer"] ) {
1159             NS_DURING
1160                 composer = [[self currentRemote] currentSongComposer];
1161             NS_HANDLER
1162                 [self networkError:localException];
1163             NS_ENDHANDLER
1164         }
1165
1166         if ( [df boolForKey:@"showTime"] ) {
1167             NS_DURING
1168                 time = [NSString stringWithFormat:@"%@: %@ / %@",
1169                 NSLocalizedString(@"time", @"Time"),
1170                 [[self currentRemote] currentSongElapsed],
1171                 [[self currentRemote] currentSongLength]];
1172             NS_HANDLER
1173                 [self networkError:localException];
1174             NS_ENDHANDLER
1175                         _timeUpdateCount = 0;
1176                         [self invalidateStatusWindowUpdateTimer];
1177                         _statusWindowUpdateTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(updateTime:) userInfo:nil repeats:YES];
1178         }
1179
1180         if ( [df boolForKey:@"showTrackNumber"] ) {
1181             int trackNo    = 0;
1182             int trackCount = 0;
1183             
1184             NS_DURING
1185                 trackNo    = [[self currentRemote] currentSongTrack];
1186                 trackCount = [[self currentRemote] currentAlbumTrackCount];
1187             NS_HANDLER
1188                 [self networkError:localException];
1189             NS_ENDHANDLER
1190             
1191             if ( (trackNo > 0) || (trackCount > 0) ) {
1192                 track = [NSString stringWithFormat:@"%@: %i %@ %i",
1193                     @"Track", trackNo, @"of", trackCount];
1194             }
1195         }
1196
1197         if ( [df boolForKey:@"showTrackRating"] ) {
1198             float currentRating = 0;
1199             
1200             NS_DURING
1201                 currentRating = [[self currentRemote] currentSongRating];
1202             NS_HANDLER
1203                 [self networkError:localException];
1204             NS_ENDHANDLER
1205             
1206             if (currentRating >= 0.0) {
1207                 rating = ( currentRating * 5 );
1208             }
1209         }
1210         
1211         if ( [df boolForKey:@"showPlayCount"] && ![self radioIsPlaying] && [[self currentRemote] currentSource] == ITMTRemoteLibrarySource ) {
1212             NS_DURING
1213                 playCount = [[self currentRemote] currentSongPlayCount];
1214             NS_HANDLER
1215                 [self networkError:localException];
1216             NS_ENDHANDLER
1217         }
1218     } else {
1219         title = NSLocalizedString(@"noSongPlaying", @"No song is playing.");
1220     }
1221     ITDebugLog(@"Showing current track info status window.");
1222     [statusWindowController showSongInfoWindowWithSource:source
1223                                                    title:title
1224                                                    album:album
1225                                                   artist:artist
1226                                                 composer:composer
1227                                                     time:time
1228                                                    track:track
1229                                                   rating:rating
1230                                                playCount:playCount
1231                                                    image:art];
1232 }
1233
1234 - (void)updateTime:(NSTimer *)timer
1235 {
1236         StatusWindow *sw = (StatusWindow *)[StatusWindow sharedWindow];
1237         _timeUpdateCount++;
1238         if ([sw visibilityState] != ITWindowHiddenState) {
1239                 NSString *time = nil, *length;
1240                 NS_DURING
1241                         length = [[self currentRemote] currentSongLength];
1242                         if (length) {
1243                                 time = [NSString stringWithFormat:@"%@: %@ / %@",
1244                                                         NSLocalizedString(@"time", @"Time"),
1245                                                         [[self currentRemote] currentSongElapsed],
1246                                                         length];
1247                                 [[StatusWindowController sharedController] updateTime:time];
1248                         }
1249                 NS_HANDLER
1250                         [self networkError:localException];
1251                 NS_ENDHANDLER
1252         } else {
1253                 [self invalidateStatusWindowUpdateTimer];
1254         }
1255 }
1256
1257 - (void)invalidateStatusWindowUpdateTimer
1258 {
1259         if (_statusWindowUpdateTimer) {
1260                 [_statusWindowUpdateTimer invalidate];
1261                 _statusWindowUpdateTimer = nil;
1262         }
1263 }
1264
1265 - (void)showUpcomingSongs
1266 {
1267     int numSongs = 0;
1268     NS_DURING
1269         numSongs = [[self currentRemote] numberOfSongsInPlaylistAtIndex:[[self currentRemote] currentPlaylistIndex]];
1270     NS_HANDLER
1271         [self networkError:localException];
1272     NS_ENDHANDLER
1273     
1274         [self invalidateStatusWindowUpdateTimer];
1275         
1276     ITDebugLog(@"Showing upcoming songs status window.");
1277     NS_DURING
1278         if (numSongs > 0) {
1279             int numSongsInAdvance = [df integerForKey:@"SongsInAdvance"];
1280             NSMutableArray *songList = [NSMutableArray arrayWithCapacity:numSongsInAdvance];
1281             int curTrack = [[self currentRemote] currentSongIndex];
1282             int i;
1283     
1284             for (i = curTrack + 1; i <= curTrack + numSongsInAdvance && i <= numSongs; i++) {
1285                 if ([[self currentRemote] songEnabledAtIndex:i]) {
1286                     [songList addObject:[[self currentRemote] songTitleAtIndex:i]];
1287                 } else {
1288                                         numSongsInAdvance++;
1289                                 }
1290             }
1291             
1292             if ([songList count] == 0) {
1293                 [songList addObject:NSLocalizedString(@"noUpcomingSongs", @"No upcoming songs.")];
1294             }
1295             
1296             [statusWindowController showUpcomingSongsWindowWithTitles:songList];
1297         } else {
1298             [statusWindowController showUpcomingSongsWindowWithTitles:[NSArray arrayWithObject:NSLocalizedString(@"noUpcomingSongs", @"No upcoming songs.")]];
1299         }
1300     NS_HANDLER
1301         [self networkError:localException];
1302     NS_ENDHANDLER
1303 }
1304
1305 - (void)popupMenu
1306 {
1307     if (!_popped) {
1308         _popped = YES;
1309         [self menuClicked];
1310         NSMenu *menu = [statusItem menu];
1311         [(NSCarbonMenuImpl *)[menu _menuImpl] popUpMenu:menu atLocation:[NSEvent mouseLocation] width:1 forView:nil withSelectedItem:-30 withFont:[NSFont menuFontOfSize:32]];
1312         _popped = NO;
1313     }
1314 }
1315
1316 - (void)incrementVolume
1317 {
1318     NS_DURING
1319         float volume  = [[self currentRemote] volume];
1320         float dispVol = volume;
1321         ITDebugLog(@"Incrementing volume.");
1322         volume  += 0.110;
1323         dispVol += 0.100;
1324         
1325         if (volume > 1.0) {
1326             volume  = 1.0;
1327             dispVol = 1.0;
1328         }
1329     
1330         ITDebugLog(@"Setting volume to %f", volume);
1331         [[self currentRemote] setVolume:volume];
1332     
1333         // Show volume status window
1334                 [self invalidateStatusWindowUpdateTimer];
1335         [statusWindowController showVolumeWindowWithLevel:dispVol];
1336     NS_HANDLER
1337         [self networkError:localException];
1338     NS_ENDHANDLER
1339 }
1340
1341 - (void)decrementVolume
1342 {
1343     NS_DURING
1344         float volume  = [[self currentRemote] volume];
1345         float dispVol = volume;
1346         ITDebugLog(@"Decrementing volume.");
1347         volume  -= 0.090;
1348         dispVol -= 0.100;
1349     
1350         if (volume < 0.0) {
1351             volume  = 0.0;
1352             dispVol = 0.0;
1353         }
1354         
1355         ITDebugLog(@"Setting volume to %f", volume);
1356         [[self currentRemote] setVolume:volume];
1357         
1358         //Show volume status window
1359                 [self invalidateStatusWindowUpdateTimer];
1360         [statusWindowController showVolumeWindowWithLevel:dispVol];
1361     NS_HANDLER
1362         [self networkError:localException];
1363     NS_ENDHANDLER
1364 }
1365
1366 - (void)incrementRating
1367 {
1368     NS_DURING
1369         float rating = [[self currentRemote] currentSongRating];
1370         ITDebugLog(@"Incrementing rating.");
1371         
1372         if ([[self currentRemote] currentPlaylistIndex] == 0) {
1373             ITDebugLog(@"No song playing, rating change aborted.");
1374             return;
1375         }
1376         
1377         rating += 0.2;
1378         if (rating > 1.0) {
1379             rating = 1.0;
1380         }
1381         ITDebugLog(@"Setting rating to %f", rating);
1382         [[self currentRemote] setCurrentSongRating:rating];
1383         
1384         //Show rating status window
1385                 [self invalidateStatusWindowUpdateTimer];
1386         [statusWindowController showRatingWindowWithRating:rating];
1387     NS_HANDLER
1388         [self networkError:localException];
1389     NS_ENDHANDLER
1390 }
1391
1392 - (void)decrementRating
1393 {
1394     NS_DURING
1395         float rating = [[self currentRemote] currentSongRating];
1396         ITDebugLog(@"Decrementing rating.");
1397         
1398         if ([[self currentRemote] currentPlaylistIndex] == 0) {
1399             ITDebugLog(@"No song playing, rating change aborted.");
1400             return;
1401         }
1402         
1403         rating -= 0.2;
1404         if (rating < 0.0) {
1405             rating = 0.0;
1406         }
1407         ITDebugLog(@"Setting rating to %f", rating);
1408         [[self currentRemote] setCurrentSongRating:rating];
1409         
1410         //Show rating status window
1411                 [self invalidateStatusWindowUpdateTimer];
1412         [statusWindowController showRatingWindowWithRating:rating];
1413     NS_HANDLER
1414         [self networkError:localException];
1415     NS_ENDHANDLER
1416 }
1417
1418 - (void)setRating:(ITHotKey *)sender
1419 {
1420         if ([self songIsPlaying]) {
1421                 int stars = [[sender name] characterAtIndex:9] - 48;
1422                 [self selectSongRating:stars * 20];
1423                 [statusWindowController showRatingWindowWithRating:(float)stars / 5.0];
1424         }
1425 }
1426
1427 - (void)toggleLoop
1428 {
1429     NS_DURING
1430         ITMTRemotePlayerRepeatMode repeatMode = [[self currentRemote] repeatMode];
1431         ITDebugLog(@"Toggling repeat mode.");
1432         switch (repeatMode) {
1433             case ITMTRemotePlayerRepeatOff:
1434                 repeatMode = ITMTRemotePlayerRepeatAll;
1435             break;
1436             case ITMTRemotePlayerRepeatAll:
1437                 repeatMode = ITMTRemotePlayerRepeatOne;
1438             break;
1439             case ITMTRemotePlayerRepeatOne:
1440                 repeatMode = ITMTRemotePlayerRepeatOff;
1441             break;
1442         }
1443         ITDebugLog(@"Setting repeat mode to %i", repeatMode);
1444         [[self currentRemote] setRepeatMode:repeatMode];
1445         
1446         //Show loop status window
1447                 [self invalidateStatusWindowUpdateTimer];
1448         [statusWindowController showRepeatWindowWithMode:repeatMode];
1449     NS_HANDLER
1450         [self networkError:localException];
1451     NS_ENDHANDLER
1452 }
1453
1454 - (void)toggleShuffle
1455 {
1456     NS_DURING
1457         BOOL newShuffleEnabled = ( ! [[self currentRemote] shuffleEnabled] );
1458         ITDebugLog(@"Toggling shuffle mode.");
1459         [[self currentRemote] setShuffleEnabled:newShuffleEnabled];
1460         //Show shuffle status window
1461         ITDebugLog(@"Setting shuffle mode to %i", newShuffleEnabled);
1462                 [self invalidateStatusWindowUpdateTimer];
1463         [statusWindowController showShuffleWindow:newShuffleEnabled];
1464     NS_HANDLER
1465         [self networkError:localException];
1466     NS_ENDHANDLER
1467 }
1468
1469 - (void)toggleSongShufflable
1470 {
1471         if ([self songIsPlaying]) {
1472                 NS_DURING
1473                         BOOL flag = ![[self currentRemote] currentSongShufflable];
1474                         ITDebugLog(@"Toggling shufflability.");
1475                         [[self currentRemote] setCurrentSongShufflable:flag];
1476                         //Show song shufflability status window
1477                         [self invalidateStatusWindowUpdateTimer];
1478                         [statusWindowController showSongShufflabilityWindow:flag];
1479                 NS_HANDLER
1480                         [self networkError:localException];
1481                 NS_ENDHANDLER
1482         }
1483 }
1484
1485 - (void)registerNowOK
1486 {
1487     [(StatusWindow *)[StatusWindow sharedWindow] setLocked:NO];
1488     [[StatusWindow sharedWindow] vanish:self];
1489     [[StatusWindow sharedWindow] setIgnoresMouseEvents:YES];
1490
1491     [self blingNow];
1492 }
1493
1494 - (void)registerNowCancel
1495 {
1496     [(StatusWindow *)[StatusWindow sharedWindow] setLocked:NO];
1497     [[StatusWindow sharedWindow] vanish:self];
1498     [[StatusWindow sharedWindow] setIgnoresMouseEvents:YES];
1499
1500     [NSApp terminate:self];
1501 }
1502
1503 /*************************************************************************/
1504 #pragma mark -
1505 #pragma mark NETWORK HANDLERS
1506 /*************************************************************************/
1507
1508 - (void)setServerStatus:(BOOL)newStatus
1509 {
1510     if (newStatus) {
1511         //Turn on
1512         [networkController setServerStatus:YES];
1513     } else {
1514         //Tear down
1515         [networkController setServerStatus:NO];
1516     }
1517 }
1518
1519 - (int)connectToServer
1520 {
1521     int result;
1522     ITDebugLog(@"Attempting to connect to shared remote.");
1523     result = [networkController connectToHost:[df stringForKey:@"sharedPlayerHost"]];
1524     //Connect
1525     if (result == 1) {
1526         [[PreferencesController sharedPrefs] resetRemotePlayerTextFields];
1527         currentRemote = [[[networkController networkObject] remote] retain];
1528         
1529         [self setupHotKeys];
1530         //playerRunningState = ITMTRemotePlayerRunning;
1531         playerRunningState = [[self currentRemote] playerRunningState];
1532                 if (_needsPolling) {
1533                         if (refreshTimer) {
1534                                 [refreshTimer invalidate];
1535                         }
1536                 }
1537                 
1538                 refreshTimer = [[NSTimer scheduledTimerWithTimeInterval:([networkController isConnectedToServer] ? 10.0 : 0.5)
1539                                                                  target:self
1540                                                                  selector:@selector(timerUpdate)
1541                                                                  userInfo:nil
1542                                                                  repeats:YES] retain];
1543                 
1544         [self timerUpdate];
1545         ITDebugLog(@"Connection successful.");
1546         return 1;
1547     } else if (result == 0) {
1548         ITDebugLog(@"Connection failed.");
1549         currentRemote = [remoteArray objectAtIndex:0];
1550         return 0;
1551     } else {
1552         //Do something about the password being invalid
1553         ITDebugLog(@"Connection failed.");
1554         currentRemote = [remoteArray objectAtIndex:0];
1555         return -1;
1556     }
1557 }
1558
1559 - (BOOL)disconnectFromServer
1560 {
1561     ITDebugLog(@"Disconnecting from shared remote.");
1562     //Disconnect
1563     [currentRemote release];
1564     currentRemote = [remoteArray objectAtIndex:0];
1565     [networkController disconnect];
1566     
1567     if ([[self currentRemote] playerRunningState] == ITMTRemotePlayerRunning) {
1568                 refreshTimer = nil;
1569         [self applicationLaunched:nil];
1570     } else {
1571         [self applicationTerminated:nil];
1572     }
1573         
1574     if (refreshTimer) {
1575                 [self timerUpdate];
1576         };
1577     return YES;
1578 }
1579
1580 - (void)checkForRemoteServer
1581 {
1582     [self checkForRemoteServerAndConnectImmediately:NO];
1583 }
1584
1585 - (void)checkForRemoteServerAndConnectImmediately:(BOOL)connectImmediately
1586 {
1587     ITDebugLog(@"Checking for remote server.");
1588     if (!_checkingForServer) {
1589         if (!_serverCheckLock) {
1590             _serverCheckLock = [[NSLock alloc] init];
1591         }
1592         [_serverCheckLock lock];
1593         _checkingForServer = YES;
1594         [_serverCheckLock unlock];
1595         [NSThread detachNewThreadSelector:@selector(runRemoteServerCheck:) toTarget:self withObject:[NSNumber numberWithBool:connectImmediately]];
1596     }
1597 }
1598
1599 - (void)runRemoteServerCheck:(id)sender
1600 {
1601     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1602     ITDebugLog(@"Remote server check running.");
1603     if ([networkController checkForServerAtHost:[df stringForKey:@"sharedPlayerHost"]]) {
1604         ITDebugLog(@"Remote server found.");
1605         if ([sender boolValue]) {
1606             [self performSelectorOnMainThread:@selector(connectToServer) withObject:nil waitUntilDone:NO];
1607         } else {
1608             [self performSelectorOnMainThread:@selector(remoteServerFound:) withObject:nil waitUntilDone:NO];
1609         }
1610     } else {
1611         ITDebugLog(@"Remote server not found.");
1612         [self performSelectorOnMainThread:@selector(remoteServerNotFound:) withObject:nil waitUntilDone:NO];
1613     }
1614     [_serverCheckLock lock];
1615     _checkingForServer = NO;
1616     [_serverCheckLock unlock];
1617     [pool release];
1618 }
1619
1620 - (void)remoteServerFound:(id)sender
1621 {
1622     if (![networkController isServerOn] && ![networkController isConnectedToServer]) {
1623                 [self invalidateStatusWindowUpdateTimer];
1624         [[StatusWindowController sharedController] showReconnectQueryWindow];
1625     }
1626 }
1627
1628 - (void)remoteServerNotFound:(id)sender
1629 {
1630     if (![[NetworkController sharedController] isConnectedToServer]) {
1631         [NSTimer scheduledTimerWithTimeInterval:90.0 target:self selector:@selector(checkForRemoteServer) userInfo:nil repeats:NO];
1632     }
1633 }
1634
1635 - (void)networkError:(NSException *)exception
1636 {
1637     ITDebugLog(@"Remote exception thrown: %@: %@", [exception name], [exception reason]);
1638     if ( ((exception == nil) || [[exception name] isEqualToString:NSPortTimeoutException]) && [networkController isConnectedToServer]) {
1639         //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);
1640                 [self invalidateStatusWindowUpdateTimer];
1641         [[StatusWindowController sharedController] showNetworkErrorQueryWindow];
1642         if ([self disconnectFromServer]) {
1643             [[PreferencesController sharedPrefs] resetRemotePlayerTextFields];
1644             [NSTimer scheduledTimerWithTimeInterval:90.0 target:self selector:@selector(checkForRemoteServer) userInfo:nil repeats:NO];
1645         } else {
1646             ITDebugLog(@"CRITICAL ERROR, DISCONNECTING!");
1647         }
1648     }
1649 }
1650
1651 - (void)reconnect
1652 {
1653     /*if ([self connectToServer] == 0) {
1654         [NSTimer scheduledTimerWithTimeInterval:90.0 target:self selector:@selector(checkForRemoteServer) userInfo:nil repeats:NO];
1655     }*/
1656     [self checkForRemoteServerAndConnectImmediately:YES];
1657     [(StatusWindow *)[StatusWindow sharedWindow] setLocked:NO];
1658     [[StatusWindow sharedWindow] vanish:self];
1659     [[StatusWindow sharedWindow] setIgnoresMouseEvents:YES];
1660 }
1661
1662 - (void)cancelReconnect
1663 {
1664     [(StatusWindow *)[StatusWindow sharedWindow] setLocked:NO];
1665     [[StatusWindow sharedWindow] vanish:self];
1666     [[StatusWindow sharedWindow] setIgnoresMouseEvents:YES];
1667 }
1668
1669 /*************************************************************************/
1670 #pragma mark -
1671 #pragma mark WORKSPACE NOTIFICATION HANDLERS
1672 /*************************************************************************/
1673
1674 - (void)applicationLaunched:(NSNotification *)note
1675 {
1676     NS_DURING
1677         if (!note || ([[[note userInfo] objectForKey:@"NSApplicationName"] isEqualToString:[[self currentRemote] playerFullName]] && ![[NetworkController sharedController] isConnectedToServer])) {
1678             ITDebugLog(@"Remote application launched.");
1679             playerRunningState = ITMTRemotePlayerRunning;
1680             [[self currentRemote] begin];
1681             [self setLatestSongIdentifier:@""];
1682             [self timerUpdate];
1683                         if (_needsPolling) {
1684                                 refreshTimer = [[NSTimer scheduledTimerWithTimeInterval:([networkController isConnectedToServer] ? 10.0 : 0.5)
1685                                                                         target:self
1686                                                                         selector:@selector(timerUpdate)
1687                                                                         userInfo:nil
1688                                                                         repeats:YES] retain];
1689                         }
1690             //[NSThread detachNewThreadSelector:@selector(startTimerInNewThread) toTarget:self withObject:nil];
1691                         if (![df boolForKey:@"UsePollingOnly"]) {
1692                                 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(trackChanged:) name:@"ITMTTrackChanged" object:nil];
1693                         }
1694             [self setupHotKeys];
1695         }
1696     NS_HANDLER
1697         [self networkError:localException];
1698     NS_ENDHANDLER
1699 }
1700
1701  - (void)applicationTerminated:(NSNotification *)note
1702  {
1703     NS_DURING
1704         if (!note || [[[note userInfo] objectForKey:@"NSApplicationName"] isEqualToString:[[self currentRemote] playerFullName]] && ![[NetworkController sharedController] isConnectedToServer]) {
1705             ITDebugLog(@"Remote application terminated.");
1706             playerRunningState = ITMTRemotePlayerNotRunning;
1707             [[self currentRemote] halt];
1708             [refreshTimer invalidate];
1709             [refreshTimer release];
1710             refreshTimer = nil;
1711                         [[NSNotificationCenter defaultCenter] removeObserver:self name:@"ITMTTrackChanged" object:nil];
1712                         [statusItem setEnabled:YES];
1713                         [statusItem setToolTip:@"iTunes not running."];
1714             [self clearHotKeys];
1715
1716                         
1717             if ([df objectForKey:@"ShowPlayer"] != nil) {
1718                 ITHotKey *hotKey;
1719                 ITDebugLog(@"Setting up show player hot key.");
1720                 hotKey = [[ITHotKey alloc] init];
1721                 [hotKey setName:@"ShowPlayer"];
1722                 [hotKey setKeyCombo:[ITKeyCombo keyComboWithPlistRepresentation:[df objectForKey:@"ShowPlayer"]]];
1723                 [hotKey setTarget:self];
1724                 [hotKey setAction:@selector(showPlayer)];
1725                 [[ITHotKeyCenter sharedCenter] registerHotKey:[hotKey autorelease]];
1726             }
1727         }
1728     NS_HANDLER
1729         [self networkError:localException];
1730     NS_ENDHANDLER
1731  }
1732
1733
1734 /*************************************************************************/
1735 #pragma mark -
1736 #pragma mark NSApplication DELEGATE METHODS
1737 /*************************************************************************/
1738
1739 - (void)applicationWillTerminate:(NSNotification *)note
1740 {
1741         [[[NSWorkspace sharedWorkspace] notificationCenter] removeObserver:self];
1742     [networkController stopRemoteServerSearch];
1743     [self clearHotKeys];
1744     [[NSStatusBar systemStatusBar] removeStatusItem:statusItem];
1745 }
1746
1747 - (void)applicationDidBecomeActive:(NSNotification *)note
1748 {
1749         //This appears to not work in 10.4
1750         if (_open && !blinged && ![[ITAboutWindowController sharedController] isVisible] && ![NSApp mainWindow] && ([[StatusWindow sharedWindow] exitMode] == ITTransientStatusWindowExitAfterDelay)) {
1751                 [[MainController sharedController] showPreferences];
1752         }
1753 }
1754
1755 /*************************************************************************/
1756 #pragma mark -
1757 #pragma mark DEALLOCATION METHOD
1758 /*************************************************************************/
1759
1760 - (void)dealloc
1761 {
1762     [self applicationTerminated:nil];
1763     [bling release];
1764     [statusItem release];
1765     [statusWindowController release];
1766     [menuController release];
1767     [networkController release];
1768     [_serverCheckLock release];
1769     [super dealloc];
1770 }
1771
1772 @end