1 #import "MainController.h"
2 #import "PreferencesController.h"
3 #import "HotKeyCenter.h"
4 #import "StatusWindow.h"
6 @interface MainController(Private)
7 - (ITMTRemote *)loadRemote;
9 - (void)rebuildUpcomingSongsMenu;
10 - (void)rebuildPlaylistMenu;
11 - (void)rebuildEQPresetsMenu;
14 - (void)setKeyEquivalentForCode:(short)code andModifiers:(long)modifiers
15 onItem:(NSMenuItem *)item;
19 @implementation MainController
21 /*************************************************************************/
23 #pragma mark INITIALIZATION METHODS
24 /*************************************************************************/
28 if ( ( self = [super init] ) ) {
29 remoteArray = [[NSMutableArray alloc] initWithCapacity:1];
30 statusWindow = [StatusWindow sharedWindow];
35 - (void)applicationDidFinishLaunching:(NSNotification *)note
37 currentRemote = [self loadRemote];
38 [currentRemote begin];
40 //Setup for notification of the remote player launching or quitting
41 [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self selector:@selector(applicationTerminated:) name:NSWorkspaceDidTerminateApplicationNotification object:nil];
42 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationLaunched:) name:NSWorkspaceDidLaunchApplicationNotification object:nil];
44 [self registerDefaults];
46 menu = [[NSMenu alloc] initWithTitle:@""];
48 if ( ( [currentRemote playerRunningState] == ITMTRemotePlayerRunning ) ) {
49 [self applicationLaunched:nil];
51 [self applicationTerminated:nil];
54 statusItem = [[ITStatusItem alloc] initWithStatusBar:[NSStatusBar systemStatusBar] withLength:NSSquareStatusItemLength];
56 [statusItem setImage:[NSImage imageNamed:@"menu"]];
57 [statusItem setAlternateImage:[NSImage imageNamed:@"selected_image"]];
58 [statusItem setMenu:menu];
59 // Below line of code is for creating builds for Beta Testers
60 // [statusItem setToolTip:@[NSString stringWithFormat:@"This Nontransferable Beta (Built on %s) of iThink Software's MenuTunes is Registered to: Beta Tester (betatester@somedomain.com).",__DATE__]];
63 - (ITMTRemote *)loadRemote
65 NSString *folderPath = [[NSBundle mainBundle] builtInPlugInsPath];
68 NSArray *bundlePathList = [NSBundle pathsForResourcesOfType:@"remote" inDirectory:folderPath];
69 NSEnumerator *enumerator = [bundlePathList objectEnumerator];
72 while ( (bundlePath = [enumerator nextObject]) ) {
73 NSBundle* remoteBundle = [NSBundle bundleWithPath:bundlePath];
76 Class remoteClass = [remoteBundle principalClass];
78 if ([remoteClass conformsToProtocol:@protocol(ITMTRemote)] &&
79 [remoteClass isKindOfClass:[NSObject class]]) {
81 id remote = [remoteClass remote];
82 [remoteArray addObject:remote];
87 // if ( [remoteArray count] > 0 ) { // UNCOMMENT WHEN WE HAVE > 1 PLUGIN
88 // if ( [remoteArray count] > 1 ) {
89 // [remoteArray sortUsingSelector:@selector(sortAlpha:)];
91 // [self loadModuleAccessUI]; //Comment out this line to disable remote visibility
94 // NSLog(@"%@", [remoteArray objectAtIndex:0]); //DEBUG
95 return [remoteArray objectAtIndex:0];
101 - (void)applicationLaunched:(NSNotification *)note
103 if (!note || [[[note userInfo] objectForKey:@"NSApplicationName"] isEqualToString:[currentRemote playerFullName]]) {
104 [NSThread detachNewThreadSelector:@selector(startTimerInNewThread) toTarget:self withObject:nil];
107 [statusItem setMenu:menu];
109 isAppRunning = ITMTRemotePlayerRunning;
113 isAppRunning = ITMTRemotePlayerRunning;
116 - (void)applicationTerminated:(NSNotification *)note
118 if (!note || [[[note userInfo] objectForKey:@"NSApplicationName"] isEqualToString:[currentRemote playerFullName]]) {
119 NSMenu *notRunningMenu = [[NSMenu alloc] initWithTitle:@""];
120 [[notRunningMenu addItemWithTitle:[NSString stringWithFormat:@"Open %@", [currentRemote playerSimpleName]] action:@selector(showPlayer:) keyEquivalent:@""] setTarget:self];
121 [notRunningMenu addItem:[NSMenuItem separatorItem]];
122 [[notRunningMenu addItemWithTitle:@"Preferences" action:@selector(showPreferences:) keyEquivalent:@""] setTarget:self];
123 [[notRunningMenu addItemWithTitle:@"Quit" action:@selector(quitMenuTunes:) keyEquivalent:@""] setTarget:self];
124 [statusItem setMenu:[menu autorelease]];
126 [refreshTimer invalidate];
127 [refreshTimer release];
135 - (void)startTimerInNewThread
137 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
138 NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
139 refreshTimer = [[NSTimer scheduledTimerWithTimeInterval:0.5 target:self selector:@selector(timerUpdate) userInfo:nil repeats:YES] retain];
144 /*************************************************************************/
146 #pragma mark INSTANCE METHODS
147 /*************************************************************************/
149 - (void)registerDefaults
151 NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
152 if (![defaults objectForKey:@"menu"]) {
154 NSMutableDictionary *loginwindow;
155 NSMutableArray *loginarray;
159 [NSArray arrayWithObjects:
173 @"Current Track Info",
174 nil] forKey:@"menu"];
176 [defaults synchronize];
177 loginwindow = [[defaults persistentDomainForName:@"loginwindow"] mutableCopy];
178 loginarray = [loginwindow objectForKey:@"AutoLaunchedApplicationDictionary"];
180 for (i = 0; i < [loginarray count]; i++) {
181 NSDictionary *tempDict = [loginarray objectAtIndex:i];
182 if ([[[tempDict objectForKey:@"Path"] lastPathComponent] isEqualToString:[[[NSBundle mainBundle] bundlePath] lastPathComponent]]) {
189 //We must fix it so it is no longer suxy
191 if (NSRunInformationalAlertPanel(@"Auto-launch MenuTunes", @"Would you like MenuTunes to automatically launch at login?", @"Yes", @"No", nil) == NSOKButton) {
192 AEDesc scriptDesc, resultDesc;
193 NSString *script = [NSString stringWithFormat:@"tell application \"System Events\"\nmake new login item at end of login items with properties {path:\"%@\", kind:\"APPLICATION\"}\nend tell", [[NSBundle mainBundle] bundlePath]];
194 ComponentInstance asComponent = OpenDefaultComponent(kOSAComponentType, kAppleScriptSubtype);
196 AECreateDesc(typeChar, [script cString], [script cStringLength],
199 OSADoScript(asComponent, &scriptDesc, kOSANullScript, typeChar, kOSAModeCanInteract, &resultDesc);
201 AEDisposeDesc(&scriptDesc);
202 AEDisposeDesc(&resultDesc);
204 CloseComponent(asComponent);
209 if (![defaults integerForKey:@"SongsInAdvance"])
211 [defaults setInteger:5 forKey:@"SongsInAdvance"];
214 if (![defaults objectForKey:@"showName"]) {
215 [defaults setBool:YES forKey:@"showName"];
218 if (![defaults objectForKey:@"showArtist"]) {
219 [defaults setBool:YES forKey:@"showArtist"];
222 if (![defaults objectForKey:@"showAlbum"]) {
223 [defaults setBool:NO forKey:@"showAlbum"];
226 if (![defaults objectForKey:@"showTime"]) {
227 [defaults setBool:NO forKey:@"showTime"];
231 //Recreate the status item menu
234 NSArray *myMenu = [[NSUserDefaults standardUserDefaults] arrayForKey:@"menu"];
239 lastPlaylistIndex = -1;
240 didHaveAlbumName = ([[currentRemote currentSongAlbum] length] > 0);
241 didHaveArtistName = ([[currentRemote currentSongArtist] length] > 0);
243 while ([menu numberOfItems] > 0) {
244 [menu removeItemAtIndex:0];
247 playPauseMenuItem = nil;
248 upcomingSongsItem = nil;
249 songRatingMenuItem = nil;
251 [playlistMenu release];
257 for (i = 0; i < [myMenu count]; i++) {
258 NSString *item = [myMenu objectAtIndex:i];
259 if ([item isEqualToString:@"Play/Pause"]) {
260 KeyCombo *tempCombo = [[NSUserDefaults standardUserDefaults] keyComboForKey:@"PlayPause"];
261 playPauseMenuItem = [menu addItemWithTitle:@"Play"
262 action:@selector(playPause:)
264 [playPauseMenuItem setTarget:self];
267 [self setKeyEquivalentForCode:[tempCombo keyCode]
268 andModifiers:[tempCombo modifiers] onItem:playPauseMenuItem];
271 } else if ([item isEqualToString:@"Next Track"]) {
272 KeyCombo *tempCombo = [[NSUserDefaults standardUserDefaults] keyComboForKey:@"NextTrack"];
273 NSMenuItem *nextTrack = [menu addItemWithTitle:@"Next Track"
274 action:@selector(nextSong:)
277 [nextTrack setTarget:self];
279 [self setKeyEquivalentForCode:[tempCombo keyCode]
280 andModifiers:[tempCombo modifiers] onItem:nextTrack];
283 } else if ([item isEqualToString:@"Previous Track"]) {
284 KeyCombo *tempCombo = [[NSUserDefaults standardUserDefaults] keyComboForKey:@"PrevTrack"];
285 NSMenuItem *prevTrack = [menu addItemWithTitle:@"Previous Track"
286 action:@selector(prevSong:)
289 [prevTrack setTarget:self];
291 [self setKeyEquivalentForCode:[tempCombo keyCode]
292 andModifiers:[tempCombo modifiers] onItem:prevTrack];
295 } else if ([item isEqualToString:@"Fast Forward"]) {
296 [[menu addItemWithTitle:@"Fast Forward"
297 action:@selector(fastForward:)
298 keyEquivalent:@""] setTarget:self];
299 } else if ([item isEqualToString:@"Rewind"]) {
300 [[menu addItemWithTitle:@"Rewind"
301 action:@selector(rewind:)
302 keyEquivalent:@""] setTarget:self];
303 } else if ([item isEqualToString:@"Upcoming Songs"]) {
304 upcomingSongsItem = [menu addItemWithTitle:@"Upcoming Songs"
307 } else if ([item isEqualToString:@"Playlists"]) {
308 playlistItem = [menu addItemWithTitle:@"Playlists"
311 } else if ([item isEqualToString:@"EQ Presets"]) {
312 eqItem = [menu addItemWithTitle:@"EQ Presets"
315 } else if ([item isEqualToString:@"PreferencesÉ"]) {
316 [[menu addItemWithTitle:@"PreferencesÉ"
317 action:@selector(showPreferences:)
318 keyEquivalent:@""] setTarget:self];
319 } else if ([item isEqualToString:@"Quit"]) {
320 [[menu addItemWithTitle:@"Quit"
321 action:@selector(quitMenuTunes:)
322 keyEquivalent:@""] setTarget:self];
323 } else if ([item isEqualToString:@"Current Track Info"]) {
324 trackInfoIndex = [menu numberOfItems];
325 [menu addItemWithTitle:@"No Song"
328 } else if ([item isEqualToString:@"Song Rating"]) {
329 unichar fullstar = 0x2605;
330 unichar emptystar = 0x2606;
331 NSString *fullStarChar = [NSString stringWithCharacters:&fullstar length:1];
332 NSString *emptyStarChar = [NSString stringWithCharacters:&emptystar length:1];
335 songRatingMenuItem = [menu addItemWithTitle:@"Song Rating"
339 ratingMenu = [[NSMenu alloc] initWithTitle:@""];
341 item = [ratingMenu addItemWithTitle:[NSString stringWithFormat:@"%@%@%@%@%@", emptyStarChar, emptyStarChar, emptyStarChar, emptyStarChar, emptyStarChar] action:@selector(selectSongRating:) keyEquivalent:@""];
342 [item setTarget:self];
345 item = [ratingMenu addItemWithTitle:[NSString stringWithFormat:@"%@%@%@%@%@", fullStarChar, emptyStarChar, emptyStarChar, emptyStarChar, emptyStarChar] action:@selector(selectSongRating:) keyEquivalent:@""];
346 [item setTarget:self];
349 item = [ratingMenu addItemWithTitle:[NSString stringWithFormat:@"%@%@%@%@%@", fullStarChar, fullStarChar, emptyStarChar, emptyStarChar, emptyStarChar] action:@selector(selectSongRating:) keyEquivalent:@""];
350 [item setTarget:self];
353 item = [ratingMenu addItemWithTitle:[NSString stringWithFormat:@"%@%@%@%@%@", fullStarChar, fullStarChar, fullStarChar, emptyStarChar, emptyStarChar] action:@selector(selectSongRating:) keyEquivalent:@""];
354 [item setTarget:self];
357 item = [ratingMenu addItemWithTitle:[NSString stringWithFormat:@"%@%@%@%@%@", fullStarChar, fullStarChar, fullStarChar, fullStarChar, emptyStarChar] action:@selector(selectSongRating:) keyEquivalent:@""];
358 [item setTarget:self];
361 item = [ratingMenu addItemWithTitle:[NSString stringWithFormat:@"%@%@%@%@%@", fullStarChar, fullStarChar, fullStarChar, fullStarChar, fullStarChar] action:@selector(selectSongRating:) keyEquivalent:@""];
362 [item setTarget:self];
364 } else if ([item isEqualToString:@"<separator>"]) {
365 [menu addItem:[NSMenuItem separatorItem]];
369 [self timerUpdate]; //Updates dynamic info in the menu
375 //Updates the menu with current player state, song, and upcoming songs
378 NSMenuItem *menuItem;
379 NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
381 if ( ( isAppRunning == ITMTRemotePlayerNotRunning ) ) {
385 if (upcomingSongsItem) {
386 [self rebuildUpcomingSongsMenu];
390 [self rebuildPlaylistMenu];
394 [self rebuildEQPresetsMenu];
396 if (trackInfoIndex > -1) {
397 NSString *curSongName, *curAlbumName = @"", *curArtistName = @"";
398 curSongName = [currentRemote currentSongTitle];
400 if ([defaults boolForKey:@"showAlbum"]) {
401 curAlbumName = [currentRemote currentSongAlbum];
404 if ([defaults boolForKey:@"showArtist"]) {
405 curArtistName = [currentRemote currentSongArtist];
408 if ([curSongName length] > 0) {
409 int index = [menu indexOfItemWithTitle:@"Now Playing"];
411 if ([defaults boolForKey:@"showName"]) {
412 [menu removeItemAtIndex:index + 1];
414 if (didHaveAlbumName && [defaults boolForKey:@"showAlbum"]) {
415 [menu removeItemAtIndex:index + 1];
417 if (didHaveArtistName && [defaults boolForKey:@"showArtist"]) {
418 [menu removeItemAtIndex:index + 1];
420 if ([defaults boolForKey:@"showTime"]) {
421 [menu removeItemAtIndex:index + 1];
425 if (!isPlayingRadio) {
426 if ([defaults boolForKey:@"showTime"]) {
427 menuItem = [[NSMenuItem alloc] initWithTitle:[NSString stringWithFormat:@" %@", [currentRemote currentSongLength]]
430 [menu insertItem:menuItem atIndex:trackInfoIndex + 1];
434 if ([curArtistName length] > 0) {
435 menuItem = [[NSMenuItem alloc] initWithTitle:[NSString stringWithFormat:@" %@", curArtistName]
438 [menu insertItem:menuItem atIndex:trackInfoIndex + 1];
442 if ([curAlbumName length] > 0) {
443 menuItem = [[NSMenuItem alloc] initWithTitle:[NSString stringWithFormat:@" %@", curAlbumName]
446 [menu insertItem:menuItem atIndex:trackInfoIndex + 1];
450 if (songRatingMenuItem) {
451 int rating = (int)[currentRemote currentSongRating] * 10;
453 for (i = 0; i < 5; i++) {
454 [[ratingMenu itemAtIndex:i] setState:NSOffState];
455 [[ratingMenu itemAtIndex:i] setTarget:self];
457 [[ratingMenu itemAtIndex:rating / 2] setState:NSOnState];
461 if ([defaults boolForKey:@"showName"]) {
462 menuItem = [[NSMenuItem alloc] initWithTitle:[NSString stringWithFormat:@" %@", curSongName]
465 [menu insertItem:menuItem atIndex:trackInfoIndex + 1];
470 menuItem = [[NSMenuItem alloc] initWithTitle:@"Now Playing" action:nil keyEquivalent:@""];
471 [menu removeItemAtIndex:[menu indexOfItemWithTitle:@"No Song"]];
472 [menu insertItem:menuItem atIndex:trackInfoIndex];
475 [songRatingMenuItem setSubmenu:ratingMenu];
476 [songRatingMenuItem setEnabled:YES];
478 } else if ([menu indexOfItemWithTitle:@"No Song"] == -1) {
479 [menu removeItemAtIndex:trackInfoIndex];
481 if ([defaults boolForKey:@"showName"] == YES) {
482 [menu removeItemAtIndex:trackInfoIndex];
485 if ([defaults boolForKey:@"showTime"] == YES) {
486 [menu removeItemAtIndex:trackInfoIndex];
489 if (didHaveArtistName && [defaults boolForKey:@"showArtist"]) {
490 [menu removeItemAtIndex:trackInfoIndex];
493 if (didHaveAlbumName && [defaults boolForKey:@"showAlbum"]) {
494 [menu removeItemAtIndex:trackInfoIndex];
497 menuItem = [[NSMenuItem alloc] initWithTitle:@"No Song" action:nil keyEquivalent:@""];
498 [menu insertItem:menuItem atIndex:trackInfoIndex];
502 if ([defaults boolForKey:@"showArtist"]) {
503 didHaveArtistName = (([curArtistName length] > 0) ? YES : NO);
506 if ([defaults boolForKey:@"showAlbum"]) {
507 didHaveAlbumName = (([curAlbumName length] > 0) ? YES : NO);
513 //Rebuild the upcoming songs submenu. Can be improved a lot.
514 - (void)rebuildUpcomingSongsMenu
516 int curIndex = [currentRemote currentPlaylistIndex];
517 int numSongs = [currentRemote numberOfSongsInPlaylistAtIndex:curIndex];
518 int numSongsInAdvance = [[NSUserDefaults standardUserDefaults] integerForKey:@"SongsInAdvance"];
520 if (!isPlayingRadio) {
522 int curTrack = [currentRemote currentSongIndex];
525 [upcomingSongsMenu release];
526 upcomingSongsMenu = [[NSMenu alloc] initWithTitle:@""];
527 [upcomingSongsItem setSubmenu:upcomingSongsMenu];
528 [upcomingSongsItem setEnabled:YES];
530 for (i = curTrack + 1; i <= curTrack + numSongsInAdvance; i++) {
532 NSString *curSong = [currentRemote songTitleAtIndex:i];
533 NSMenuItem *songItem;
534 songItem = [[NSMenuItem alloc] initWithTitle:curSong action:@selector(playTrack:) keyEquivalent:@""];
535 [songItem setTarget:self];
536 [songItem setRepresentedObject:[NSNumber numberWithInt:i]];
537 [upcomingSongsMenu addItem:songItem];
545 [upcomingSongsItem setSubmenu:nil];
546 [upcomingSongsItem setEnabled:NO];
550 - (void)rebuildPlaylistMenu
552 NSArray *playlists = [currentRemote playlists];
553 int i, curPlaylist = [currentRemote currentPlaylistIndex];
555 if (isPlayingRadio) {
558 if (playlistMenu && ([playlists count] == [playlistMenu numberOfItems]))
561 [playlistMenu release];
562 playlistMenu = [[NSMenu alloc] initWithTitle:@""];
564 for (i = 0; i < [playlists count]; i++) {
565 NSString *playlistName = [playlists objectAtIndex:i];
566 NSMenuItem *tempItem;
567 tempItem = [[NSMenuItem alloc] initWithTitle:playlistName action:@selector(selectPlaylist:) keyEquivalent:@""];
568 [tempItem setTarget:self];
569 [tempItem setRepresentedObject:[NSNumber numberWithInt:i + 1]];
570 [playlistMenu addItem:tempItem];
573 [playlistItem setSubmenu:playlistMenu];
574 [playlistItem setEnabled:YES];
577 [[playlistMenu itemAtIndex:curPlaylist - 1] setState:NSOnState];
581 //Build a menu with the list of all available EQ presets
582 - (void)rebuildEQPresetsMenu
584 NSArray *eqPresets = [currentRemote eqPresets];
585 NSMenuItem *enabledItem;
588 if (eqMenu && ([[currentRemote eqPresets] count] == [eqMenu numberOfItems]))
592 eqMenu = [[NSMenu alloc] initWithTitle:@""];
594 enabledItem = [eqMenu addItemWithTitle:@"Disabled"
595 action:@selector(toggleEqualizer)
598 if ([currentRemote equalizerEnabled] == NO) {
599 [enabledItem setState:NSOnState];
602 [eqMenu addItem:[NSMenuItem separatorItem]];
604 for (i = 0; i < [eqPresets count]; i++) {
605 NSString *setName = [eqPresets objectAtIndex:i];
606 NSMenuItem *tempItem;
608 tempItem = [[NSMenuItem alloc] initWithTitle:setName action:@selector(selectEQPreset:) keyEquivalent:@""];
609 [tempItem setTarget:self];
610 [tempItem setRepresentedObject:[NSNumber numberWithInt:i]];
611 [eqMenu addItem:tempItem];
615 [eqItem setSubmenu:eqMenu];
617 [[eqMenu itemAtIndex:[currentRemote currentEQPresetIndex] + 1] setState:NSOnState];
622 [[HotKeyCenter sharedCenter] removeHotKey:@"PlayPause"];
623 [[HotKeyCenter sharedCenter] removeHotKey:@"NextTrack"];
624 [[HotKeyCenter sharedCenter] removeHotKey:@"PrevTrack"];
625 [[HotKeyCenter sharedCenter] removeHotKey:@"TrackInfo"];
626 [[HotKeyCenter sharedCenter] removeHotKey:@"UpcomingSongs"];
631 NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
633 if ([defaults objectForKey:@"PlayPause"] != nil) {
634 [[HotKeyCenter sharedCenter] addHotKey:@"PlayPause"
635 combo:[defaults keyComboForKey:@"PlayPause"]
636 target:self action:@selector(playPause:)];
639 if ([defaults objectForKey:@"NextTrack"] != nil) {
640 [[HotKeyCenter sharedCenter] addHotKey:@"NextTrack"
641 combo:[defaults keyComboForKey:@"NextTrack"]
642 target:self action:@selector(nextSong:)];
645 if ([defaults objectForKey:@"PrevTrack"] != nil) {
646 [[HotKeyCenter sharedCenter] addHotKey:@"PrevTrack"
647 combo:[defaults keyComboForKey:@"PrevTrack"]
648 target:self action:@selector(prevSong:)];
651 if ([defaults objectForKey:@"TrackInfo"] != nil) {
652 [[HotKeyCenter sharedCenter] addHotKey:@"TrackInfo"
653 combo:[defaults keyComboForKey:@"TrackInfo"]
654 target:self action:@selector(showCurrentTrackInfo)];
657 if ([defaults objectForKey:@"UpcomingSongs"] != nil) {
658 [[HotKeyCenter sharedCenter] addHotKey:@"UpcomingSongs"
659 combo:[defaults keyComboForKey:@"UpcomingSongs"]
660 target:self action:@selector(showUpcomingSongs)];
664 //Called when the timer fires.
667 int playlist = [currentRemote currentPlaylistIndex];
668 ITMTRemotePlayerPlayingState playerPlayingState = [currentRemote playerPlayingState];
670 if ((playlist > 0) || playerPlayingState != ITMTRemotePlayerStopped) {
671 int trackPlayingIndex = [currentRemote currentSongIndex];
673 if (trackPlayingIndex != lastSongIndex) {
674 BOOL wasPlayingRadio = isPlayingRadio;
675 isPlayingRadio = ([currentRemote classOfPlaylistAtIndex:playlist] == ITMTRemotePlayerRadioPlaylist);
677 if (isPlayingRadio && !wasPlayingRadio) {
679 for (i = 0; i < [playlistMenu numberOfItems]; i++)
681 [[playlistMenu itemAtIndex:i] setState:NSOffState];
684 [[playlistMenu itemAtIndex:playlist - 1] setState:NSOnState];
687 if (wasPlayingRadio) {
688 NSMenuItem *temp = [[NSMenuItem alloc] initWithTitle:@"" action:NULL keyEquivalent:@""];
689 [menu insertItem:temp atIndex:trackInfoIndex + 1];
694 lastSongIndex = trackPlayingIndex;
696 if (playlist != lastPlaylistIndex) {
697 BOOL wasPlayingRadio = isPlayingRadio;
698 isPlayingRadio = ([currentRemote classOfPlaylistAtIndex:playlist] == ITMTRemotePlayerRadioPlaylist);
700 if (isPlayingRadio && !wasPlayingRadio) {
702 for (i = 0; i < [playlistMenu numberOfItems]; i++) {
703 [[playlistMenu itemAtIndex:i] setState:NSOffState];
707 if (wasPlayingRadio) {
708 NSMenuItem *temp = [[NSMenuItem alloc] initWithTitle:@"" action:NULL keyEquivalent:@""];
709 [menu insertItem:temp atIndex:trackInfoIndex + 1];
713 if (!isPlayingRadio) {
715 for (i = 0; i < [playlistMenu numberOfItems]; i++)
717 [[playlistMenu itemAtIndex:i] setState:NSOffState];
719 [[playlistMenu itemAtIndex:playlist - 1] setState:NSOnState];
723 lastSongIndex = trackPlayingIndex;
724 lastPlaylistIndex = playlist;
727 //Update Play/Pause menu item
728 if (playPauseMenuItem){
729 if (playerPlayingState == ITMTRemotePlayerPlaying) {
730 [playPauseMenuItem setTitle:@"Pause"];
732 [playPauseMenuItem setTitle:@"Play"];
735 } else if ((lastPlaylistIndex > 0) && (playlist == 0)) {
736 NSMenuItem *menuItem;
737 NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
738 //Remote the now playing item and add no song item
739 [menu removeItemAtIndex:trackInfoIndex];
741 if ([defaults boolForKey:@"showName"] == YES) {
742 [menu removeItemAtIndex:trackInfoIndex];
745 if ([defaults boolForKey:@"showTime"] == YES) {
746 [menu removeItemAtIndex:trackInfoIndex];
749 if (didHaveArtistName && [defaults boolForKey:@"showArtist"]) {
750 [menu removeItemAtIndex:trackInfoIndex];
753 if (didHaveAlbumName && [defaults boolForKey:@"showAlbum"]) {
754 [menu removeItemAtIndex:trackInfoIndex];
757 [playPauseMenuItem setTitle:@"Play"];
759 didHaveArtistName = NO;
760 didHaveAlbumName = NO;
761 lastPlaylistIndex = -1;
764 [upcomingSongsItem setSubmenu:nil];
765 [upcomingSongsItem setEnabled:NO];
767 [songRatingMenuItem setSubmenu:nil];
768 [songRatingMenuItem setEnabled:NO];
770 menuItem = [[NSMenuItem alloc] initWithTitle:@"No Song" action:nil keyEquivalent:@""];
771 [menu insertItem:menuItem atIndex:trackInfoIndex];
778 // Selectors - called from status item menu
782 // Plugin dependent selectors
784 - (void)playTrack:(id)sender
786 [currentRemote switchToSongAtIndex:[[sender representedObject] intValue]];
789 - (void)selectPlaylist:(id)sender
791 int playlist = [[sender representedObject] intValue];
792 if (!isPlayingRadio) {
793 int curPlaylist = [currentRemote currentPlaylistIndex];
794 if (curPlaylist > 0) {
795 [[playlistMenu itemAtIndex:curPlaylist - 1] setState:NSOffState];
798 [currentRemote switchToPlaylistAtIndex:playlist];
799 [[playlistMenu itemAtIndex:playlist - 1] setState:NSOnState];
803 - (void)selectEQPreset:(id)sender
805 int curSet = [currentRemote currentEQPresetIndex];
806 int item = [[sender representedObject] intValue];
807 [currentRemote switchToEQAtIndex:item];
808 [[eqMenu itemAtIndex:curSet + 1] setState:NSOffState];
809 [[eqMenu itemAtIndex:item + 2] setState:NSOnState];
812 - (void)playPause:(id)sender
814 ITMTRemotePlayerPlayingState state = [currentRemote playerPlayingState];
816 if (state == ITMTRemotePlayerPlaying) {
817 [currentRemote pause];
818 [playPauseMenuItem setTitle:@"Play"];
819 } else if ((state == ITMTRemotePlayerForwarding) || (state == ITMTRemotePlayerRewinding)) {
820 [currentRemote pause];
821 [currentRemote play];
823 [currentRemote play];
824 [playPauseMenuItem setTitle:@"Pause"];
828 - (void)nextSong:(id)sender
830 [currentRemote goToNextSong];
833 - (void)prevSong:(id)sender
835 [currentRemote goToPreviousSong];
838 - (void)fastForward:(id)sender
840 [currentRemote forward];
841 [playPauseMenuItem setTitle:@"Play"];
844 - (void)rewind:(id)sender
846 [currentRemote rewind];
847 [playPauseMenuItem setTitle:@"Play"];
850 - (void)toggleEqualizer
852 [currentRemote setEqualizerEnabled:![currentRemote equalizerEnabled]];
855 - (void)selectSongRating:(id)sender
857 [[ratingMenu itemAtIndex:([currentRemote currentSongRating] / 20)] setState:NSOffState];
858 [currentRemote setCurrentSongRating:(float)[sender tag] / 100.0];
859 [sender setState:NSOnState];
864 // Plugin independent selectors
867 - (void)quitMenuTunes:(id)sender
869 [NSApp terminate:self];
872 - (void)showPreferences:(id)sender
874 if (!prefsController) {
875 prefsController = [[PreferencesController alloc] initWithMenuTunes:self];
880 - (void)closePreferences
882 if ( ( isAppRunning == ITMTRemotePlayerRunning) ) {
885 [prefsController release];
886 prefsController = nil;
889 - (void)showPlayer:(id)sender
891 if ( ( isAppRunning == ITMTRemotePlayerRunning) ) {
892 [currentRemote showPrimaryInterface];
894 if (![[NSWorkspace sharedWorkspace] launchApplication:[currentRemote playerFullName]]) {
895 NSLog(@"Error Launching Player");
903 // Show Current Track Info And Show Upcoming Songs Floaters
907 - (void)showCurrentTrackInfo
909 NSString *trackName = [currentRemote currentSongTitle];
910 if (!statusWindow && [trackName length]) {
911 NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
912 NSString *stringToShow = @"";
914 if ([defaults boolForKey:@"showName"]) {
915 if ([defaults boolForKey:@"showArtist"]) {
916 NSString *trackArtist = [currentRemote currentSongArtist];
917 trackName = [NSString stringWithFormat:@"%@ - %@", trackArtist, trackName];
919 stringToShow = [stringToShow stringByAppendingString:trackName];
920 stringToShow = [stringToShow stringByAppendingString:@"\n"];
923 if ([defaults boolForKey:@"showAlbum"]) {
924 NSString *trackAlbum = [currentRemote currentSongAlbum];
925 if ([trackAlbum length]) {
926 stringToShow = [stringToShow stringByAppendingString:trackAlbum];
927 stringToShow = [stringToShow stringByAppendingString:@"\n"];
931 if ([defaults boolForKey:@"showTime"]) {
932 NSString *trackTime = [currentRemote currentSongLength];
933 if ([trackTime length]) {
934 stringToShow = [NSString stringWithFormat:@"%@Total Time: %@\n", stringToShow, trackTime];
939 int trackTimeLeft = [[currentRemote currentSongRemaining] intValue];
940 int minutes = trackTimeLeft / 60, seconds = trackTimeLeft % 60;
942 stringToShow = [stringToShow stringByAppendingString:
943 [NSString stringWithFormat:@"Time Remaining: %i:0%i", minutes, seconds]];
945 stringToShow = [stringToShow stringByAppendingString:
946 [NSString stringWithFormat:@"Time Remaining: %i:%i", minutes, seconds]];
950 [statusWindow setText:stringToShow];
951 [NSTimer scheduledTimerWithTimeInterval:3.0
953 selector:@selector(fadeAndCloseStatusWindow)
959 - (void)showUpcomingSongs
961 int curPlaylist = [currentRemote currentPlaylistIndex];
963 int numSongs = [currentRemote numberOfSongsInPlaylistAtIndex:curPlaylist];
966 int numSongsInAdvance = [[NSUserDefaults standardUserDefaults] integerForKey:@"SongsInAdvance"];
967 int curTrack = [currentRemote currentSongIndex];
969 NSString *songs = @"";
971 statusWindow = [ITTransientStatusWindow sharedWindow];
972 for (i = curTrack + 1; i <= curTrack + numSongsInAdvance; i++) {
974 NSString *curSong = [currentRemote songTitleAtIndex:i];
975 songs = [songs stringByAppendingString:curSong];
976 songs = [songs stringByAppendingString:@"\n"];
979 [statusWindow setText:songs];
980 [NSTimer scheduledTimerWithTimeInterval:3.0
982 selector:@selector(fadeAndCloseStatusWindow)
989 - (void)fadeAndCloseStatusWindow
991 [statusWindow orderOut:self];
994 - (void)setKeyEquivalentForCode:(short)code andModifiers:(long)modifiers
995 onItem:(NSMenuItem *)item
997 unichar charcode = 'a';
999 long cocoaModifiers = 0;
1000 static long carbonToCocoa[6][2] =
1002 { cmdKey, NSCommandKeyMask },
1003 { optionKey, NSAlternateKeyMask },
1004 { controlKey, NSControlKeyMask },
1005 { shiftKey, NSShiftKeyMask },
1008 for (i = 0; i < 6; i++) {
1009 if (modifiers & carbonToCocoa[i][0]) {
1010 cocoaModifiers += carbonToCocoa[i][1];
1013 [item setKeyEquivalentModifierMask:cocoaModifiers];
1015 //Missing key combos for some keys. Must find them later.
1029 /*MenuRef menuRef = _NSGetCarbonMenu([item menu]);
1030 NSLog(@"%@", menuRef);
1031 SetMenuItemCommandKey(menuRef, 0, NO, 49);
1032 SetMenuItemModifiers(menuRef, 0, kMenuNoCommandModifier);
1033 SetMenuItemKeyGlyph(menuRef, 0, kMenuBlankGlyph);
1039 charcode = NSDeleteFunctionKey;
1055 charcode = NSF5FunctionKey;
1059 charcode = NSF6FunctionKey;
1063 charcode = NSF7FunctionKey;
1067 charcode = NSF3FunctionKey;
1071 charcode = NSF8FunctionKey;
1075 charcode = NSF9FunctionKey;
1079 charcode = NSF11FunctionKey;
1083 charcode = NSF3FunctionKey;
1087 charcode = NSF14FunctionKey;
1091 charcode = NSF10FunctionKey;
1095 charcode = NSF12FunctionKey;
1099 charcode = NSF13FunctionKey;
1103 charcode = NSInsertFunctionKey;
1107 charcode = NSHomeFunctionKey;
1111 charcode = NSPageUpFunctionKey;
1115 charcode = NSDeleteFunctionKey;
1119 charcode = NSF4FunctionKey;
1123 charcode = NSEndFunctionKey;
1127 charcode = NSF2FunctionKey;
1131 charcode = NSPageDownFunctionKey;
1135 charcode = NSF1FunctionKey;
1139 charcode = NSLeftArrowFunctionKey;
1143 charcode = NSRightArrowFunctionKey;
1147 charcode = NSDownArrowFunctionKey;
1151 charcode = NSUpArrowFunctionKey;
1155 if (charcode == 'a') {
1156 unsigned long state;
1161 kchr = (Ptr) GetScriptVariable(smCurrentScript, smKCHRCache);
1162 keyTrans = KeyTranslate(kchr, code, &state);
1163 charCode = keyTrans;
1164 [item setKeyEquivalent:[NSString stringWithCString:&charCode length:1]];
1165 } else if (charcode != 'b') {
1166 [item setKeyEquivalent:[NSString stringWithCharacters:&charcode length:1]];
1170 /*************************************************************************/
1172 #pragma mark NSApplication DELEGATE METHODS
1173 /*************************************************************************/
1175 - (void)applicationWillTerminate:(NSNotification *)note
1177 [self clearHotKeys];
1178 [[NSStatusBar systemStatusBar] removeStatusItem:statusItem];
1182 /*************************************************************************/
1184 #pragma mark DEALLOCATION METHODS
1185 /*************************************************************************/
1190 [refreshTimer invalidate];
1191 [refreshTimer release];
1194 [currentRemote halt];
1195 [statusItem release];