Finally fixed all the memory leaks, I hope. Fixed iTunesRemote to not
[MenuTunes.git] / MainController.m
1 #import "MainController.h"
2 #import "MenuController.h"
3 #import "PreferencesController.h"
4 #import "NetworkController.h"
5 #import "NetworkObject.h"
6 #import <ITKit/ITHotKeyCenter.h>
7 #import <ITKit/ITHotKey.h>
8 #import <ITKit/ITKeyCombo.h>
9 #import <ITKit/ITCategory-NSMenu.h>
10 #import "StatusWindow.h"
11 #import "StatusWindowController.h"
12 #import "StatusItemHack.h"
13
14 @interface NSMenu (MenuImpl)
15 - (id)_menuImpl;
16 @end
17
18 @interface NSCarbonMenuImpl:NSObject
19 {
20     NSMenu *_menu;
21 }
22
23 + (void)initialize;
24 + (void)setupForNoMenuBar;
25 - (void)dealloc;
26 - (void)setMenu:fp8;
27 - menu;
28 - (void)itemChanged:fp8;
29 - (void)itemAdded:fp8;
30 - (void)itemRemoved:fp8;
31 - (void)performActionWithHighlightingForItemAtIndex:(int)fp8;
32 - (void)performMenuAction:(SEL)fp8 withTarget:fp12;
33 - (void)setupCarbonMenuBar;
34 - (void)setAsMainCarbonMenuBar;
35 - (void)clearAsMainCarbonMenuBar;
36 - (void)popUpMenu:fp8 atLocation:(NSPoint)fp12 width:(float)fp20 forView:fp24 withSelectedItem:(int)fp28 withFont:fp32;
37 - (void)_popUpContextMenu:fp8 withEvent:fp12 forView:fp16 withFont:fp20;
38 - (void)_popUpContextMenu:fp8 withEvent:fp12 forView:fp16;
39 - window;
40 @end
41
42 @implementation NSImage (SmoothAdditions)
43
44 - (NSImage *)imageScaledSmoothlyToSize:(NSSize)scaledSize
45 {
46     NSImage *newImage;
47     NSImageRep *rep = [self bestRepresentationForDevice:nil];
48     
49     newImage = [[NSImage alloc] initWithSize:scaledSize];
50     [newImage lockFocus];
51     {
52         [[NSGraphicsContext currentContext] setImageInterpolation:NSImageInterpolationHigh];
53         [[NSGraphicsContext currentContext] setShouldAntialias:YES];
54         [rep drawInRect:NSMakeRect(3, 3, scaledSize.width - 6, scaledSize.height - 6)];
55     }
56     [newImage unlockFocus];
57     return [newImage autorelease];
58 }
59
60 @end
61
62 @interface MainController(Private)
63 - (ITMTRemote *)loadRemote;
64 - (void)setLatestSongIdentifier:(NSString *)newIdentifier;
65 - (void)applicationLaunched:(NSNotification *)note;
66 - (void)applicationTerminated:(NSNotification *)note;
67 @end
68
69 static MainController *sharedController;
70
71 @implementation MainController
72
73 + (MainController *)sharedController
74 {
75     return sharedController;
76 }
77
78 /*************************************************************************/
79 #pragma mark -
80 #pragma mark INITIALIZATION/DEALLOCATION METHODS
81 /*************************************************************************/
82
83 - (id)init
84 {
85     if ( ( self = [super init] ) ) {
86         sharedController = self;
87         
88         remoteArray = [[NSMutableArray alloc] initWithCapacity:1];
89         [[PreferencesController sharedPrefs] setController:self];
90         statusWindowController = [StatusWindowController sharedController];
91         menuController = [[MenuController alloc] init];
92         df = [[NSUserDefaults standardUserDefaults] retain];
93         timerUpdating = NO;
94         blinged = NO;
95     }
96     return self;
97 }
98
99 - (void)applicationDidFinishLaunching:(NSNotification *)note
100 {
101     //Turn on debug mode if needed
102     if ([df boolForKey:@"ITDebugMode"]) {
103         SetITDebugMode(YES);
104     }
105     
106     if (([df integerForKey:@"appVersion"] < 1200) && ([df integerForKey:@"SongsInAdvance"] > 0)) {
107         [df removePersistentDomainForName:@"com.ithinksw.menutunes"];
108         [df synchronize];
109         [[PreferencesController sharedPrefs] registerDefaults];
110         [[StatusWindowController sharedController] showPreferencesUpdateWindow];
111     }
112     
113     currentRemote = [self loadRemote];
114     [[self currentRemote] begin];
115     
116     //Turn on network stuff if needed
117     networkController = [[NetworkController alloc] init];
118     if ([df boolForKey:@"enableSharing"]) {
119         [self setServerStatus:YES];
120     } else if ([df boolForKey:@"useSharedPlayer"]) {
121         [self checkForRemoteServerAndConnectImmediately:YES];
122     }
123     
124     //Setup for notification of the remote player launching or quitting
125     [[[NSWorkspace sharedWorkspace] notificationCenter]
126             addObserver:self
127             selector:@selector(applicationTerminated:)
128             name:NSWorkspaceDidTerminateApplicationNotification
129             object:nil];
130     
131     [[[NSWorkspace sharedWorkspace] notificationCenter]
132             addObserver:self
133             selector:@selector(applicationLaunched:)
134             name:NSWorkspaceDidLaunchApplicationNotification
135             object:nil];
136     
137     if (![df objectForKey:@"menu"]) {  // If this is nil, defaults have never been registered.
138         [[PreferencesController sharedPrefs] registerDefaults];
139     }
140     
141     if ([df boolForKey:@"ITMTNoStatusItem"]) {
142         statusItem = nil;
143     } else {
144         [StatusItemHack install];
145         statusItem = [[ITStatusItem alloc]
146                 initWithStatusBar:[NSStatusBar systemStatusBar]
147                 withLength:NSSquareStatusItemLength];
148     }
149     
150     bling = [[MTBlingController alloc] init];
151     [self blingTime];
152     registerTimer = [[NSTimer scheduledTimerWithTimeInterval:10.0
153                              target:self
154                              selector:@selector(blingTime)
155                              userInfo:nil
156                              repeats:YES] retain];
157     
158     NS_DURING
159         if ([[self currentRemote] playerRunningState] == ITMTRemotePlayerRunning) {
160             [self applicationLaunched:nil];
161         } else {
162             if ([df boolForKey:@"LaunchPlayerWithMT"])
163                 [self showPlayer];
164             else
165                 [self applicationTerminated:nil];
166         }
167     NS_HANDLER
168         [self networkError:localException];
169     NS_ENDHANDLER
170     
171     [statusItem setImage:[NSImage imageNamed:@"MenuNormal"]];
172     [statusItem setAlternateImage:[NSImage imageNamed:@"MenuInverted"]];
173
174     [networkController startRemoteServerSearch];
175     [NSApp deactivate];
176 }
177
178 - (ITMTRemote *)loadRemote
179 {
180     NSString *folderPath = [[NSBundle mainBundle] builtInPlugInsPath];
181     ITDebugLog(@"Gathering remotes.");
182     if (folderPath) {
183         NSArray      *bundlePathList = [NSBundle pathsForResourcesOfType:@"remote" inDirectory:folderPath];
184         NSEnumerator *enumerator     = [bundlePathList objectEnumerator];
185         NSString     *bundlePath;
186
187         while ( (bundlePath = [enumerator nextObject]) ) {
188             NSBundle* remoteBundle = [NSBundle bundleWithPath:bundlePath];
189
190             if (remoteBundle) {
191                 Class remoteClass = [remoteBundle principalClass];
192
193                 if ([remoteClass conformsToProtocol:@protocol(ITMTRemote)] &&
194                     [(NSObject *)remoteClass isKindOfClass:[NSObject class]]) {
195                     id remote = [remoteClass remote];
196                     ITDebugLog(@"Adding remote at path %@", bundlePath);
197                     [remoteArray addObject:remote];
198                 }
199             }
200         }
201
202 //      if ( [remoteArray count] > 0 ) {  // UNCOMMENT WHEN WE HAVE > 1 PLUGIN
203 //          if ( [remoteArray count] > 1 ) {
204 //              [remoteArray sortUsingSelector:@selector(sortAlpha:)];
205 //          }
206 //          [self loadModuleAccessUI]; //Comment out this line to disable remote visibility
207 //      }
208     }
209 //  NSLog(@"%@", [remoteArray objectAtIndex:0]);  //DEBUG
210     return [remoteArray objectAtIndex:0];
211 }
212
213 /*************************************************************************/
214 #pragma mark -
215 #pragma mark INSTANCE METHODS
216 /*************************************************************************/
217
218 /*- (void)startTimerInNewThread
219 {
220     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
221     NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
222     refreshTimer = [[NSTimer scheduledTimerWithTimeInterval:0.5
223                              target:self
224                              selector:@selector(timerUpdate)
225                              userInfo:nil
226                              repeats:YES] retain];
227     [runLoop run];
228     ITDebugLog(@"Timer started.");
229     [pool release];
230 }*/
231
232 - (void)setBlingTime:(NSDate*)date
233 {
234     NSMutableDictionary *globalPrefs;
235     [df synchronize];
236     globalPrefs = [[df persistentDomainForName:@".GlobalPreferences"] mutableCopy];
237     if (date) {
238         [globalPrefs setObject:date forKey:@"ITMTTrialStart"];
239         [globalPrefs setObject:[NSNumber numberWithInt:MT_CURRENT_VERSION] forKey:@"ITMTTrialVers"];
240     } else {
241         [globalPrefs removeObjectForKey:@"ITMTTrialStart"];
242         [globalPrefs removeObjectForKey:@"ITMTTrialVers"];
243     }
244     [df setPersistentDomain:globalPrefs forName:@".GlobalPreferences"];
245     [df synchronize];
246     [globalPrefs release];
247 }
248
249 - (NSDate*)getBlingTime
250 {
251     [df synchronize];
252     return [[df persistentDomainForName:@".GlobalPreferences"] objectForKey:@"ITMTTrialStart"];
253 }
254
255 - (void)blingTime
256 {
257     NSDate *now = [NSDate date];
258     if (![self blingBling]) {
259         if ( (! [self getBlingTime] ) || ([now timeIntervalSinceDate:[self getBlingTime]] < 0) ) {
260             [self setBlingTime:now];
261         } else if ([[[df persistentDomainForName:@".GlobalPreferences"] objectForKey:@"ITMTTrialVers"] intValue] < MT_CURRENT_VERSION) {
262             if ([now timeIntervalSinceDate:[self getBlingTime]] >= 345600) {
263                 [self setBlingTime:[now addTimeInterval:-259200]];
264             } else {
265                 NSMutableDictionary *globalPrefs;
266                 [df synchronize];
267                 globalPrefs = [[df persistentDomainForName:@".GlobalPreferences"] mutableCopy];
268                 [globalPrefs setObject:[NSNumber numberWithInt:MT_CURRENT_VERSION] forKey:@"ITMTTrialVers"];
269                 [df setPersistentDomain:globalPrefs forName:@".GlobalPreferences"];
270                 [df synchronize];
271                 [globalPrefs release];
272             }
273         }
274         
275         if ( ([now timeIntervalSinceDate:[self getBlingTime]] >= 604800) && (blinged != YES) ) {
276             blinged = YES;
277             [statusItem setEnabled:NO];
278             [self clearHotKeys];
279             if ([refreshTimer isValid]) {
280                 [refreshTimer invalidate];
281             }
282             [statusWindowController showRegistrationQueryWindow];
283         }
284     } else {
285         if (blinged) {
286             [statusItem setEnabled:YES];
287             [self setupHotKeys];
288             if (![refreshTimer isValid]) {
289                 [refreshTimer release];
290                 refreshTimer = [[NSTimer scheduledTimerWithTimeInterval:([networkController isConnectedToServer] ? 10.0 : 0.5)
291                              target:self
292                              selector:@selector(timerUpdate)
293                              userInfo:nil
294                              repeats:YES] retain];
295             }
296             blinged = NO;
297         }
298         [self setBlingTime:nil];
299     }
300 }
301
302 - (void)blingNow
303 {
304     [bling showPanel];
305 }
306
307 - (BOOL)blingBling
308 {
309     if ( ! ([bling checkDone] == 2475) ) {
310         return NO;
311     } else {
312         return YES;
313     }
314 }
315
316 - (BOOL)songIsPlaying
317 {
318     NSString *identifier = nil;
319     NS_DURING
320         identifier = [[self currentRemote] playerStateUniqueIdentifier];
321     NS_HANDLER
322         [self networkError:localException];
323     NS_ENDHANDLER
324     return ( ! ([identifier isEqualToString:@"0-0"]) );
325 }
326
327 - (BOOL)radioIsPlaying
328 {
329     ITMTRemotePlayerPlaylistClass class = nil;
330     NS_DURING
331         class = [[self currentRemote] currentPlaylistClass];
332     NS_HANDLER
333         [self networkError:localException];
334     NS_ENDHANDLER
335     return (class  == ITMTRemotePlayerRadioPlaylist );
336 }
337
338 - (BOOL)songChanged
339 {
340     NSString *identifier = nil;
341     NS_DURING
342         identifier = [[self currentRemote] playerStateUniqueIdentifier];
343     NS_HANDLER
344         [self networkError:localException];
345     NS_ENDHANDLER
346     return ( ! [identifier isEqualToString:_latestSongIdentifier] );
347 }
348
349 - (NSString *)latestSongIdentifier
350 {
351     return _latestSongIdentifier;
352 }
353
354 - (void)setLatestSongIdentifier:(NSString *)newIdentifier
355 {
356     ITDebugLog(@"Setting latest song identifier:");
357     ITDebugLog(@"   - Identifier: %@", newIdentifier);
358     [_latestSongIdentifier autorelease];
359     _latestSongIdentifier = [newIdentifier retain];
360 }
361
362 - (void)timerUpdate
363 {
364         if ([[self currentRemote] playerStateUniqueIdentifier] == nil) {
365                 if ([statusItem isEnabled]) {
366                         [statusItem setToolTip:@"iTunes not responding."];
367                         [[ITHotKeyCenter sharedCenter] setEnabled:NO];
368                 }
369                 [statusItem setEnabled:NO];
370                 return;
371         } else if (![statusItem isEnabled]) {
372                 [statusItem setEnabled:YES];
373                 [statusItem setToolTip:_toolTip];
374                 [[ITHotKeyCenter sharedCenter] setEnabled:YES];
375                 return;
376         }
377         
378         if ( [self songChanged] && (timerUpdating != YES) && (playerRunningState == ITMTRemotePlayerRunning) ) {
379         ITDebugLog(@"The song changed. '%@'", _latestSongIdentifier);
380         if ([df boolForKey:@"runScripts"]) {
381             NSArray *scripts = [[NSFileManager defaultManager] directoryContentsAtPath:[NSHomeDirectory() stringByAppendingPathComponent:@"Library/Application Support/MenuTunes/Scripts"]];
382             NSEnumerator *scriptsEnum = [scripts objectEnumerator];
383             NSString *nextScript;
384             ITDebugLog(@"Running AppleScripts for song change.");
385             while ( (nextScript = [scriptsEnum nextObject]) ) {
386                 NSDictionary *error;
387                 NSAppleScript *currentScript = [[NSAppleScript alloc] initWithContentsOfURL:[NSURL fileURLWithPath:[[NSHomeDirectory() stringByAppendingPathComponent:@"Library/Application Support/MenuTunes/Scripts"] stringByAppendingPathComponent:nextScript]] error:&error];
388                 ITDebugLog(@"Running script: %@", nextScript);
389                 if (!currentScript || ![currentScript executeAndReturnError:nil]) {
390                     ITDebugLog(@"Error running script %@.", nextScript);
391                 }
392                 [currentScript release];
393             }
394         }
395         
396         timerUpdating = YES;
397         [statusItem setEnabled:NO];
398                 
399         NS_DURING
400             latestPlaylistClass = [[self currentRemote] currentPlaylistClass];
401                         
402                         if ([menuController rebuildSubmenus]) {
403                                 if ( [df boolForKey:@"showSongInfoOnChange"] ) {
404                                         [self performSelector:@selector(showCurrentTrackInfo) withObject:nil afterDelay:0.0];
405                                 }
406                                 
407                                 [self setLatestSongIdentifier:[[self currentRemote] playerStateUniqueIdentifier]];
408                                 //Create the tooltip for the status item
409                                 if ( [df boolForKey:@"showToolTip"] ) {
410                                         NSString *artist = [[self currentRemote] currentSongArtist];
411                                         NSString *title = [[self currentRemote] currentSongTitle];
412                                         ITDebugLog(@"Creating status item tooltip.");
413                                         if (artist) {
414                                                 _toolTip = [NSString stringWithFormat:@"%@ - %@", artist, title];
415                                         } else if (title) {
416                                                 _toolTip = title;
417                                         } else {
418                                                 _toolTip = @"No Song Playing";
419                                         }
420                                         [statusItem setToolTip:_toolTip];
421                                 } else {
422                                         [statusItem setToolTip:nil];
423                                 }
424                         }
425         NS_HANDLER
426             [self networkError:localException];
427         NS_ENDHANDLER
428         timerUpdating = NO;
429         [statusItem setEnabled:YES];
430     }
431         
432     if ([networkController isConnectedToServer]) {
433         [statusItem setMenu:([[self currentRemote] playerRunningState] == ITMTRemotePlayerRunning) ? [menuController menu] : [menuController menuForNoPlayer]];
434     }
435 }
436
437 - (void)menuClicked
438 {
439     ITDebugLog(@"Menu clicked.");
440         
441         if ( ([[self currentRemote] playerStateUniqueIdentifier] == nil) && playerRunningState == ITMTRemotePlayerRunning ) {
442                 if ([statusItem isEnabled]) {
443                         [statusItem setToolTip:@"iTunes not responding."];
444                         [[ITHotKeyCenter sharedCenter] setEnabled:NO];
445                 }
446                 [statusItem setEnabled:NO];
447                 return;
448         } else if (![statusItem isEnabled]) {
449                 [statusItem setEnabled:YES];
450                 [statusItem setToolTip:_toolTip];
451                 [[ITHotKeyCenter sharedCenter] setEnabled:YES];
452                 return;
453         }
454         
455     if ([networkController isConnectedToServer]) {
456         //Used the cached version
457         return;
458     }
459     
460     NS_DURING
461         if ([[self currentRemote] playerRunningState] == ITMTRemotePlayerRunning) {
462             [statusItem setMenu:[menuController menu]];
463         } else {
464             [statusItem setMenu:[menuController menuForNoPlayer]];
465         }
466     NS_HANDLER
467         [self networkError:localException];
468     NS_ENDHANDLER
469 }
470
471 //
472 //
473 // Menu Selectors
474 //
475 //
476
477 - (void)playPause
478 {
479     NS_DURING
480         ITMTRemotePlayerPlayingState state = [[self currentRemote] playerPlayingState];
481         ITDebugLog(@"Play/Pause toggled");
482         if (state == ITMTRemotePlayerPlaying) {
483             [[self currentRemote] pause];
484         } else if ((state == ITMTRemotePlayerForwarding) || (state == ITMTRemotePlayerRewinding)) {
485             [[self currentRemote] pause];
486             [[self currentRemote] play];
487         } else {
488             [[self currentRemote] play];
489         }
490     NS_HANDLER
491         [self networkError:localException];
492     NS_ENDHANDLER
493     
494     [self timerUpdate];
495 }
496
497 - (void)nextSong
498 {
499     ITDebugLog(@"Going to next song.");
500     NS_DURING
501         [[self currentRemote] goToNextSong];
502     NS_HANDLER
503         [self networkError:localException];
504     NS_ENDHANDLER
505     [self timerUpdate];
506 }
507
508 - (void)prevSong
509 {
510     ITDebugLog(@"Going to previous song.");
511     NS_DURING
512         [[self currentRemote] goToPreviousSong];
513     NS_HANDLER
514         [self networkError:localException];
515     NS_ENDHANDLER
516     [self timerUpdate];
517 }
518
519 - (void)fastForward
520 {
521     ITDebugLog(@"Fast forwarding.");
522     NS_DURING
523         [[self currentRemote] forward];
524     NS_HANDLER
525         [self networkError:localException];
526     NS_ENDHANDLER
527     [self timerUpdate];
528 }
529
530 - (void)rewind
531 {
532     ITDebugLog(@"Rewinding.");
533     NS_DURING
534         [[self currentRemote] rewind];
535     NS_HANDLER
536         [self networkError:localException];
537     NS_ENDHANDLER
538     [self timerUpdate];
539 }
540
541 - (void)selectPlaylistAtIndex:(int)index
542 {
543     ITDebugLog(@"Selecting playlist %i", index);
544     NS_DURING
545         [[self currentRemote] switchToPlaylistAtIndex:(index % 1000) ofSourceAtIndex:(index / 1000)];
546         //[[self currentRemote] switchToPlaylistAtIndex:index];
547     NS_HANDLER
548         [self networkError:localException];
549     NS_ENDHANDLER
550     [self timerUpdate];
551 }
552
553 - (void)selectSongAtIndex:(int)index
554 {
555     ITDebugLog(@"Selecting song %i", index);
556     NS_DURING
557         [[self currentRemote] switchToSongAtIndex:index];
558     NS_HANDLER
559         [self networkError:localException];
560     NS_ENDHANDLER
561     [self timerUpdate];
562 }
563
564 - (void)selectSongRating:(int)rating
565 {
566     ITDebugLog(@"Selecting song rating %i", rating);
567     NS_DURING
568         [[self currentRemote] setCurrentSongRating:(float)rating / 100.0];
569     NS_HANDLER
570         [self networkError:localException];
571     NS_ENDHANDLER
572     [self timerUpdate];
573 }
574
575 - (void)selectEQPresetAtIndex:(int)index
576 {
577     ITDebugLog(@"Selecting EQ preset %i", index);
578     NS_DURING
579         if (index == -1) {
580             [[self currentRemote] setEqualizerEnabled:![[self currentRemote] equalizerEnabled]];
581         } else {
582             [[self currentRemote] switchToEQAtIndex:index];
583         }
584     NS_HANDLER
585         [self networkError:localException];
586     NS_ENDHANDLER
587     [self timerUpdate];
588 }
589
590 - (void)makePlaylistWithTerm:(NSString *)term ofType:(int)type
591 {
592     ITDebugLog(@"Making playlist with term %@, type %i", term, type);
593     NS_DURING
594         [[self currentRemote] makePlaylistWithTerm:term ofType:type];
595     NS_HANDLER
596         [self networkError:localException];
597     NS_ENDHANDLER
598     ITDebugLog(@"Done making playlist");
599 }
600
601 - (void)showPlayer
602 {
603     ITDebugLog(@"Beginning show player.");
604     //if ( ( playerRunningState == ITMTRemotePlayerRunning) ) {
605         ITDebugLog(@"Showing player interface.");
606         NS_DURING
607             [[self currentRemote] showPrimaryInterface];
608         NS_HANDLER
609             [self networkError:localException];
610         NS_ENDHANDLER
611     /*} else {
612         ITDebugLog(@"Launching player.");
613         NS_DURING
614             NSString *path;
615             if ( (path = [df stringForKey:@"CustomPlayerPath"]) ) {
616             } else {
617                 pathITDebugLog(@"Showing player interface."); = [[self currentRemote] playerFullName];
618             }
619             if (![[NSWorkspace sharedWorkspace] launchApplication:path]) {
620                 ITDebugLog(@"Error Launching Player");
621             }
622         NS_HANDLER
623             [self networkError:localException];
624         NS_ENDHANDLER
625     }*/
626     ITDebugLog(@"Finished show player.");
627 }
628
629 - (void)showPreferences
630 {
631     ITDebugLog(@"Show preferences.");
632     [[PreferencesController sharedPrefs] showPrefsWindow:self];
633 }
634
635 - (void)showPreferencesAndClose
636 {
637     ITDebugLog(@"Show preferences.");
638     [[PreferencesController sharedPrefs] showPrefsWindow:self];
639     [[StatusWindow sharedWindow] setLocked:NO];
640     [[StatusWindow sharedWindow] vanish:self];
641     [[StatusWindow sharedWindow] setIgnoresMouseEvents:YES];
642 }
643
644 - (void)showTestWindow
645 {
646     [self showCurrentTrackInfo];
647 }
648
649 - (void)quitMenuTunes
650 {
651     ITDebugLog(@"Quitting MenuTunes.");
652     [NSApp terminate:self];
653 }
654
655 //
656 //
657
658 - (MenuController *)menuController
659 {
660     return menuController;
661 }
662
663 - (void)closePreferences
664 {
665     ITDebugLog(@"Preferences closed.");
666     if ( ( playerRunningState == ITMTRemotePlayerRunning) ) {
667         [self setupHotKeys];
668     }
669 }
670
671 - (ITMTRemote *)currentRemote
672 {
673     if ([networkController isConnectedToServer] && ![[networkController networkObject] isValid]) {
674         [self networkError:nil];
675         return nil;
676     }
677     return currentRemote;
678 }
679
680 //
681 //
682 // Hot key setup
683 //
684 //
685
686 - (void)clearHotKeys
687 {
688     NSEnumerator *hotKeyEnumerator = [[[ITHotKeyCenter sharedCenter] allHotKeys] objectEnumerator];
689     ITHotKey *nextHotKey;
690     ITDebugLog(@"Clearing hot keys.");
691     while ( (nextHotKey = [hotKeyEnumerator nextObject]) ) {
692         [[ITHotKeyCenter sharedCenter] unregisterHotKey:nextHotKey];
693     }
694     ITDebugLog(@"Done clearing hot keys.");
695 }
696
697 - (void)setupHotKeys
698 {
699     ITHotKey *hotKey;
700     ITDebugLog(@"Setting up hot keys.");
701     
702     if (playerRunningState == ITMTRemotePlayerNotRunning && ![[NetworkController sharedController] isConnectedToServer]) {
703         return;
704     }
705     
706     if ([df objectForKey:@"PlayPause"] != nil) {
707         ITDebugLog(@"Setting up play pause hot key.");
708         hotKey = [[ITHotKey alloc] init];
709         [hotKey setName:@"PlayPause"];
710         [hotKey setKeyCombo:[ITKeyCombo keyComboWithPlistRepresentation:[df objectForKey:@"PlayPause"]]];
711         [hotKey setTarget:self];
712         [hotKey setAction:@selector(playPause)];
713         [[ITHotKeyCenter sharedCenter] registerHotKey:[hotKey autorelease]];
714     }
715     
716     if ([df objectForKey:@"NextTrack"] != nil) {
717         ITDebugLog(@"Setting up next track hot key.");
718         hotKey = [[ITHotKey alloc] init];
719         [hotKey setName:@"NextTrack"];
720         [hotKey setKeyCombo:[ITKeyCombo keyComboWithPlistRepresentation:[df objectForKey:@"NextTrack"]]];
721         [hotKey setTarget:self];
722         [hotKey setAction:@selector(nextSong)];
723         [[ITHotKeyCenter sharedCenter] registerHotKey:[hotKey autorelease]];
724     }
725     
726     if ([df objectForKey:@"PrevTrack"] != nil) {
727         ITDebugLog(@"Setting up previous track hot key.");
728         hotKey = [[ITHotKey alloc] init];
729         [hotKey setName:@"PrevTrack"];
730         [hotKey setKeyCombo:[ITKeyCombo keyComboWithPlistRepresentation:[df objectForKey:@"PrevTrack"]]];
731         [hotKey setTarget:self];
732         [hotKey setAction:@selector(prevSong)];
733         [[ITHotKeyCenter sharedCenter] registerHotKey:[hotKey autorelease]];
734     }
735     
736     if ([df objectForKey:@"FastForward"] != nil) {
737         ITDebugLog(@"Setting up fast forward hot key.");
738         hotKey = [[ITHotKey alloc] init];
739         [hotKey setName:@"FastForward"];
740         [hotKey setKeyCombo:[ITKeyCombo keyComboWithPlistRepresentation:[df objectForKey:@"FastForward"]]];
741         [hotKey setTarget:self];
742         [hotKey setAction:@selector(fastForward)];
743         [[ITHotKeyCenter sharedCenter] registerHotKey:[hotKey autorelease]];
744     }
745     
746     if ([df objectForKey:@"Rewind"] != nil) {
747         ITDebugLog(@"Setting up rewind hot key.");
748         hotKey = [[ITHotKey alloc] init];
749         [hotKey setName:@"Rewind"];
750         [hotKey setKeyCombo:[ITKeyCombo keyComboWithPlistRepresentation:[df objectForKey:@"Rewind"]]];
751         [hotKey setTarget:self];
752         [hotKey setAction:@selector(rewind)];
753         [[ITHotKeyCenter sharedCenter] registerHotKey:[hotKey autorelease]];
754     }
755     
756     if ([df objectForKey:@"ShowPlayer"] != nil) {
757         ITDebugLog(@"Setting up show player hot key.");
758         hotKey = [[ITHotKey alloc] init];
759         [hotKey setName:@"ShowPlayer"];
760         [hotKey setKeyCombo:[ITKeyCombo keyComboWithPlistRepresentation:[df objectForKey:@"ShowPlayer"]]];
761         [hotKey setTarget:self];
762         [hotKey setAction:@selector(showPlayer)];
763         [[ITHotKeyCenter sharedCenter] registerHotKey:[hotKey autorelease]];
764     }
765     
766     if ([df objectForKey:@"TrackInfo"] != nil) {
767         ITDebugLog(@"Setting up track info hot key.");
768         hotKey = [[ITHotKey alloc] init];
769         [hotKey setName:@"TrackInfo"];
770         [hotKey setKeyCombo:[ITKeyCombo keyComboWithPlistRepresentation:[df objectForKey:@"TrackInfo"]]];
771         [hotKey setTarget:self];
772         [hotKey setAction:@selector(showCurrentTrackInfo)];
773         [[ITHotKeyCenter sharedCenter] registerHotKey:[hotKey autorelease]];
774     }
775     
776     if ([df objectForKey:@"UpcomingSongs"] != nil) {
777         ITDebugLog(@"Setting up upcoming songs hot key.");
778         hotKey = [[ITHotKey alloc] init];
779         [hotKey setName:@"UpcomingSongs"];
780         [hotKey setKeyCombo:[ITKeyCombo keyComboWithPlistRepresentation:[df objectForKey:@"UpcomingSongs"]]];
781         [hotKey setTarget:self];
782         [hotKey setAction:@selector(showUpcomingSongs)];
783         [[ITHotKeyCenter sharedCenter] registerHotKey:[hotKey autorelease]];
784     }
785     
786     if ([df objectForKey:@"ToggleLoop"] != nil) {
787         ITDebugLog(@"Setting up toggle loop hot key.");
788         hotKey = [[ITHotKey alloc] init];
789         [hotKey setName:@"ToggleLoop"];
790         [hotKey setKeyCombo:[ITKeyCombo keyComboWithPlistRepresentation:[df objectForKey:@"ToggleLoop"]]];
791         [hotKey setTarget:self];
792         [hotKey setAction:@selector(toggleLoop)];
793         [[ITHotKeyCenter sharedCenter] registerHotKey:[hotKey autorelease]];
794     }
795     
796     if ([df objectForKey:@"ToggleShuffle"] != nil) {
797         ITDebugLog(@"Setting up toggle shuffle hot key.");
798         hotKey = [[ITHotKey alloc] init];
799         [hotKey setName:@"ToggleShuffle"];
800         [hotKey setKeyCombo:[ITKeyCombo keyComboWithPlistRepresentation:[df objectForKey:@"ToggleShuffle"]]];
801         [hotKey setTarget:self];
802         [hotKey setAction:@selector(toggleShuffle)];
803         [[ITHotKeyCenter sharedCenter] registerHotKey:[hotKey autorelease]];
804     }
805     
806     if ([df objectForKey:@"IncrementVolume"] != nil) {
807         ITDebugLog(@"Setting up increment volume hot key.");
808         hotKey = [[ITHotKey alloc] init];
809         [hotKey setName:@"IncrementVolume"];
810         [hotKey setKeyCombo:[ITKeyCombo keyComboWithPlistRepresentation:[df objectForKey:@"IncrementVolume"]]];
811         [hotKey setTarget:self];
812         [hotKey setAction:@selector(incrementVolume)];
813         [[ITHotKeyCenter sharedCenter] registerHotKey:[hotKey autorelease]];
814     }
815     
816     if ([df objectForKey:@"DecrementVolume"] != nil) {
817         ITDebugLog(@"Setting up decrement volume hot key.");
818         hotKey = [[ITHotKey alloc] init];
819         [hotKey setName:@"DecrementVolume"];
820         [hotKey setKeyCombo:[ITKeyCombo keyComboWithPlistRepresentation:[df objectForKey:@"DecrementVolume"]]];
821         [hotKey setTarget:self];
822         [hotKey setAction:@selector(decrementVolume)];
823         [[ITHotKeyCenter sharedCenter] registerHotKey:[hotKey autorelease]];
824     }
825     
826     if ([df objectForKey:@"IncrementRating"] != nil) {
827         ITDebugLog(@"Setting up increment rating hot key.");
828         hotKey = [[ITHotKey alloc] init];
829         [hotKey setName:@"IncrementRating"];
830         [hotKey setKeyCombo:[ITKeyCombo keyComboWithPlistRepresentation:[df objectForKey:@"IncrementRating"]]];
831         [hotKey setTarget:self];
832         [hotKey setAction:@selector(incrementRating)];
833         [[ITHotKeyCenter sharedCenter] registerHotKey:[hotKey autorelease]];
834     }
835     
836     if ([df objectForKey:@"DecrementRating"] != nil) {
837         ITDebugLog(@"Setting up decrement rating hot key.");
838         hotKey = [[ITHotKey alloc] init];
839         [hotKey setName:@"DecrementRating"];
840         [hotKey setKeyCombo:[ITKeyCombo keyComboWithPlistRepresentation:[df objectForKey:@"DecrementRating"]]];
841         [hotKey setTarget:self];
842         [hotKey setAction:@selector(decrementRating)];
843         [[ITHotKeyCenter sharedCenter] registerHotKey:[hotKey autorelease]];
844     }
845     
846     if ([df objectForKey:@"PopupMenu"] != nil) {
847         ITDebugLog(@"Setting up popup menu hot key.");
848         hotKey = [[ITHotKey alloc] init];
849         [hotKey setName:@"PopupMenu"];
850         [hotKey setKeyCombo:[ITKeyCombo keyComboWithPlistRepresentation:[df objectForKey:@"PopupMenu"]]];
851         [hotKey setTarget:self];
852         [hotKey setAction:@selector(popupMenu)];
853         [[ITHotKeyCenter sharedCenter] registerHotKey:[hotKey autorelease]];
854     }
855     
856     int i;
857     for (i = 0; i <= 5; i++) {
858         NSString *curName = [NSString stringWithFormat:@"SetRating%i", i];
859         if ([df objectForKey:curName] != nil) {
860             ITDebugLog(@"Setting up set rating %i hot key.", i);
861             hotKey = [[ITHotKey alloc] init];
862             [hotKey setName:curName];
863             [hotKey setKeyCombo:[ITKeyCombo keyComboWithPlistRepresentation:[df objectForKey:curName]]];
864             [hotKey setTarget:self];
865             [hotKey setAction:@selector(setRating:)];
866             [[ITHotKeyCenter sharedCenter] registerHotKey:[hotKey autorelease]];
867         }
868     }
869     ITDebugLog(@"Finished setting up hot keys.");
870 }
871
872 - (void)showCurrentTrackInfo
873 {
874     ITMTRemotePlayerSource  source      = 0;
875     NSString               *title       = nil;
876     NSString               *album       = nil;
877     NSString               *artist      = nil;
878     NSString               *composer    = nil;
879     NSString               *time        = nil;
880     NSString               *track       = nil;
881     NSImage                *art         = nil;
882     int                     rating      = -1;
883     int                     playCount   = -1;
884     
885     ITDebugLog(@"Showing track info status window.");
886     
887     NS_DURING
888         source      = [[self currentRemote] currentSource];
889         title       = [[self currentRemote] currentSongTitle];
890     NS_HANDLER
891         [self networkError:localException];
892     NS_ENDHANDLER
893     
894     if ( title ) {
895         if ( [df boolForKey:@"showAlbumArtwork"] ) {
896             NSSize oldSize, newSize;
897              NS_DURING
898                  art = [[self currentRemote] currentSongAlbumArt];
899                  oldSize = [art size];
900                  if (oldSize.width > oldSize.height) newSize = NSMakeSize(110,oldSize.height * (110.0f / oldSize.width));
901                  else newSize = NSMakeSize(oldSize.width * (110.0f / oldSize.height),110);
902                 art = [[[[NSImage alloc] initWithData:[art TIFFRepresentation]] autorelease] imageScaledSmoothlyToSize:newSize];
903             NS_HANDLER
904                 [self networkError:localException];
905             NS_ENDHANDLER
906         }
907         
908         if ( [df boolForKey:@"showAlbum"] ) {
909             NS_DURING
910                 album = [[self currentRemote] currentSongAlbum];
911             NS_HANDLER
912                 [self networkError:localException];
913             NS_ENDHANDLER
914         }
915
916         if ( [df boolForKey:@"showArtist"] ) {
917             NS_DURING
918                 artist = [[self currentRemote] currentSongArtist];
919             NS_HANDLER
920                 [self networkError:localException];
921             NS_ENDHANDLER
922         }
923
924         if ( [df boolForKey:@"showComposer"] ) {
925             NS_DURING
926                 composer = [[self currentRemote] currentSongComposer];
927             NS_HANDLER
928                 [self networkError:localException];
929             NS_ENDHANDLER
930         }
931
932         if ( [df boolForKey:@"showTime"] ) {
933             NS_DURING
934                 time = [NSString stringWithFormat:@"%@: %@ / %@",
935                 NSLocalizedString(@"time", @"Time"),
936                 [[self currentRemote] currentSongElapsed],
937                 [[self currentRemote] currentSongLength]];
938             NS_HANDLER
939                 [self networkError:localException];
940             NS_ENDHANDLER
941         }
942
943         if ( [df boolForKey:@"showTrackNumber"] ) {
944             int trackNo    = 0;
945             int trackCount = 0;
946             
947             NS_DURING
948                 trackNo    = [[self currentRemote] currentSongTrack];
949                 trackCount = [[self currentRemote] currentAlbumTrackCount];
950             NS_HANDLER
951                 [self networkError:localException];
952             NS_ENDHANDLER
953             
954             if ( (trackNo > 0) || (trackCount > 0) ) {
955                 track = [NSString stringWithFormat:@"%@: %i %@ %i",
956                     @"Track", trackNo, @"of", trackCount];
957             }
958         }
959
960         if ( [df boolForKey:@"showTrackRating"] ) {
961             float currentRating = 0;
962             
963             NS_DURING
964                 currentRating = [[self currentRemote] currentSongRating];
965             NS_HANDLER
966                 [self networkError:localException];
967             NS_ENDHANDLER
968             
969             if (currentRating >= 0.0) {
970                 rating = ( currentRating * 5 );
971             }
972         }
973         
974         if ( [df boolForKey:@"showPlayCount"] && ![self radioIsPlaying] && [[self currentRemote] currentSource] == ITMTRemoteLibrarySource ) {
975             NS_DURING
976                 playCount = [[self currentRemote] currentSongPlayCount];
977             NS_HANDLER
978                 [self networkError:localException];
979             NS_ENDHANDLER
980         }
981     } else {
982         title = NSLocalizedString(@"noSongPlaying", @"No song is playing.");
983     }
984     ITDebugLog(@"Showing current track info status window.");
985     [statusWindowController showSongInfoWindowWithSource:source
986                                                    title:title
987                                                    album:album
988                                                   artist:artist
989                                                 composer:composer
990                                                     time:time
991                                                    track:track
992                                                   rating:rating
993                                                playCount:playCount
994                                                    image:art];
995 }
996
997 - (void)showUpcomingSongs
998 {
999     int numSongs = 0;
1000     NS_DURING
1001         numSongs = [[self currentRemote] numberOfSongsInPlaylistAtIndex:[[self currentRemote] currentPlaylistIndex]];
1002     NS_HANDLER
1003         [self networkError:localException];
1004     NS_ENDHANDLER
1005     
1006     ITDebugLog(@"Showing upcoming songs status window.");
1007     NS_DURING
1008         if (numSongs > 0) {
1009             int numSongsInAdvance = [df integerForKey:@"SongsInAdvance"];
1010             NSMutableArray *songList = [NSMutableArray arrayWithCapacity:numSongsInAdvance];
1011             int curTrack = [[self currentRemote] currentSongIndex];
1012             int i;
1013     
1014             for (i = curTrack + 1; i <= curTrack + numSongsInAdvance; i++) {
1015                 if (i <= numSongs) {
1016                     [songList addObject:[[self currentRemote] songTitleAtIndex:i]];
1017                 }
1018             }
1019             
1020             if ([songList count] == 0) {
1021                 [songList addObject:NSLocalizedString(@"noUpcomingSongs", @"No upcoming songs.")];
1022             }
1023             
1024             [statusWindowController showUpcomingSongsWindowWithTitles:songList];
1025         } else {
1026             [statusWindowController showUpcomingSongsWindowWithTitles:[NSArray arrayWithObject:NSLocalizedString(@"noUpcomingSongs", @"No upcoming songs.")]];
1027         }
1028     NS_HANDLER
1029         [self networkError:localException];
1030     NS_ENDHANDLER
1031 }
1032
1033 - (void)popupMenu
1034 {
1035     if (!_popped) {
1036         _popped = YES;
1037         [self menuClicked];
1038         NSMenu *menu = [statusItem menu];
1039         [(NSCarbonMenuImpl *)[menu _menuImpl] popUpMenu:menu atLocation:[NSEvent mouseLocation] width:1 forView:nil withSelectedItem:-30 withFont:[NSFont menuFontOfSize:32]];
1040         _popped = NO;
1041     }
1042 }
1043
1044 - (void)incrementVolume
1045 {
1046     NS_DURING
1047         float volume  = [[self currentRemote] volume];
1048         float dispVol = volume;
1049         ITDebugLog(@"Incrementing volume.");
1050         volume  += 0.110;
1051         dispVol += 0.100;
1052         
1053         if (volume > 1.0) {
1054             volume  = 1.0;
1055             dispVol = 1.0;
1056         }
1057     
1058         ITDebugLog(@"Setting volume to %f", volume);
1059         [[self currentRemote] setVolume:volume];
1060     
1061         // Show volume status window
1062         [statusWindowController showVolumeWindowWithLevel:dispVol];
1063     NS_HANDLER
1064         [self networkError:localException];
1065     NS_ENDHANDLER
1066 }
1067
1068 - (void)decrementVolume
1069 {
1070     NS_DURING
1071         float volume  = [[self currentRemote] volume];
1072         float dispVol = volume;
1073         ITDebugLog(@"Decrementing volume.");
1074         volume  -= 0.090;
1075         dispVol -= 0.100;
1076     
1077         if (volume < 0.0) {
1078             volume  = 0.0;
1079             dispVol = 0.0;
1080         }
1081         
1082         ITDebugLog(@"Setting volume to %f", volume);
1083         [[self currentRemote] setVolume:volume];
1084         
1085         //Show volume status window
1086         [statusWindowController showVolumeWindowWithLevel:dispVol];
1087     NS_HANDLER
1088         [self networkError:localException];
1089     NS_ENDHANDLER
1090 }
1091
1092 - (void)incrementRating
1093 {
1094     NS_DURING
1095         float rating = [[self currentRemote] currentSongRating];
1096         ITDebugLog(@"Incrementing rating.");
1097         
1098         if ([[self currentRemote] currentPlaylistIndex] == 0) {
1099             ITDebugLog(@"No song playing, rating change aborted.");
1100             return;
1101         }
1102         
1103         rating += 0.2;
1104         if (rating > 1.0) {
1105             rating = 1.0;
1106         }
1107         ITDebugLog(@"Setting rating to %f", rating);
1108         [[self currentRemote] setCurrentSongRating:rating];
1109         
1110         //Show rating status window
1111         [statusWindowController showRatingWindowWithRating:rating];
1112     NS_HANDLER
1113         [self networkError:localException];
1114     NS_ENDHANDLER
1115 }
1116
1117 - (void)decrementRating
1118 {
1119     NS_DURING
1120         float rating = [[self currentRemote] currentSongRating];
1121         ITDebugLog(@"Decrementing rating.");
1122         
1123         if ([[self currentRemote] currentPlaylistIndex] == 0) {
1124             ITDebugLog(@"No song playing, rating change aborted.");
1125             return;
1126         }
1127         
1128         rating -= 0.2;
1129         if (rating < 0.0) {
1130             rating = 0.0;
1131         }
1132         ITDebugLog(@"Setting rating to %f", rating);
1133         [[self currentRemote] setCurrentSongRating:rating];
1134         
1135         //Show rating status window
1136         [statusWindowController showRatingWindowWithRating:rating];
1137     NS_HANDLER
1138         [self networkError:localException];
1139     NS_ENDHANDLER
1140 }
1141
1142 - (void)setRating:(ITHotKey *)sender
1143 {
1144     int stars = [[sender name] characterAtIndex:9] - 48;
1145     [self selectSongRating:stars * 20];
1146     [statusWindowController showRatingWindowWithRating:(float)stars / 5.0];
1147 }
1148
1149 - (void)toggleLoop
1150 {
1151     NS_DURING
1152         ITMTRemotePlayerRepeatMode repeatMode = [[self currentRemote] repeatMode];
1153         ITDebugLog(@"Toggling repeat mode.");
1154         switch (repeatMode) {
1155             case ITMTRemotePlayerRepeatOff:
1156                 repeatMode = ITMTRemotePlayerRepeatAll;
1157             break;
1158             case ITMTRemotePlayerRepeatAll:
1159                 repeatMode = ITMTRemotePlayerRepeatOne;
1160             break;
1161             case ITMTRemotePlayerRepeatOne:
1162                 repeatMode = ITMTRemotePlayerRepeatOff;
1163             break;
1164         }
1165         ITDebugLog(@"Setting repeat mode to %i", repeatMode);
1166         [[self currentRemote] setRepeatMode:repeatMode];
1167         
1168         //Show loop status window
1169         [statusWindowController showRepeatWindowWithMode:repeatMode];
1170     NS_HANDLER
1171         [self networkError:localException];
1172     NS_ENDHANDLER
1173 }
1174
1175 - (void)toggleShuffle
1176 {
1177     NS_DURING
1178         BOOL newShuffleEnabled = ( ! [[self currentRemote] shuffleEnabled] );
1179         ITDebugLog(@"Toggling shuffle mode.");
1180         [[self currentRemote] setShuffleEnabled:newShuffleEnabled];
1181         //Show shuffle status window
1182         ITDebugLog(@"Setting shuffle mode to %i", newShuffleEnabled);
1183         [statusWindowController showShuffleWindow:newShuffleEnabled];
1184     NS_HANDLER
1185         [self networkError:localException];
1186     NS_ENDHANDLER
1187 }
1188
1189 - (void)registerNowOK
1190 {
1191     [[StatusWindow sharedWindow] setLocked:NO];
1192     [[StatusWindow sharedWindow] vanish:self];
1193     [[StatusWindow sharedWindow] setIgnoresMouseEvents:YES];
1194
1195     [self blingNow];
1196 }
1197
1198 - (void)registerNowCancel
1199 {
1200     [[StatusWindow sharedWindow] setLocked:NO];
1201     [[StatusWindow sharedWindow] vanish:self];
1202     [[StatusWindow sharedWindow] setIgnoresMouseEvents:YES];
1203
1204     [NSApp terminate:self];
1205 }
1206
1207 /*************************************************************************/
1208 #pragma mark -
1209 #pragma mark NETWORK HANDLERS
1210 /*************************************************************************/
1211
1212 - (void)setServerStatus:(BOOL)newStatus
1213 {
1214     if (newStatus) {
1215         //Turn on
1216         [networkController setServerStatus:YES];
1217     } else {
1218         //Tear down
1219         [networkController setServerStatus:NO];
1220     }
1221 }
1222
1223 - (int)connectToServer
1224 {
1225     int result;
1226     ITDebugLog(@"Attempting to connect to shared remote.");
1227     result = [networkController connectToHost:[df stringForKey:@"sharedPlayerHost"]];
1228     //Connect
1229     if (result == 1) {
1230         [[PreferencesController sharedPrefs] resetRemotePlayerTextFields];
1231         currentRemote = [[[networkController networkObject] remote] retain];
1232         
1233         [self setupHotKeys];
1234         //playerRunningState = ITMTRemotePlayerRunning;
1235         playerRunningState = [[self currentRemote] playerRunningState];
1236                 
1237         [refreshTimer invalidate];
1238         refreshTimer = [[NSTimer scheduledTimerWithTimeInterval:([networkController isConnectedToServer] ? 10.0 : 0.5)
1239                                 target:self
1240                                 selector:@selector(timerUpdate)
1241                                 userInfo:nil
1242                                 repeats:YES] retain];
1243         [self timerUpdate];
1244         ITDebugLog(@"Connection successful.");
1245         return 1;
1246     } else if (result == 0) {
1247         ITDebugLog(@"Connection failed.");
1248         currentRemote = [remoteArray objectAtIndex:0];
1249         return 0;
1250     } else {
1251         //Do something about the password being invalid
1252         ITDebugLog(@"Connection failed.");
1253         currentRemote = [remoteArray objectAtIndex:0];
1254         return -1;
1255     }
1256 }
1257
1258 - (BOOL)disconnectFromServer
1259 {
1260     ITDebugLog(@"Disconnecting from shared remote.");
1261     //Disconnect
1262     [currentRemote release];
1263     currentRemote = [remoteArray objectAtIndex:0];
1264     [networkController disconnect];
1265     
1266     if ([[self currentRemote] playerRunningState] == ITMTRemotePlayerRunning) {
1267         [self applicationLaunched:nil];
1268     } else {
1269         [self applicationTerminated:nil];
1270     }
1271     [self timerUpdate];
1272     return YES;
1273 }
1274
1275 - (void)checkForRemoteServer
1276 {
1277     [self checkForRemoteServerAndConnectImmediately:NO];
1278 }
1279
1280 - (void)checkForRemoteServerAndConnectImmediately:(BOOL)connectImmediately
1281 {
1282     ITDebugLog(@"Checking for remote server.");
1283     if (!_checkingForServer) {
1284         if (!_serverCheckLock) {
1285             _serverCheckLock = [[NSLock alloc] init];
1286         }
1287         [_serverCheckLock lock];
1288         _checkingForServer = YES;
1289         [_serverCheckLock unlock];
1290         [NSThread detachNewThreadSelector:@selector(runRemoteServerCheck:) toTarget:self withObject:[NSNumber numberWithBool:connectImmediately]];
1291     }
1292 }
1293
1294 - (void)runRemoteServerCheck:(id)sender
1295 {
1296     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1297     ITDebugLog(@"Remote server check running.");
1298     if ([networkController checkForServerAtHost:[df stringForKey:@"sharedPlayerHost"]]) {
1299         ITDebugLog(@"Remote server found.");
1300         if ([sender boolValue]) {
1301             [self performSelectorOnMainThread:@selector(connectToServer) withObject:nil waitUntilDone:NO];
1302         } else {
1303             [self performSelectorOnMainThread:@selector(remoteServerFound:) withObject:nil waitUntilDone:NO];
1304         }
1305     } else {
1306         ITDebugLog(@"Remote server not found.");
1307         [self performSelectorOnMainThread:@selector(remoteServerNotFound:) withObject:nil waitUntilDone:NO];
1308     }
1309     [_serverCheckLock lock];
1310     _checkingForServer = NO;
1311     [_serverCheckLock unlock];
1312     [pool release];
1313 }
1314
1315 - (void)remoteServerFound:(id)sender
1316 {
1317     if (![networkController isServerOn] && ![networkController isConnectedToServer]) {
1318         [[StatusWindowController sharedController] showReconnectQueryWindow];
1319     }
1320 }
1321
1322 - (void)remoteServerNotFound:(id)sender
1323 {
1324     if (![[NetworkController sharedController] isConnectedToServer]) {
1325         [NSTimer scheduledTimerWithTimeInterval:90.0 target:self selector:@selector(checkForRemoteServer) userInfo:nil repeats:NO];
1326     }
1327 }
1328
1329 - (void)networkError:(NSException *)exception
1330 {
1331     ITDebugLog(@"Remote exception thrown: %@: %@", [exception name], [exception reason]);
1332     if ( ((exception == nil) || [[exception name] isEqualToString:NSPortTimeoutException]) && [networkController isConnectedToServer]) {
1333         //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);
1334         [[StatusWindowController sharedController] showNetworkErrorQueryWindow];
1335         if ([self disconnectFromServer]) {
1336             [[PreferencesController sharedPrefs] resetRemotePlayerTextFields];
1337             [NSTimer scheduledTimerWithTimeInterval:90.0 target:self selector:@selector(checkForRemoteServer) userInfo:nil repeats:NO];
1338         } else {
1339             ITDebugLog(@"CRITICAL ERROR, DISCONNECTING!");
1340         }
1341     }
1342 }
1343
1344 - (void)reconnect
1345 {
1346     /*if ([self connectToServer] == 0) {
1347         [NSTimer scheduledTimerWithTimeInterval:90.0 target:self selector:@selector(checkForRemoteServer) userInfo:nil repeats:NO];
1348     }*/
1349     [self checkForRemoteServerAndConnectImmediately:YES];
1350     [[StatusWindow sharedWindow] setLocked:NO];
1351     [[StatusWindow sharedWindow] vanish:self];
1352     [[StatusWindow sharedWindow] setIgnoresMouseEvents:YES];
1353 }
1354
1355 - (void)cancelReconnect
1356 {
1357     [[StatusWindow sharedWindow] setLocked:NO];
1358     [[StatusWindow sharedWindow] vanish:self];
1359     [[StatusWindow sharedWindow] setIgnoresMouseEvents:YES];
1360 }
1361
1362 /*************************************************************************/
1363 #pragma mark -
1364 #pragma mark WORKSPACE NOTIFICATION HANDLERS
1365 /*************************************************************************/
1366
1367 - (void)applicationLaunched:(NSNotification *)note
1368 {
1369     NS_DURING
1370         if (!note || ([[[note userInfo] objectForKey:@"NSApplicationName"] isEqualToString:[[self currentRemote] playerFullName]] && ![[NetworkController sharedController] isConnectedToServer])) {
1371             ITDebugLog(@"Remote application launched.");
1372             playerRunningState = ITMTRemotePlayerRunning;
1373             [[self currentRemote] begin];
1374             [self setLatestSongIdentifier:@""];
1375             [self timerUpdate];
1376             refreshTimer = [[NSTimer scheduledTimerWithTimeInterval:([networkController isConnectedToServer] ? 10.0 : 0.5)
1377                                 target:self
1378                                 selector:@selector(timerUpdate)
1379                                 userInfo:nil
1380                                 repeats:YES] retain];
1381             //[NSThread detachNewThreadSelector:@selector(startTimerInNewThread) toTarget:self withObject:nil];
1382             [self setupHotKeys];
1383         }
1384     NS_HANDLER
1385         [self networkError:localException];
1386     NS_ENDHANDLER
1387 }
1388
1389  - (void)applicationTerminated:(NSNotification *)note
1390  {
1391     NS_DURING
1392         if (!note || [[[note userInfo] objectForKey:@"NSApplicationName"] isEqualToString:[[self currentRemote] playerFullName]] && ![[NetworkController sharedController] isConnectedToServer]) {
1393             ITDebugLog(@"Remote application terminated.");
1394             playerRunningState = ITMTRemotePlayerNotRunning;
1395             [[self currentRemote] halt];
1396             [refreshTimer invalidate];
1397             [refreshTimer release];
1398             refreshTimer = nil;
1399                         [statusItem setEnabled:YES];
1400                         [statusItem setToolTip:@"iTunes not running."];
1401             [self clearHotKeys];
1402             
1403             if ([df objectForKey:@"ShowPlayer"] != nil) {
1404                 ITHotKey *hotKey;
1405                 ITDebugLog(@"Setting up show player hot key.");
1406                 hotKey = [[ITHotKey alloc] init];
1407                 [hotKey setName:@"ShowPlayer"];
1408                 [hotKey setKeyCombo:[ITKeyCombo keyComboWithPlistRepresentation:[df objectForKey:@"ShowPlayer"]]];
1409                 [hotKey setTarget:self];
1410                 [hotKey setAction:@selector(showPlayer)];
1411                 [[ITHotKeyCenter sharedCenter] registerHotKey:[hotKey autorelease]];
1412             }
1413         }
1414     NS_HANDLER
1415         [self networkError:localException];
1416     NS_ENDHANDLER
1417  }
1418
1419
1420 /*************************************************************************/
1421 #pragma mark -
1422 #pragma mark NSApplication DELEGATE METHODS
1423 /*************************************************************************/
1424
1425 - (void)applicationWillTerminate:(NSNotification *)note
1426 {
1427     [networkController stopRemoteServerSearch];
1428     [self clearHotKeys];
1429     [[NSStatusBar systemStatusBar] removeStatusItem:statusItem];
1430 }
1431
1432
1433 /*************************************************************************/
1434 #pragma mark -
1435 #pragma mark DEALLOCATION METHOD
1436 /*************************************************************************/
1437
1438 - (void)dealloc
1439 {
1440     [self applicationTerminated:nil];
1441     [bling release];
1442     [statusItem release];
1443     [statusWindowController release];
1444     [menuController release];
1445     [networkController release];
1446     [_serverCheckLock release];
1447     [super dealloc];
1448 }
1449
1450 @end