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;
16 static MainController *sharedController;
18 @implementation MainController
20 + (MainController *)sharedController
22 return sharedController;
25 /*************************************************************************/
27 #pragma mark INITIALIZATION/DEALLOCATION METHODS
28 /*************************************************************************/
32 if ( ( self = [super init] ) ) {
33 sharedController = self;
35 remoteArray = [[NSMutableArray alloc] initWithCapacity:1];
36 statusWindowController = [[StatusWindowController alloc] init];
37 menuController = [[MenuController alloc] init];
38 df = [[NSUserDefaults standardUserDefaults] retain];
39 [self setLatestSongIdentifier:@"0-0"];
44 - (void)applicationDidFinishLaunching:(NSNotification *)note
46 currentRemote = [self loadRemote];
47 [currentRemote begin];
49 //Setup for notification of the remote player launching or quitting
50 [[[NSWorkspace sharedWorkspace] notificationCenter]
52 selector:@selector(applicationTerminated:)
53 name:NSWorkspaceDidTerminateApplicationNotification
56 [[[NSWorkspace sharedWorkspace] notificationCenter]
58 selector:@selector(applicationLaunched:)
59 name:NSWorkspaceDidLaunchApplicationNotification
62 if ( ! [df objectForKey:@"menu"] ) { // If this is nil, defaults have never been registered.
63 [[PreferencesController sharedPrefs] registerDefaults];
66 [StatusItemHack install];
67 statusItem = [[ITStatusItem alloc]
68 initWithStatusBar:[NSStatusBar systemStatusBar]
69 withLength:NSSquareStatusItemLength];
71 [statusItem setImage:[NSImage imageNamed:@"menu"]];
72 [statusItem setAlternateImage:[NSImage imageNamed:@"selected_image"]];
75 - (ITMTRemote *)loadRemote
77 NSString *folderPath = [[NSBundle mainBundle] builtInPlugInsPath];
80 NSArray *bundlePathList = [NSBundle pathsForResourcesOfType:@"remote" inDirectory:folderPath];
81 NSEnumerator *enumerator = [bundlePathList objectEnumerator];
84 while ( (bundlePath = [enumerator nextObject]) ) {
85 NSBundle* remoteBundle = [NSBundle bundleWithPath:bundlePath];
88 Class remoteClass = [remoteBundle principalClass];
90 if ([remoteClass conformsToProtocol:@protocol(ITMTRemote)] &&
91 [remoteClass isKindOfClass:[NSObject class]]) {
93 id remote = [remoteClass remote];
94 [remoteArray addObject:remote];
99 // if ( [remoteArray count] > 0 ) { // UNCOMMENT WHEN WE HAVE > 1 PLUGIN
100 // if ( [remoteArray count] > 1 ) {
101 // [remoteArray sortUsingSelector:@selector(sortAlpha:)];
103 // [self loadModuleAccessUI]; //Comment out this line to disable remote visibility
106 // NSLog(@"%@", [remoteArray objectAtIndex:0]); //DEBUG
107 return [remoteArray objectAtIndex:0];
110 /*************************************************************************/
112 #pragma mark INSTANCE METHODS
113 /*************************************************************************/
115 - (void)startTimerInNewThread
117 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
118 NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
119 refreshTimer = [[NSTimer scheduledTimerWithTimeInterval:0.5
121 selector:@selector(timerUpdate)
123 repeats:YES] retain];
128 - (BOOL)songIsPlaying
130 return ( ! ([[currentRemote currentSongUniqueIdentifier] isEqualToString:@"0-0"]) );
133 - (BOOL)radioIsPlaying
135 return ( [currentRemote currentPlaylistClass] == ITMTRemotePlayerRadioPlaylist );
140 return ( ! [[currentRemote currentSongUniqueIdentifier] isEqualToString:_latestSongIdentifier] );
143 - (NSString *)latestSongIdentifier
145 return _latestSongIdentifier;
148 - (void)setLatestSongIdentifier:(NSString *)newIdentifier
150 [_latestSongIdentifier autorelease];
151 _latestSongIdentifier = [newIdentifier copy];
156 if ( ( [self songChanged] ) ||
157 ( ([self radioIsPlaying]) && (latestPlaylistClass != ITMTRemotePlayerRadioPlaylist) ) ||
158 ( (! [self radioIsPlaying]) && (latestPlaylistClass == ITMTRemotePlayerRadioPlaylist) ) ) {
159 [self setLatestSongIdentifier:[currentRemote currentSongUniqueIdentifier]];
160 latestPlaylistClass = [currentRemote currentPlaylistClass];
162 if ( [df boolForKey:@"showSongInfoOnChange"] ) {
163 [self showCurrentTrackInfo];
170 if ([currentRemote playerRunningState] == ITMTRemotePlayerRunning) {
171 [statusItem setMenu:[menuController menu]];
173 [statusItem setMenu:[menuController menuForNoPlayer]];
185 ITMTRemotePlayerPlayingState state = [currentRemote playerPlayingState];
187 if (state == ITMTRemotePlayerPlaying) {
188 [currentRemote pause];
189 } else if ((state == ITMTRemotePlayerForwarding) || (state == ITMTRemotePlayerRewinding)) {
190 [currentRemote pause];
191 [currentRemote play];
193 [currentRemote play];
199 [currentRemote goToNextSong];
204 [currentRemote goToPreviousSong];
209 [currentRemote forward];
214 [currentRemote rewind];
217 - (void)selectPlaylistAtIndex:(int)index
219 [currentRemote switchToPlaylistAtIndex:index];
222 - (void)selectSongAtIndex:(int)index
224 [currentRemote switchToSongAtIndex:index];
227 - (void)selectSongRating:(int)rating
229 [currentRemote setCurrentSongRating:(float)rating / 100.0];
232 - (void)selectEQPresetAtIndex:(int)index
234 [currentRemote switchToEQAtIndex:index];
237 - (void)showPreferences
239 [[PreferencesController sharedPrefs] setController:self];
240 [[PreferencesController sharedPrefs] showPrefsWindow:self];
243 - (void)quitMenuTunes
245 [NSApp terminate:self];
251 - (void)showPlayer:(id)sender
253 if ( ( playerRunningState == ITMTRemotePlayerRunning) ) {
254 [currentRemote showPrimaryInterface];
256 if (![[NSWorkspace sharedWorkspace] launchApplication:[currentRemote playerFullName]]) {
257 NSLog(@"Error Launching Player");
262 - (void)closePreferences
264 if ( ( playerRunningState == ITMTRemotePlayerRunning) ) {
269 - (ITMTRemote *)currentRemote
271 return currentRemote;
282 [[HotKeyCenter sharedCenter] removeHotKey:@"PlayPause"];
283 [[HotKeyCenter sharedCenter] removeHotKey:@"NextTrack"];
284 [[HotKeyCenter sharedCenter] removeHotKey:@"PrevTrack"];
285 [[HotKeyCenter sharedCenter] removeHotKey:@"TrackInfo"];
286 [[HotKeyCenter sharedCenter] removeHotKey:@"UpcomingSongs"];
287 [[HotKeyCenter sharedCenter] removeHotKey:@"ToggleLoop"];
288 [[HotKeyCenter sharedCenter] removeHotKey:@"ToggleShuffle"];
289 [[HotKeyCenter sharedCenter] removeHotKey:@"IncrementVolume"];
290 [[HotKeyCenter sharedCenter] removeHotKey:@"DecrementVolume"];
291 [[HotKeyCenter sharedCenter] removeHotKey:@"IncrementRating"];
292 [[HotKeyCenter sharedCenter] removeHotKey:@"DecrementRating"];
297 if ([df objectForKey:@"PlayPause"] != nil) {
298 [[HotKeyCenter sharedCenter] addHotKey:@"PlayPause"
299 combo:[df keyComboForKey:@"PlayPause"]
300 target:self action:@selector(playPause:)];
303 if ([df objectForKey:@"NextTrack"] != nil) {
304 [[HotKeyCenter sharedCenter] addHotKey:@"NextTrack"
305 combo:[df keyComboForKey:@"NextTrack"]
306 target:self action:@selector(nextSong:)];
309 if ([df objectForKey:@"PrevTrack"] != nil) {
310 [[HotKeyCenter sharedCenter] addHotKey:@"PrevTrack"
311 combo:[df keyComboForKey:@"PrevTrack"]
312 target:self action:@selector(prevSong:)];
315 if ([df objectForKey:@"TrackInfo"] != nil) {
316 [[HotKeyCenter sharedCenter] addHotKey:@"TrackInfo"
317 combo:[df keyComboForKey:@"TrackInfo"]
318 target:self action:@selector(showCurrentTrackInfo)];
321 if ([df objectForKey:@"UpcomingSongs"] != nil) {
322 [[HotKeyCenter sharedCenter] addHotKey:@"UpcomingSongs"
323 combo:[df keyComboForKey:@"UpcomingSongs"]
324 target:self action:@selector(showUpcomingSongs)];
327 if ([df objectForKey:@"ToggleLoop"] != nil) {
328 [[HotKeyCenter sharedCenter] addHotKey:@"ToggleLoop"
329 combo:[df keyComboForKey:@"ToggleLoop"]
330 target:self action:NULL/*Set this to something*/];
333 if ([df objectForKey:@"ToggleShuffle"] != nil) {
334 [[HotKeyCenter sharedCenter] addHotKey:@"ToggleShuffle"
335 combo:[df keyComboForKey:@"ToggleShuffle"]
336 target:self action:NULL/*Set this to something*/];
339 if ([df objectForKey:@"IncrementVolume"] != nil) {
340 [[HotKeyCenter sharedCenter] addHotKey:@"IncrementVolume"
341 combo:[df keyComboForKey:@"IncrementVolume"]
342 target:self action:NULL/*Set this to something*/];
345 if ([df objectForKey:@"DecrementVolume"] != nil) {
346 [[HotKeyCenter sharedCenter] addHotKey:@"DecrementVolume"
347 combo:[df keyComboForKey:@"DecrementVolume"]
348 target:self action:NULL/*Set this to something*/];
351 if ([df objectForKey:@"IncrementRating"] != nil) {
352 [[HotKeyCenter sharedCenter] addHotKey:@"IncrementRating"
353 combo:[df keyComboForKey:@"IncrementRating"]
354 target:self action:NULL/*Set this to something*/];
357 if ([df objectForKey:@"DecrementRating"] != nil) {
358 [[HotKeyCenter sharedCenter] addHotKey:@"DecrementRating"
359 combo:[df keyComboForKey:@"DecrementRating"]
360 target:self action:NULL/*Set this to something*/];
364 - (void)showCurrentTrackInfo
366 NSString *title = [currentRemote currentSongTitle];
369 NSString *album = nil;
370 NSString *artist = nil;
371 NSString *time = nil;
376 if ( [df boolForKey:@"showAlbum"] ) {
377 album = [currentRemote currentSongAlbum];
380 if ( [df boolForKey:@"showArtist"] ) {
381 artist = [currentRemote currentSongArtist];
384 if ( [df boolForKey:@"showTime"] ) {
385 time = [currentRemote currentSongLength];
388 if ( [df boolForKey:@"showNumber"] ) {
389 trackNumber = [currentRemote currentSongTrack];
390 trackTotal = [currentRemote currentAlbumTrackCount];
393 if ( [df boolForKey:@"showRating"] ) {
394 rating = ( [currentRemote currentSongRating] * 5 );
397 [statusWindowController showSongWindowWithTitle:title
401 trackNumber:trackNumber
402 trackTotal:trackTotal
405 title = @"No song is playing.";
406 [statusWindowController showSongWindowWithTitle:title
416 - (void)showUpcomingSongs
418 int curPlaylist = [currentRemote currentPlaylistIndex];
419 int numSongs = [currentRemote numberOfSongsInPlaylistAtIndex:curPlaylist];
422 NSMutableArray *songList = [NSMutableArray arrayWithCapacity:5];
423 int numSongsInAdvance = [df integerForKey:@"SongsInAdvance"];
424 int curTrack = [currentRemote currentSongIndex];
427 for (i = curTrack + 1; i <= curTrack + numSongsInAdvance; i++) {
429 [songList addObject:[currentRemote songTitleAtIndex:i]];
433 [statusWindowController showUpcomingSongsWithTitles:songList];
436 [statusWindowController showUpcomingSongsWithTitles:[NSArray arrayWithObject:@"No upcoming songs."]];
440 /*************************************************************************/
442 #pragma mark WORKSPACE NOTIFICATION HANDLERS
443 /*************************************************************************/
445 - (void)applicationLaunched:(NSNotification *)note
447 if (!note || [[[note userInfo] objectForKey:@"NSApplicationName"] isEqualToString:[currentRemote playerFullName]]) {
448 [NSThread detachNewThreadSelector:@selector(startTimerInNewThread) toTarget:self withObject:nil];
450 playerRunningState = ITMTRemotePlayerRunning;
454 - (void)applicationTerminated:(NSNotification *)note
456 if (!note || [[[note userInfo] objectForKey:@"NSApplicationName"] isEqualToString:[currentRemote playerFullName]]) {
458 NSMenu *notRunningMenu = [[NSMenu alloc] initWithTitle:@""];
459 [notRunningMenu addItemWithTitle:[NSString stringWithFormat:@"Open %@", [currentRemote playerSimpleName]] action:@selector(showPlayer:) keyEquivalent:@""];
460 [notRunningMenu addItem:[NSMenuItem separatorItem]];
461 [notRunningMenu addItemWithTitle:@"Preferences" action:@selector(showPreferences:) keyEquivalent:@""];
462 [notRunningMenu addItemWithTitle:@"Quit" action:@selector(quitMenuTunes:) keyEquivalent:@""];
464 [refreshTimer invalidate];
465 [refreshTimer release];
468 playerRunningState = ITMTRemotePlayerNotRunning;
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];
501 [menuController release];