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 //[statusItem setMenu:[self menu]];
160 [self setLatestSongIdentifier:[currentRemote currentSongUniqueIdentifier]];
161 latestPlaylistClass = [currentRemote currentPlaylistClass];
163 if ( [df boolForKey:@"showSongInfoOnChange"] ) {
164 [self showCurrentTrackInfo];
168 //Update Play/Pause menu item
170 if ([currentRemote playerPlayingState] == ITMTRemotePlayerPlaying) {
171 [playPauseItem setTitle:@"Pause"];
173 [playPauseItem setTitle:@"Play"];
181 [statusItem setMenu:[menuController menu]];
182 NSLog(@"The menu was clix0r3d, do something!");
193 ITMTRemotePlayerPlayingState state = [currentRemote playerPlayingState];
195 if (state == ITMTRemotePlayerPlaying) {
196 [currentRemote pause];
197 } else if ((state == ITMTRemotePlayerForwarding) || (state == ITMTRemotePlayerRewinding)) {
198 [currentRemote pause];
199 [currentRemote play];
201 [currentRemote play];
207 [currentRemote goToNextSong];
212 [currentRemote goToPreviousSong];
217 [currentRemote forward];
222 [currentRemote rewind];
225 - (void)selectPlaylistAtIndex:(int)index
227 [currentRemote switchToPlaylistAtIndex:index];
230 - (void)selectSongAtIndex:(int)index
232 [currentRemote switchToSongAtIndex:index];
235 - (void)selectSongRating:(int)rating
237 [currentRemote setCurrentSongRating:(float)rating / 100.0];
240 - (void)selectEQPresetAtIndex:(int)index
242 [currentRemote switchToEQAtIndex:index];
245 - (void)showPreferences
247 [[PreferencesController sharedPrefs] setController:self];
248 [[PreferencesController sharedPrefs] showPrefsWindow:self];
251 - (void)quitMenuTunes
253 [NSApp terminate:self];
259 - (void)showPlayer:(id)sender
261 if ( ( playerRunningState == ITMTRemotePlayerRunning) ) {
262 [currentRemote showPrimaryInterface];
264 if (![[NSWorkspace sharedWorkspace] launchApplication:[currentRemote playerFullName]]) {
265 NSLog(@"Error Launching Player");
270 - (void)closePreferences
272 if ( ( playerRunningState == ITMTRemotePlayerRunning) ) {
277 - (ITMTRemote *)currentRemote
279 return currentRemote;
290 [[HotKeyCenter sharedCenter] removeHotKey:@"PlayPause"];
291 [[HotKeyCenter sharedCenter] removeHotKey:@"NextTrack"];
292 [[HotKeyCenter sharedCenter] removeHotKey:@"PrevTrack"];
293 [[HotKeyCenter sharedCenter] removeHotKey:@"TrackInfo"];
294 [[HotKeyCenter sharedCenter] removeHotKey:@"UpcomingSongs"];
295 [[HotKeyCenter sharedCenter] removeHotKey:@"ToggleLoop"];
296 [[HotKeyCenter sharedCenter] removeHotKey:@"ToggleShuffle"];
297 [[HotKeyCenter sharedCenter] removeHotKey:@"IncrementVolume"];
298 [[HotKeyCenter sharedCenter] removeHotKey:@"DecrementVolume"];
299 [[HotKeyCenter sharedCenter] removeHotKey:@"IncrementRating"];
300 [[HotKeyCenter sharedCenter] removeHotKey:@"DecrementRating"];
305 if ([df objectForKey:@"PlayPause"] != nil) {
306 [[HotKeyCenter sharedCenter] addHotKey:@"PlayPause"
307 combo:[df keyComboForKey:@"PlayPause"]
308 target:self action:@selector(playPause:)];
311 if ([df objectForKey:@"NextTrack"] != nil) {
312 [[HotKeyCenter sharedCenter] addHotKey:@"NextTrack"
313 combo:[df keyComboForKey:@"NextTrack"]
314 target:self action:@selector(nextSong:)];
317 if ([df objectForKey:@"PrevTrack"] != nil) {
318 [[HotKeyCenter sharedCenter] addHotKey:@"PrevTrack"
319 combo:[df keyComboForKey:@"PrevTrack"]
320 target:self action:@selector(prevSong:)];
323 if ([df objectForKey:@"TrackInfo"] != nil) {
324 [[HotKeyCenter sharedCenter] addHotKey:@"TrackInfo"
325 combo:[df keyComboForKey:@"TrackInfo"]
326 target:self action:@selector(showCurrentTrackInfo)];
329 if ([df objectForKey:@"UpcomingSongs"] != nil) {
330 [[HotKeyCenter sharedCenter] addHotKey:@"UpcomingSongs"
331 combo:[df keyComboForKey:@"UpcomingSongs"]
332 target:self action:@selector(showUpcomingSongs)];
335 if ([df objectForKey:@"ToggleLoop"] != nil) {
336 [[HotKeyCenter sharedCenter] addHotKey:@"ToggleLoop"
337 combo:[df keyComboForKey:@"ToggleLoop"]
338 target:self action:NULL/*Set this to something*/];
341 if ([df objectForKey:@"ToggleShuffle"] != nil) {
342 [[HotKeyCenter sharedCenter] addHotKey:@"ToggleShuffle"
343 combo:[df keyComboForKey:@"ToggleShuffle"]
344 target:self action:NULL/*Set this to something*/];
347 if ([df objectForKey:@"IncrementVolume"] != nil) {
348 [[HotKeyCenter sharedCenter] addHotKey:@"IncrementVolume"
349 combo:[df keyComboForKey:@"IncrementVolume"]
350 target:self action:NULL/*Set this to something*/];
353 if ([df objectForKey:@"DecrementVolume"] != nil) {
354 [[HotKeyCenter sharedCenter] addHotKey:@"DecrementVolume"
355 combo:[df keyComboForKey:@"DecrementVolume"]
356 target:self action:NULL/*Set this to something*/];
359 if ([df objectForKey:@"IncrementRating"] != nil) {
360 [[HotKeyCenter sharedCenter] addHotKey:@"IncrementRating"
361 combo:[df keyComboForKey:@"IncrementRating"]
362 target:self action:NULL/*Set this to something*/];
365 if ([df objectForKey:@"DecrementRating"] != nil) {
366 [[HotKeyCenter sharedCenter] addHotKey:@"DecrementRating"
367 combo:[df keyComboForKey:@"DecrementRating"]
368 target:self action:NULL/*Set this to something*/];
372 - (void)showCurrentTrackInfo
374 NSString *title = [currentRemote currentSongTitle];
377 NSString *album = nil;
378 NSString *artist = nil;
379 NSString *time = nil;
384 if ( [df boolForKey:@"showAlbum"] ) {
385 album = [currentRemote currentSongAlbum];
388 if ( [df boolForKey:@"showArtist"] ) {
389 artist = [currentRemote currentSongArtist];
392 if ( [df boolForKey:@"showTime"] ) {
393 time = [currentRemote currentSongLength];
396 if ( [df boolForKey:@"showNumber"] ) {
397 trackNumber = [currentRemote currentSongTrack];
398 trackTotal = [currentRemote currentAlbumTrackCount];
401 if ( [df boolForKey:@"showRating"] ) {
402 rating = ( [currentRemote currentSongRating] * 5 );
405 [statusWindowController showSongWindowWithTitle:title
409 trackNumber:trackNumber
410 trackTotal:trackTotal
413 title = @"No song is playing.";
414 [statusWindowController showSongWindowWithTitle:title
424 - (void)showUpcomingSongs
426 int curPlaylist = [currentRemote currentPlaylistIndex];
427 int numSongs = [currentRemote numberOfSongsInPlaylistAtIndex:curPlaylist];
430 NSMutableArray *songList = [NSMutableArray arrayWithCapacity:5];
431 int numSongsInAdvance = [df integerForKey:@"SongsInAdvance"];
432 int curTrack = [currentRemote currentSongIndex];
435 for (i = curTrack + 1; i <= curTrack + numSongsInAdvance; i++) {
437 [songList addObject:[currentRemote songTitleAtIndex:i]];
441 [statusWindowController showUpcomingSongsWithTitles:songList];
444 [statusWindowController showUpcomingSongsWithTitles:[NSArray arrayWithObject:@"No upcoming songs."]];
448 /*************************************************************************/
450 #pragma mark WORKSPACE NOTIFICATION HANDLERS
451 /*************************************************************************/
453 - (void)applicationLaunched:(NSNotification *)note
455 if (!note || [[[note userInfo] objectForKey:@"NSApplicationName"] isEqualToString:[currentRemote playerFullName]]) {
456 [NSThread detachNewThreadSelector:@selector(startTimerInNewThread) toTarget:self withObject:nil];
458 playerRunningState = ITMTRemotePlayerRunning;
462 - (void)applicationTerminated:(NSNotification *)note
464 if (!note || [[[note userInfo] objectForKey:@"NSApplicationName"] isEqualToString:[currentRemote playerFullName]]) {
466 NSMenu *notRunningMenu = [[NSMenu alloc] initWithTitle:@""];
467 [notRunningMenu addItemWithTitle:[NSString stringWithFormat:@"Open %@", [currentRemote playerSimpleName]] action:@selector(showPlayer:) keyEquivalent:@""];
468 [notRunningMenu addItem:[NSMenuItem separatorItem]];
469 [notRunningMenu addItemWithTitle:@"Preferences" action:@selector(showPreferences:) keyEquivalent:@""];
470 [notRunningMenu addItemWithTitle:@"Quit" action:@selector(quitMenuTunes:) keyEquivalent:@""];
472 [refreshTimer invalidate];
473 [refreshTimer release];
476 playerRunningState = ITMTRemotePlayerNotRunning;
478 [statusItem setMenu:[self menuForNoPlayer]];
483 /*************************************************************************/
485 #pragma mark NSApplication DELEGATE METHODS
486 /*************************************************************************/
488 - (void)applicationWillTerminate:(NSNotification *)note
491 [[NSStatusBar systemStatusBar] removeStatusItem:statusItem];
495 /*************************************************************************/
497 #pragma mark DEALLOCATION METHOD
498 /*************************************************************************/
503 [refreshTimer invalidate];
504 [refreshTimer release];
508 [currentRemote halt];
509 [statusItem release];
510 [statusWindowController release];
511 [menuController release];