1 #import "NewMainController.h"
2 #import "MenuController.h"
3 #import "PreferencesController.h"
4 #import "HotKeyCenter.h"
5 #import "StatusWindowController.h"
6 #import "StatusItemHack.h"
8 @interface MainController(Private)
9 - (ITMTRemote *)loadRemote;
12 - (void)setLatestSongIdentifier:(NSString *)newIdentifier;
13 - (void)showCurrentTrackInfo;
14 - (void)applicationLaunched:(NSNotification *)note;
15 - (void)applicationTerminated:(NSNotification *)note;
18 static MainController *sharedController;
20 @implementation MainController
22 + (MainController *)sharedController
24 return sharedController;
27 /*************************************************************************/
29 #pragma mark INITIALIZATION/DEALLOCATION METHODS
30 /*************************************************************************/
34 if ( ( self = [super init] ) ) {
35 sharedController = self;
37 remoteArray = [[NSMutableArray alloc] initWithCapacity:1];
38 statusWindowController = [[StatusWindowController alloc] init];
39 menuController = [[MenuController alloc] init];
40 df = [[NSUserDefaults standardUserDefaults] retain];
45 - (void)applicationDidFinishLaunching:(NSNotification *)note
47 currentRemote = [self loadRemote];
48 [currentRemote begin];
50 //Setup for notification of the remote player launching or quitting
51 [[[NSWorkspace sharedWorkspace] notificationCenter]
53 selector:@selector(applicationTerminated:)
54 name:NSWorkspaceDidTerminateApplicationNotification
57 [[[NSWorkspace sharedWorkspace] notificationCenter]
59 selector:@selector(applicationLaunched:)
60 name:NSWorkspaceDidLaunchApplicationNotification
63 if ( ! [df objectForKey:@"menu"] ) { // If this is nil, defaults have never been registered.
64 [[PreferencesController sharedPrefs] registerDefaults];
67 [StatusItemHack install];
68 statusItem = [[ITStatusItem alloc]
69 initWithStatusBar:[NSStatusBar systemStatusBar]
70 withLength:NSSquareStatusItemLength];
72 if ([currentRemote playerRunningState] == ITMTRemotePlayerRunning) {
73 [self applicationLaunched:nil];
75 [self applicationTerminated:nil];
78 [statusItem setImage:[NSImage imageNamed:@"menu"]];
79 [statusItem setAlternateImage:[NSImage imageNamed:@"selected_image"]];
82 - (ITMTRemote *)loadRemote
84 NSString *folderPath = [[NSBundle mainBundle] builtInPlugInsPath];
87 NSArray *bundlePathList = [NSBundle pathsForResourcesOfType:@"remote" inDirectory:folderPath];
88 NSEnumerator *enumerator = [bundlePathList objectEnumerator];
91 while ( (bundlePath = [enumerator nextObject]) ) {
92 NSBundle* remoteBundle = [NSBundle bundleWithPath:bundlePath];
95 Class remoteClass = [remoteBundle principalClass];
97 if ([remoteClass conformsToProtocol:@protocol(ITMTRemote)] &&
98 [remoteClass isKindOfClass:[NSObject class]]) {
100 id remote = [remoteClass remote];
101 [remoteArray addObject:remote];
106 // if ( [remoteArray count] > 0 ) { // UNCOMMENT WHEN WE HAVE > 1 PLUGIN
107 // if ( [remoteArray count] > 1 ) {
108 // [remoteArray sortUsingSelector:@selector(sortAlpha:)];
110 // [self loadModuleAccessUI]; //Comment out this line to disable remote visibility
113 // NSLog(@"%@", [remoteArray objectAtIndex:0]); //DEBUG
114 return [remoteArray objectAtIndex:0];
117 /*************************************************************************/
119 #pragma mark INSTANCE METHODS
120 /*************************************************************************/
122 - (void)startTimerInNewThread
124 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
125 NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
126 refreshTimer = [[NSTimer scheduledTimerWithTimeInterval:0.5
128 selector:@selector(timerUpdate)
130 repeats:YES] retain];
135 - (BOOL)songIsPlaying
137 return ( ! ([[currentRemote currentSongUniqueIdentifier] isEqualToString:@"0-0"]) );
140 - (BOOL)radioIsPlaying
142 return ( [currentRemote currentPlaylistClass] == ITMTRemotePlayerRadioPlaylist );
147 return ( ! [[currentRemote currentSongUniqueIdentifier] isEqualToString:_latestSongIdentifier] );
150 - (NSString *)latestSongIdentifier
152 return _latestSongIdentifier;
155 - (void)setLatestSongIdentifier:(NSString *)newIdentifier
157 [_latestSongIdentifier autorelease];
158 _latestSongIdentifier = [newIdentifier copy];
163 //This huge if statement is being nasty
164 /*if ( ( [self songChanged] ) ||
165 ( ([self radioIsPlaying]) && (latestPlaylistClass != ITMTRemotePlayerRadioPlaylist) ) ||
166 ( (! [self radioIsPlaying]) && (latestPlaylistClass == ITMTRemotePlayerRadioPlaylist) ) )*/
168 if ([self songChanged]) {
169 [self setLatestSongIdentifier:[currentRemote currentSongUniqueIdentifier]];
170 latestPlaylistClass = [currentRemote currentPlaylistClass];
171 [menuController rebuildSubmenus];
173 if ( [df boolForKey:@"showSongInfoOnChange"] ) {
174 [self showCurrentTrackInfo];
181 if ([currentRemote playerRunningState] == ITMTRemotePlayerRunning) {
182 [statusItem setMenu:[menuController menu]];
184 [statusItem setMenu:[menuController menuForNoPlayer]];
196 ITMTRemotePlayerPlayingState state = [currentRemote playerPlayingState];
198 if (state == ITMTRemotePlayerPlaying) {
199 [currentRemote pause];
200 } else if ((state == ITMTRemotePlayerForwarding) || (state == ITMTRemotePlayerRewinding)) {
201 [currentRemote pause];
202 [currentRemote play];
204 [currentRemote play];
210 [currentRemote goToNextSong];
215 [currentRemote goToPreviousSong];
220 [currentRemote forward];
225 [currentRemote rewind];
228 - (void)selectPlaylistAtIndex:(int)index
230 [currentRemote switchToPlaylistAtIndex:index];
233 - (void)selectSongAtIndex:(int)index
235 [currentRemote switchToSongAtIndex:index];
238 - (void)selectSongRating:(int)rating
240 [currentRemote setCurrentSongRating:(float)rating / 100.0];
243 - (void)selectEQPresetAtIndex:(int)index
245 [currentRemote switchToEQAtIndex:index];
250 if ( ( playerRunningState == ITMTRemotePlayerRunning) ) {
251 [currentRemote showPrimaryInterface];
253 if (![[NSWorkspace sharedWorkspace] launchApplication:[currentRemote playerFullName]]) {
254 NSLog(@"Error Launching Player");
259 - (void)showPreferences
261 [[PreferencesController sharedPrefs] setController:self];
262 [[PreferencesController sharedPrefs] showPrefsWindow:self];
265 - (void)quitMenuTunes
267 [NSApp terminate:self];
273 - (void)closePreferences
275 if ( ( playerRunningState == ITMTRemotePlayerRunning) ) {
280 - (ITMTRemote *)currentRemote
282 return currentRemote;
293 [[HotKeyCenter sharedCenter] removeHotKey:@"PlayPause"];
294 [[HotKeyCenter sharedCenter] removeHotKey:@"NextTrack"];
295 [[HotKeyCenter sharedCenter] removeHotKey:@"PrevTrack"];
296 [[HotKeyCenter sharedCenter] removeHotKey:@"TrackInfo"];
297 [[HotKeyCenter sharedCenter] removeHotKey:@"UpcomingSongs"];
298 [[HotKeyCenter sharedCenter] removeHotKey:@"ToggleLoop"];
299 [[HotKeyCenter sharedCenter] removeHotKey:@"ToggleShuffle"];
300 [[HotKeyCenter sharedCenter] removeHotKey:@"IncrementVolume"];
301 [[HotKeyCenter sharedCenter] removeHotKey:@"DecrementVolume"];
302 [[HotKeyCenter sharedCenter] removeHotKey:@"IncrementRating"];
303 [[HotKeyCenter sharedCenter] removeHotKey:@"DecrementRating"];
308 if ([df objectForKey:@"PlayPause"] != nil) {
309 [[HotKeyCenter sharedCenter] addHotKey:@"PlayPause"
310 combo:[df keyComboForKey:@"PlayPause"]
311 target:self action:@selector(playPause)];
314 if ([df objectForKey:@"NextTrack"] != nil) {
315 [[HotKeyCenter sharedCenter] addHotKey:@"NextTrack"
316 combo:[df keyComboForKey:@"NextTrack"]
317 target:self action:@selector(nextSong)];
320 if ([df objectForKey:@"PrevTrack"] != nil) {
321 [[HotKeyCenter sharedCenter] addHotKey:@"PrevTrack"
322 combo:[df keyComboForKey:@"PrevTrack"]
323 target:self action:@selector(prevSong)];
326 if ([df objectForKey:@"TrackInfo"] != nil) {
327 [[HotKeyCenter sharedCenter] addHotKey:@"TrackInfo"
328 combo:[df keyComboForKey:@"TrackInfo"]
329 target:self action:@selector(showCurrentTrackInfo)];
332 if ([df objectForKey:@"UpcomingSongs"] != nil) {
333 [[HotKeyCenter sharedCenter] addHotKey:@"UpcomingSongs"
334 combo:[df keyComboForKey:@"UpcomingSongs"]
335 target:self action:@selector(showUpcomingSongs)];
338 if ([df objectForKey:@"ToggleLoop"] != nil) {
339 [[HotKeyCenter sharedCenter] addHotKey:@"ToggleLoop"
340 combo:[df keyComboForKey:@"ToggleLoop"]
341 target:self action:@selector(toggleLoop)];
344 if ([df objectForKey:@"ToggleShuffle"] != nil) {
345 [[HotKeyCenter sharedCenter] addHotKey:@"ToggleShuffle"
346 combo:[df keyComboForKey:@"ToggleShuffle"]
347 target:self action:@selector(toggleShuffle)];
350 if ([df objectForKey:@"IncrementVolume"] != nil) {
351 [[HotKeyCenter sharedCenter] addHotKey:@"IncrementVolume"
352 combo:[df keyComboForKey:@"IncrementVolume"]
353 target:self action:@selector(incrementVolume)];
356 if ([df objectForKey:@"DecrementVolume"] != nil) {
357 [[HotKeyCenter sharedCenter] addHotKey:@"DecrementVolume"
358 combo:[df keyComboForKey:@"DecrementVolume"]
359 target:self action:@selector(decrementVolume)];
362 if ([df objectForKey:@"IncrementRating"] != nil) {
363 [[HotKeyCenter sharedCenter] addHotKey:@"IncrementRating"
364 combo:[df keyComboForKey:@"IncrementRating"]
365 target:self action:@selector(incrementRating)];
368 if ([df objectForKey:@"DecrementRating"] != nil) {
369 [[HotKeyCenter sharedCenter] addHotKey:@"DecrementRating"
370 combo:[df keyComboForKey:@"DecrementRating"]
371 target:self action:@selector(decrementRating)];
375 - (void)showCurrentTrackInfo
377 NSString *title = [currentRemote currentSongTitle];
380 NSString *album = nil;
381 NSString *artist = nil;
382 NSString *time = nil;
387 if ( [df boolForKey:@"showAlbum"] ) {
388 album = [currentRemote currentSongAlbum];
391 if ( [df boolForKey:@"showArtist"] ) {
392 artist = [currentRemote currentSongArtist];
395 if ( [df boolForKey:@"showTime"] ) {
396 time = [currentRemote currentSongLength];
399 if ( [df boolForKey:@"showNumber"] ) {
400 trackNumber = [currentRemote currentSongTrack];
401 trackTotal = [currentRemote currentAlbumTrackCount];
404 if ( [df boolForKey:@"showRating"] ) {
405 rating = ( [currentRemote currentSongRating] * 5 );
408 [statusWindowController showSongWindowWithTitle:title
412 trackNumber:trackNumber
413 trackTotal:trackTotal
416 title = @"No song is playing.";
417 [statusWindowController showSongWindowWithTitle:title
427 - (void)showUpcomingSongs
429 int curPlaylist = [currentRemote currentPlaylistIndex];
430 int numSongs = [currentRemote numberOfSongsInPlaylistAtIndex:curPlaylist];
433 NSMutableArray *songList = [NSMutableArray arrayWithCapacity:5];
434 int numSongsInAdvance = [df integerForKey:@"SongsInAdvance"];
435 int curTrack = [currentRemote currentSongIndex];
438 for (i = curTrack + 1; i <= curTrack + numSongsInAdvance; i++) {
440 [songList addObject:[currentRemote songTitleAtIndex:i]];
444 [statusWindowController showUpcomingSongsWithTitles:songList];
447 [statusWindowController showUpcomingSongsWithTitles:[NSArray arrayWithObject:@"No upcoming songs."]];
451 - (void)incrementVolume
453 float volume = [currentRemote volume];
458 [currentRemote setVolume:volume];
460 //Show volume status window
461 [statusWindowController showVolumeWindowWithLevel:volume];
464 - (void)decrementVolume
466 float volume = [currentRemote volume];
471 [currentRemote setVolume:volume];
473 //Show volume status window
474 [statusWindowController showVolumeWindowWithLevel:volume];
477 - (void)incrementRating
479 float rating = [currentRemote currentSongRating];
484 [currentRemote setCurrentSongRating:rating];
486 //Show rating status window
487 [statusWindowController showRatingWindowWithLevel:rating];
490 - (void)decrementRating
492 float rating = [currentRemote currentSongRating];
497 [currentRemote setCurrentSongRating:rating];
499 //Show rating status window
500 [statusWindowController showRatingWindowWithLevel:rating];
505 ITMTRemotePlayerRepeatMode repeatMode = [currentRemote repeatMode];
507 switch (repeatMode) {
508 case ITMTRemotePlayerRepeatOff:
509 repeatMode = ITMTRemotePlayerRepeatAll;
511 case ITMTRemotePlayerRepeatAll:
512 repeatMode = ITMTRemotePlayerRepeatOne;
514 case ITMTRemotePlayerRepeatOne:
515 repeatMode = ITMTRemotePlayerRepeatOff;
518 [currentRemote setRepeatMode:repeatMode];
520 //Show loop status window
521 [statusWindowController showLoopWindowWithMode:repeatMode];
524 - (void)toggleShuffle
526 bool newShuffleEnabled = ![currentRemote shuffleEnabled];
527 [currentRemote setShuffleEnabled:newShuffleEnabled];
528 //Show shuffle status window
529 [statusWindowController showLoopWindowWithMode:newShuffleEnabled];
532 /*************************************************************************/
534 #pragma mark WORKSPACE NOTIFICATION HANDLERS
535 /*************************************************************************/
537 - (void)applicationLaunched:(NSNotification *)note
539 if (!note || [[[note userInfo] objectForKey:@"NSApplicationName"] isEqualToString:[currentRemote playerFullName]]) {
540 [currentRemote begin];
541 [self setLatestSongIdentifier:@""];
543 [NSThread detachNewThreadSelector:@selector(startTimerInNewThread) toTarget:self withObject:nil];
545 playerRunningState = ITMTRemotePlayerRunning;
549 - (void)applicationTerminated:(NSNotification *)note
551 if (!note || [[[note userInfo] objectForKey:@"NSApplicationName"] isEqualToString:[currentRemote playerFullName]]) {
552 [currentRemote halt];
553 [refreshTimer invalidate];
554 [refreshTimer release];
557 playerRunningState = ITMTRemotePlayerNotRunning;
562 /*************************************************************************/
564 #pragma mark NSApplication DELEGATE METHODS
565 /*************************************************************************/
567 - (void)applicationWillTerminate:(NSNotification *)note
570 [[NSStatusBar systemStatusBar] removeStatusItem:statusItem];
574 /*************************************************************************/
576 #pragma mark DEALLOCATION METHOD
577 /*************************************************************************/
581 [self applicationTerminated:nil];
582 [statusItem release];
583 [statusWindowController release];
584 [menuController release];