Added a songEnabledAtIndex: method to ITMTRemote.
[MenuTunes.git] / MainController.m
index 3b9342b..cfc9518 100755 (executable)
@@ -64,6 +64,8 @@
 - (void)setLatestSongIdentifier:(NSString *)newIdentifier;
 - (void)applicationLaunched:(NSNotification *)note;
 - (void)applicationTerminated:(NSNotification *)note;
+
+- (void)invalidateStatusWindowUpdateTimer;
 @end
 
 static MainController *sharedController;
@@ -85,6 +87,8 @@ static MainController *sharedController;
     if ( ( self = [super init] ) ) {
         sharedController = self;
         
+               _statusWindowUpdateTimer = nil;
+               
         remoteArray = [[NSMutableArray alloc] initWithCapacity:1];
         [[PreferencesController sharedPrefs] setController:self];
         statusWindowController = [StatusWindowController sharedController];
@@ -98,11 +102,32 @@ static MainController *sharedController;
 
 - (void)applicationDidFinishLaunching:(NSNotification *)note
 {
+       NSString *iTunesPath = [df stringForKey:@"CustomPlayerPath"];
+       NSDictionary *iTunesInfoPlist;
+       float iTunesVersion;
+       
     //Turn on debug mode if needed
-    if ([df boolForKey:@"ITDebugMode"]) {
+       /*if ((GetCurrentKeyModifiers() & (controlKey | rightControlKey)) != 0)
+    if ((GetCurrentKeyModifiers() & (optionKey | rightOptionKey)) != 0)
+    if ((GetCurrentKeyModifiers() & (shiftKey | rightShiftKey)) != 0)*/
+    if ([df boolForKey:@"ITDebugMode"] || ((GetCurrentKeyModifiers() & (controlKey | rightControlKey)) != 0)) {
         SetITDebugMode(YES);
+               [[StatusWindowController sharedController] showDebugModeEnabledWindow];
     }
-    
+
+       //Check if iTunes 4.7 or later is installed     
+       if (!iTunesPath) {
+               iTunesPath = [[NSWorkspace sharedWorkspace] fullPathForApplication:@"iTunes.app"];
+       }
+       iTunesInfoPlist = [[NSBundle bundleWithPath:iTunesPath] infoDictionary];
+       iTunesVersion = [[iTunesInfoPlist objectForKey:@"CFBundleVersion"] floatValue];
+       ITDebugLog(@"iTunes version found: %f.", iTunesVersion);
+       if (iTunesVersion >= 4.7) {
+               _needsPolling = NO;
+       } else {
+               _needsPolling = YES;
+       }
+       
     if (([df integerForKey:@"appVersion"] < 1200) && ([df integerForKey:@"SongsInAdvance"] > 0)) {
         [df removePersistentDomainForName:@"com.ithinksw.menutunes"];
         [df synchronize];
@@ -147,13 +172,13 @@ static MainController *sharedController;
                 withLength:NSSquareStatusItemLength];
     }
     
-    bling = [[MTBlingController alloc] init];
+    /*bling = [[MTBlingController alloc] init];
     [self blingTime];
     registerTimer = [[NSTimer scheduledTimerWithTimeInterval:10.0
                              target:self
                              selector:@selector(blingTime)
                              userInfo:nil
-                             repeats:YES] retain];
+                             repeats:YES] retain];*/
     
     NS_DURING
         if ([[self currentRemote] playerRunningState] == ITMTRemotePlayerRunning) {
@@ -174,6 +199,14 @@ static MainController *sharedController;
     [networkController startRemoteServerSearch];
     [NSApp deactivate];
        [self performSelector:@selector(rawr) withObject:nil afterDelay:1.0];
+       
+       bling = [[MTBlingController alloc] init];
+    [self blingTime];
+    registerTimer = [[NSTimer scheduledTimerWithTimeInterval:10.0
+                             target:self
+                             selector:@selector(blingTime)
+                             userInfo:nil
+                             repeats:YES] retain];
 }
 
 - (void)rawr
@@ -181,13 +214,6 @@ static MainController *sharedController;
        _open = YES;
 }
 
-- (void)applicationWillBecomeActive:(NSNotification *)note
-{
-       if (_open) {
-               [[MainController sharedController] showPreferences];
-       }
-}
-
 - (ITMTRemote *)loadRemote
 {
     NSString *folderPath = [[NSBundle mainBundle] builtInPlugInsPath];
@@ -288,7 +314,7 @@ static MainController *sharedController;
         if ( ([now timeIntervalSinceDate:[self getBlingTime]] >= 604800) && (blinged != YES) ) {
             blinged = YES;
             [statusItem setEnabled:NO];
-            [self clearHotKeys];
+                       [[ITHotKeyCenter sharedCenter] setEnabled:NO];
             if ([refreshTimer isValid]) {
                 [refreshTimer invalidate];
                                [refreshTimer release];
@@ -299,8 +325,8 @@ static MainController *sharedController;
     } else {
         if (blinged) {
             [statusItem setEnabled:YES];
-            [self setupHotKeys];
-            if (![refreshTimer isValid]) {
+            [[ITHotKeyCenter sharedCenter] setEnabled:YES];
+            if (_needsPolling && ![refreshTimer isValid]) {
                 [refreshTimer release];
                 refreshTimer = [[NSTimer scheduledTimerWithTimeInterval:([networkController isConnectedToServer] ? 10.0 : 0.5)
                              target:self
@@ -377,17 +403,15 @@ static MainController *sharedController;
 - (void)timerUpdate
 {
        NSString *identifier = [[self currentRemote] playerStateUniqueIdentifier];
-       if (identifier == nil) {
+       if (refreshTimer && identifier == nil) {
                if ([statusItem isEnabled]) {
                        [statusItem setToolTip:@"iTunes not responding."];
-                       [[ITHotKeyCenter sharedCenter] setEnabled:NO];
                }
                [statusItem setEnabled:NO];
                return;
        } else if (![statusItem isEnabled]) {
                [statusItem setEnabled:YES];
                [statusItem setToolTip:_toolTip];
-               [[ITHotKeyCenter sharedCenter] setEnabled:YES];
                return;
        }
        
@@ -430,7 +454,7 @@ static MainController *sharedController;
                                        } else if (title) {
                                                _toolTip = title;
                                        } else {
-                                               _toolTip = @"No Song Playing";
+                                               _toolTip = NSLocalizedString(@"noSongPlaying", @"No song is playing.");
                                        }
                                        [statusItem setToolTip:_toolTip];
                                } else {
@@ -456,20 +480,18 @@ static MainController *sharedController;
        if (([[self currentRemote] playerStateUniqueIdentifier] == nil) && playerRunningState == ITMTRemotePlayerRunning) {
                if (refreshTimer) {
                        if ([statusItem isEnabled]) {
-                               [statusItem setToolTip:@"iTunes not responding."];
-                               [[ITHotKeyCenter sharedCenter] setEnabled:NO];
+                               [statusItem setToolTip:NSLocalizedString(@"iTunesNotResponding", @"iTunes is not responding.")];
                        }
                        [statusItem setEnabled:NO];
                } else {
                        NSMenu *menu = [[NSMenu alloc] init];
-                       [menu addItemWithTitle:@"iTunes Not Responding" action:nil keyEquivalent:@""];
+                       [menu addItemWithTitle:NSLocalizedString(@"iTunesNotResponding", @"iTunes is not responding.") action:nil keyEquivalent:@""];
                        [statusItem setMenu:[menu autorelease]];
                }
                return;
        } else if (![statusItem isEnabled]) {
                [statusItem setEnabled:YES];
                [statusItem setToolTip:_toolTip];
-               [[ITHotKeyCenter sharedCenter] setEnabled:YES];
                return;
        }
        
@@ -492,12 +514,12 @@ static MainController *sharedController;
 - (void)trackChanged:(NSNotification *)note
 {
        //If we're running the timer, shut it off since we don't need it!
-       if (refreshTimer && [refreshTimer isValid]) {
+       /*if (refreshTimer && [refreshTimer isValid]) {
                ITDebugLog(@"Invalidating refresh timer.");
                [refreshTimer invalidate];
                [refreshTimer release];
                refreshTimer = nil;
-       }
+       }*/
        
        if (![self songChanged]) {
                return;
@@ -546,7 +568,7 @@ static MainController *sharedController;
                                } else if (title) {
                                        _toolTip = title;
                                } else {
-                                       _toolTip = @"No Song Playing";
+                                       _toolTip = NSLocalizedString(@"noSongPlaying", @"No song is playing.");;
                                }
                                [statusItem setToolTip:_toolTip];
                        } else {
@@ -750,7 +772,7 @@ static MainController *sharedController;
 {
     ITDebugLog(@"Show preferences.");
     [[PreferencesController sharedPrefs] showPrefsWindow:self];
-    [[StatusWindow sharedWindow] setLocked:NO];
+    [(StatusWindow *)[StatusWindow sharedWindow] setLocked:NO];
     [[StatusWindow sharedWindow] vanish:self];
     [[StatusWindow sharedWindow] setIgnoresMouseEvents:YES];
 }
@@ -957,6 +979,16 @@ static MainController *sharedController;
         [[ITHotKeyCenter sharedCenter] registerHotKey:[hotKey autorelease]];
     }
     
+       if ([df objectForKey:@"ToggleShufflability"] != nil) {
+        ITDebugLog(@"Setting up toggle song shufflability hot key.");
+        hotKey = [[ITHotKey alloc] init];
+        [hotKey setName:@"ToggleShufflability"];
+        [hotKey setKeyCombo:[ITKeyCombo keyComboWithPlistRepresentation:[df objectForKey:@"ToggleShufflability"]]];
+        [hotKey setTarget:self];
+        [hotKey setAction:@selector(toggleSongShufflable)];
+        [[ITHotKeyCenter sharedCenter] registerHotKey:[hotKey autorelease]];
+    }
+       
     if ([df objectForKey:@"PopupMenu"] != nil) {
         ITDebugLog(@"Setting up popup menu hot key.");
         hotKey = [[ITHotKey alloc] init];
@@ -995,7 +1027,7 @@ static MainController *sharedController;
     NSImage                *art         = nil;
     int                     rating      = -1;
     int                     playCount   = -1;
-    
+       
     ITDebugLog(@"Showing track info status window.");
     
     NS_DURING
@@ -1052,6 +1084,9 @@ static MainController *sharedController;
             NS_HANDLER
                 [self networkError:localException];
             NS_ENDHANDLER
+                       _timeUpdateCount = 0;
+                       [self invalidateStatusWindowUpdateTimer];
+                       _statusWindowUpdateTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(updateTime:) userInfo:nil repeats:YES];
         }
 
         if ( [df boolForKey:@"showTrackNumber"] ) {
@@ -1108,6 +1143,37 @@ static MainController *sharedController;
                                                    image:art];
 }
 
+- (void)updateTime:(NSTimer *)timer
+{
+       StatusWindow *sw = [StatusWindow sharedWindow];
+       _timeUpdateCount++;
+       if (_timeUpdateCount < (int)[sw exitDelay] + (int)[[sw exitEffect] effectTime] + (int)[[sw entryEffect] effectTime]) {
+               NSString *time = nil, *length;
+               NS_DURING
+                       length = [[self currentRemote] currentSongLength];
+                       if (length) {
+                               time = [NSString stringWithFormat:@"%@: %@ / %@",
+                                                       NSLocalizedString(@"time", @"Time"),
+                                                       [[self currentRemote] currentSongElapsed],
+                                                       length];
+                               [[StatusWindowController sharedController] updateTime:time];
+                       }
+               NS_HANDLER
+                       [self networkError:localException];
+               NS_ENDHANDLER
+       } else {
+               [self invalidateStatusWindowUpdateTimer];
+       }
+}
+
+- (void)invalidateStatusWindowUpdateTimer
+{
+       if (_statusWindowUpdateTimer) {
+               [_statusWindowUpdateTimer invalidate];
+               _statusWindowUpdateTimer = nil;
+       }
+}
+
 - (void)showUpcomingSongs
 {
     int numSongs = 0;
@@ -1117,6 +1183,8 @@ static MainController *sharedController;
         [self networkError:localException];
     NS_ENDHANDLER
     
+       [self invalidateStatusWindowUpdateTimer];
+       
     ITDebugLog(@"Showing upcoming songs status window.");
     NS_DURING
         if (numSongs > 0) {
@@ -1125,10 +1193,12 @@ static MainController *sharedController;
             int curTrack = [[self currentRemote] currentSongIndex];
             int i;
     
-            for (i = curTrack + 1; i <= curTrack + numSongsInAdvance; i++) {
-                if (i <= numSongs) {
+            for (i = curTrack + 1; i <= curTrack + numSongsInAdvance && i <= numSongs; i++) {
+                if ([[self currentRemote] songEnabledAtIndex:i]) {
                     [songList addObject:[[self currentRemote] songTitleAtIndex:i]];
-                }
+                } else {
+                                       numSongsInAdvance++;
+                               }
             }
             
             if ([songList count] == 0) {
@@ -1173,6 +1243,7 @@ static MainController *sharedController;
         [[self currentRemote] setVolume:volume];
     
         // Show volume status window
+               [self invalidateStatusWindowUpdateTimer];
         [statusWindowController showVolumeWindowWithLevel:dispVol];
     NS_HANDLER
         [self networkError:localException];
@@ -1197,6 +1268,7 @@ static MainController *sharedController;
         [[self currentRemote] setVolume:volume];
         
         //Show volume status window
+               [self invalidateStatusWindowUpdateTimer];
         [statusWindowController showVolumeWindowWithLevel:dispVol];
     NS_HANDLER
         [self networkError:localException];
@@ -1222,6 +1294,7 @@ static MainController *sharedController;
         [[self currentRemote] setCurrentSongRating:rating];
         
         //Show rating status window
+               [self invalidateStatusWindowUpdateTimer];
         [statusWindowController showRatingWindowWithRating:rating];
     NS_HANDLER
         [self networkError:localException];
@@ -1247,6 +1320,7 @@ static MainController *sharedController;
         [[self currentRemote] setCurrentSongRating:rating];
         
         //Show rating status window
+               [self invalidateStatusWindowUpdateTimer];
         [statusWindowController showRatingWindowWithRating:rating];
     NS_HANDLER
         [self networkError:localException];
@@ -1280,6 +1354,7 @@ static MainController *sharedController;
         [[self currentRemote] setRepeatMode:repeatMode];
         
         //Show loop status window
+               [self invalidateStatusWindowUpdateTimer];
         [statusWindowController showRepeatWindowWithMode:repeatMode];
     NS_HANDLER
         [self networkError:localException];
@@ -1294,15 +1369,32 @@ static MainController *sharedController;
         [[self currentRemote] setShuffleEnabled:newShuffleEnabled];
         //Show shuffle status window
         ITDebugLog(@"Setting shuffle mode to %i", newShuffleEnabled);
+               [self invalidateStatusWindowUpdateTimer];
         [statusWindowController showShuffleWindow:newShuffleEnabled];
     NS_HANDLER
         [self networkError:localException];
     NS_ENDHANDLER
 }
 
+- (void)toggleSongShufflable
+{
+       if ([self songIsPlaying]) {
+               NS_DURING
+                       BOOL flag = ![[self currentRemote] currentSongShufflable];
+                       ITDebugLog(@"Toggling shufflability.");
+                       [[self currentRemote] setCurrentSongShufflable:flag];
+                       //Show song shufflability status window
+                       [self invalidateStatusWindowUpdateTimer];
+                       [statusWindowController showSongShufflabilityWindow:flag];
+               NS_HANDLER
+                       [self networkError:localException];
+               NS_ENDHANDLER
+       }
+}
+
 - (void)registerNowOK
 {
-    [[StatusWindow sharedWindow] setLocked:NO];
+    [(StatusWindow *)[StatusWindow sharedWindow] setLocked:NO];
     [[StatusWindow sharedWindow] vanish:self];
     [[StatusWindow sharedWindow] setIgnoresMouseEvents:YES];
 
@@ -1311,7 +1403,7 @@ static MainController *sharedController;
 
 - (void)registerNowCancel
 {
-    [[StatusWindow sharedWindow] setLocked:NO];
+    [(StatusWindow *)[StatusWindow sharedWindow] setLocked:NO];
     [[StatusWindow sharedWindow] vanish:self];
     [[StatusWindow sharedWindow] setIgnoresMouseEvents:YES];
 
@@ -1347,14 +1439,16 @@ static MainController *sharedController;
         [self setupHotKeys];
         //playerRunningState = ITMTRemotePlayerRunning;
         playerRunningState = [[self currentRemote] playerRunningState];
-               if (refreshTimer) {
-                       [refreshTimer invalidate];
+               if (_needsPolling) {
+                       if (refreshTimer) {
+                               [refreshTimer invalidate];
+                       }
+                       refreshTimer = [[NSTimer scheduledTimerWithTimeInterval:([networkController isConnectedToServer] ? 10.0 : 0.5)
+                                                                        target:self
+                                                                        selector:@selector(timerUpdate)
+                                                                        userInfo:nil
+                                                                        repeats:YES] retain];
                }
-        refreshTimer = [[NSTimer scheduledTimerWithTimeInterval:([networkController isConnectedToServer] ? 10.0 : 0.5)
-                                target:self
-                                selector:@selector(timerUpdate)
-                                userInfo:nil
-                                repeats:YES] retain];
         [self timerUpdate];
         ITDebugLog(@"Connection successful.");
         return 1;
@@ -1432,6 +1526,7 @@ static MainController *sharedController;
 - (void)remoteServerFound:(id)sender
 {
     if (![networkController isServerOn] && ![networkController isConnectedToServer]) {
+               [self invalidateStatusWindowUpdateTimer];
         [[StatusWindowController sharedController] showReconnectQueryWindow];
     }
 }
@@ -1448,6 +1543,7 @@ static MainController *sharedController;
     ITDebugLog(@"Remote exception thrown: %@: %@", [exception name], [exception reason]);
     if ( ((exception == nil) || [[exception name] isEqualToString:NSPortTimeoutException]) && [networkController isConnectedToServer]) {
         //NSRunCriticalAlertPanel(@"Remote MenuTunes Disconnected", @"The MenuTunes server you were connected to stopped responding or quit. MenuTunes will revert back to the local player.", @"OK", nil, nil);
+               [self invalidateStatusWindowUpdateTimer];
         [[StatusWindowController sharedController] showNetworkErrorQueryWindow];
         if ([self disconnectFromServer]) {
             [[PreferencesController sharedPrefs] resetRemotePlayerTextFields];
@@ -1464,14 +1560,14 @@ static MainController *sharedController;
         [NSTimer scheduledTimerWithTimeInterval:90.0 target:self selector:@selector(checkForRemoteServer) userInfo:nil repeats:NO];
     }*/
     [self checkForRemoteServerAndConnectImmediately:YES];
-    [[StatusWindow sharedWindow] setLocked:NO];
+    [(StatusWindow *)[StatusWindow sharedWindow] setLocked:NO];
     [[StatusWindow sharedWindow] vanish:self];
     [[StatusWindow sharedWindow] setIgnoresMouseEvents:YES];
 }
 
 - (void)cancelReconnect
 {
-    [[StatusWindow sharedWindow] setLocked:NO];
+    [(StatusWindow *)[StatusWindow sharedWindow] setLocked:NO];
     [[StatusWindow sharedWindow] vanish:self];
     [[StatusWindow sharedWindow] setIgnoresMouseEvents:YES];
 }
@@ -1490,11 +1586,13 @@ static MainController *sharedController;
             [[self currentRemote] begin];
             [self setLatestSongIdentifier:@""];
             [self timerUpdate];
-            refreshTimer = [[NSTimer scheduledTimerWithTimeInterval:([networkController isConnectedToServer] ? 10.0 : 0.5)
-                                target:self
-                                selector:@selector(timerUpdate)
-                                userInfo:nil
-                                repeats:YES] retain];
+                       if (_needsPolling) {
+                               refreshTimer = [[NSTimer scheduledTimerWithTimeInterval:([networkController isConnectedToServer] ? 10.0 : 0.5)
+                                                                       target:self
+                                                                       selector:@selector(timerUpdate)
+                                                                       userInfo:nil
+                                                                       repeats:YES] retain];
+                       }
             //[NSThread detachNewThreadSelector:@selector(startTimerInNewThread) toTarget:self withObject:nil];
                        if (![df boolForKey:@"UsePollingOnly"]) {
                                [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(trackChanged:) name:@"ITMTTrackChanged" object:nil];
@@ -1516,7 +1614,7 @@ static MainController *sharedController;
             [refreshTimer invalidate];
             [refreshTimer release];
             refreshTimer = nil;
-                       [[NSNotificationCenter defaultCenter] removeObserver:self];
+                       [[NSNotificationCenter defaultCenter] removeObserver:self name:@"ITMTTrackChanged" object:nil];
                        [statusItem setEnabled:YES];
                        [statusItem setToolTip:@"iTunes not running."];
             [self clearHotKeys];
@@ -1552,6 +1650,13 @@ static MainController *sharedController;
     [[NSStatusBar systemStatusBar] removeStatusItem:statusItem];
 }
 
+- (void)applicationDidBecomeActive:(NSNotification *)note
+{
+       //This appears to not work in 10.4
+       if (_open && !blinged && ![[ITAboutWindowController sharedController] isVisible] && ![NSApp mainWindow] && ([[StatusWindow sharedWindow] exitMode] == ITTransientStatusWindowExitAfterDelay)) {
+               [[MainController sharedController] showPreferences];
+       }
+}
 
 /*************************************************************************/
 #pragma mark -