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];
46 - (void)applicationDidFinishLaunching:(NSNotification *)note
48 currentRemote = [self loadRemote];
49 [currentRemote begin];
51 //Setup for notification of the remote player launching or quitting
52 [[[NSWorkspace sharedWorkspace] notificationCenter]
54 selector:@selector(applicationTerminated:)
55 name:NSWorkspaceDidTerminateApplicationNotification
58 [[[NSWorkspace sharedWorkspace] notificationCenter]
60 selector:@selector(applicationLaunched:)
61 name:NSWorkspaceDidLaunchApplicationNotification
64 if ( ! [df objectForKey:@"menu"] ) { // If this is nil, defaults have never been registered.
65 [[PreferencesController sharedPrefs] registerDefaults];
68 [StatusItemHack install];
69 statusItem = [[ITStatusItem alloc]
70 initWithStatusBar:[NSStatusBar systemStatusBar]
71 withLength:NSSquareStatusItemLength];
73 if ([currentRemote playerRunningState] == ITMTRemotePlayerRunning) {
74 [self applicationLaunched:nil];
76 [self applicationTerminated:nil];
79 [statusItem setImage:[NSImage imageNamed:@"menu"]];
80 [statusItem setAlternateImage:[NSImage imageNamed:@"selected_image"]];
83 - (ITMTRemote *)loadRemote
85 NSString *folderPath = [[NSBundle mainBundle] builtInPlugInsPath];
88 NSArray *bundlePathList = [NSBundle pathsForResourcesOfType:@"remote" inDirectory:folderPath];
89 NSEnumerator *enumerator = [bundlePathList objectEnumerator];
92 while ( (bundlePath = [enumerator nextObject]) ) {
93 NSBundle* remoteBundle = [NSBundle bundleWithPath:bundlePath];
96 Class remoteClass = [remoteBundle principalClass];
98 if ([remoteClass conformsToProtocol:@protocol(ITMTRemote)] &&
99 [remoteClass isKindOfClass:[NSObject class]]) {
101 id remote = [remoteClass remote];
102 [remoteArray addObject:remote];
107 // if ( [remoteArray count] > 0 ) { // UNCOMMENT WHEN WE HAVE > 1 PLUGIN
108 // if ( [remoteArray count] > 1 ) {
109 // [remoteArray sortUsingSelector:@selector(sortAlpha:)];
111 // [self loadModuleAccessUI]; //Comment out this line to disable remote visibility
114 // NSLog(@"%@", [remoteArray objectAtIndex:0]); //DEBUG
115 return [remoteArray objectAtIndex:0];
118 /*************************************************************************/
120 #pragma mark INSTANCE METHODS
121 /*************************************************************************/
123 - (void)startTimerInNewThread
125 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
126 NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
127 refreshTimer = [[NSTimer scheduledTimerWithTimeInterval:0.5
129 selector:@selector(timerUpdate)
131 repeats:YES] retain];
136 - (BOOL)songIsPlaying
138 return ( ! ([[currentRemote currentSongUniqueIdentifier] isEqualToString:@"0-0"]) );
141 - (BOOL)radioIsPlaying
143 return ( [currentRemote currentPlaylistClass] == ITMTRemotePlayerRadioPlaylist );
148 return ( ! [[currentRemote currentSongUniqueIdentifier] isEqualToString:_latestSongIdentifier] );
151 - (NSString *)latestSongIdentifier
153 return _latestSongIdentifier;
156 - (void)setLatestSongIdentifier:(NSString *)newIdentifier
158 [_latestSongIdentifier autorelease];
159 _latestSongIdentifier = [newIdentifier copy];
164 if ( ( [self songChanged] ) ||
165 ( ([self radioIsPlaying]) && (latestPlaylistClass != ITMTRemotePlayerRadioPlaylist) ) ||
166 ( (! [self radioIsPlaying]) && (latestPlaylistClass == ITMTRemotePlayerRadioPlaylist) ) ) {
167 [self setLatestSongIdentifier:[currentRemote currentSongUniqueIdentifier]];
168 latestPlaylistClass = [currentRemote currentPlaylistClass];
169 [menuController rebuildSubmenus];
171 if ( [df boolForKey:@"showSongInfoOnChange"] ) {
172 [self showCurrentTrackInfo];
179 if ([currentRemote playerRunningState] == ITMTRemotePlayerRunning) {
180 [statusItem setMenu:[menuController menu]];
182 [statusItem setMenu:[menuController menuForNoPlayer]];
194 ITMTRemotePlayerPlayingState state = [currentRemote playerPlayingState];
196 if (state == ITMTRemotePlayerPlaying) {
197 [currentRemote pause];
198 } else if ((state == ITMTRemotePlayerForwarding) || (state == ITMTRemotePlayerRewinding)) {
199 [currentRemote pause];
200 [currentRemote play];
202 [currentRemote play];
208 [currentRemote goToNextSong];
213 [currentRemote goToPreviousSong];
218 [currentRemote forward];
223 [currentRemote rewind];
226 - (void)selectPlaylistAtIndex:(int)index
228 [currentRemote switchToPlaylistAtIndex:index];
231 - (void)selectSongAtIndex:(int)index
233 [currentRemote switchToSongAtIndex:index];
236 - (void)selectSongRating:(int)rating
238 [currentRemote setCurrentSongRating:(float)rating / 100.0];
241 - (void)selectEQPresetAtIndex:(int)index
243 [currentRemote switchToEQAtIndex:index];
248 if ( ( playerRunningState == ITMTRemotePlayerRunning) ) {
249 [currentRemote showPrimaryInterface];
251 if (![[NSWorkspace sharedWorkspace] launchApplication:[currentRemote playerFullName]]) {
252 NSLog(@"Error Launching Player");
257 - (void)showPreferences
259 [[PreferencesController sharedPrefs] setController:self];
260 [[PreferencesController sharedPrefs] showPrefsWindow:self];
263 - (void)quitMenuTunes
265 [NSApp terminate:self];
271 - (void)closePreferences
273 if ( ( playerRunningState == ITMTRemotePlayerRunning) ) {
278 - (ITMTRemote *)currentRemote
280 return currentRemote;
291 [[HotKeyCenter sharedCenter] removeHotKey:@"PlayPause"];
292 [[HotKeyCenter sharedCenter] removeHotKey:@"NextTrack"];
293 [[HotKeyCenter sharedCenter] removeHotKey:@"PrevTrack"];
294 [[HotKeyCenter sharedCenter] removeHotKey:@"TrackInfo"];
295 [[HotKeyCenter sharedCenter] removeHotKey:@"UpcomingSongs"];
296 [[HotKeyCenter sharedCenter] removeHotKey:@"ToggleLoop"];
297 [[HotKeyCenter sharedCenter] removeHotKey:@"ToggleShuffle"];
298 [[HotKeyCenter sharedCenter] removeHotKey:@"IncrementVolume"];
299 [[HotKeyCenter sharedCenter] removeHotKey:@"DecrementVolume"];
300 [[HotKeyCenter sharedCenter] removeHotKey:@"IncrementRating"];
301 [[HotKeyCenter sharedCenter] removeHotKey:@"DecrementRating"];
306 if ([df objectForKey:@"PlayPause"] != nil) {
307 [[HotKeyCenter sharedCenter] addHotKey:@"PlayPause"
308 combo:[df keyComboForKey:@"PlayPause"]
309 target:self action:@selector(playPause)];
312 if ([df objectForKey:@"NextTrack"] != nil) {
313 [[HotKeyCenter sharedCenter] addHotKey:@"NextTrack"
314 combo:[df keyComboForKey:@"NextTrack"]
315 target:self action:@selector(nextSong)];
318 if ([df objectForKey:@"PrevTrack"] != nil) {
319 [[HotKeyCenter sharedCenter] addHotKey:@"PrevTrack"
320 combo:[df keyComboForKey:@"PrevTrack"]
321 target:self action:@selector(prevSong)];
324 if ([df objectForKey:@"TrackInfo"] != nil) {
325 [[HotKeyCenter sharedCenter] addHotKey:@"TrackInfo"
326 combo:[df keyComboForKey:@"TrackInfo"]
327 target:self action:@selector(showCurrentTrackInfo)];
330 if ([df objectForKey:@"UpcomingSongs"] != nil) {
331 [[HotKeyCenter sharedCenter] addHotKey:@"UpcomingSongs"
332 combo:[df keyComboForKey:@"UpcomingSongs"]
333 target:self action:@selector(showUpcomingSongs)];
336 if ([df objectForKey:@"ToggleLoop"] != nil) {
337 [[HotKeyCenter sharedCenter] addHotKey:@"ToggleLoop"
338 combo:[df keyComboForKey:@"ToggleLoop"]
339 target:self action:@selector(toggleLoop)];
342 if ([df objectForKey:@"ToggleShuffle"] != nil) {
343 [[HotKeyCenter sharedCenter] addHotKey:@"ToggleShuffle"
344 combo:[df keyComboForKey:@"ToggleShuffle"]
345 target:self action:@selector(toggleShuffle)];
348 if ([df objectForKey:@"IncrementVolume"] != nil) {
349 [[HotKeyCenter sharedCenter] addHotKey:@"IncrementVolume"
350 combo:[df keyComboForKey:@"IncrementVolume"]
351 target:self action:@selector(incrementVolume)];
354 if ([df objectForKey:@"DecrementVolume"] != nil) {
355 [[HotKeyCenter sharedCenter] addHotKey:@"DecrementVolume"
356 combo:[df keyComboForKey:@"DecrementVolume"]
357 target:self action:@selector(decrementVolume)];
360 if ([df objectForKey:@"IncrementRating"] != nil) {
361 [[HotKeyCenter sharedCenter] addHotKey:@"IncrementRating"
362 combo:[df keyComboForKey:@"IncrementRating"]
363 target:self action:@selector(incrementRating)];
366 if ([df objectForKey:@"DecrementRating"] != nil) {
367 [[HotKeyCenter sharedCenter] addHotKey:@"DecrementRating"
368 combo:[df keyComboForKey:@"DecrementRating"]
369 target:self action:@selector(decrementRating)];
373 - (void)showCurrentTrackInfo
375 NSString *title = [currentRemote currentSongTitle];
378 NSString *album = nil;
379 NSString *artist = nil;
380 NSString *time = nil;
385 if ( [df boolForKey:@"showAlbum"] ) {
386 album = [currentRemote currentSongAlbum];
389 if ( [df boolForKey:@"showArtist"] ) {
390 artist = [currentRemote currentSongArtist];
393 if ( [df boolForKey:@"showTime"] ) {
394 time = [currentRemote currentSongLength];
397 if ( [df boolForKey:@"showNumber"] ) {
398 trackNumber = [currentRemote currentSongTrack];
399 trackTotal = [currentRemote currentAlbumTrackCount];
402 if ( [df boolForKey:@"showRating"] ) {
403 rating = ( [currentRemote currentSongRating] * 5 );
406 [statusWindowController showSongWindowWithTitle:title
410 trackNumber:trackNumber
411 trackTotal:trackTotal
414 title = @"No song is playing.";
415 [statusWindowController showSongWindowWithTitle:title
425 - (void)showUpcomingSongs
427 int curPlaylist = [currentRemote currentPlaylistIndex];
428 int numSongs = [currentRemote numberOfSongsInPlaylistAtIndex:curPlaylist];
431 NSMutableArray *songList = [NSMutableArray arrayWithCapacity:5];
432 int numSongsInAdvance = [df integerForKey:@"SongsInAdvance"];
433 int curTrack = [currentRemote currentSongIndex];
436 for (i = curTrack + 1; i <= curTrack + numSongsInAdvance; i++) {
438 [songList addObject:[currentRemote songTitleAtIndex:i]];
442 [statusWindowController showUpcomingSongsWithTitles:songList];
445 [statusWindowController showUpcomingSongsWithTitles:[NSArray arrayWithObject:@"No upcoming songs."]];
449 - (void)incrementVolume
451 float volume = [currentRemote volume];
456 [currentRemote setVolume:volume];
458 //Show volume status window
461 - (void)decrementVolume
463 float volume = [currentRemote volume];
468 [currentRemote setVolume:volume];
470 //Show volume status window
473 - (void)incrementRating
475 float rating = [currentRemote currentSongRating];
480 [currentRemote setCurrentSongRating:rating];
482 //Show rating status window
485 - (void)decrementRating
487 float rating = [currentRemote currentSongRating];
492 [currentRemote setCurrentSongRating:rating];
494 //Show rating status window
499 ITMTRemotePlayerRepeatMode repeatMode = [currentRemote repeatMode];
501 switch (repeatMode) {
502 case ITMTRemotePlayerRepeatOff:
503 repeatMode = ITMTRemotePlayerRepeatAll;
505 case ITMTRemotePlayerRepeatAll:
506 repeatMode = ITMTRemotePlayerRepeatOne;
508 case ITMTRemotePlayerRepeatOne:
509 repeatMode = ITMTRemotePlayerRepeatOff;
512 [currentRemote setRepeatMode:repeatMode];
514 //Show loop status window
517 - (void)toggleShuffle
519 [currentRemote setShuffleEnabled:![currentRemote shuffleEnabled]];
520 //Show shuffle status window
523 /*************************************************************************/
525 #pragma mark WORKSPACE NOTIFICATION HANDLERS
526 /*************************************************************************/
528 - (void)applicationLaunched:(NSNotification *)note
530 if (!note || [[[note userInfo] objectForKey:@"NSApplicationName"] isEqualToString:[currentRemote playerFullName]]) {
531 [self setLatestSongIdentifier:@""];
533 [NSThread detachNewThreadSelector:@selector(startTimerInNewThread) toTarget:self withObject:nil];
535 playerRunningState = ITMTRemotePlayerRunning;
539 - (void)applicationTerminated:(NSNotification *)note
541 if (!note || [[[note userInfo] objectForKey:@"NSApplicationName"] isEqualToString:[currentRemote playerFullName]]) {
542 [refreshTimer invalidate];
543 [refreshTimer release];
546 playerRunningState = ITMTRemotePlayerNotRunning;
551 /*************************************************************************/
553 #pragma mark NSApplication DELEGATE METHODS
554 /*************************************************************************/
556 - (void)applicationWillTerminate:(NSNotification *)note
559 [[NSStatusBar systemStatusBar] removeStatusItem:statusItem];
563 /*************************************************************************/
565 #pragma mark DEALLOCATION METHOD
566 /*************************************************************************/
571 [refreshTimer invalidate];
572 [refreshTimer release];
576 [currentRemote halt];
577 [statusItem release];
578 [statusWindowController release];
579 [menuController release];