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;
15 - (void)applicationLaunched:(NSNotification *)note;
16 - (void)applicationTerminated:(NSNotification *)note;
19 static MainController *sharedController;
21 @implementation MainController
23 + (MainController *)sharedController
25 return sharedController;
28 /*************************************************************************/
30 #pragma mark INITIALIZATION/DEALLOCATION METHODS
31 /*************************************************************************/
35 if ( ( self = [super init] ) ) {
36 sharedController = self;
38 remoteArray = [[NSMutableArray alloc] initWithCapacity:1];
39 statusWindowController = [[StatusWindowController alloc] init];
40 menuController = [[MenuController alloc] init];
41 df = [[NSUserDefaults standardUserDefaults] retain];
42 [self setLatestSongIdentifier:@"0-0"];
47 - (void)applicationDidFinishLaunching:(NSNotification *)note
49 currentRemote = [self loadRemote];
50 [currentRemote begin];
52 //Setup for notification of the remote player launching or quitting
53 [[[NSWorkspace sharedWorkspace] notificationCenter]
55 selector:@selector(applicationTerminated:)
56 name:NSWorkspaceDidTerminateApplicationNotification
59 [[[NSWorkspace sharedWorkspace] notificationCenter]
61 selector:@selector(applicationLaunched:)
62 name:NSWorkspaceDidLaunchApplicationNotification
65 if ( ! [df objectForKey:@"menu"] ) { // If this is nil, defaults have never been registered.
66 [[PreferencesController sharedPrefs] registerDefaults];
69 [StatusItemHack install];
70 statusItem = [[ITStatusItem alloc]
71 initWithStatusBar:[NSStatusBar systemStatusBar]
72 withLength:NSSquareStatusItemLength];
74 if ([currentRemote playerRunningState] == ITMTRemotePlayerRunning) {
75 [self applicationLaunched:nil];
77 [self applicationTerminated:nil];
80 [statusItem setImage:[NSImage imageNamed:@"menu"]];
81 [statusItem setAlternateImage:[NSImage imageNamed:@"selected_image"]];
84 - (ITMTRemote *)loadRemote
86 NSString *folderPath = [[NSBundle mainBundle] builtInPlugInsPath];
89 NSArray *bundlePathList = [NSBundle pathsForResourcesOfType:@"remote" inDirectory:folderPath];
90 NSEnumerator *enumerator = [bundlePathList objectEnumerator];
93 while ( (bundlePath = [enumerator nextObject]) ) {
94 NSBundle* remoteBundle = [NSBundle bundleWithPath:bundlePath];
97 Class remoteClass = [remoteBundle principalClass];
99 if ([remoteClass conformsToProtocol:@protocol(ITMTRemote)] &&
100 [remoteClass isKindOfClass:[NSObject class]]) {
102 id remote = [remoteClass remote];
103 [remoteArray addObject:remote];
108 // if ( [remoteArray count] > 0 ) { // UNCOMMENT WHEN WE HAVE > 1 PLUGIN
109 // if ( [remoteArray count] > 1 ) {
110 // [remoteArray sortUsingSelector:@selector(sortAlpha:)];
112 // [self loadModuleAccessUI]; //Comment out this line to disable remote visibility
115 // NSLog(@"%@", [remoteArray objectAtIndex:0]); //DEBUG
116 return [remoteArray objectAtIndex:0];
119 /*************************************************************************/
121 #pragma mark INSTANCE METHODS
122 /*************************************************************************/
124 - (void)startTimerInNewThread
126 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
127 NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
128 refreshTimer = [[NSTimer scheduledTimerWithTimeInterval:0.5
130 selector:@selector(timerUpdate)
132 repeats:YES] retain];
137 - (BOOL)songIsPlaying
139 return ( ! ([[currentRemote currentSongUniqueIdentifier] isEqualToString:@"0-0"]) );
142 - (BOOL)radioIsPlaying
144 return ( [currentRemote currentPlaylistClass] == ITMTRemotePlayerRadioPlaylist );
149 return ( ! [[currentRemote currentSongUniqueIdentifier] isEqualToString:_latestSongIdentifier] );
152 - (NSString *)latestSongIdentifier
154 return _latestSongIdentifier;
157 - (void)setLatestSongIdentifier:(NSString *)newIdentifier
159 [_latestSongIdentifier autorelease];
160 _latestSongIdentifier = [newIdentifier copy];
165 if ( ( [self songChanged] ) ||
166 ( ([self radioIsPlaying]) && (latestPlaylistClass != ITMTRemotePlayerRadioPlaylist) ) ||
167 ( (! [self radioIsPlaying]) && (latestPlaylistClass == ITMTRemotePlayerRadioPlaylist) ) ) {
168 [self setLatestSongIdentifier:[currentRemote currentSongUniqueIdentifier]];
169 latestPlaylistClass = [currentRemote currentPlaylistClass];
170 [menuController rebuildSubmenus];
172 if ( [df boolForKey:@"showSongInfoOnChange"] ) {
173 [self showCurrentTrackInfo];
180 if ([currentRemote playerRunningState] == ITMTRemotePlayerRunning) {
181 [statusItem setMenu:[menuController menu]];
183 [statusItem setMenu:[menuController menuForNoPlayer]];
195 ITMTRemotePlayerPlayingState state = [currentRemote playerPlayingState];
197 if (state == ITMTRemotePlayerPlaying) {
198 [currentRemote pause];
199 } else if ((state == ITMTRemotePlayerForwarding) || (state == ITMTRemotePlayerRewinding)) {
200 [currentRemote pause];
201 [currentRemote play];
203 [currentRemote play];
209 [currentRemote goToNextSong];
214 [currentRemote goToPreviousSong];
219 [currentRemote forward];
224 [currentRemote rewind];
227 - (void)selectPlaylistAtIndex:(int)index
229 [currentRemote switchToPlaylistAtIndex:index];
232 - (void)selectSongAtIndex:(int)index
234 [currentRemote switchToSongAtIndex:index];
237 - (void)selectSongRating:(int)rating
239 [currentRemote setCurrentSongRating:(float)rating / 100.0];
242 - (void)selectEQPresetAtIndex:(int)index
244 [currentRemote switchToEQAtIndex:index];
249 if ( ( playerRunningState == ITMTRemotePlayerRunning) ) {
250 [currentRemote showPrimaryInterface];
252 if (![[NSWorkspace sharedWorkspace] launchApplication:[currentRemote playerFullName]]) {
253 NSLog(@"Error Launching Player");
258 - (void)showPreferences
260 [[PreferencesController sharedPrefs] setController:self];
261 [[PreferencesController sharedPrefs] showPrefsWindow:self];
264 - (void)quitMenuTunes
266 [NSApp terminate:self];
272 - (void)closePreferences
274 if ( ( playerRunningState == ITMTRemotePlayerRunning) ) {
279 - (ITMTRemote *)currentRemote
281 return currentRemote;
292 [[HotKeyCenter sharedCenter] removeHotKey:@"PlayPause"];
293 [[HotKeyCenter sharedCenter] removeHotKey:@"NextTrack"];
294 [[HotKeyCenter sharedCenter] removeHotKey:@"PrevTrack"];
295 [[HotKeyCenter sharedCenter] removeHotKey:@"TrackInfo"];
296 [[HotKeyCenter sharedCenter] removeHotKey:@"UpcomingSongs"];
297 [[HotKeyCenter sharedCenter] removeHotKey:@"ToggleLoop"];
298 [[HotKeyCenter sharedCenter] removeHotKey:@"ToggleShuffle"];
299 [[HotKeyCenter sharedCenter] removeHotKey:@"IncrementVolume"];
300 [[HotKeyCenter sharedCenter] removeHotKey:@"DecrementVolume"];
301 [[HotKeyCenter sharedCenter] removeHotKey:@"IncrementRating"];
302 [[HotKeyCenter sharedCenter] removeHotKey:@"DecrementRating"];
307 if ([df objectForKey:@"PlayPause"] != nil) {
308 [[HotKeyCenter sharedCenter] addHotKey:@"PlayPause"
309 combo:[df keyComboForKey:@"PlayPause"]
310 target:self action:@selector(playPause)];
313 if ([df objectForKey:@"NextTrack"] != nil) {
314 [[HotKeyCenter sharedCenter] addHotKey:@"NextTrack"
315 combo:[df keyComboForKey:@"NextTrack"]
316 target:self action:@selector(nextSong)];
319 if ([df objectForKey:@"PrevTrack"] != nil) {
320 [[HotKeyCenter sharedCenter] addHotKey:@"PrevTrack"
321 combo:[df keyComboForKey:@"PrevTrack"]
322 target:self action:@selector(prevSong)];
325 if ([df objectForKey:@"TrackInfo"] != nil) {
326 [[HotKeyCenter sharedCenter] addHotKey:@"TrackInfo"
327 combo:[df keyComboForKey:@"TrackInfo"]
328 target:self action:@selector(showCurrentTrackInfo)];
331 if ([df objectForKey:@"UpcomingSongs"] != nil) {
332 [[HotKeyCenter sharedCenter] addHotKey:@"UpcomingSongs"
333 combo:[df keyComboForKey:@"UpcomingSongs"]
334 target:self action:@selector(showUpcomingSongs)];
337 /* if ([df objectForKey:@"ToggleLoop"] != nil) {
338 [[HotKeyCenter sharedCenter] addHotKey:@"ToggleLoop"
339 combo:[df keyComboForKey:@"ToggleLoop"]
340 target:self action:NULL];
343 if ([df objectForKey:@"ToggleShuffle"] != nil) {
344 [[HotKeyCenter sharedCenter] addHotKey:@"ToggleShuffle"
345 combo:[df keyComboForKey:@"ToggleShuffle"]
346 target:self action:NULL];
349 if ([df objectForKey:@"IncrementVolume"] != nil) {
350 [[HotKeyCenter sharedCenter] addHotKey:@"IncrementVolume"
351 combo:[df keyComboForKey:@"IncrementVolume"]
352 target:self action:NULL];
355 if ([df objectForKey:@"DecrementVolume"] != nil) {
356 [[HotKeyCenter sharedCenter] addHotKey:@"DecrementVolume"
357 combo:[df keyComboForKey:@"DecrementVolume"]
358 target:self action:NULL];
361 if ([df objectForKey:@"IncrementRating"] != nil) {
362 [[HotKeyCenter sharedCenter] addHotKey:@"IncrementRating"
363 combo:[df keyComboForKey:@"IncrementRating"]
364 target:self action:NULL];
367 if ([df objectForKey:@"DecrementRating"] != nil) {
368 [[HotKeyCenter sharedCenter] addHotKey:@"DecrementRating"
369 combo:[df keyComboForKey:@"DecrementRating"]
370 target:self action:NULL];
374 - (void)showCurrentTrackInfo
376 NSString *title = [currentRemote currentSongTitle];
379 NSString *album = nil;
380 NSString *artist = nil;
381 NSString *time = nil;
386 if ( [df boolForKey:@"showAlbum"] ) {
387 album = [currentRemote currentSongAlbum];
390 if ( [df boolForKey:@"showArtist"] ) {
391 artist = [currentRemote currentSongArtist];
394 if ( [df boolForKey:@"showTime"] ) {
395 time = [currentRemote currentSongLength];
398 if ( [df boolForKey:@"showNumber"] ) {
399 trackNumber = [currentRemote currentSongTrack];
400 trackTotal = [currentRemote currentAlbumTrackCount];
403 if ( [df boolForKey:@"showRating"] ) {
404 rating = ( [currentRemote currentSongRating] * 5 );
407 [statusWindowController showSongWindowWithTitle:title
411 trackNumber:trackNumber
412 trackTotal:trackTotal
415 title = @"No song is playing.";
416 [statusWindowController showSongWindowWithTitle:title
426 - (void)showUpcomingSongs
428 int curPlaylist = [currentRemote currentPlaylistIndex];
429 int numSongs = [currentRemote numberOfSongsInPlaylistAtIndex:curPlaylist];
432 NSMutableArray *songList = [NSMutableArray arrayWithCapacity:5];
433 int numSongsInAdvance = [df integerForKey:@"SongsInAdvance"];
434 int curTrack = [currentRemote currentSongIndex];
437 for (i = curTrack + 1; i <= curTrack + numSongsInAdvance; i++) {
439 [songList addObject:[currentRemote songTitleAtIndex:i]];
443 [statusWindowController showUpcomingSongsWithTitles:songList];
446 [statusWindowController showUpcomingSongsWithTitles:[NSArray arrayWithObject:@"No upcoming songs."]];
450 /*************************************************************************/
452 #pragma mark WORKSPACE NOTIFICATION HANDLERS
453 /*************************************************************************/
455 - (void)applicationLaunched:(NSNotification *)note
457 if (!note || [[[note userInfo] objectForKey:@"NSApplicationName"] isEqualToString:[currentRemote playerFullName]]) {
459 [NSThread detachNewThreadSelector:@selector(startTimerInNewThread) toTarget:self withObject:nil];
461 playerRunningState = ITMTRemotePlayerRunning;
465 - (void)applicationTerminated:(NSNotification *)note
467 if (!note || [[[note userInfo] objectForKey:@"NSApplicationName"] isEqualToString:[currentRemote playerFullName]]) {
468 [refreshTimer invalidate];
469 [refreshTimer release];
472 playerRunningState = ITMTRemotePlayerNotRunning;
477 /*************************************************************************/
479 #pragma mark NSApplication DELEGATE METHODS
480 /*************************************************************************/
482 - (void)applicationWillTerminate:(NSNotification *)note
485 [[NSStatusBar systemStatusBar] removeStatusItem:statusItem];
489 /*************************************************************************/
491 #pragma mark DEALLOCATION METHOD
492 /*************************************************************************/
497 [refreshTimer invalidate];
498 [refreshTimer release];
502 [currentRemote halt];
503 [statusItem release];
504 [statusWindowController release];
505 [menuController release];