1 #import "NewMainController.h"
2 #import "PreferencesController.h"
3 #import "HotKeyCenter.h"
4 #import "StatusWindowController.h"
6 @interface MainController(Private)
7 - (ITMTRemote *)loadRemote;
10 - (void)setLatestSongIdentifier:(NSString *)newIdentifier;
11 - (void)showCurrentTrackInfo;
14 static MainController *sharedController;
16 @implementation MainController
18 + (MainController *)sharedController
20 return sharedController;
23 /*************************************************************************/
25 #pragma mark INITIALIZATION/DEALLOCATION METHODS
26 /*************************************************************************/
30 if ( ( self = [super init] ) ) {
31 sharedController = self;
33 remoteArray = [[NSMutableArray alloc] initWithCapacity:1];
34 statusWindowController = [[StatusWindowController alloc] init];
35 df = [[NSUserDefaults standardUserDefaults] retain];
36 [self setLatestSongIdentifier:@"0-0"];
41 - (void)applicationDidFinishLaunching:(NSNotification *)note
43 currentRemote = [self loadRemote];
44 [currentRemote begin];
46 //Setup for notification of the remote player launching or quitting
47 [[[NSWorkspace sharedWorkspace] notificationCenter]
49 selector:@selector(applicationTerminated:)
50 name:NSWorkspaceDidTerminateApplicationNotification
53 [[[NSWorkspace sharedWorkspace] notificationCenter]
55 selector:@selector(applicationLaunched:)
56 name:NSWorkspaceDidLaunchApplicationNotification
59 if ( ! [df objectForKey:@"menu"] ) { // If this is nil, defaults have never been registered.
60 [[PreferencesController sharedPrefs] registerDefaults];
63 statusItem = [[ITStatusItem alloc]
64 initWithStatusBar:[NSStatusBar systemStatusBar]
65 withLength:NSSquareStatusItemLength];
67 [statusItem setImage:[NSImage imageNamed:@"menu"]];
68 [statusItem setAlternateImage:[NSImage imageNamed:@"selected_image"]];
71 - (ITMTRemote *)loadRemote
73 NSString *folderPath = [[NSBundle mainBundle] builtInPlugInsPath];
76 NSArray *bundlePathList = [NSBundle pathsForResourcesOfType:@"remote" inDirectory:folderPath];
77 NSEnumerator *enumerator = [bundlePathList objectEnumerator];
80 while ( (bundlePath = [enumerator nextObject]) ) {
81 NSBundle* remoteBundle = [NSBundle bundleWithPath:bundlePath];
84 Class remoteClass = [remoteBundle principalClass];
86 if ([remoteClass conformsToProtocol:@protocol(ITMTRemote)] &&
87 [remoteClass isKindOfClass:[NSObject class]]) {
89 id remote = [remoteClass remote];
90 [remoteArray addObject:remote];
95 // if ( [remoteArray count] > 0 ) { // UNCOMMENT WHEN WE HAVE > 1 PLUGIN
96 // if ( [remoteArray count] > 1 ) {
97 // [remoteArray sortUsingSelector:@selector(sortAlpha:)];
99 // [self loadModuleAccessUI]; //Comment out this line to disable remote visibility
102 // NSLog(@"%@", [remoteArray objectAtIndex:0]); //DEBUG
103 return [remoteArray objectAtIndex:0];
106 /*************************************************************************/
108 #pragma mark INSTANCE METHODS
109 /*************************************************************************/
111 - (void)startTimerInNewThread
113 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
114 NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
115 refreshTimer = [[NSTimer scheduledTimerWithTimeInterval:0.5
117 selector:@selector(timerUpdate)
119 repeats:YES] retain];
124 - (BOOL)songIsPlaying
126 return ( ! ([[currentRemote currentSongUniqueIdentifier] isEqualToString:@"0-0"]) );
129 - (BOOL)radioIsPlaying
131 return ( [currentRemote currentPlaylistClass] == ITMTRemotePlayerRadioPlaylist );
136 return ( ! [[currentRemote currentSongUniqueIdentifier] isEqualToString:_latestSongIdentifier] );
139 - (NSString *)latestSongIdentifier
141 return _latestSongIdentifier;
144 - (void)setLatestSongIdentifier:(NSString *)newIdentifier
146 [_latestSongIdentifier autorelease];
147 _latestSongIdentifier = [newIdentifier copy];
152 if ( ( [self songChanged] ) ||
153 ( ([self radioIsPlaying]) && (latestPlaylistClass != ITMTRemotePlayerRadioPlaylist) ) ||
154 ( (! [self radioIsPlaying]) && (latestPlaylistClass == ITMTRemotePlayerRadioPlaylist) ) ) {
155 //[statusItem setMenu:[self menu]];
156 [self setLatestSongIdentifier:[currentRemote currentSongUniqueIdentifier]];
157 latestPlaylistClass = [currentRemote currentPlaylistClass];
159 if ( [df boolForKey:@"showSongInfoOnChange"] ) {
160 [self showCurrentTrackInfo];
164 //Update Play/Pause menu item
166 if ([currentRemote playerPlayingState] == ITMTRemotePlayerPlaying) {
167 [playPauseItem setTitle:@"Pause"];
169 [playPauseItem setTitle:@"Play"];
183 ITMTRemotePlayerPlayingState state = [currentRemote playerPlayingState];
185 if (state == ITMTRemotePlayerPlaying) {
186 [currentRemote pause];
187 } else if ((state == ITMTRemotePlayerForwarding) || (state == ITMTRemotePlayerRewinding)) {
188 [currentRemote pause];
189 [currentRemote play];
191 [currentRemote play];
197 [currentRemote goToNextSong];
202 [currentRemote goToPreviousSong];
207 [currentRemote forward];
212 [currentRemote rewind];
215 - (void)selectPlaylistAtIndex:(int)index
217 [currentRemote switchToPlaylistAtIndex:index];
220 - (void)selectSongAtIndex:(int)index
222 [currentRemote switchToSongAtIndex:index];
225 - (void)selectSongRating:(int)rating
227 [currentRemote setCurrentSongRating:(float)rating / 100.0];
230 - (void)selectEQPresetAtIndex:(int)index
232 [currentRemote switchToEQAtIndex:index];
235 - (void)showPreferences
237 [[PreferencesController sharedPrefs] setController:self];
238 [[PreferencesController sharedPrefs] showPrefsWindow:self];
241 - (void)quitMenuTunes
243 [NSApp terminate:self];
249 - (void)showPlayer:(id)sender
251 if ( ( playerRunningState == ITMTRemotePlayerRunning) ) {
252 [currentRemote showPrimaryInterface];
254 if (![[NSWorkspace sharedWorkspace] launchApplication:[currentRemote playerFullName]]) {
255 NSLog(@"Error Launching Player");
260 - (void)closePreferences
262 if ( ( playerRunningState == ITMTRemotePlayerRunning) ) {
267 - (ITMTRemote *)currentRemote
269 return currentRemote;
280 [[HotKeyCenter sharedCenter] removeHotKey:@"PlayPause"];
281 [[HotKeyCenter sharedCenter] removeHotKey:@"NextTrack"];
282 [[HotKeyCenter sharedCenter] removeHotKey:@"PrevTrack"];
283 [[HotKeyCenter sharedCenter] removeHotKey:@"TrackInfo"];
284 [[HotKeyCenter sharedCenter] removeHotKey:@"UpcomingSongs"];
285 [[HotKeyCenter sharedCenter] removeHotKey:@"ToggleLoop"];
286 [[HotKeyCenter sharedCenter] removeHotKey:@"ToggleShuffle"];
287 [[HotKeyCenter sharedCenter] removeHotKey:@"IncrementVolume"];
288 [[HotKeyCenter sharedCenter] removeHotKey:@"DecrementVolume"];
289 [[HotKeyCenter sharedCenter] removeHotKey:@"IncrementRating"];
290 [[HotKeyCenter sharedCenter] removeHotKey:@"DecrementRating"];
295 if ([df objectForKey:@"PlayPause"] != nil) {
296 [[HotKeyCenter sharedCenter] addHotKey:@"PlayPause"
297 combo:[df keyComboForKey:@"PlayPause"]
298 target:self action:@selector(playPause:)];
301 if ([df objectForKey:@"NextTrack"] != nil) {
302 [[HotKeyCenter sharedCenter] addHotKey:@"NextTrack"
303 combo:[df keyComboForKey:@"NextTrack"]
304 target:self action:@selector(nextSong:)];
307 if ([df objectForKey:@"PrevTrack"] != nil) {
308 [[HotKeyCenter sharedCenter] addHotKey:@"PrevTrack"
309 combo:[df keyComboForKey:@"PrevTrack"]
310 target:self action:@selector(prevSong:)];
313 if ([df objectForKey:@"TrackInfo"] != nil) {
314 [[HotKeyCenter sharedCenter] addHotKey:@"TrackInfo"
315 combo:[df keyComboForKey:@"TrackInfo"]
316 target:self action:@selector(showCurrentTrackInfo)];
319 if ([df objectForKey:@"UpcomingSongs"] != nil) {
320 [[HotKeyCenter sharedCenter] addHotKey:@"UpcomingSongs"
321 combo:[df keyComboForKey:@"UpcomingSongs"]
322 target:self action:@selector(showUpcomingSongs)];
325 if ([df objectForKey:@"ToggleLoop"] != nil) {
326 [[HotKeyCenter sharedCenter] addHotKey:@"ToggleLoop"
327 combo:[df keyComboForKey:@"ToggleLoop"]
328 target:self action:NULL/*Set this to something*/];
331 if ([df objectForKey:@"ToggleShuffle"] != nil) {
332 [[HotKeyCenter sharedCenter] addHotKey:@"ToggleShuffle"
333 combo:[df keyComboForKey:@"ToggleShuffle"]
334 target:self action:NULL/*Set this to something*/];
337 if ([df objectForKey:@"IncrementVolume"] != nil) {
338 [[HotKeyCenter sharedCenter] addHotKey:@"IncrementVolume"
339 combo:[df keyComboForKey:@"IncrementVolume"]
340 target:self action:NULL/*Set this to something*/];
343 if ([df objectForKey:@"DecrementVolume"] != nil) {
344 [[HotKeyCenter sharedCenter] addHotKey:@"DecrementVolume"
345 combo:[df keyComboForKey:@"DecrementVolume"]
346 target:self action:NULL/*Set this to something*/];
349 if ([df objectForKey:@"IncrementRating"] != nil) {
350 [[HotKeyCenter sharedCenter] addHotKey:@"IncrementRating"
351 combo:[df keyComboForKey:@"IncrementRating"]
352 target:self action:NULL/*Set this to something*/];
355 if ([df objectForKey:@"DecrementRating"] != nil) {
356 [[HotKeyCenter sharedCenter] addHotKey:@"DecrementRating"
357 combo:[df keyComboForKey:@"DecrementRating"]
358 target:self action:NULL/*Set this to something*/];
362 - (void)showCurrentTrackInfo
364 NSString *title = [currentRemote currentSongTitle];
367 NSString *album = nil;
368 NSString *artist = nil;
369 NSString *time = nil;
374 if ( [df boolForKey:@"showAlbum"] ) {
375 album = [currentRemote currentSongAlbum];
378 if ( [df boolForKey:@"showArtist"] ) {
379 artist = [currentRemote currentSongArtist];
382 if ( [df boolForKey:@"showTime"] ) {
383 time = [currentRemote currentSongLength];
386 if ( [df boolForKey:@"showNumber"] ) {
387 trackNumber = [currentRemote currentSongTrack];
388 trackTotal = [currentRemote currentAlbumTrackCount];
391 if ( [df boolForKey:@"showRating"] ) {
392 rating = ( [currentRemote currentSongRating] * 5 );
395 [statusWindowController showSongWindowWithTitle:title
399 trackNumber:trackNumber
400 trackTotal:trackTotal
403 title = @"No song is playing.";
404 [statusWindowController showSongWindowWithTitle:title
414 - (void)showUpcomingSongs
416 int curPlaylist = [currentRemote currentPlaylistIndex];
417 int numSongs = [currentRemote numberOfSongsInPlaylistAtIndex:curPlaylist];
420 NSMutableArray *songList = [NSMutableArray arrayWithCapacity:5];
421 int numSongsInAdvance = [df integerForKey:@"SongsInAdvance"];
422 int curTrack = [currentRemote currentSongIndex];
425 for (i = curTrack + 1; i <= curTrack + numSongsInAdvance; i++) {
427 [songList addObject:[currentRemote songTitleAtIndex:i]];
431 [statusWindowController showUpcomingSongsWithTitles:songList];
434 [statusWindowController showUpcomingSongsWithTitles:[NSArray arrayWithObject:@"No upcoming songs."]];
438 /*************************************************************************/
440 #pragma mark WORKSPACE NOTIFICATION HANDLERS
441 /*************************************************************************/
443 - (void)applicationLaunched:(NSNotification *)note
445 if (!note || [[[note userInfo] objectForKey:@"NSApplicationName"] isEqualToString:[currentRemote playerFullName]]) {
446 [NSThread detachNewThreadSelector:@selector(startTimerInNewThread) toTarget:self withObject:nil];
448 playerRunningState = ITMTRemotePlayerRunning;
452 - (void)applicationTerminated:(NSNotification *)note
454 if (!note || [[[note userInfo] objectForKey:@"NSApplicationName"] isEqualToString:[currentRemote playerFullName]]) {
456 NSMenu *notRunningMenu = [[NSMenu alloc] initWithTitle:@""];
457 [notRunningMenu addItemWithTitle:[NSString stringWithFormat:@"Open %@", [currentRemote playerSimpleName]] action:@selector(showPlayer:) keyEquivalent:@""];
458 [notRunningMenu addItem:[NSMenuItem separatorItem]];
459 [notRunningMenu addItemWithTitle:@"Preferences" action:@selector(showPreferences:) keyEquivalent:@""];
460 [notRunningMenu addItemWithTitle:@"Quit" action:@selector(quitMenuTunes:) keyEquivalent:@""];
462 [refreshTimer invalidate];
463 [refreshTimer release];
466 playerRunningState = ITMTRemotePlayerNotRunning;
468 [statusItem setMenu:[self menuForNoPlayer]];
473 /*************************************************************************/
475 #pragma mark NSApplication DELEGATE METHODS
476 /*************************************************************************/
478 - (void)applicationWillTerminate:(NSNotification *)note
481 [[NSStatusBar systemStatusBar] removeStatusItem:statusItem];
485 /*************************************************************************/
487 #pragma mark DEALLOCATION METHOD
488 /*************************************************************************/
493 [refreshTimer invalidate];
494 [refreshTimer release];
498 [currentRemote halt];
499 [statusItem release];
500 [statusWindowController release];