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