3 ¥ Make preferences window pretty
5 ¥ Apple Events! Apple Events! Apple Events!
9 #import "PreferencesController.h"
10 #import "HotKeyCenter.h"
11 #import "StatusWindowController.h"
13 @interface MenuTunes(Private)
14 - (void)registerDefaultsIfNeeded;
16 - (void)rebuildUpcomingSongsMenu;
17 - (void)rebuildPlaylistMenu;
18 - (void)rebuildEQPresetsMenu;
20 - (NSString *)runScriptAndReturnResult:(NSString *)script;
22 - (void)sendAEWithEventClass:(AEEventClass)eventClass andEventID:(AEEventID)eventID;
26 @implementation MenuTunes
28 /*************************************************************************/
30 #pragma mark INITIALIZATION METHODS
31 /*************************************************************************/
33 - (void)applicationDidFinishLaunching:(NSNotification *)note
35 asComponent = OpenDefaultComponent(kOSAComponentType, kAppleScriptSubtype);
37 [self registerDefaultsIfNeeded];
39 menu = [[NSMenu alloc] initWithTitle:@""];
40 iTunesPSN = [self iTunesPSN]; //Get PSN of iTunes if it's running
42 if (!((iTunesPSN.highLongOfPSN == kNoProcess) && (iTunesPSN.lowLongOfPSN == 0)))
45 refreshTimer = [NSTimer scheduledTimerWithTimeInterval:3.5
47 selector:@selector(timerUpdate)
53 menu = [[NSMenu alloc] initWithTitle:@""];
54 [[menu addItemWithTitle:@"Open iTunes" action:@selector(openiTunes:) keyEquivalent:@""] setTarget:self];
55 [[menu addItemWithTitle:@"Preferences" action:@selector(showPreferences:) keyEquivalent:@""] setTarget:self];
56 [[menu addItemWithTitle:@"Quit" action:@selector(quitMenuTunes:) keyEquivalent:@""] setTarget:self];
57 [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self selector:@selector(iTunesLaunched:) name:NSWorkspaceDidLaunchApplicationNotification object:nil];
61 statusItem = [[NSStatusBar systemStatusBar] statusItemWithLength:NSSquareStatusItemLength];
62 [[[statusItem _button] cell] setType:NSNullCellType];
63 [statusItem setImage:[NSImage imageNamed:@"menu"]];
64 [[statusItem _button] setAlternateImage:[NSImage imageNamed:@"selected_image"]];
65 [statusItem setHighlightMode:YES];
66 [statusItem setMenu:menu];
67 // Below line of code is for creating builds for Beta Testers
68 // [statusItem setToolTip:@"This Nontransferable Beta (Built on __DATE__) of iThink Software's MenuTunes is Registered to: Beta Tester (betatester@somedomain.com)."];
73 /*************************************************************************/
75 #pragma mark INSTANCE METHODS
76 /*************************************************************************/
78 - (void)registerDefaultsIfNeeded
80 if (![[NSUserDefaults standardUserDefaults] objectForKey:@"menu"]) {
81 [[NSUserDefaults standardUserDefaults] setObject:
82 [NSArray arrayWithObjects:
95 @"Current Track Info",
100 //Recreate the status item menu
103 NSArray *myMenu = [[NSUserDefaults standardUserDefaults] arrayForKey:@"menu"];
107 didHaveAlbumName = ([[self runScriptAndReturnResult:@"return album of current track"] length] > 0);
109 while ([menu numberOfItems] > 0) {
110 [menu removeItemAtIndex:0];
113 playPauseMenuItem = nil;
114 upcomingSongsItem = nil;
116 [playlistMenu release];
122 for (i = 0; i < [myMenu count]; i++) {
123 NSString *item = [myMenu objectAtIndex:i];
124 if ([item isEqualToString:@"Play/Pause"]) {
125 playPauseMenuItem = [menu addItemWithTitle:@"Play"
126 action:@selector(playPause:)
128 [playPauseMenuItem setTarget:self];
129 } else if ([item isEqualToString:@"Next Track"]) {
130 [[menu addItemWithTitle:@"Next Track"
131 action:@selector(nextSong:)
132 keyEquivalent:@""] setTarget:self];
133 } else if ([item isEqualToString:@"Previous Track"]) {
134 [[menu addItemWithTitle:@"Previous Track"
135 action:@selector(prevSong:)
136 keyEquivalent:@""] setTarget:self];
137 } else if ([item isEqualToString:@"Fast Forward"]) {
138 [[menu addItemWithTitle:@"Fast Forward"
139 action:@selector(fastForward:)
140 keyEquivalent:@""] setTarget:self];
141 } else if ([item isEqualToString:@"Rewind"]) {
142 [[menu addItemWithTitle:@"Rewind"
143 action:@selector(rewind:)
144 keyEquivalent:@""] setTarget:self];
145 } else if ([item isEqualToString:@"Upcoming Songs"]) {
146 upcomingSongsItem = [menu addItemWithTitle:@"Upcoming Songs"
149 } else if ([item isEqualToString:@"Playlists"]) {
150 playlistItem = [menu addItemWithTitle:@"Playlists"
153 } else if ([item isEqualToString:@"EQ Presets"]) {
154 eqItem = [menu addItemWithTitle:@"EQ Presets"
157 } else if ([item isEqualToString:@"PreferencesÉ"]) {
158 [[menu addItemWithTitle:@"PreferencesÉ"
159 action:@selector(showPreferences:)
160 keyEquivalent:@""] setTarget:self];
161 } else if ([item isEqualToString:@"Quit"]) {
162 [[menu addItemWithTitle:@"Quit"
163 action:@selector(quitMenuTunes:)
164 keyEquivalent:@""] setTarget:self];
165 } else if ([item isEqualToString:@"Current Track Info"]) {
166 trackInfoIndex = [menu numberOfItems];
167 [menu addItemWithTitle:@"No Song"
170 } else if ([item isEqualToString:@"<separator>"]) {
171 [menu addItem:[NSMenuItem separatorItem]];
175 curTrackIndex = -1; //Force update of everything
176 [self timerUpdate]; //Updates dynamic info in the menu
182 //Updates the menu with current player state, song, and upcoming songs
185 NSString *curAlbumName = [self runScriptAndReturnResult:@"return album of current track"];
186 NSMenuItem *menuItem;
187 if ((iTunesPSN.highLongOfPSN == kNoProcess) && (iTunesPSN.lowLongOfPSN == 0)) {
191 if (upcomingSongsItem) {
192 [self rebuildUpcomingSongsMenu];
195 [self rebuildPlaylistMenu];
198 [self rebuildEQPresetsMenu];
201 if (trackInfoIndex > -1)
203 NSString *curSongName;
204 curSongName = [self runScriptAndReturnResult:@"return name of current track"];
205 if ([curSongName length] > 0) {
206 int index = [menu indexOfItemWithTitle:@"Now Playing"];
209 [menu removeItemAtIndex:index + 1];
210 if (didHaveAlbumName) {
211 [menu removeItemAtIndex:index + 1];
214 if (!isPlayingRadio) {
215 if ([curAlbumName length] > 0) {
216 menuItem = [[NSMenuItem alloc] initWithTitle:[NSString stringWithFormat:@" %@", curAlbumName]
219 [menu insertItem:menuItem atIndex:trackInfoIndex + 1];
223 menuItem = [[NSMenuItem alloc] initWithTitle:[NSString stringWithFormat:@" %@", curSongName]
226 [menu insertItem:menuItem atIndex:trackInfoIndex + 1];
230 menuItem = [[NSMenuItem alloc] initWithTitle:@"Now Playing" action:nil keyEquivalent:@""];
231 [menu removeItemAtIndex:[menu indexOfItemWithTitle:@"No Song"]];
232 [menu insertItem:menuItem atIndex:trackInfoIndex];
236 } else if ([menu indexOfItemWithTitle:@"No Song"] == -1) {
237 [menu removeItemAtIndex:trackInfoIndex];
238 [menu removeItemAtIndex:trackInfoIndex];
240 if (didHaveAlbumName) {
241 [menu removeItemAtIndex:trackInfoIndex];
244 menuItem = [[NSMenuItem alloc] initWithTitle:@"No Song" action:nil keyEquivalent:@""];
245 [menu insertItem:menuItem atIndex:trackInfoIndex];
249 didHaveAlbumName = (([curAlbumName length] > 0) ? YES : NO);
252 //Rebuild the upcoming songs submenu. Can be improved a lot.
253 - (void)rebuildUpcomingSongsMenu
255 int numSongs = [[self runScriptAndReturnResult:@"return number of tracks in current playlist"] intValue];
256 int numSongsInAdvance = [[NSUserDefaults standardUserDefaults] integerForKey:@"SongsInAdvance"];
257 if (!isPlayingRadio) {
259 int curTrack = [[self runScriptAndReturnResult:@"return index of current track"] intValue];
262 [upcomingSongsMenu release];
263 upcomingSongsMenu = [[NSMenu alloc] initWithTitle:@""];
264 [upcomingSongsItem setSubmenu:upcomingSongsMenu];
265 [upcomingSongsItem setEnabled:YES];
267 for (i = curTrack + 1; i <= curTrack + numSongsInAdvance; i++) {
269 NSString *curSong = [self runScriptAndReturnResult:[NSString stringWithFormat:@"return name of track %i of current playlist", i]];
270 NSMenuItem *songItem;
271 songItem = [[NSMenuItem alloc] initWithTitle:curSong action:@selector(playTrack:) keyEquivalent:@""];
272 [songItem setTarget:self];
273 [songItem setRepresentedObject:[NSNumber numberWithInt:i]];
274 [upcomingSongsMenu addItem:songItem];
282 [upcomingSongsItem setSubmenu:nil];
283 [upcomingSongsItem setEnabled:NO];
287 - (void)rebuildPlaylistMenu
289 int numPlaylists = [[self runScriptAndReturnResult:@"return number of playlists"] intValue];
290 int i, curPlaylist = [[self runScriptAndReturnResult:@"return index of current playlist"] intValue];
297 if (playlistMenu && (numPlaylists == [playlistMenu numberOfItems]))
300 [playlistMenu release];
301 playlistMenu = [[NSMenu alloc] initWithTitle:@""];
303 for (i = 1; i <= numPlaylists; i++) {
304 NSString *playlistName = [self runScriptAndReturnResult:[NSString stringWithFormat:@"return name of playlist %i", i]];
305 NSMenuItem *tempItem;
306 tempItem = [[NSMenuItem alloc] initWithTitle:playlistName action:@selector(selectPlaylist:) keyEquivalent:@""];
307 [tempItem setTarget:self];
308 [tempItem setRepresentedObject:[NSNumber numberWithInt:i]];
309 [playlistMenu addItem:tempItem];
312 [playlistItem setSubmenu:playlistMenu];
315 [[playlistMenu itemAtIndex:curPlaylist - 1] setState:NSOnState];
319 //Build a menu with the list of all available EQ presets
320 - (void)rebuildEQPresetsMenu
322 int numSets = [[self runScriptAndReturnResult:@"return number of EQ presets"] intValue];
325 if (eqMenu && (numSets == [eqMenu numberOfItems]))
329 eqMenu = [[NSMenu alloc] initWithTitle:@""];
331 for (i = 1; i <= numSets; i++) {
332 NSString *setName = [self runScriptAndReturnResult:[NSString stringWithFormat:@"return name of EQ preset %i", i]];
333 NSMenuItem *tempItem;
334 tempItem = [[NSMenuItem alloc] initWithTitle:setName action:@selector(selectEQPreset:) keyEquivalent:@""];
335 [tempItem setTarget:self];
336 [tempItem setRepresentedObject:[NSNumber numberWithInt:i]];
337 [eqMenu addItem:tempItem];
340 [eqItem setSubmenu:eqMenu];
342 [[eqMenu itemAtIndex:[[self runScriptAndReturnResult:@"return index of current EQ preset"] intValue] - 1] setState:NSOnState];
347 [[HotKeyCenter sharedCenter] removeHotKey:@"PlayPause"];
348 [[HotKeyCenter sharedCenter] removeHotKey:@"NextTrack"];
349 [[HotKeyCenter sharedCenter] removeHotKey:@"PrevTrack"];
350 [[HotKeyCenter sharedCenter] removeHotKey:@"TrackInfo"];
351 [[HotKeyCenter sharedCenter] removeHotKey:@"UpcomingSongs"];
356 NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
358 if ([defaults objectForKey:@"PlayPause"] != nil) {
359 [[HotKeyCenter sharedCenter] addHotKey:@"PlayPause"
360 combo:[defaults keyComboForKey:@"PlayPause"]
361 target:self action:@selector(playPause:)];
364 if ([defaults objectForKey:@"NextTrack"] != nil) {
365 [[HotKeyCenter sharedCenter] addHotKey:@"NextTrack"
366 combo:[defaults keyComboForKey:@"NextTrack"]
367 target:self action:@selector(nextSong:)];
370 if ([defaults objectForKey:@"PrevTrack"] != nil) {
371 [[HotKeyCenter sharedCenter] addHotKey:@"PrevTrack"
372 combo:[defaults keyComboForKey:@"PrevTrack"]
373 target:self action:@selector(prevSong:)];
376 if ([defaults objectForKey:@"TrackInfo"] != nil) {
377 [[HotKeyCenter sharedCenter] addHotKey:@"TrackInfo"
378 combo:[defaults keyComboForKey:@"TrackInfo"]
379 target:self action:@selector(showCurrentTrackInfo)];
382 if ([defaults objectForKey:@"UpcomingSongs"] != nil) {
383 [[HotKeyCenter sharedCenter] addHotKey:@"UpcomingSongs"
384 combo:[defaults keyComboForKey:@"UpcomingSongs"]
385 target:self action:@selector(showUpcomingSongs)];
389 //Runs an AppleScript and returns the result as an NSString after stripping quotes, if needed. It takes in script and automatically adds the tell iTunes and end tell statements.
390 - (NSString *)runScriptAndReturnResult:(NSString *)script
392 AEDesc scriptDesc, resultDesc;
397 script = [NSString stringWithFormat:@"tell application \"iTunes\"\n%@\nend tell", script];
399 AECreateDesc(typeChar, [script cString], [script cStringLength],
402 OSADoScript(asComponent, &scriptDesc, kOSANullScript, typeChar, kOSAModeCanInteract, &resultDesc);
404 length = AEGetDescDataSize(&resultDesc);
405 buffer = malloc(length);
407 AEGetDescData(&resultDesc, buffer, length);
408 AEDisposeDesc(&scriptDesc);
409 AEDisposeDesc(&resultDesc);
410 result = [NSString stringWithCString:buffer length:length];
411 if ( (! [result isEqualToString:@""]) &&
412 ([result characterAtIndex:0] == '\"') &&
413 ([result characterAtIndex:[result length] - 1] == '\"') ) {
414 result = [result substringWithRange:NSMakeRange(1, [result length] - 2)];
421 //Called when the timer fires.
425 if (GetProcessPID(&iTunesPSN, &pid) == noErr) {
426 int trackPlayingIndex = [[self runScriptAndReturnResult:@"return index of current track"] intValue];
428 if (trackPlayingIndex != curTrackIndex) {
429 bool wasPlayingRadio = isPlayingRadio;
430 isPlayingRadio = [[self runScriptAndReturnResult:@"return class of current playlist"] isEqualToString:@"radio tuner playlist"];
431 if (isPlayingRadio && !wasPlayingRadio) {
433 for (i = 0; i < [playlistMenu numberOfItems]; i++)
435 [[playlistMenu itemAtIndex:i] setState:NSOffState];
438 if (wasPlayingRadio) {
439 NSMenuItem *temp = [[NSMenuItem alloc] initWithTitle:@"" action:NULL keyEquivalent:@""];
440 [menu insertItem:temp atIndex:trackInfoIndex + 1];
444 curTrackIndex = trackPlayingIndex;
448 int playlist = [[self runScriptAndReturnResult:@"return index of current playlist"] intValue];
449 if (playlist != curPlaylistIndex) {
450 bool wasPlayingRadio = isPlayingRadio;
451 isPlayingRadio = [[self runScriptAndReturnResult:@"return class of current playlist"] isEqualToString:@"radio tuner playlist"];
452 if (isPlayingRadio && !wasPlayingRadio) {
454 for (i = 0; i < [playlistMenu numberOfItems]; i++)
456 [[playlistMenu itemAtIndex:i] setState:NSOffState];
459 if (wasPlayingRadio) {
460 NSMenuItem *temp = [[NSMenuItem alloc] initWithTitle:@"" action:NULL keyEquivalent:@""];
461 [menu insertItem:temp atIndex:trackInfoIndex + 1];
465 curTrackIndex = trackPlayingIndex;
466 curPlaylistIndex = playlist;
469 //Update Play/Pause menu item
470 if (playPauseMenuItem){
471 if ([[self runScriptAndReturnResult:@"return player state"] isEqualToString:@"playing"]) {
472 [playPauseMenuItem setTitle:@"Pause"];
474 [playPauseMenuItem setTitle:@"Play"];
479 menu = [[NSMenu alloc] initWithTitle:@""];
480 [[menu addItemWithTitle:@"Open iTunes" action:@selector(openiTunes:) keyEquivalent:@""] setTarget:self];
481 [[menu addItemWithTitle:@"Preferences" action:@selector(showPreferences:) keyEquivalent:@""] setTarget:self];
482 [[menu addItemWithTitle:@"Quit" action:@selector(quitMenuTunes:) keyEquivalent:@""] setTarget:self];
483 [statusItem setMenu:menu];
485 [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self selector:@selector(iTunesLaunched:) name:NSWorkspaceDidLaunchApplicationNotification object:nil];
486 [refreshTimer invalidate];
492 - (void)iTunesLaunched:(NSNotification *)note
494 NSDictionary *info = [note userInfo];
496 iTunesPSN.highLongOfPSN = [[info objectForKey:@"NSApplicationProcessSerialNumberHigh"] longValue];
497 iTunesPSN.lowLongOfPSN = [[info objectForKey:@"NSApplicationProcessSerialNumberLow"] longValue];
500 refreshTimer = [NSTimer scheduledTimerWithTimeInterval:3.5 target:self selector:@selector(timerUpdate) userInfo:nil repeats:YES];
502 [self rebuildMenu]; //Rebuild the menu since no songs will be playing
503 [statusItem setMenu:menu]; //Set the menu back to the main one
504 [[[NSWorkspace sharedWorkspace] notificationCenter] removeObserver:self];
507 //Return the PSN of iTunes, if it's running
508 - (ProcessSerialNumber)iTunesPSN
510 ProcessSerialNumber procNum;
511 procNum.highLongOfPSN = kNoProcess;
512 procNum.lowLongOfPSN = 0;
514 while ( (GetNextProcess(&procNum) == noErr) ) {
515 CFStringRef procName;
516 if ( (CopyProcessName(&procNum, &procName) == noErr) ) {
517 if ([(NSString *)procName isEqualToString:@"iTunes"]) {
520 [(NSString *)procName release];
526 //Send an AppleEvent with a given event ID
527 - (void)sendAEWithEventClass:(AEEventClass)eventClass
528 andEventID:(AEEventID)eventID
530 OSType iTunesType = 'hook';
531 AppleEvent event, reply;
533 AEBuildAppleEvent(eventClass, eventID, typeApplSignature, &iTunesType, sizeof(iTunesType), kAutoGenerateReturnID, kAnyTransactionID, &event, nil, "");
535 AESend(&event, &reply, kAENoReply, kAENormalPriority, kAEDefaultTimeout, nil, nil);
536 AEDisposeDesc(&event);
537 AEDisposeDesc(&reply);
541 // Selectors - called from status item menu
544 - (void)playTrack:(id)sender
546 [self runScriptAndReturnResult:[NSString stringWithFormat:@"play track %i of current playlist", [[sender representedObject] intValue]]];
550 - (void)selectPlaylist:(id)sender
552 int playlist = [[sender representedObject] intValue];
553 if (!isPlayingRadio) {
554 int curPlaylist = [[self runScriptAndReturnResult:@"return index of current playlist"] intValue];
555 [[playlistMenu itemAtIndex:curPlaylist - 1] setState:NSOffState];
557 [self runScriptAndReturnResult:[NSString stringWithFormat:@"play playlist %i", playlist]];
558 [[playlistMenu itemAtIndex:playlist - 1] setState:NSOnState];
561 - (void)selectEQPreset:(id)sender
563 int curSet = [[self runScriptAndReturnResult:@"return index of current EQ preset"] intValue];
564 int item = [[sender representedObject] intValue];
565 [self runScriptAndReturnResult:[NSString stringWithFormat:@"set current EQ preset to EQ preset %i", item]];
566 [self runScriptAndReturnResult:@"set EQ enabled to 1"];
567 [[eqMenu itemAtIndex:curSet - 1] setState:NSOffState];
568 [[eqMenu itemAtIndex:item - 1] setState:NSOnState];
571 - (void)playPause:(id)sender
573 NSString *state = [self runScriptAndReturnResult:@"return player state"];
574 if ([state isEqualToString:@"playing"]) {
575 [self sendAEWithEventClass:'hook' andEventID:'Paus'];
576 [playPauseMenuItem setTitle:@"Play"];
577 } else if ([state isEqualToString:@"fast forwarding"] || [state
578 isEqualToString:@"rewinding"]) {
579 [self sendAEWithEventClass:'hook' andEventID:'Paus'];
580 [self sendAEWithEventClass:'hook' andEventID:'Play'];
582 [self sendAEWithEventClass:'hook' andEventID:'Play'];
583 [playPauseMenuItem setTitle:@"Pause"];
587 - (void)nextSong:(id)sender
589 [self sendAEWithEventClass:'hook' andEventID:'Next'];
592 - (void)prevSong:(id)sender
594 [self sendAEWithEventClass:'hook' andEventID:'Prev'];
597 - (void)fastForward:(id)sender
599 [self sendAEWithEventClass:'hook' andEventID:'Fast'];
602 - (void)rewind:(id)sender
604 [self sendAEWithEventClass:'hook' andEventID:'Rwnd'];
607 - (void)quitMenuTunes:(id)sender
609 [NSApp terminate:self];
612 - (void)openiTunes:(id)sender
614 [[NSWorkspace sharedWorkspace] launchApplication:@"iTunes"];
617 - (void)showPreferences:(id)sender
619 if (!prefsController) {
620 prefsController = [[PreferencesController alloc] initWithMenuTunes:self];
626 - (void)closePreferences
628 if (!((iTunesPSN.highLongOfPSN == kNoProcess) && (iTunesPSN.lowLongOfPSN == 0))) {
631 [prefsController release];
632 prefsController = nil;
637 // Show Current Track Info And Show Upcoming Songs Floaters
641 - (void)showCurrentTrackInfo
643 NSString *trackName = [self runScriptAndReturnResult:@"return name of current track"];
644 if (!statusController && [trackName length]) {
645 NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
646 NSString *stringToShow = @"";
649 if ([defaults boolForKey:@"showName"]) {
650 if ([defaults boolForKey:@"showArtist"]) {
651 NSString *trackArtist = [self runScriptAndReturnResult:@"return artist of current track"];
652 trackName = [NSString stringWithFormat:@"%@ - %@", trackArtist, trackName];
654 stringToShow = [stringToShow stringByAppendingString:trackName];
655 stringToShow = [stringToShow stringByAppendingString:@"\n"];
656 if ([trackName length] > 38) {
662 if ([defaults boolForKey:@"showAlbum"]) {
663 NSString *trackAlbum = [self runScriptAndReturnResult:@"return album of current track"];
664 if ([trackAlbum length]) {
665 stringToShow = [stringToShow stringByAppendingString:trackAlbum];
666 stringToShow = [stringToShow stringByAppendingString:@"\n"];
671 if ([defaults boolForKey:@"showTime"]) {
672 NSString *trackTime = [self runScriptAndReturnResult:@"return time of current track"];
673 if ([trackTime length]) {
674 stringToShow = [NSString stringWithFormat:@"%@Total Time: %@\n", stringToShow, trackTime];
680 int trackTimeLeft = [[self runScriptAndReturnResult:@"return (duration of current track) - player position"] intValue];
681 int minutes = trackTimeLeft / 60, seconds = trackTimeLeft % 60;
683 stringToShow = [stringToShow stringByAppendingString:
684 [NSString stringWithFormat:@"Time Remaining: %i:0%i", minutes, seconds]];
686 stringToShow = [stringToShow stringByAppendingString:
687 [NSString stringWithFormat:@"Time Remaining: %i:%i", minutes, seconds]];
691 statusController = [[StatusWindowController alloc] init];
692 [statusController setTrackInfo:stringToShow lines:lines];
693 [NSTimer scheduledTimerWithTimeInterval:3.0
695 selector:@selector(fadeAndCloseStatusWindow)
701 - (void)showUpcomingSongs
703 if (!statusController) {
704 int numSongs = [[self runScriptAndReturnResult:@"return number of tracks in current playlist"] intValue];
707 int numSongsInAdvance = [[NSUserDefaults standardUserDefaults] integerForKey:@"SongsInAdvance"];
708 int curTrack = [[self runScriptAndReturnResult:@"return index of current track"] intValue];
710 NSString *songs = @"";
712 statusController = [[StatusWindowController alloc] init];
713 for (i = curTrack + 1; i <= curTrack + numSongsInAdvance; i++) {
715 NSString *curSong = [self runScriptAndReturnResult:
716 [NSString stringWithFormat:@"return name of track %i of current playlist", i]];
717 songs = [songs stringByAppendingString:curSong];
718 songs = [songs stringByAppendingString:@"\n"];
721 [statusController setUpcomingSongs:songs numSongs:numSongsInAdvance];
722 [NSTimer scheduledTimerWithTimeInterval:3.0
724 selector:@selector(fadeAndCloseStatusWindow)
731 - (void)fadeAndCloseStatusWindow
733 [statusController fadeWindowOut];
734 [statusController release];
735 statusController = nil;
738 /*************************************************************************/
740 #pragma mark NSApplication DELEGATE METHODS
741 /*************************************************************************/
743 - (void)applicationWillTerminate:(NSNotification *)note
746 [[NSStatusBar systemStatusBar] removeStatusItem:statusItem];
750 /*************************************************************************/
752 #pragma mark DEALLOCATION METHODS
753 /*************************************************************************/
758 [refreshTimer invalidate];
761 CloseComponent(asComponent);
762 [statusItem release];