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];
48 - (void)applicationDidFinishLaunching:(NSNotification *)note
50 //Turn on debug mode if needed
51 if ([df boolForKey:@"ITDebugMode"]) {
55 currentRemote = [self loadRemote];
56 [currentRemote begin];
58 //Setup for notification of the remote player launching or quitting
59 [[[NSWorkspace sharedWorkspace] notificationCenter]
61 selector:@selector(applicationTerminated:)
62 name:NSWorkspaceDidTerminateApplicationNotification
65 [[[NSWorkspace sharedWorkspace] notificationCenter]
67 selector:@selector(applicationLaunched:)
68 name:NSWorkspaceDidLaunchApplicationNotification
71 if ( ! [df objectForKey:@"menu"] ) { // If this is nil, defaults have never been registered.
72 [[PreferencesController sharedPrefs] registerDefaults];
75 [StatusItemHack install];
76 statusItem = [[ITStatusItem alloc]
77 initWithStatusBar:[NSStatusBar systemStatusBar]
78 withLength:NSSquareStatusItemLength];
80 bling = [[MTBlingController alloc] init];
82 registerTimer = [[NSTimer scheduledTimerWithTimeInterval:10.0
84 selector:@selector(blingTime)
88 if ([currentRemote playerRunningState] == ITMTRemotePlayerRunning) {
89 [self applicationLaunched:nil];
91 if ([df boolForKey:@"LaunchPlayerWithMT"])
94 [self applicationTerminated:nil];
97 [statusItem setImage:[NSImage imageNamed:@"MenuNormal"]];
98 [statusItem setAlternateImage:[NSImage imageNamed:@"MenuInverted"]];
103 - (ITMTRemote *)loadRemote
105 NSString *folderPath = [[NSBundle mainBundle] builtInPlugInsPath];
106 ITDebugLog(@"Gathering remotes.");
108 NSArray *bundlePathList = [NSBundle pathsForResourcesOfType:@"remote" inDirectory:folderPath];
109 NSEnumerator *enumerator = [bundlePathList objectEnumerator];
110 NSString *bundlePath;
112 while ( (bundlePath = [enumerator nextObject]) ) {
113 NSBundle* remoteBundle = [NSBundle bundleWithPath:bundlePath];
116 Class remoteClass = [remoteBundle principalClass];
118 if ([remoteClass conformsToProtocol:@protocol(ITMTRemote)] &&
119 [remoteClass isKindOfClass:[NSObject class]]) {
120 id remote = [remoteClass remote];
121 ITDebugLog(@"Adding remote at path %@", bundlePath);
122 [remoteArray addObject:remote];
127 // if ( [remoteArray count] > 0 ) { // UNCOMMENT WHEN WE HAVE > 1 PLUGIN
128 // if ( [remoteArray count] > 1 ) {
129 // [remoteArray sortUsingSelector:@selector(sortAlpha:)];
131 // [self loadModuleAccessUI]; //Comment out this line to disable remote visibility
134 // NSLog(@"%@", [remoteArray objectAtIndex:0]); //DEBUG
135 return [remoteArray objectAtIndex:0];
138 /*************************************************************************/
140 #pragma mark INSTANCE METHODS
141 /*************************************************************************/
143 /*- (void)startTimerInNewThread
145 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
146 NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
147 refreshTimer = [[NSTimer scheduledTimerWithTimeInterval:0.5
149 selector:@selector(timerUpdate)
151 repeats:YES] retain];
153 ITDebugLog(@"Timer started.");
157 - (void)setBlingTime:(NSDate*)date
159 NSMutableDictionary *globalPrefs;
161 globalPrefs = [[df persistentDomainForName:@".GlobalPreferences"] mutableCopy];
163 [globalPrefs setObject:date forKey:@"ITMTTrialStart"];
165 [globalPrefs removeObjectForKey:@"ITMTTrialStart"];
167 [df setPersistentDomain:globalPrefs forName:@".GlobalPreferences"];
169 [globalPrefs release];
172 - (NSDate*)getBlingTime
175 return [[df persistentDomainForName:@".GlobalPreferences"] objectForKey:@"ITMTTrialStart"];
180 NSDate *now = [NSDate date];
181 if (![self blingBling]) {
182 if ( (! [self getBlingTime] ) || ([now timeIntervalSinceDate:[self getBlingTime]] < 0) ) {
183 [self setBlingTime:now];
185 if ( ([now timeIntervalSinceDate:[self getBlingTime]] >= 604800) ) {
186 [statusItem setEnabled:NO];
188 if ([refreshTimer isValid]) {
189 [refreshTimer invalidate];
191 if ([registerTimer isValid]) {
192 [registerTimer invalidate];
194 [statusWindowController showRegistrationQueryWindow];
197 [self setBlingTime:nil];
208 if ( ! ([bling checkDone] == 2475) ) {
215 - (BOOL)songIsPlaying
217 return ( ! ([[currentRemote playerStateUniqueIdentifier] isEqualToString:@"0-0"]) );
220 - (BOOL)radioIsPlaying
222 return ( [currentRemote currentPlaylistClass] == ITMTRemotePlayerRadioPlaylist );
227 return ( ! [[currentRemote playerStateUniqueIdentifier] isEqualToString:_latestSongIdentifier] );
230 - (NSString *)latestSongIdentifier
232 return _latestSongIdentifier;
235 - (void)setLatestSongIdentifier:(NSString *)newIdentifier
237 ITDebugLog(@"Setting latest song identifier to %@", newIdentifier);
238 [_latestSongIdentifier autorelease];
239 _latestSongIdentifier = [newIdentifier copy];
244 if ( [self songChanged] && (timerUpdating != YES) ) {
245 ITDebugLog(@"The song changed.");
247 latestPlaylistClass = [currentRemote currentPlaylistClass];
248 [menuController rebuildSubmenus];
250 if ( [df boolForKey:@"showSongInfoOnChange"] ) {
251 [self performSelector:@selector(showCurrentTrackInfo) withObject:nil afterDelay:0.0];
254 [self setLatestSongIdentifier:[currentRemote playerStateUniqueIdentifier]];
262 ITDebugLog(@"Menu clicked.");
263 if ([currentRemote playerRunningState] == ITMTRemotePlayerRunning) {
264 [statusItem setMenu:[menuController menu]];
266 [statusItem setMenu:[menuController menuForNoPlayer]];
278 ITMTRemotePlayerPlayingState state = [currentRemote playerPlayingState];
279 ITDebugLog(@"Play/Pause toggled");
280 if (state == ITMTRemotePlayerPlaying) {
281 [currentRemote pause];
282 } else if ((state == ITMTRemotePlayerForwarding) || (state == ITMTRemotePlayerRewinding)) {
283 [currentRemote pause];
284 [currentRemote play];
286 [currentRemote play];
293 ITDebugLog(@"Going to next song.");
294 [currentRemote goToNextSong];
300 ITDebugLog(@"Going to previous song.");
301 [currentRemote goToPreviousSong];
307 ITDebugLog(@"Fast forwarding.");
308 [currentRemote forward];
314 ITDebugLog(@"Rewinding.");
315 [currentRemote rewind];
319 - (void)selectPlaylistAtIndex:(int)index
321 ITDebugLog(@"Selecting playlist %i", index);
322 [currentRemote switchToPlaylistAtIndex:index];
326 - (void)selectSongAtIndex:(int)index
328 ITDebugLog(@"Selecting song %i", index);
329 [currentRemote switchToSongAtIndex:index];
333 - (void)selectSongRating:(int)rating
335 ITDebugLog(@"Selecting song rating %i", rating);
336 [currentRemote setCurrentSongRating:(float)rating / 100.0];
340 - (void)selectEQPresetAtIndex:(int)index
342 ITDebugLog(@"Selecting EQ preset %i", index);
343 [currentRemote switchToEQAtIndex:index];
349 ITDebugLog(@"Beginning show player.");
350 if ( ( playerRunningState == ITMTRemotePlayerRunning) ) {
351 ITDebugLog(@"Showing player interface.");
352 [currentRemote showPrimaryInterface];
354 ITDebugLog(@"Launching player.");
355 if (![[NSWorkspace sharedWorkspace] launchApplication:[currentRemote playerFullName]]) {
356 ITDebugLog(@"Error Launching Player");
359 ITDebugLog(@"Finished show player.");
362 - (void)showPreferences
364 ITDebugLog(@"Show preferences.");
365 [[PreferencesController sharedPrefs] setController:self];
366 [[PreferencesController sharedPrefs] showPrefsWindow:self];
369 - (void)quitMenuTunes
371 ITDebugLog(@"Quitting MenuTunes.");
372 [NSApp terminate:self];
378 - (void)closePreferences
380 ITDebugLog(@"Preferences closed.");
381 if ( ( playerRunningState == ITMTRemotePlayerRunning) ) {
386 - (ITMTRemote *)currentRemote
388 return currentRemote;
399 NSEnumerator *hotKeyEnumerator = [[[ITHotKeyCenter sharedCenter] allHotKeys] objectEnumerator];
400 ITHotKey *nextHotKey;
401 ITDebugLog(@"Clearing hot keys.");
402 while ( (nextHotKey = [hotKeyEnumerator nextObject]) ) {
403 [[ITHotKeyCenter sharedCenter] unregisterHotKey:nextHotKey];
405 ITDebugLog(@"Done clearing hot keys.");
411 ITDebugLog(@"Setting up hot keys.");
413 if (playerRunningState == ITMTRemotePlayerNotRunning) {
417 if ([df objectForKey:@"PlayPause"] != nil) {
418 ITDebugLog(@"Setting up play pause hot key.");
419 hotKey = [[ITHotKey alloc] init];
420 [hotKey setName:@"PlayPause"];
421 [hotKey setKeyCombo:[ITKeyCombo keyComboWithPlistRepresentation:[df objectForKey:@"PlayPause"]]];
422 [hotKey setTarget:self];
423 [hotKey setAction:@selector(playPause)];
424 [[ITHotKeyCenter sharedCenter] registerHotKey:[hotKey autorelease]];
427 if ([df objectForKey:@"NextTrack"] != nil) {
428 ITDebugLog(@"Setting up next track hot key.");
429 hotKey = [[ITHotKey alloc] init];
430 [hotKey setName:@"NextTrack"];
431 [hotKey setKeyCombo:[ITKeyCombo keyComboWithPlistRepresentation:[df objectForKey:@"NextTrack"]]];
432 [hotKey setTarget:self];
433 [hotKey setAction:@selector(nextSong)];
434 [[ITHotKeyCenter sharedCenter] registerHotKey:[hotKey autorelease]];
437 if ([df objectForKey:@"PrevTrack"] != nil) {
438 ITDebugLog(@"Setting up previous track hot key.");
439 hotKey = [[ITHotKey alloc] init];
440 [hotKey setName:@"PrevTrack"];
441 [hotKey setKeyCombo:[ITKeyCombo keyComboWithPlistRepresentation:[df objectForKey:@"PrevTrack"]]];
442 [hotKey setTarget:self];
443 [hotKey setAction:@selector(prevSong)];
444 [[ITHotKeyCenter sharedCenter] registerHotKey:[hotKey autorelease]];
447 if ([df objectForKey:@"ShowPlayer"] != nil) {
448 ITDebugLog(@"Setting up show player hot key.");
449 hotKey = [[ITHotKey alloc] init];
450 [hotKey setName:@"ShowPlayer"];
451 [hotKey setKeyCombo:[ITKeyCombo keyComboWithPlistRepresentation:[df objectForKey:@"ShowPlayer"]]];
452 [hotKey setTarget:self];
453 [hotKey setAction:@selector(showPlayer)];
454 [[ITHotKeyCenter sharedCenter] registerHotKey:[hotKey autorelease]];
457 if ([df objectForKey:@"TrackInfo"] != nil) {
458 ITDebugLog(@"Setting up track info hot key.");
459 hotKey = [[ITHotKey alloc] init];
460 [hotKey setName:@"TrackInfo"];
461 [hotKey setKeyCombo:[ITKeyCombo keyComboWithPlistRepresentation:[df objectForKey:@"TrackInfo"]]];
462 [hotKey setTarget:self];
463 [hotKey setAction:@selector(showCurrentTrackInfo)];
464 [[ITHotKeyCenter sharedCenter] registerHotKey:[hotKey autorelease]];
467 if ([df objectForKey:@"UpcomingSongs"] != nil) {
468 ITDebugLog(@"Setting up upcoming songs hot key.");
469 hotKey = [[ITHotKey alloc] init];
470 [hotKey setName:@"UpcomingSongs"];
471 [hotKey setKeyCombo:[ITKeyCombo keyComboWithPlistRepresentation:[df objectForKey:@"UpcomingSongs"]]];
472 [hotKey setTarget:self];
473 [hotKey setAction:@selector(showUpcomingSongs)];
474 [[ITHotKeyCenter sharedCenter] registerHotKey:[hotKey autorelease]];
477 if ([df objectForKey:@"ToggleLoop"] != nil) {
478 ITDebugLog(@"Setting up toggle loop hot key.");
479 hotKey = [[ITHotKey alloc] init];
480 [hotKey setName:@"ToggleLoop"];
481 [hotKey setKeyCombo:[ITKeyCombo keyComboWithPlistRepresentation:[df objectForKey:@"ToggleLoop"]]];
482 [hotKey setTarget:self];
483 [hotKey setAction:@selector(toggleLoop)];
484 [[ITHotKeyCenter sharedCenter] registerHotKey:[hotKey autorelease]];
487 if ([df objectForKey:@"ToggleShuffle"] != nil) {
488 ITDebugLog(@"Setting up toggle shuffle hot key.");
489 hotKey = [[ITHotKey alloc] init];
490 [hotKey setName:@"ToggleShuffle"];
491 [hotKey setKeyCombo:[ITKeyCombo keyComboWithPlistRepresentation:[df objectForKey:@"ToggleShuffle"]]];
492 [hotKey setTarget:self];
493 [hotKey setAction:@selector(toggleShuffle)];
494 [[ITHotKeyCenter sharedCenter] registerHotKey:[hotKey autorelease]];
497 if ([df objectForKey:@"IncrementVolume"] != nil) {
498 ITDebugLog(@"Setting up increment volume hot key.");
499 hotKey = [[ITHotKey alloc] init];
500 [hotKey setName:@"IncrementVolume"];
501 [hotKey setKeyCombo:[ITKeyCombo keyComboWithPlistRepresentation:[df objectForKey:@"IncrementVolume"]]];
502 [hotKey setTarget:self];
503 [hotKey setAction:@selector(incrementVolume)];
504 [[ITHotKeyCenter sharedCenter] registerHotKey:[hotKey autorelease]];
507 if ([df objectForKey:@"DecrementVolume"] != nil) {
508 ITDebugLog(@"Setting up decrement volume hot key.");
509 hotKey = [[ITHotKey alloc] init];
510 [hotKey setName:@"DecrementVolume"];
511 [hotKey setKeyCombo:[ITKeyCombo keyComboWithPlistRepresentation:[df objectForKey:@"DecrementVolume"]]];
512 [hotKey setTarget:self];
513 [hotKey setAction:@selector(decrementVolume)];
514 [[ITHotKeyCenter sharedCenter] registerHotKey:[hotKey autorelease]];
517 if ([df objectForKey:@"IncrementRating"] != nil) {
518 ITDebugLog(@"Setting up increment rating hot key.");
519 hotKey = [[ITHotKey alloc] init];
520 [hotKey setName:@"IncrementRating"];
521 [hotKey setKeyCombo:[ITKeyCombo keyComboWithPlistRepresentation:[df objectForKey:@"IncrementRating"]]];
522 [hotKey setTarget:self];
523 [hotKey setAction:@selector(incrementRating)];
524 [[ITHotKeyCenter sharedCenter] registerHotKey:[hotKey autorelease]];
527 if ([df objectForKey:@"DecrementRating"] != nil) {
528 ITDebugLog(@"Setting up decrement rating hot key.");
529 hotKey = [[ITHotKey alloc] init];
530 [hotKey setName:@"DecrementRating"];
531 [hotKey setKeyCombo:[ITKeyCombo keyComboWithPlistRepresentation:[df objectForKey:@"DecrementRating"]]];
532 [hotKey setTarget:self];
533 [hotKey setAction:@selector(decrementRating)];
534 [[ITHotKeyCenter sharedCenter] registerHotKey:[hotKey autorelease]];
536 ITDebugLog(@"Finished setting up hot keys.");
539 - (void)showCurrentTrackInfo
541 ITMTRemotePlayerSource source = [currentRemote currentSource];
542 NSString *title = [currentRemote currentSongTitle];
543 NSString *album = nil;
544 NSString *artist = nil;
545 NSString *time = nil;
546 NSString *track = nil;
549 ITDebugLog(@"Showing track info status window.");
553 if ( [df boolForKey:@"showAlbum"] ) {
554 album = [currentRemote currentSongAlbum];
557 if ( [df boolForKey:@"showArtist"] ) {
558 artist = [currentRemote currentSongArtist];
561 if ( [df boolForKey:@"showTime"] ) {
562 time = [NSString stringWithFormat:@"%@: %@ / %@",
564 [currentRemote currentSongElapsed],
565 [currentRemote currentSongLength]];
568 if ( [df boolForKey:@"showTrackNumber"] ) {
569 int trackNo = [currentRemote currentSongTrack];
570 int trackCount = [currentRemote currentAlbumTrackCount];
572 if ( (trackNo > 0) || (trackCount > 0) ) {
573 track = [NSString stringWithFormat:@"%@: %i %@ %i",
574 @"Track", trackNo, @"of", trackCount];
578 if ( [df boolForKey:@"showTrackRating"] ) {
579 float currentRating = [currentRemote currentSongRating];
580 if (currentRating >= 0.0) {
581 rating = ( currentRating * 5 );
586 title = NSLocalizedString(@"noSongPlaying", @"No song is playing.");
589 [statusWindowController showSongInfoWindowWithSource:source
598 - (void)showUpcomingSongs
600 int curPlaylist = [currentRemote currentPlaylistIndex];
601 int numSongs = [currentRemote numberOfSongsInPlaylistAtIndex:curPlaylist];
602 ITDebugLog(@"Showing upcoming songs status window.");
604 NSMutableArray *songList = [NSMutableArray arrayWithCapacity:5];
605 int numSongsInAdvance = [df integerForKey:@"SongsInAdvance"];
606 int curTrack = [currentRemote currentSongIndex];
609 for (i = curTrack + 1; i <= curTrack + numSongsInAdvance; i++) {
611 [songList addObject:[currentRemote songTitleAtIndex:i]];
615 [statusWindowController showUpcomingSongsWindowWithTitles:songList];
618 [statusWindowController showUpcomingSongsWindowWithTitles:[NSArray arrayWithObject:NSLocalizedString(@"noUpcomingSongs", @"No upcoming songs.")]];
622 - (void)incrementVolume
624 float volume = [currentRemote volume];
625 float dispVol = volume;
626 ITDebugLog(@"Incrementing volume.");
635 ITDebugLog(@"Setting volume to %f", volume);
636 [currentRemote setVolume:volume];
638 // Show volume status window
639 [statusWindowController showVolumeWindowWithLevel:dispVol];
642 - (void)decrementVolume
644 float volume = [currentRemote volume];
645 float dispVol = volume;
646 ITDebugLog(@"Decrementing volume.");
655 ITDebugLog(@"Setting volume to %f", volume);
656 [currentRemote setVolume:volume];
658 //Show volume status window
659 [statusWindowController showVolumeWindowWithLevel:dispVol];
662 - (void)incrementRating
664 float rating = [currentRemote currentSongRating];
665 ITDebugLog(@"Incrementing rating.");
667 if ([currentRemote currentPlaylistIndex] == 0) {
668 ITDebugLog(@"No song playing, rating change aborted.");
676 ITDebugLog(@"Setting rating to %f", rating);
677 [currentRemote setCurrentSongRating:rating];
679 //Show rating status window
680 [statusWindowController showRatingWindowWithRating:rating];
683 - (void)decrementRating
685 float rating = [currentRemote currentSongRating];
686 ITDebugLog(@"Decrementing rating.");
688 if ([currentRemote currentPlaylistIndex] == 0) {
689 ITDebugLog(@"No song playing, rating change aborted.");
697 ITDebugLog(@"Setting rating to %f", rating);
698 [currentRemote setCurrentSongRating:rating];
700 //Show rating status window
701 [statusWindowController showRatingWindowWithRating:rating];
706 ITMTRemotePlayerRepeatMode repeatMode = [currentRemote repeatMode];
707 ITDebugLog(@"Toggling repeat mode.");
708 switch (repeatMode) {
709 case ITMTRemotePlayerRepeatOff:
710 repeatMode = ITMTRemotePlayerRepeatAll;
712 case ITMTRemotePlayerRepeatAll:
713 repeatMode = ITMTRemotePlayerRepeatOne;
715 case ITMTRemotePlayerRepeatOne:
716 repeatMode = ITMTRemotePlayerRepeatOff;
719 ITDebugLog(@"Setting repeat mode to %i", repeatMode);
720 [currentRemote setRepeatMode:repeatMode];
722 //Show loop status window
723 [statusWindowController showRepeatWindowWithMode:repeatMode];
726 - (void)toggleShuffle
728 BOOL newShuffleEnabled = ( ! [currentRemote shuffleEnabled] );
729 ITDebugLog(@"Toggling shuffle mode.");
730 [currentRemote setShuffleEnabled:newShuffleEnabled];
731 //Show shuffle status window
732 ITDebugLog(@"Setting shuffle mode to %i", newShuffleEnabled);
733 [statusWindowController showShuffleWindow:newShuffleEnabled];
736 - (void)registerNowOK
738 [[StatusWindow sharedWindow] setLocked:NO];
739 [[StatusWindow sharedWindow] vanish:self];
740 [[StatusWindow sharedWindow] setIgnoresMouseEvents:YES];
745 - (void)registerNowCancel
747 [[StatusWindow sharedWindow] setLocked:NO];
748 [[StatusWindow sharedWindow] vanish:self];
749 [[StatusWindow sharedWindow] setIgnoresMouseEvents:YES];
751 [NSApp terminate:self];
755 /*************************************************************************/
757 #pragma mark WORKSPACE NOTIFICATION HANDLERS
758 /*************************************************************************/
760 - (void)applicationLaunched:(NSNotification *)note
762 if (!note || [[[note userInfo] objectForKey:@"NSApplicationName"] isEqualToString:[currentRemote playerFullName]]) {
763 ITDebugLog(@"Remote application launched.");
764 [currentRemote begin];
765 [self setLatestSongIdentifier:@""];
767 refreshTimer = [[NSTimer scheduledTimerWithTimeInterval:0.5
769 selector:@selector(timerUpdate)
771 repeats:YES] retain];
772 //[NSThread detachNewThreadSelector:@selector(startTimerInNewThread) toTarget:self withObject:nil];
774 playerRunningState = ITMTRemotePlayerRunning;
778 - (void)applicationTerminated:(NSNotification *)note
780 if (!note || [[[note userInfo] objectForKey:@"NSApplicationName"] isEqualToString:[currentRemote playerFullName]]) {
781 ITDebugLog(@"Remote application terminated.");
782 [currentRemote halt];
783 [refreshTimer invalidate];
784 [refreshTimer release];
786 [registerTimer invalidate];
787 [registerTimer release];
790 playerRunningState = ITMTRemotePlayerNotRunning;
795 /*************************************************************************/
797 #pragma mark NSApplication DELEGATE METHODS
798 /*************************************************************************/
800 - (void)applicationWillTerminate:(NSNotification *)note
803 [[NSStatusBar systemStatusBar] removeStatusItem:statusItem];
807 /*************************************************************************/
809 #pragma mark DEALLOCATION METHOD
810 /*************************************************************************/
814 [self applicationTerminated:nil];
816 [statusItem release];
817 [statusWindowController release];
818 [menuController release];