OMG HUEG COMMIT!!! IT SI LIEK AN XBOX, BECAUES IT SI SO HUEG!! ALL STATUS WINDOW...
[MenuTunes.git] / MainController.m
1 #import "MainController.h"
2 #import "MenuController.h"
3 #import "PreferencesController.h"
4 #import <ITKit/ITHotKeyCenter.h>
5 #import <ITKit/ITHotKey.h>
6 #import <ITKit/ITKeyCombo.h>
7 #import "StatusWindowController.h"
8 #import "StatusItemHack.h"
9
10 @interface MainController(Private)
11 - (ITMTRemote *)loadRemote;
12 - (void)timerUpdate;
13 - (void)setLatestSongIdentifier:(NSString *)newIdentifier;
14 - (void)showCurrentTrackInfo;
15 - (void)applicationLaunched:(NSNotification *)note;
16 - (void)applicationTerminated:(NSNotification *)note;
17 @end
18
19 static MainController *sharedController;
20
21 @implementation MainController
22
23 + (MainController *)sharedController
24 {
25     return sharedController;
26 }
27
28 /*************************************************************************/
29 #pragma mark -
30 #pragma mark INITIALIZATION/DEALLOCATION METHODS
31 /*************************************************************************/
32
33 - (id)init
34 {
35     if ( ( self = [super init] ) ) {
36         sharedController = self;
37         
38         remoteArray = [[NSMutableArray alloc] initWithCapacity:1];
39         statusWindowController = [StatusWindowController sharedController];
40         menuController = [[MenuController alloc] init];
41         df = [[NSUserDefaults standardUserDefaults] retain];
42     }
43     return self;
44 }
45
46 - (void)applicationDidFinishLaunching:(NSNotification *)note
47 {
48     //Turn on debug mode if needed
49     if ([df boolForKey:@"ITDebugMode"]) {
50         SetITDebugMode(YES);
51     }
52     
53     bling = [[MTBlingController alloc] init];
54     blingDate = nil;
55     
56     currentRemote = [self loadRemote];
57     [currentRemote begin];
58     
59     //Setup for notification of the remote player launching or quitting
60     [[[NSWorkspace sharedWorkspace] notificationCenter]
61             addObserver:self
62             selector:@selector(applicationTerminated:)
63             name:NSWorkspaceDidTerminateApplicationNotification
64             object:nil];
65     
66     [[[NSWorkspace sharedWorkspace] notificationCenter]
67             addObserver:self
68             selector:@selector(applicationLaunched:)
69             name:NSWorkspaceDidLaunchApplicationNotification
70             object:nil];
71     
72     if ( ! [df objectForKey:@"menu"] ) {  // If this is nil, defaults have never been registered.
73         [[PreferencesController sharedPrefs] registerDefaults];
74     }
75     
76     [StatusItemHack install];
77     statusItem = [[ITStatusItem alloc]
78             initWithStatusBar:[NSStatusBar systemStatusBar]
79             withLength:NSSquareStatusItemLength];
80     
81     if ([currentRemote playerRunningState] == ITMTRemotePlayerRunning) {
82         [self applicationLaunched:nil];
83     } else {
84         if ([df boolForKey:@"LaunchPlayerWithMT"])
85             [self showPlayer];
86         else
87             [self applicationTerminated:nil];
88     }
89     
90     [statusItem setImage:[NSImage imageNamed:@"MenuNormal"]];
91     [statusItem setAlternateImage:[NSImage imageNamed:@"MenuInverted"]];
92
93     [NSApp deactivate];
94 }
95
96 - (ITMTRemote *)loadRemote
97 {
98     NSString *folderPath = [[NSBundle mainBundle] builtInPlugInsPath];
99     ITDebugLog(@"Gathering remotes.");
100     if (folderPath) {
101         NSArray      *bundlePathList = [NSBundle pathsForResourcesOfType:@"remote" inDirectory:folderPath];
102         NSEnumerator *enumerator     = [bundlePathList objectEnumerator];
103         NSString     *bundlePath;
104
105         while ( (bundlePath = [enumerator nextObject]) ) {
106             NSBundle* remoteBundle = [NSBundle bundleWithPath:bundlePath];
107
108             if (remoteBundle) {
109                 Class remoteClass = [remoteBundle principalClass];
110
111                 if ([remoteClass conformsToProtocol:@protocol(ITMTRemote)] &&
112                     [remoteClass isKindOfClass:[NSObject class]]) {
113                     id remote = [remoteClass remote];
114                     ITDebugLog(@"Adding remote at path %@", bundlePath);
115                     [remoteArray addObject:remote];
116                 }
117             }
118         }
119
120 //      if ( [remoteArray count] > 0 ) {  // UNCOMMENT WHEN WE HAVE > 1 PLUGIN
121 //          if ( [remoteArray count] > 1 ) {
122 //              [remoteArray sortUsingSelector:@selector(sortAlpha:)];
123 //          }
124 //          [self loadModuleAccessUI]; //Comment out this line to disable remote visibility
125 //      }
126     }
127 //  NSLog(@"%@", [remoteArray objectAtIndex:0]);  //DEBUG
128     return [remoteArray objectAtIndex:0];
129 }
130
131 /*************************************************************************/
132 #pragma mark -
133 #pragma mark INSTANCE METHODS
134 /*************************************************************************/
135
136 /*- (void)startTimerInNewThread
137 {
138     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
139     NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
140     refreshTimer = [[NSTimer scheduledTimerWithTimeInterval:0.5
141                              target:self
142                              selector:@selector(timerUpdate)
143                              userInfo:nil
144                              repeats:YES] retain];
145     [runLoop run];
146     ITDebugLog(@"Timer started.");
147     [pool release];
148 }*/
149
150 - (void)blingTime
151 {
152     NSDate *now = [NSDate date];
153     if ( (! blingDate) || ([now timeIntervalSinceDate:blingDate] >= 86400) ) {
154         [bling showPanelIfNeeded];
155         [blingDate autorelease];
156         blingDate = [now retain];
157     }
158 }
159
160 - (void)blingNow
161 {
162     [bling showPanel];
163     [blingDate autorelease];
164     blingDate = [[NSDate date] retain];
165 }
166
167 - (BOOL)blingBling
168 {
169     if ( ! ([bling checkDone] == 2475) ) {
170         return NO;
171     } else {
172         return YES;
173     }
174 }
175
176 - (BOOL)songIsPlaying
177 {
178     return ( ! ([[currentRemote playerStateUniqueIdentifier] isEqualToString:@"0-0"]) );
179 }
180
181 - (BOOL)radioIsPlaying
182 {
183     return ( [currentRemote currentPlaylistClass] == ITMTRemotePlayerRadioPlaylist );
184 }
185
186 - (BOOL)songChanged
187 {
188     return ( ! [[currentRemote playerStateUniqueIdentifier] isEqualToString:_latestSongIdentifier] );
189 }
190
191 - (NSString *)latestSongIdentifier
192 {
193     return _latestSongIdentifier;
194 }
195
196 - (void)setLatestSongIdentifier:(NSString *)newIdentifier
197 {
198     ITDebugLog(@"Setting latest song identifier to %@", newIdentifier);
199     [_latestSongIdentifier autorelease];
200     _latestSongIdentifier = [newIdentifier copy];
201 }
202
203 - (void)timerUpdate
204 {
205     if ( [self songChanged] ) {
206         ITDebugLog(@"The song changed.");
207         [self setLatestSongIdentifier:[currentRemote playerStateUniqueIdentifier]];
208         latestPlaylistClass = [currentRemote currentPlaylistClass];
209         [menuController rebuildSubmenus];
210
211         if ( [df boolForKey:@"showSongInfoOnChange"] ) {
212             [self performSelector:@selector(showCurrentTrackInfo) withObject:nil afterDelay:0.0];
213         }
214     }
215 }
216
217 - (void)menuClicked
218 {
219     ITDebugLog(@"Menu clicked.");
220     if ([currentRemote playerRunningState] == ITMTRemotePlayerRunning) {
221         [statusItem setMenu:[menuController menu]];
222     } else {
223         [statusItem setMenu:[menuController menuForNoPlayer]];
224     }
225 }
226
227 //
228 //
229 // Menu Selectors
230 //
231 //
232
233 - (void)playPause
234 {
235     ITMTRemotePlayerPlayingState state = [currentRemote playerPlayingState];
236     ITDebugLog(@"Play/Pause toggled");
237     if (state == ITMTRemotePlayerPlaying) {
238         [currentRemote pause];
239     } else if ((state == ITMTRemotePlayerForwarding) || (state == ITMTRemotePlayerRewinding)) {
240         [currentRemote pause];
241         [currentRemote play];
242     } else {
243         [currentRemote play];
244     }
245     [self timerUpdate];
246 }
247
248 - (void)nextSong
249 {
250     ITDebugLog(@"Going to next song.");
251     [currentRemote goToNextSong];
252     [self timerUpdate];
253 }
254
255 - (void)prevSong
256 {
257     ITDebugLog(@"Going to previous song.");
258     [currentRemote goToPreviousSong];
259     [self timerUpdate];
260 }
261
262 - (void)fastForward
263 {
264     ITDebugLog(@"Fast forwarding.");
265     [currentRemote forward];
266     [self timerUpdate];
267 }
268
269 - (void)rewind
270 {
271     ITDebugLog(@"Rewinding.");
272     [currentRemote rewind];
273     [self timerUpdate];
274 }
275
276 - (void)selectPlaylistAtIndex:(int)index
277 {
278     ITDebugLog(@"Selecting playlist %i", index);
279     [currentRemote switchToPlaylistAtIndex:index];
280     [self timerUpdate];
281 }
282
283 - (void)selectSongAtIndex:(int)index
284 {
285     ITDebugLog(@"Selecting song %i", index);
286     [currentRemote switchToSongAtIndex:index];
287     [self timerUpdate];
288 }
289
290 - (void)selectSongRating:(int)rating
291 {
292     ITDebugLog(@"Selecting song rating %i", rating);
293     [currentRemote setCurrentSongRating:(float)rating / 100.0];
294     [self timerUpdate];
295 }
296
297 - (void)selectEQPresetAtIndex:(int)index
298 {
299     ITDebugLog(@"Selecting EQ preset %i", index);
300     [currentRemote switchToEQAtIndex:index];
301     [self timerUpdate];
302 }
303
304 - (void)showPlayer
305 {
306     ITDebugLog(@"Beginning show player.");
307     if ( ( playerRunningState == ITMTRemotePlayerRunning) ) {
308         ITDebugLog(@"Showing player interface.");
309         [currentRemote showPrimaryInterface];
310     } else {
311         ITDebugLog(@"Launching player.");
312         if (![[NSWorkspace sharedWorkspace] launchApplication:[currentRemote playerFullName]]) {
313             ITDebugLog(@"Error Launching Player");
314         }
315     }
316     ITDebugLog(@"Finished show player.");
317 }
318
319 - (void)showPreferences
320 {
321     ITDebugLog(@"Show preferences.");
322     [[PreferencesController sharedPrefs] setController:self];
323     [[PreferencesController sharedPrefs] showPrefsWindow:self];
324 }
325
326 - (void)quitMenuTunes
327 {
328     ITDebugLog(@"Quitting MenuTunes.");
329     [NSApp terminate:self];
330 }
331
332 //
333 //
334
335 - (void)closePreferences
336 {
337     ITDebugLog(@"Preferences closed.");
338     if ( ( playerRunningState == ITMTRemotePlayerRunning) ) {
339         [self setupHotKeys];
340     }
341 }
342
343 - (ITMTRemote *)currentRemote
344 {
345     return currentRemote;
346 }
347
348 //
349 //
350 // Hot key setup
351 //
352 //
353
354 - (void)clearHotKeys
355 {
356     NSEnumerator *hotKeyEnumerator = [[[ITHotKeyCenter sharedCenter] allHotKeys] objectEnumerator];
357     ITHotKey *nextHotKey;
358     ITDebugLog(@"Clearing hot keys.");
359     while ( (nextHotKey = [hotKeyEnumerator nextObject]) ) {
360         [[ITHotKeyCenter sharedCenter] unregisterHotKey:nextHotKey];
361     }
362     ITDebugLog(@"Done clearing hot keys.");
363 }
364
365 - (void)setupHotKeys
366 {
367     ITHotKey *hotKey;
368     ITDebugLog(@"Setting up hot keys.");
369     if ([df objectForKey:@"PlayPause"] != nil) {
370         ITDebugLog(@"Setting up play pause hot key.");
371         hotKey = [[ITHotKey alloc] init];
372         [hotKey setName:@"PlayPause"];
373         [hotKey setKeyCombo:[ITKeyCombo keyComboWithPlistRepresentation:[df objectForKey:@"PlayPause"]]];
374         [hotKey setTarget:self];
375         [hotKey setAction:@selector(playPause)];
376         [[ITHotKeyCenter sharedCenter] registerHotKey:[hotKey autorelease]];
377     }
378     
379     if ([df objectForKey:@"NextTrack"] != nil) {
380         ITDebugLog(@"Setting up next track hot key.");
381         hotKey = [[ITHotKey alloc] init];
382         [hotKey setName:@"NextTrack"];
383         [hotKey setKeyCombo:[ITKeyCombo keyComboWithPlistRepresentation:[df objectForKey:@"NextTrack"]]];
384         [hotKey setTarget:self];
385         [hotKey setAction:@selector(nextSong)];
386         [[ITHotKeyCenter sharedCenter] registerHotKey:[hotKey autorelease]];
387     }
388     
389     if ([df objectForKey:@"PrevTrack"] != nil) {
390         ITDebugLog(@"Setting up previous track hot key.");
391         hotKey = [[ITHotKey alloc] init];
392         [hotKey setName:@"PrevTrack"];
393         [hotKey setKeyCombo:[ITKeyCombo keyComboWithPlistRepresentation:[df objectForKey:@"PrevTrack"]]];
394         [hotKey setTarget:self];
395         [hotKey setAction:@selector(prevSong)];
396         [[ITHotKeyCenter sharedCenter] registerHotKey:[hotKey autorelease]];
397     }
398     
399     if ([df objectForKey:@"ShowPlayer"] != nil) {
400         ITDebugLog(@"Setting up show player hot key.");
401         hotKey = [[ITHotKey alloc] init];
402         [hotKey setName:@"ShowPlayer"];
403         [hotKey setKeyCombo:[ITKeyCombo keyComboWithPlistRepresentation:[df objectForKey:@"ShowPlayer"]]];
404         [hotKey setTarget:self];
405         [hotKey setAction:@selector(showPlayer)];
406         [[ITHotKeyCenter sharedCenter] registerHotKey:[hotKey autorelease]];
407     }
408     
409     if ([df objectForKey:@"TrackInfo"] != nil) {
410         ITDebugLog(@"Setting up track info hot key.");
411         hotKey = [[ITHotKey alloc] init];
412         [hotKey setName:@"TrackInfo"];
413         [hotKey setKeyCombo:[ITKeyCombo keyComboWithPlistRepresentation:[df objectForKey:@"TrackInfo"]]];
414         [hotKey setTarget:self];
415         [hotKey setAction:@selector(showCurrentTrackInfo)];
416         [[ITHotKeyCenter sharedCenter] registerHotKey:[hotKey autorelease]];
417     }
418     
419     if ([df objectForKey:@"UpcomingSongs"] != nil) {
420         ITDebugLog(@"Setting up upcoming songs hot key.");
421         hotKey = [[ITHotKey alloc] init];
422         [hotKey setName:@"UpcomingSongs"];
423         [hotKey setKeyCombo:[ITKeyCombo keyComboWithPlistRepresentation:[df objectForKey:@"UpcomingSongs"]]];
424         [hotKey setTarget:self];
425         [hotKey setAction:@selector(showUpcomingSongs)];
426         [[ITHotKeyCenter sharedCenter] registerHotKey:[hotKey autorelease]];
427     }
428     
429     if ([df objectForKey:@"ToggleLoop"] != nil) {
430         ITDebugLog(@"Setting up toggle loop hot key.");
431         hotKey = [[ITHotKey alloc] init];
432         [hotKey setName:@"ToggleLoop"];
433         [hotKey setKeyCombo:[ITKeyCombo keyComboWithPlistRepresentation:[df objectForKey:@"ToggleLoop"]]];
434         [hotKey setTarget:self];
435         [hotKey setAction:@selector(toggleLoop)];
436         [[ITHotKeyCenter sharedCenter] registerHotKey:[hotKey autorelease]];
437     }
438     
439     if ([df objectForKey:@"ToggleShuffle"] != nil) {
440         ITDebugLog(@"Setting up toggle shuffle hot key.");
441         hotKey = [[ITHotKey alloc] init];
442         [hotKey setName:@"ToggleShuffle"];
443         [hotKey setKeyCombo:[ITKeyCombo keyComboWithPlistRepresentation:[df objectForKey:@"ToggleShuffle"]]];
444         [hotKey setTarget:self];
445         [hotKey setAction:@selector(toggleShuffle)];
446         [[ITHotKeyCenter sharedCenter] registerHotKey:[hotKey autorelease]];
447     }
448     
449     if ([df objectForKey:@"IncrementVolume"] != nil) {
450         ITDebugLog(@"Setting up increment volume hot key.");
451         hotKey = [[ITHotKey alloc] init];
452         [hotKey setName:@"IncrementVolume"];
453         [hotKey setKeyCombo:[ITKeyCombo keyComboWithPlistRepresentation:[df objectForKey:@"IncrementVolume"]]];
454         [hotKey setTarget:self];
455         [hotKey setAction:@selector(incrementVolume)];
456         [[ITHotKeyCenter sharedCenter] registerHotKey:[hotKey autorelease]];
457     }
458     
459     if ([df objectForKey:@"DecrementVolume"] != nil) {
460         ITDebugLog(@"Setting up decrement volume hot key.");
461         hotKey = [[ITHotKey alloc] init];
462         [hotKey setName:@"DecrementVolume"];
463         [hotKey setKeyCombo:[ITKeyCombo keyComboWithPlistRepresentation:[df objectForKey:@"DecrementVolume"]]];
464         [hotKey setTarget:self];
465         [hotKey setAction:@selector(decrementVolume)];
466         [[ITHotKeyCenter sharedCenter] registerHotKey:[hotKey autorelease]];
467     }
468     
469     if ([df objectForKey:@"IncrementRating"] != nil) {
470         ITDebugLog(@"Setting up increment rating hot key.");
471         hotKey = [[ITHotKey alloc] init];
472         [hotKey setName:@"IncrementRating"];
473         [hotKey setKeyCombo:[ITKeyCombo keyComboWithPlistRepresentation:[df objectForKey:@"IncrementRating"]]];
474         [hotKey setTarget:self];
475         [hotKey setAction:@selector(incrementRating)];
476         [[ITHotKeyCenter sharedCenter] registerHotKey:[hotKey autorelease]];
477     }
478     
479     if ([df objectForKey:@"DecrementRating"] != nil) {
480         ITDebugLog(@"Setting up decrement rating hot key.");
481         hotKey = [[ITHotKey alloc] init];
482         [hotKey setName:@"DecrementRating"];
483         [hotKey setKeyCombo:[ITKeyCombo keyComboWithPlistRepresentation:[df objectForKey:@"DecrementRating"]]];
484         [hotKey setTarget:self];
485         [hotKey setAction:@selector(decrementRating)];
486         [[ITHotKeyCenter sharedCenter] registerHotKey:[hotKey autorelease]];
487     }
488     ITDebugLog(@"Finished setting up hot keys.");
489 }
490
491 - (void)showCurrentTrackInfo
492 {
493     ITMTRemotePlayerSource  source      = [currentRemote currentSource];
494     NSString               *title       = [currentRemote currentSongTitle];
495     NSString               *album       = nil;
496     NSString               *artist      = nil;
497     NSString               *time        = nil;
498     NSString               *track       = nil;
499     int                     rating      = -1;
500     
501     ITDebugLog(@"Showing track info status window.");
502     
503     if ( title ) {
504
505         if ( [df boolForKey:@"showAlbum"] ) {
506             album = [currentRemote currentSongAlbum];
507         }
508
509         if ( [df boolForKey:@"showArtist"] ) {
510             artist = [currentRemote currentSongArtist];
511         }
512
513         if ( [df boolForKey:@"showTime"] ) {
514             time = [NSString stringWithFormat:@"%@: %@ / %@",
515                 @"Time",
516                 [currentRemote currentSongElapsed],
517                 [currentRemote currentSongLength]];
518         }
519
520         if ( [df boolForKey:@"showTrackNumber"] ) {
521             int trackNo    = [currentRemote currentSongTrack];
522             int trackCount = [currentRemote currentAlbumTrackCount];
523             
524             if ( (trackNo > 0) || (trackCount > 0) ) {
525                 track = [NSString stringWithFormat:@"%@: %i %@ %i",
526                     @"Track", trackNo, @"of", trackCount];
527             }
528         }
529
530         if ( [df boolForKey:@"showTrackRating"] ) {
531             rating = ( [currentRemote currentSongRating] * 5 );
532         }
533         
534     } else {
535         title = NSLocalizedString(@"noSongPlaying", @"No song is playing.");
536     }
537
538     [statusWindowController showSongInfoWindowWithSource:source
539                                                    title:title
540                                                    album:album
541                                                   artist:artist
542                                                     time:time
543                                                    track:track
544                                                   rating:rating];
545 }
546
547 - (void)showUpcomingSongs
548 {
549     int curPlaylist = [currentRemote currentPlaylistIndex];
550     int numSongs = [currentRemote numberOfSongsInPlaylistAtIndex:curPlaylist];
551     ITDebugLog(@"Showing upcoming songs status window.");
552     if (numSongs > 0) {
553         NSMutableArray *songList = [NSMutableArray arrayWithCapacity:5];
554         int numSongsInAdvance = [df integerForKey:@"SongsInAdvance"];
555         int curTrack = [currentRemote currentSongIndex];
556         int i;
557
558         for (i = curTrack + 1; i <= curTrack + numSongsInAdvance; i++) {
559             if (i <= numSongs) {
560                 [songList addObject:[currentRemote songTitleAtIndex:i]];
561             }
562         }
563         
564         [statusWindowController showUpcomingSongsWindowWithTitles:songList];
565         
566     } else {
567         [statusWindowController showUpcomingSongsWindowWithTitles:[NSArray arrayWithObject:NSLocalizedString(@"noUpcomingSongs", @"No upcoming songs.")]];
568     }
569 }
570
571 - (void)incrementVolume
572 {
573     float volume  = [currentRemote volume];
574     float dispVol = volume;
575     ITDebugLog(@"Incrementing volume.");
576     volume  += 0.110;
577     dispVol += 0.100;
578     
579     if (volume > 1.0) {
580         volume  = 1.0;
581         dispVol = 1.0;
582     }
583
584     ITDebugLog(@"Setting volume to %f", volume);
585     [currentRemote setVolume:volume];
586
587     // Show volume status window
588     [statusWindowController showVolumeWindowWithLevel:dispVol];
589 }
590
591 - (void)decrementVolume
592 {
593     float volume  = [currentRemote volume];
594     float dispVol = volume;
595     ITDebugLog(@"Decrementing volume.");
596     volume  -= 0.090;
597     dispVol -= 0.100;
598
599     if (volume < 0.0) {
600         volume  = 0.0;
601         dispVol = 0.0;
602     }
603     
604     ITDebugLog(@"Setting volume to %f", volume);
605     [currentRemote setVolume:volume];
606     
607     //Show volume status window
608     [statusWindowController showVolumeWindowWithLevel:dispVol];
609 }
610
611 - (void)incrementRating
612 {
613     float rating = [currentRemote currentSongRating];
614     ITDebugLog(@"Incrementing rating.");
615     rating += 0.2;
616     if (rating > 1.0) {
617         rating = 1.0;
618     }
619     ITDebugLog(@"Setting rating to %f", rating);
620     [currentRemote setCurrentSongRating:rating];
621     
622     //Show rating status window
623     [statusWindowController showRatingWindowWithRating:rating];
624 }
625
626 - (void)decrementRating
627 {
628     float rating = [currentRemote currentSongRating];
629     ITDebugLog(@"Decrementing rating.");
630     rating -= 0.2;
631     if (rating < 0.0) {
632         rating = 0.0;
633     }
634     ITDebugLog(@"Setting rating to %f", rating);
635     [currentRemote setCurrentSongRating:rating];
636     
637     //Show rating status window
638     [statusWindowController showRatingWindowWithRating:rating];
639 }
640
641 - (void)toggleLoop
642 {
643     ITMTRemotePlayerRepeatMode repeatMode = [currentRemote repeatMode];
644     ITDebugLog(@"Toggling repeat mode.");
645     switch (repeatMode) {
646         case ITMTRemotePlayerRepeatOff:
647             repeatMode = ITMTRemotePlayerRepeatAll;
648         break;
649         case ITMTRemotePlayerRepeatAll:
650             repeatMode = ITMTRemotePlayerRepeatOne;
651         break;
652         case ITMTRemotePlayerRepeatOne:
653             repeatMode = ITMTRemotePlayerRepeatOff;
654         break;
655     }
656     ITDebugLog(@"Setting repeat mode to %i", repeatMode);
657     [currentRemote setRepeatMode:repeatMode];
658     
659     //Show loop status window
660     [statusWindowController showRepeatWindowWithMode:repeatMode];
661 }
662
663 - (void)toggleShuffle
664 {
665     BOOL newShuffleEnabled = ( ! [currentRemote shuffleEnabled] );
666     ITDebugLog(@"Toggling shuffle mode.");
667     [currentRemote setShuffleEnabled:newShuffleEnabled];
668     //Show shuffle status window
669     ITDebugLog(@"Setting shuffle mode to %i", newShuffleEnabled);
670     [statusWindowController showShuffleWindow:newShuffleEnabled];
671 }
672
673 /*************************************************************************/
674 #pragma mark -
675 #pragma mark WORKSPACE NOTIFICATION HANDLERS
676 /*************************************************************************/
677
678 - (void)applicationLaunched:(NSNotification *)note
679 {
680     if (!note || [[[note userInfo] objectForKey:@"NSApplicationName"] isEqualToString:[currentRemote playerFullName]]) {
681         ITDebugLog(@"Remote application launched.");
682         [currentRemote begin];
683         [self setLatestSongIdentifier:@""];
684         [self timerUpdate];
685         refreshTimer = [[NSTimer scheduledTimerWithTimeInterval:0.5
686                              target:self
687                              selector:@selector(timerUpdate)
688                              userInfo:nil
689                              repeats:YES] retain];
690         //[NSThread detachNewThreadSelector:@selector(startTimerInNewThread) toTarget:self withObject:nil];
691         [self setupHotKeys];
692         playerRunningState = ITMTRemotePlayerRunning;
693     }
694 }
695
696  - (void)applicationTerminated:(NSNotification *)note
697  {
698      if (!note || [[[note userInfo] objectForKey:@"NSApplicationName"] isEqualToString:[currentRemote playerFullName]]) {
699         ITDebugLog(@"Remote application terminated.");
700         [currentRemote halt];
701         [refreshTimer invalidate];
702         [refreshTimer release];
703         refreshTimer = nil;
704         [self clearHotKeys];
705         playerRunningState = ITMTRemotePlayerNotRunning;
706      }
707  }
708
709
710 /*************************************************************************/
711 #pragma mark -
712 #pragma mark NSApplication DELEGATE METHODS
713 /*************************************************************************/
714
715 - (void)applicationWillTerminate:(NSNotification *)note
716 {
717     [self clearHotKeys];
718     [[NSStatusBar systemStatusBar] removeStatusItem:statusItem];
719 }
720
721
722 /*************************************************************************/
723 #pragma mark -
724 #pragma mark DEALLOCATION METHOD
725 /*************************************************************************/
726
727 - (void)dealloc
728 {
729     [self applicationTerminated:nil];
730     [bling release];
731     [statusItem release];
732     [statusWindowController release];
733     [menuController release];
734     [super dealloc];
735 }
736
737
738 @end