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