1 #import "NewMainController.h"
2 #import "PreferencesController.h"
3 #import "HotKeyCenter.h"
4 #import "StatusWindowController.h"
6 @interface MainController(Private)
7 - (ITMTRemote *)loadRemote;
12 @implementation MainController
14 /*************************************************************************/
16 #pragma mark INITIALIZATION/DEALLOCATION METHODS
17 /*************************************************************************/
21 if ( ( self = [super init] ) ) {
22 remoteArray = [[NSMutableArray alloc] initWithCapacity:1];
23 statusWindowController = [[StatusWindowController alloc] init];
24 df = [[NSUserDefaults standardUserDefaults] retain];
25 [self setLatestSongIdentifier:@"0-0"];
30 - (void)applicationDidFinishLaunching:(NSNotification *)note
32 currentRemote = [self loadRemote];
33 [currentRemote begin];
35 //Setup for notification of the remote player launching or quitting
36 [[[NSWorkspace sharedWorkspace] notificationCenter]
38 selector:@selector(applicationTerminated:)
39 name:NSWorkspaceDidTerminateApplicationNotification
42 [[[NSWorkspace sharedWorkspace] notificationCenter]
44 selector:@selector(applicationLaunched:)
45 name:NSWorkspaceDidLaunchApplicationNotification
48 if ( ! [df objectForKey:@"menu"] ) { // If this is nil, defaults have never been registered.
49 [[PreferencesController sharedPrefs] registerDefaults];
52 statusItem = [[ITStatusItem alloc]
53 initWithStatusBar:[NSStatusBar systemStatusBar]
54 withLength:NSSquareStatusItemLength];
56 [statusItem setImage:[NSImage imageNamed:@"menu"]];
57 [statusItem setAlternateImage:[NSImage imageNamed:@"selected_image"]];
60 - (ITMTRemote *)loadRemote
62 NSString *folderPath = [[NSBundle mainBundle] builtInPlugInsPath];
65 NSArray *bundlePathList = [NSBundle pathsForResourcesOfType:@"remote" inDirectory:folderPath];
66 NSEnumerator *enumerator = [bundlePathList objectEnumerator];
69 while ( (bundlePath = [enumerator nextObject]) ) {
70 NSBundle* remoteBundle = [NSBundle bundleWithPath:bundlePath];
73 Class remoteClass = [remoteBundle principalClass];
75 if ([remoteClass conformsToProtocol:@protocol(ITMTRemote)] &&
76 [remoteClass isKindOfClass:[NSObject class]]) {
78 id remote = [remoteClass remote];
79 [remoteArray addObject:remote];
84 // if ( [remoteArray count] > 0 ) { // UNCOMMENT WHEN WE HAVE > 1 PLUGIN
85 // if ( [remoteArray count] > 1 ) {
86 // [remoteArray sortUsingSelector:@selector(sortAlpha:)];
88 // [self loadModuleAccessUI]; //Comment out this line to disable remote visibility
91 // NSLog(@"%@", [remoteArray objectAtIndex:0]); //DEBUG
92 return [remoteArray objectAtIndex:0];
95 /*************************************************************************/
97 #pragma mark INSTANCE METHODS
98 /*************************************************************************/
100 - (void)startTimerInNewThread
102 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
103 NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
104 refreshTimer = [[NSTimer scheduledTimerWithTimeInterval:0.5
106 selector:@selector(timerUpdate)
108 repeats:YES] retain];
113 - (BOOL)songIsPlaying
115 return ( ! ([[currentRemote currentSongUniqueIdentifier] isEqualToString:@"0-0"]) );
118 - (BOOL)radioIsPlaying
120 return ( [currentRemote currentPlaylistClass] == ITMTRemotePlayerRadioPlaylist );
125 return ( ! [[currentRemote currentSongUniqueIdentifier] isEqualToString:_latestSongIdentifier] );
128 - (NSString *)latestSongIdentifier
130 return _latestSongIdentifier;
133 - (void)setLatestSongIdentifier:(NSString *)newIdentifier
135 [_latestSongIdentifier autorelease];
136 _latestSongIdentifier = [newIdentifier copy];
141 if ( ( [self songChanged] ) ||
142 ( ([self radioIsPlaying]) && (latestPlaylistClass != ITMTRemotePlayerRadioPlaylist) ) ||
143 ( (! [self radioIsPlaying]) && (latestPlaylistClass == ITMTRemotePlayerRadioPlaylist) ) ) {
144 //[statusItem setMenu:[self menu]];
145 [self setLatestSongIdentifier:[currentRemote currentSongUniqueIdentifier]];
146 latestPlaylistClass = [currentRemote currentPlaylistClass];
148 if ( [df boolForKey:@"showSongInfoOnChange"] ) {
149 [self showCurrentTrackInfo];
153 //Update Play/Pause menu item
155 if ([currentRemote playerPlayingState] == ITMTRemotePlayerPlaying) {
156 [playPauseItem setTitle:@"Pause"];
158 [playPauseItem setTitle:@"Play"];
171 - (void)selectSong:(id)sender
173 [currentRemote switchToSongAtIndex:[[sender representedObject] intValue]];
176 - (void)selectPlaylist:(id)sender
178 int playlist = [sender tag];
179 [currentRemote switchToPlaylistAtIndex:playlist];
182 - (void)selectEQPreset:(id)sender
184 int curSet = [currentRemote currentEQPresetIndex];
185 int item = [sender tag];
187 [currentRemote switchToEQAtIndex:item];
188 [[eqMenu itemAtIndex:curSet - 1] setState:NSOffState];
189 [[eqMenu itemAtIndex:item] setState:NSOnState];
193 - (void)selectSongRating:(id)sender
195 int newRating = [sender tag];
196 // [[ratingMenu itemAtIndex:lastSongRating] setState:NSOffState];
197 [sender setState:NSOnState];
198 [currentRemote setCurrentSongRating:(float)newRating / 100.0];
199 lastSongRating = newRating / 20;
203 - (void)playPause:(id)sender
205 ITMTRemotePlayerPlayingState state = [currentRemote playerPlayingState];
207 if (state == ITMTRemotePlayerPlaying) {
208 [currentRemote pause];
209 [playPauseItem setTitle:@"Play"];
210 } else if ((state == ITMTRemotePlayerForwarding) || (state == ITMTRemotePlayerRewinding)) {
211 [currentRemote pause];
212 [currentRemote play];
214 [currentRemote play];
215 [playPauseItem setTitle:@"Pause"];
219 - (void)nextSong:(id)sender
221 [currentRemote goToNextSong];
224 - (void)prevSong:(id)sender
226 [currentRemote goToPreviousSong];
229 - (void)fastForward:(id)sender
231 [currentRemote forward];
232 [playPauseItem setTitle:@"Play"];
235 - (void)rewind:(id)sender
237 [currentRemote rewind];
238 [playPauseItem setTitle:@"Play"];
244 - (void)quitMenuTunes:(id)sender
246 [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)showPreferences:(id)sender
262 [[PreferencesController sharedPrefs] setController:self];
263 [[PreferencesController sharedPrefs] showPrefsWindow:self];
266 - (void)closePreferences
268 if ( ( playerRunningState == ITMTRemotePlayerRunning) ) {
273 - (ITMTRemote *)currentRemote
275 return currentRemote;
286 [[HotKeyCenter sharedCenter] removeHotKey:@"PlayPause"];
287 [[HotKeyCenter sharedCenter] removeHotKey:@"NextTrack"];
288 [[HotKeyCenter sharedCenter] removeHotKey:@"PrevTrack"];
289 [[HotKeyCenter sharedCenter] removeHotKey:@"TrackInfo"];
290 [[HotKeyCenter sharedCenter] removeHotKey:@"UpcomingSongs"];
291 [[HotKeyCenter sharedCenter] removeHotKey:@"ToggleLoop"];
292 [[HotKeyCenter sharedCenter] removeHotKey:@"ToggleShuffle"];
293 [[HotKeyCenter sharedCenter] removeHotKey:@"IncrementVolume"];
294 [[HotKeyCenter sharedCenter] removeHotKey:@"DecrementVolume"];
295 [[HotKeyCenter sharedCenter] removeHotKey:@"IncrementRating"];
296 [[HotKeyCenter sharedCenter] removeHotKey:@"DecrementRating"];
301 if ([df objectForKey:@"PlayPause"] != nil) {
302 [[HotKeyCenter sharedCenter] addHotKey:@"PlayPause"
303 combo:[df keyComboForKey:@"PlayPause"]
304 target:self action:@selector(playPause:)];
307 if ([df objectForKey:@"NextTrack"] != nil) {
308 [[HotKeyCenter sharedCenter] addHotKey:@"NextTrack"
309 combo:[df keyComboForKey:@"NextTrack"]
310 target:self action:@selector(nextSong:)];
313 if ([df objectForKey:@"PrevTrack"] != nil) {
314 [[HotKeyCenter sharedCenter] addHotKey:@"PrevTrack"
315 combo:[df keyComboForKey:@"PrevTrack"]
316 target:self action:@selector(prevSong:)];
319 if ([df objectForKey:@"TrackInfo"] != nil) {
320 [[HotKeyCenter sharedCenter] addHotKey:@"TrackInfo"
321 combo:[df keyComboForKey:@"TrackInfo"]
322 target:self action:@selector(showCurrentTrackInfo)];
325 if ([df objectForKey:@"UpcomingSongs"] != nil) {
326 [[HotKeyCenter sharedCenter] addHotKey:@"UpcomingSongs"
327 combo:[df keyComboForKey:@"UpcomingSongs"]
328 target:self action:@selector(showUpcomingSongs)];
331 if ([df objectForKey:@"ToggleLoop"] != nil) {
332 [[HotKeyCenter sharedCenter] addHotKey:@"ToggleLoop"
333 combo:[df keyComboForKey:@"ToggleLoop"]
334 target:self action:NULL/*Set this to something*/];
337 if ([df objectForKey:@"ToggleShuffle"] != nil) {
338 [[HotKeyCenter sharedCenter] addHotKey:@"ToggleShuffle"
339 combo:[df keyComboForKey:@"ToggleShuffle"]
340 target:self action:NULL/*Set this to something*/];
343 if ([df objectForKey:@"IncrementVolume"] != nil) {
344 [[HotKeyCenter sharedCenter] addHotKey:@"IncrementVolume"
345 combo:[df keyComboForKey:@"IncrementVolume"]
346 target:self action:NULL/*Set this to something*/];
349 if ([df objectForKey:@"DecrementVolume"] != nil) {
350 [[HotKeyCenter sharedCenter] addHotKey:@"DecrementVolume"
351 combo:[df keyComboForKey:@"DecrementVolume"]
352 target:self action:NULL/*Set this to something*/];
355 if ([df objectForKey:@"IncrementRating"] != nil) {
356 [[HotKeyCenter sharedCenter] addHotKey:@"IncrementRating"
357 combo:[df keyComboForKey:@"IncrementRating"]
358 target:self action:NULL/*Set this to something*/];
361 if ([df objectForKey:@"DecrementRating"] != nil) {
362 [[HotKeyCenter sharedCenter] addHotKey:@"DecrementRating"
363 combo:[df keyComboForKey:@"DecrementRating"]
364 target:self action:NULL/*Set this to something*/];
368 - (void)showCurrentTrackInfo
370 NSString *title = [currentRemote currentSongTitle];
373 NSString *album = nil;
374 NSString *artist = nil;
375 NSString *time = nil;
380 if ( [df boolForKey:@"showAlbum"] ) {
381 album = [currentRemote currentSongAlbum];
384 if ( [df boolForKey:@"showArtist"] ) {
385 artist = [currentRemote currentSongArtist];
388 if ( [df boolForKey:@"showTime"] ) {
389 time = [currentRemote currentSongLength];
392 if ( [df boolForKey:@"showNumber"] ) {
393 trackNumber = [currentRemote currentSongTrack];
394 trackTotal = [currentRemote currentAlbumTrackCount];
397 if ( [df boolForKey:@"showRating"] ) {
398 rating = ( [currentRemote currentSongRating] * 5 );
401 [statusWindowController showSongWindowWithTitle:title
405 trackNumber:trackNumber
406 trackTotal:trackTotal
409 title = @"No song is playing.";
410 [statusWindowController showSongWindowWithTitle:title
420 - (void)showUpcomingSongs
422 int curPlaylist = [currentRemote currentPlaylistIndex];
423 int numSongs = [currentRemote numberOfSongsInPlaylistAtIndex:curPlaylist];
426 NSMutableArray *songList = [NSMutableArray arrayWithCapacity:5];
427 int numSongsInAdvance = [df integerForKey:@"SongsInAdvance"];
428 int curTrack = [currentRemote currentSongIndex];
431 for (i = curTrack + 1; i <= curTrack + numSongsInAdvance; i++) {
433 [songList addObject:[currentRemote songTitleAtIndex:i]];
437 [statusWindowController showUpcomingSongsWithTitles:songList];
440 [statusWindowController showUpcomingSongsWithTitles:[NSArray arrayWithObject:@"No upcoming songs."]];
444 /*************************************************************************/
446 #pragma mark WORKSPACE NOTIFICATION HANDLERS
447 /*************************************************************************/
449 - (void)applicationLaunched:(NSNotification *)note
451 if (!note || [[[note userInfo] objectForKey:@"NSApplicationName"] isEqualToString:[currentRemote playerFullName]]) {
452 [NSThread detachNewThreadSelector:@selector(startTimerInNewThread) toTarget:self withObject:nil];
454 playerRunningState = ITMTRemotePlayerRunning;
458 - (void)applicationTerminated:(NSNotification *)note
460 if (!note || [[[note userInfo] objectForKey:@"NSApplicationName"] isEqualToString:[currentRemote playerFullName]]) {
462 NSMenu *notRunningMenu = [[NSMenu alloc] initWithTitle:@""];
463 [notRunningMenu addItemWithTitle:[NSString stringWithFormat:@"Open %@", [currentRemote playerSimpleName]] action:@selector(showPlayer:) keyEquivalent:@""];
464 [notRunningMenu addItem:[NSMenuItem separatorItem]];
465 [notRunningMenu addItemWithTitle:@"Preferences" action:@selector(showPreferences:) keyEquivalent:@""];
466 [notRunningMenu addItemWithTitle:@"Quit" action:@selector(quitMenuTunes:) keyEquivalent:@""];
468 [refreshTimer invalidate];
469 [refreshTimer release];
472 playerRunningState = ITMTRemotePlayerNotRunning;
474 [statusItem setMenu:[self menuForNoPlayer]];
479 /*************************************************************************/
481 #pragma mark NSApplication DELEGATE METHODS
482 /*************************************************************************/
484 - (void)applicationWillTerminate:(NSNotification *)note
487 [[NSStatusBar systemStatusBar] removeStatusItem:statusItem];
491 /*************************************************************************/
493 #pragma mark DEALLOCATION METHOD
494 /*************************************************************************/
499 [refreshTimer invalidate];
500 [refreshTimer release];
504 [currentRemote halt];
505 [statusItem release];
506 [statusWindowController release];