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