1 #import "MainController.h"
2 #import "MenuController.h"
3 #import "PreferencesController.h"
4 #import <ITKit/ITHotKeyCenter.h>
5 #import <ITKit/ITHotKey.h>
6 #import <ITKit/ITKeyCombo.h>
7 #import "StatusWindow.h"
8 #import "StatusWindowController.h"
9 #import "StatusItemHack.h"
11 @interface MainController(Private)
12 - (ITMTRemote *)loadRemote;
14 - (void)setLatestSongIdentifier:(NSString *)newIdentifier;
15 - (void)showCurrentTrackInfo;
16 - (void)applicationLaunched:(NSNotification *)note;
17 - (void)applicationTerminated:(NSNotification *)note;
20 static MainController *sharedController;
22 @implementation MainController
24 + (MainController *)sharedController
26 return sharedController;
29 /*************************************************************************/
31 #pragma mark INITIALIZATION/DEALLOCATION METHODS
32 /*************************************************************************/
36 if ( ( self = [super init] ) ) {
37 sharedController = self;
39 remoteArray = [[NSMutableArray alloc] initWithCapacity:1];
40 statusWindowController = [StatusWindowController sharedController];
41 menuController = [[MenuController alloc] init];
42 df = [[NSUserDefaults standardUserDefaults] retain];
49 - (void)applicationDidFinishLaunching:(NSNotification *)note
51 //Turn on debug mode if needed
52 if ([df boolForKey:@"ITDebugMode"]) {
56 currentRemote = [self loadRemote];
57 [currentRemote begin];
59 //Setup for notification of the remote player launching or quitting
60 [[[NSWorkspace sharedWorkspace] notificationCenter]
62 selector:@selector(applicationTerminated:)
63 name:NSWorkspaceDidTerminateApplicationNotification
66 [[[NSWorkspace sharedWorkspace] notificationCenter]
68 selector:@selector(applicationLaunched:)
69 name:NSWorkspaceDidLaunchApplicationNotification
72 if ( ! [df objectForKey:@"menu"] ) { // If this is nil, defaults have never been registered.
73 [[PreferencesController sharedPrefs] registerDefaults];
76 [StatusItemHack install];
77 statusItem = [[ITStatusItem alloc]
78 initWithStatusBar:[NSStatusBar systemStatusBar]
79 withLength:NSSquareStatusItemLength];
81 bling = [[MTBlingController alloc] init];
83 registerTimer = [[NSTimer scheduledTimerWithTimeInterval:10.0
85 selector:@selector(blingTime)
89 if ([currentRemote playerRunningState] == ITMTRemotePlayerRunning) {
90 [self applicationLaunched:nil];
92 if ([df boolForKey:@"LaunchPlayerWithMT"])
95 [self applicationTerminated:nil];
98 [statusItem setImage:[NSImage imageNamed:@"MenuNormal"]];
99 [statusItem setAlternateImage:[NSImage imageNamed:@"MenuInverted"]];
104 - (ITMTRemote *)loadRemote
106 NSString *folderPath = [[NSBundle mainBundle] builtInPlugInsPath];
107 ITDebugLog(@"Gathering remotes.");
109 NSArray *bundlePathList = [NSBundle pathsForResourcesOfType:@"remote" inDirectory:folderPath];
110 NSEnumerator *enumerator = [bundlePathList objectEnumerator];
111 NSString *bundlePath;
113 while ( (bundlePath = [enumerator nextObject]) ) {
114 NSBundle* remoteBundle = [NSBundle bundleWithPath:bundlePath];
117 Class remoteClass = [remoteBundle principalClass];
119 if ([remoteClass conformsToProtocol:@protocol(ITMTRemote)] &&
120 [remoteClass isKindOfClass:[NSObject class]]) {
121 id remote = [remoteClass remote];
122 ITDebugLog(@"Adding remote at path %@", bundlePath);
123 [remoteArray addObject:remote];
128 // if ( [remoteArray count] > 0 ) { // UNCOMMENT WHEN WE HAVE > 1 PLUGIN
129 // if ( [remoteArray count] > 1 ) {
130 // [remoteArray sortUsingSelector:@selector(sortAlpha:)];
132 // [self loadModuleAccessUI]; //Comment out this line to disable remote visibility
135 // NSLog(@"%@", [remoteArray objectAtIndex:0]); //DEBUG
136 return [remoteArray objectAtIndex:0];
139 /*************************************************************************/
141 #pragma mark INSTANCE METHODS
142 /*************************************************************************/
144 /*- (void)startTimerInNewThread
146 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
147 NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
148 refreshTimer = [[NSTimer scheduledTimerWithTimeInterval:0.5
150 selector:@selector(timerUpdate)
152 repeats:YES] retain];
154 ITDebugLog(@"Timer started.");
158 - (void)setBlingTime:(NSDate*)date
160 NSMutableDictionary *globalPrefs;
162 globalPrefs = [[df persistentDomainForName:@".GlobalPreferences"] mutableCopy];
164 [globalPrefs setObject:date forKey:@"ITMTTrialStart"];
166 [globalPrefs removeObjectForKey:@"ITMTTrialStart"];
168 [df setPersistentDomain:globalPrefs forName:@".GlobalPreferences"];
170 [globalPrefs release];
173 - (NSDate*)getBlingTime
176 return [[df persistentDomainForName:@".GlobalPreferences"] objectForKey:@"ITMTTrialStart"];
181 NSDate *now = [NSDate date];
182 if (![self blingBling]) {
183 if ( (! [self getBlingTime] ) || ([now timeIntervalSinceDate:[self getBlingTime]] < 0) ) {
184 [self setBlingTime:now];
186 if ( ([now timeIntervalSinceDate:[self getBlingTime]] >= 604800) && (blinged != YES) ) {
188 [statusItem setEnabled:NO];
190 if ([refreshTimer isValid]) {
191 [refreshTimer invalidate];
193 [statusWindowController showRegistrationQueryWindow];
197 [statusItem setEnabled:YES];
199 if (![refreshTimer isValid]) {
200 [refreshTimer release];
201 refreshTimer = refreshTimer = [[NSTimer scheduledTimerWithTimeInterval:0.5
203 selector:@selector(timerUpdate)
205 repeats:YES] retain];
209 [self setBlingTime:nil];
220 if ( ! ([bling checkDone] == 2475) ) {
227 - (BOOL)songIsPlaying
229 return ( ! ([[currentRemote playerStateUniqueIdentifier] isEqualToString:@"0-0"]) );
232 - (BOOL)radioIsPlaying
234 return ( [currentRemote currentPlaylistClass] == ITMTRemotePlayerRadioPlaylist );
239 return ( ! [[currentRemote playerStateUniqueIdentifier] isEqualToString:_latestSongIdentifier] );
242 - (NSString *)latestSongIdentifier
244 return _latestSongIdentifier;
247 - (void)setLatestSongIdentifier:(NSString *)newIdentifier
249 ITDebugLog(@"Setting latest song identifier to %@", newIdentifier);
250 [_latestSongIdentifier autorelease];
251 _latestSongIdentifier = [newIdentifier copy];
256 if ( [self songChanged] && (timerUpdating != YES) ) {
257 ITDebugLog(@"The song changed.");
259 latestPlaylistClass = [currentRemote currentPlaylistClass];
260 [menuController rebuildSubmenus];
262 if ( [df boolForKey:@"showSongInfoOnChange"] ) {
263 [self performSelector:@selector(showCurrentTrackInfo) withObject:nil afterDelay:0.0];
266 [self setLatestSongIdentifier:[currentRemote playerStateUniqueIdentifier]];
274 ITDebugLog(@"Menu clicked.");
275 if ([currentRemote playerRunningState] == ITMTRemotePlayerRunning) {
276 [statusItem setMenu:[menuController menu]];
278 [statusItem setMenu:[menuController menuForNoPlayer]];
290 ITMTRemotePlayerPlayingState state = [currentRemote playerPlayingState];
291 ITDebugLog(@"Play/Pause toggled");
292 if (state == ITMTRemotePlayerPlaying) {
293 [currentRemote pause];
294 } else if ((state == ITMTRemotePlayerForwarding) || (state == ITMTRemotePlayerRewinding)) {
295 [currentRemote pause];
296 [currentRemote play];
298 [currentRemote play];
305 ITDebugLog(@"Going to next song.");
306 [currentRemote goToNextSong];
312 ITDebugLog(@"Going to previous song.");
313 [currentRemote goToPreviousSong];
319 ITDebugLog(@"Fast forwarding.");
320 [currentRemote forward];
326 ITDebugLog(@"Rewinding.");
327 [currentRemote rewind];
331 - (void)selectPlaylistAtIndex:(int)index
333 ITDebugLog(@"Selecting playlist %i", index);
334 [currentRemote switchToPlaylistAtIndex:index];
338 - (void)selectSongAtIndex:(int)index
340 ITDebugLog(@"Selecting song %i", index);
341 [currentRemote switchToSongAtIndex:index];
345 - (void)selectSongRating:(int)rating
347 ITDebugLog(@"Selecting song rating %i", rating);
348 [currentRemote setCurrentSongRating:(float)rating / 100.0];
352 - (void)selectEQPresetAtIndex:(int)index
354 ITDebugLog(@"Selecting EQ preset %i", index);
355 [currentRemote switchToEQAtIndex:index];
361 ITDebugLog(@"Beginning show player.");
362 if ( ( playerRunningState == ITMTRemotePlayerRunning) ) {
363 ITDebugLog(@"Showing player interface.");
364 [currentRemote showPrimaryInterface];
366 ITDebugLog(@"Launching player.");
367 if (![[NSWorkspace sharedWorkspace] launchApplication:[currentRemote playerFullName]]) {
368 ITDebugLog(@"Error Launching Player");
371 ITDebugLog(@"Finished show player.");
374 - (void)showPreferences
376 ITDebugLog(@"Show preferences.");
377 [[PreferencesController sharedPrefs] setController:self];
378 [[PreferencesController sharedPrefs] showPrefsWindow:self];
381 - (void)quitMenuTunes
383 ITDebugLog(@"Quitting MenuTunes.");
384 [NSApp terminate:self];
390 - (void)closePreferences
392 ITDebugLog(@"Preferences closed.");
393 if ( ( playerRunningState == ITMTRemotePlayerRunning) ) {
398 - (ITMTRemote *)currentRemote
400 return currentRemote;
411 NSEnumerator *hotKeyEnumerator = [[[ITHotKeyCenter sharedCenter] allHotKeys] objectEnumerator];
412 ITHotKey *nextHotKey;
413 ITDebugLog(@"Clearing hot keys.");
414 while ( (nextHotKey = [hotKeyEnumerator nextObject]) ) {
415 [[ITHotKeyCenter sharedCenter] unregisterHotKey:nextHotKey];
417 ITDebugLog(@"Done clearing hot keys.");
423 ITDebugLog(@"Setting up hot keys.");
425 if (playerRunningState == ITMTRemotePlayerNotRunning) {
429 if ([df objectForKey:@"PlayPause"] != nil) {
430 ITDebugLog(@"Setting up play pause hot key.");
431 hotKey = [[ITHotKey alloc] init];
432 [hotKey setName:@"PlayPause"];
433 [hotKey setKeyCombo:[ITKeyCombo keyComboWithPlistRepresentation:[df objectForKey:@"PlayPause"]]];
434 [hotKey setTarget:self];
435 [hotKey setAction:@selector(playPause)];
436 [[ITHotKeyCenter sharedCenter] registerHotKey:[hotKey autorelease]];
439 if ([df objectForKey:@"NextTrack"] != nil) {
440 ITDebugLog(@"Setting up next track hot key.");
441 hotKey = [[ITHotKey alloc] init];
442 [hotKey setName:@"NextTrack"];
443 [hotKey setKeyCombo:[ITKeyCombo keyComboWithPlistRepresentation:[df objectForKey:@"NextTrack"]]];
444 [hotKey setTarget:self];
445 [hotKey setAction:@selector(nextSong)];
446 [[ITHotKeyCenter sharedCenter] registerHotKey:[hotKey autorelease]];
449 if ([df objectForKey:@"PrevTrack"] != nil) {
450 ITDebugLog(@"Setting up previous track hot key.");
451 hotKey = [[ITHotKey alloc] init];
452 [hotKey setName:@"PrevTrack"];
453 [hotKey setKeyCombo:[ITKeyCombo keyComboWithPlistRepresentation:[df objectForKey:@"PrevTrack"]]];
454 [hotKey setTarget:self];
455 [hotKey setAction:@selector(prevSong)];
456 [[ITHotKeyCenter sharedCenter] registerHotKey:[hotKey autorelease]];
459 if ([df objectForKey:@"ShowPlayer"] != nil) {
460 ITDebugLog(@"Setting up show player hot key.");
461 hotKey = [[ITHotKey alloc] init];
462 [hotKey setName:@"ShowPlayer"];
463 [hotKey setKeyCombo:[ITKeyCombo keyComboWithPlistRepresentation:[df objectForKey:@"ShowPlayer"]]];
464 [hotKey setTarget:self];
465 [hotKey setAction:@selector(showPlayer)];
466 [[ITHotKeyCenter sharedCenter] registerHotKey:[hotKey autorelease]];
469 if ([df objectForKey:@"TrackInfo"] != nil) {
470 ITDebugLog(@"Setting up track info hot key.");
471 hotKey = [[ITHotKey alloc] init];
472 [hotKey setName:@"TrackInfo"];
473 [hotKey setKeyCombo:[ITKeyCombo keyComboWithPlistRepresentation:[df objectForKey:@"TrackInfo"]]];
474 [hotKey setTarget:self];
475 [hotKey setAction:@selector(showCurrentTrackInfo)];
476 [[ITHotKeyCenter sharedCenter] registerHotKey:[hotKey autorelease]];
479 if ([df objectForKey:@"UpcomingSongs"] != nil) {
480 ITDebugLog(@"Setting up upcoming songs hot key.");
481 hotKey = [[ITHotKey alloc] init];
482 [hotKey setName:@"UpcomingSongs"];
483 [hotKey setKeyCombo:[ITKeyCombo keyComboWithPlistRepresentation:[df objectForKey:@"UpcomingSongs"]]];
484 [hotKey setTarget:self];
485 [hotKey setAction:@selector(showUpcomingSongs)];
486 [[ITHotKeyCenter sharedCenter] registerHotKey:[hotKey autorelease]];
489 if ([df objectForKey:@"ToggleLoop"] != nil) {
490 ITDebugLog(@"Setting up toggle loop hot key.");
491 hotKey = [[ITHotKey alloc] init];
492 [hotKey setName:@"ToggleLoop"];
493 [hotKey setKeyCombo:[ITKeyCombo keyComboWithPlistRepresentation:[df objectForKey:@"ToggleLoop"]]];
494 [hotKey setTarget:self];
495 [hotKey setAction:@selector(toggleLoop)];
496 [[ITHotKeyCenter sharedCenter] registerHotKey:[hotKey autorelease]];
499 if ([df objectForKey:@"ToggleShuffle"] != nil) {
500 ITDebugLog(@"Setting up toggle shuffle hot key.");
501 hotKey = [[ITHotKey alloc] init];
502 [hotKey setName:@"ToggleShuffle"];
503 [hotKey setKeyCombo:[ITKeyCombo keyComboWithPlistRepresentation:[df objectForKey:@"ToggleShuffle"]]];
504 [hotKey setTarget:self];
505 [hotKey setAction:@selector(toggleShuffle)];
506 [[ITHotKeyCenter sharedCenter] registerHotKey:[hotKey autorelease]];
509 if ([df objectForKey:@"IncrementVolume"] != nil) {
510 ITDebugLog(@"Setting up increment volume hot key.");
511 hotKey = [[ITHotKey alloc] init];
512 [hotKey setName:@"IncrementVolume"];
513 [hotKey setKeyCombo:[ITKeyCombo keyComboWithPlistRepresentation:[df objectForKey:@"IncrementVolume"]]];
514 [hotKey setTarget:self];
515 [hotKey setAction:@selector(incrementVolume)];
516 [[ITHotKeyCenter sharedCenter] registerHotKey:[hotKey autorelease]];
519 if ([df objectForKey:@"DecrementVolume"] != nil) {
520 ITDebugLog(@"Setting up decrement volume hot key.");
521 hotKey = [[ITHotKey alloc] init];
522 [hotKey setName:@"DecrementVolume"];
523 [hotKey setKeyCombo:[ITKeyCombo keyComboWithPlistRepresentation:[df objectForKey:@"DecrementVolume"]]];
524 [hotKey setTarget:self];
525 [hotKey setAction:@selector(decrementVolume)];
526 [[ITHotKeyCenter sharedCenter] registerHotKey:[hotKey autorelease]];
529 if ([df objectForKey:@"IncrementRating"] != nil) {
530 ITDebugLog(@"Setting up increment rating hot key.");
531 hotKey = [[ITHotKey alloc] init];
532 [hotKey setName:@"IncrementRating"];
533 [hotKey setKeyCombo:[ITKeyCombo keyComboWithPlistRepresentation:[df objectForKey:@"IncrementRating"]]];
534 [hotKey setTarget:self];
535 [hotKey setAction:@selector(incrementRating)];
536 [[ITHotKeyCenter sharedCenter] registerHotKey:[hotKey autorelease]];
539 if ([df objectForKey:@"DecrementRating"] != nil) {
540 ITDebugLog(@"Setting up decrement rating hot key.");
541 hotKey = [[ITHotKey alloc] init];
542 [hotKey setName:@"DecrementRating"];
543 [hotKey setKeyCombo:[ITKeyCombo keyComboWithPlistRepresentation:[df objectForKey:@"DecrementRating"]]];
544 [hotKey setTarget:self];
545 [hotKey setAction:@selector(decrementRating)];
546 [[ITHotKeyCenter sharedCenter] registerHotKey:[hotKey autorelease]];
548 ITDebugLog(@"Finished setting up hot keys.");
551 - (void)showCurrentTrackInfo
553 ITMTRemotePlayerSource source = [currentRemote currentSource];
554 NSString *title = [currentRemote currentSongTitle];
555 NSString *album = nil;
556 NSString *artist = nil;
557 NSString *time = nil;
558 NSString *track = nil;
561 ITDebugLog(@"Showing track info status window.");
565 if ( [df boolForKey:@"showAlbum"] ) {
566 album = [currentRemote currentSongAlbum];
569 if ( [df boolForKey:@"showArtist"] ) {
570 artist = [currentRemote currentSongArtist];
573 if ( [df boolForKey:@"showTime"] ) {
574 time = [NSString stringWithFormat:@"%@: %@ / %@",
576 [currentRemote currentSongElapsed],
577 [currentRemote currentSongLength]];
580 if ( [df boolForKey:@"showTrackNumber"] ) {
581 int trackNo = [currentRemote currentSongTrack];
582 int trackCount = [currentRemote currentAlbumTrackCount];
584 if ( (trackNo > 0) || (trackCount > 0) ) {
585 track = [NSString stringWithFormat:@"%@: %i %@ %i",
586 @"Track", trackNo, @"of", trackCount];
590 if ( [df boolForKey:@"showTrackRating"] ) {
591 float currentRating = [currentRemote currentSongRating];
592 if (currentRating >= 0.0) {
593 rating = ( currentRating * 5 );
598 title = NSLocalizedString(@"noSongPlaying", @"No song is playing.");
601 [statusWindowController showSongInfoWindowWithSource:source
610 - (void)showUpcomingSongs
612 int curPlaylist = [currentRemote currentPlaylistIndex];
613 int numSongs = [currentRemote numberOfSongsInPlaylistAtIndex:curPlaylist];
614 ITDebugLog(@"Showing upcoming songs status window.");
616 NSMutableArray *songList = [NSMutableArray arrayWithCapacity:5];
617 int numSongsInAdvance = [df integerForKey:@"SongsInAdvance"];
618 int curTrack = [currentRemote currentSongIndex];
621 for (i = curTrack + 1; i <= curTrack + numSongsInAdvance; i++) {
623 [songList addObject:[currentRemote songTitleAtIndex:i]];
627 [statusWindowController showUpcomingSongsWindowWithTitles:songList];
630 [statusWindowController showUpcomingSongsWindowWithTitles:[NSArray arrayWithObject:NSLocalizedString(@"noUpcomingSongs", @"No upcoming songs.")]];
634 - (void)incrementVolume
636 float volume = [currentRemote volume];
637 float dispVol = volume;
638 ITDebugLog(@"Incrementing volume.");
647 ITDebugLog(@"Setting volume to %f", volume);
648 [currentRemote setVolume:volume];
650 // Show volume status window
651 [statusWindowController showVolumeWindowWithLevel:dispVol];
654 - (void)decrementVolume
656 float volume = [currentRemote volume];
657 float dispVol = volume;
658 ITDebugLog(@"Decrementing volume.");
667 ITDebugLog(@"Setting volume to %f", volume);
668 [currentRemote setVolume:volume];
670 //Show volume status window
671 [statusWindowController showVolumeWindowWithLevel:dispVol];
674 - (void)incrementRating
676 float rating = [currentRemote currentSongRating];
677 ITDebugLog(@"Incrementing rating.");
679 if ([currentRemote currentPlaylistIndex] == 0) {
680 ITDebugLog(@"No song playing, rating change aborted.");
688 ITDebugLog(@"Setting rating to %f", rating);
689 [currentRemote setCurrentSongRating:rating];
691 //Show rating status window
692 [statusWindowController showRatingWindowWithRating:rating];
695 - (void)decrementRating
697 float rating = [currentRemote currentSongRating];
698 ITDebugLog(@"Decrementing rating.");
700 if ([currentRemote currentPlaylistIndex] == 0) {
701 ITDebugLog(@"No song playing, rating change aborted.");
709 ITDebugLog(@"Setting rating to %f", rating);
710 [currentRemote setCurrentSongRating:rating];
712 //Show rating status window
713 [statusWindowController showRatingWindowWithRating:rating];
718 ITMTRemotePlayerRepeatMode repeatMode = [currentRemote repeatMode];
719 ITDebugLog(@"Toggling repeat mode.");
720 switch (repeatMode) {
721 case ITMTRemotePlayerRepeatOff:
722 repeatMode = ITMTRemotePlayerRepeatAll;
724 case ITMTRemotePlayerRepeatAll:
725 repeatMode = ITMTRemotePlayerRepeatOne;
727 case ITMTRemotePlayerRepeatOne:
728 repeatMode = ITMTRemotePlayerRepeatOff;
731 ITDebugLog(@"Setting repeat mode to %i", repeatMode);
732 [currentRemote setRepeatMode:repeatMode];
734 //Show loop status window
735 [statusWindowController showRepeatWindowWithMode:repeatMode];
738 - (void)toggleShuffle
740 BOOL newShuffleEnabled = ( ! [currentRemote shuffleEnabled] );
741 ITDebugLog(@"Toggling shuffle mode.");
742 [currentRemote setShuffleEnabled:newShuffleEnabled];
743 //Show shuffle status window
744 ITDebugLog(@"Setting shuffle mode to %i", newShuffleEnabled);
745 [statusWindowController showShuffleWindow:newShuffleEnabled];
748 - (void)registerNowOK
750 [[StatusWindow sharedWindow] setLocked:NO];
751 [[StatusWindow sharedWindow] vanish:self];
752 [[StatusWindow sharedWindow] setIgnoresMouseEvents:YES];
757 - (void)registerNowCancel
759 [[StatusWindow sharedWindow] setLocked:NO];
760 [[StatusWindow sharedWindow] vanish:self];
761 [[StatusWindow sharedWindow] setIgnoresMouseEvents:YES];
763 [NSApp terminate:self];
767 /*************************************************************************/
769 #pragma mark WORKSPACE NOTIFICATION HANDLERS
770 /*************************************************************************/
772 - (void)applicationLaunched:(NSNotification *)note
774 if (!note || [[[note userInfo] objectForKey:@"NSApplicationName"] isEqualToString:[currentRemote playerFullName]]) {
775 ITDebugLog(@"Remote application launched.");
776 playerRunningState = ITMTRemotePlayerRunning;
777 [currentRemote begin];
778 [self setLatestSongIdentifier:@""];
780 refreshTimer = [[NSTimer scheduledTimerWithTimeInterval:0.5
782 selector:@selector(timerUpdate)
784 repeats:YES] retain];
785 //[NSThread detachNewThreadSelector:@selector(startTimerInNewThread) toTarget:self withObject:nil];
790 - (void)applicationTerminated:(NSNotification *)note
792 if (!note || [[[note userInfo] objectForKey:@"NSApplicationName"] isEqualToString:[currentRemote playerFullName]]) {
793 ITDebugLog(@"Remote application terminated.");
794 [currentRemote halt];
795 [refreshTimer invalidate];
796 [refreshTimer release];
799 playerRunningState = ITMTRemotePlayerNotRunning;
804 /*************************************************************************/
806 #pragma mark NSApplication DELEGATE METHODS
807 /*************************************************************************/
809 - (void)applicationWillTerminate:(NSNotification *)note
812 [[NSStatusBar systemStatusBar] removeStatusItem:statusItem];
816 /*************************************************************************/
818 #pragma mark DEALLOCATION METHOD
819 /*************************************************************************/
823 [self applicationTerminated:nil];
825 [statusItem release];
826 [statusWindowController release];
827 [menuController release];