Hitting the show track info hotkey when the status window is visible will hide it...
[MenuTunes.git] / StatusWindowController.m
1 #import "StatusWindowController.h"
2 #import "StatusWindow.h"
3 #import "PreferencesController.h"
4 #import "MainController.h"
5
6 #import <ITKit/ITTSWBackgroundView.h>
7 #import <ITKit/ITWindowEffect.h>
8 #import <ITKit/ITCutWindowEffect.h>
9 #import <ITKit/ITDissolveWindowEffect.h>
10 #import <ITKit/ITSlideHorizontallyWindowEffect.h>
11 #import <ITKit/ITSlideVerticallyWindowEffect.h>
12 #import <ITKit/ITPivotWindowEffect.h>
13
14 static StatusWindowController *sharedController;
15
16 @implementation StatusWindowController
17
18
19 + (StatusWindowController *)sharedController
20 {
21     if ( ! sharedController ) {
22         sharedController = [[StatusWindowController alloc] init];
23     }
24     
25     return sharedController;
26 }
27
28
29 - (id)init
30 {
31     if ( ( self = [super init] ) ) {
32         
33         float exitDelay;
34         NSString *entryClass;
35         NSString *exitClass;
36         NSArray  *classList = [ITWindowEffect effectClasses];
37         float entrySpeed;
38         float exitSpeed;
39                 NSArray *screens = [NSScreen screens];
40                 int screenIndex;
41         
42         NSData *colorData;
43         
44         ITWindowEffect *entryEffect;
45         ITWindowEffect *exitEffect;
46         
47                 _currentType = StatusWindowNoType;
48                 
49         _window = [[StatusWindow sharedWindow] retain];
50         df = [[NSUserDefaults standardUserDefaults] retain];
51         
52         exitDelay  = [df floatForKey:@"statusWindowVanishDelay"];
53         entryClass = [df stringForKey:@"statusWindowAppearanceEffect"];
54         exitClass  = [df stringForKey:@"statusWindowVanishEffect"];
55         entrySpeed = [df floatForKey:@"statusWindowAppearanceSpeed"];
56         exitSpeed  = [df floatForKey:@"statusWindowVanishSpeed"];
57                 
58                 screenIndex = [df integerForKey:@"statusWindowScreenIndex"];
59                 if (screenIndex >= [screens count]) {
60                         screenIndex = 0;
61                 }
62                 [_window setScreen:[screens objectAtIndex:screenIndex]];
63                 
64         [_window setExitMode:ITTransientStatusWindowExitAfterDelay];
65         [_window setExitDelay:(exitDelay ? exitDelay : 4.0)];
66         
67         [_window setHorizontalPosition:[df integerForKey:@"statusWindowHorizontalPosition"]];
68         [_window setVerticalPosition:[df integerForKey:@"statusWindowVerticalPosition"]];
69         
70         [_window setSizing:(ITTransientStatusWindowSizing)[df integerForKey:@"statusWindowSizing"]];
71         
72         if ( [classList containsObject:NSClassFromString(entryClass)] ) {
73             entryEffect = [[[NSClassFromString(entryClass) alloc] initWithWindow:_window] autorelease];
74         } else {
75             entryEffect = [[[ITCutWindowEffect alloc] initWithWindow:_window] autorelease];
76         }
77         
78         if ( [classList containsObject:NSClassFromString(exitClass)] ) {
79             exitEffect = [[[NSClassFromString(exitClass) alloc] initWithWindow:_window] autorelease];
80         } else {
81             exitEffect = [[[ITDissolveWindowEffect alloc] initWithWindow:_window] autorelease];
82         }
83         
84         [_window setEntryEffect:entryEffect];
85         [_window setExitEffect:exitEffect];
86         
87         [[_window entryEffect] setEffectTime:(entrySpeed ? entrySpeed : 0.8)];
88         [[_window exitEffect]  setEffectTime:(exitSpeed  ? exitSpeed  : 0.8)];
89         
90         [(ITTSWBackgroundView *)[_window contentView]setBackgroundMode:
91             (ITTSWBackgroundMode)[df integerForKey:@"statusWindowBackgroundMode"]];
92         
93         colorData = [df dataForKey:@"statusWindowBackgroundColor"];
94         
95         if ( colorData ) {
96             [(ITTSWBackgroundView *)[_window contentView] setBackgroundColor:
97                 (NSColor *)[NSUnarchiver unarchiveObjectWithData:colorData]];
98         } else {
99             [(ITTSWBackgroundView *)[_window contentView] setBackgroundColor:[NSColor blueColor]];
100         }
101     }
102     
103     return self;
104 }
105
106 - (void)dealloc
107 {
108     [_window release];
109     [super dealloc];
110 }
111
112 - (void)showSongInfoWindowWithSource:(ITMTRemotePlayerSource)source
113                                title:            (NSString *)title
114                                album:            (NSString *)album
115                               artist:            (NSString *)artist
116                             composer:            (NSString *)composer
117                                 time:            (NSString *)time  // FLOW: Should probably be NSDate or something.
118                                track:            (NSString *)track
119                               rating:                   (int)rating
120                            playCount:                   (int)playCount
121                                image:             (NSImage *)art
122 {
123     NSImage  *image = nil;
124     NSMutableAttributedString *text = [[NSMutableAttributedString alloc] initWithString:title];
125     
126     if ( art != nil ) {
127         image = art;
128     } else if ( source == ITMTRemoteLibrarySource ) {
129         image = [NSImage imageNamed:@"Library"];
130     } else if ( source == ITMTRemoteCDSource ) {
131         image = [NSImage imageNamed:@"CD"];
132     } else if ( source == ITMTRemoteRadioSource ) {
133         image = [NSImage imageNamed:@"Radio"];
134     } else if ( source == ITMTRemoteiPodSource ) {
135         image = [NSImage imageNamed:@"iPod"];
136     } else if ( source == ITMTRemoteGenericDeviceSource ) {
137         image = [NSImage imageNamed:@"MP3Player"];
138     } else if ( source == ITMTRemoteSharedLibrarySource ) {
139         image = [NSImage imageNamed:@"Library"];
140     }
141     
142     [_window setImage:image];
143         [_window setSizing:(ITTransientStatusWindowSizing)[df integerForKey:@"statusWindowSizing"]];
144     
145     if ( album ) {
146                 [[text mutableString] appendFormat:@"\n%@", album];
147         //text = [text stringByAppendingString:[@"\n" stringByAppendingString:album]];
148     }
149     if ( artist ) {
150                 [[text mutableString] appendFormat:@"\n%@", artist];
151         //text = [text stringByAppendingString:[@"\n" stringByAppendingString:artist]];
152     }
153     if ( composer ) {
154                 [[text mutableString] appendFormat:@"\n%@", composer];
155         //text = [text stringByAppendingString:[@"\n" stringByAppendingString:composer]];
156     }
157     if ( time ) {
158                 _timeRange = NSMakeRange([[text mutableString] length] + 1, [time length]);
159                 [[text mutableString] appendFormat:@"\n%@", time];
160         //text = [text stringByAppendingString:[@"\n" stringByAppendingString:time]];
161     }
162     if ( track ) {
163                 [[text mutableString] appendFormat:@"\n%@", track];
164         //text = [text stringByAppendingString:[@"\n" stringByAppendingString:track]];
165     }
166     if (playCount > -1) {
167                 [[text mutableString] appendFormat:@"\n%@: %i", NSLocalizedString(@"playCount", @"Play Count"), playCount];
168         //text = [text stringByAppendingString:[NSString stringWithFormat:@"\n%@: %i", NSLocalizedString(@"playCount", @"Play Count"), playCount]];
169     }
170     if ( rating > -1 ) {
171
172         NSString *ratingString = [NSString string];
173         NSString *emptyChar    = [NSString stringWithUTF8String:"☆"];
174         NSString *fullChar     = [NSString stringWithUTF8String:"★"];
175         int       i, start = [[text mutableString] length], size = 18;
176         
177         for ( i = 1; i < 6; i++ ) {
178                 
179             if ( rating >= i ) {
180                 ratingString = [ratingString stringByAppendingString:fullChar];
181             } else {
182                 ratingString = [ratingString stringByAppendingString:emptyChar];
183             }
184         }
185                 
186                 [[text mutableString] appendFormat:@"\n%@", ratingString];
187                 if ([_window sizing] == ITTransientStatusWindowSmall) {
188                         size /= SMALL_DIVISOR;
189                 } else if ([_window sizing] == ITTransientStatusWindowMini) {
190                         size /= MINI_DIVISOR;
191                 }
192                 [text setAttributes:[NSDictionary dictionaryWithObjectsAndKeys:[NSFont fontWithName:@"AppleGothic" size:size], NSFontAttributeName, nil, nil] range:NSMakeRange(start + 1, 5)];
193         //text = [text stringByAppendingString:[@"\n" stringByAppendingString:ratingString]];
194     }
195     
196         _currentType = StatusWindowTrackInfoType;
197     [_window buildTextWindowWithString:text];
198     [_window appear:self];
199         [text release];
200 }
201
202 - (void)showUpcomingSongsWindowWithTitles:(NSArray *)titleStrings
203 {
204 //  NSString *bull = [NSString stringWithUTF8String:"‣ "];
205     NSString *bull = [NSString stringWithUTF8String:"♪ "];
206     NSString *end  = [@"\n" stringByAppendingString:bull];
207     [_window setImage:[NSImage imageNamed:@"Upcoming"]];
208     [_window setSizing:(ITTransientStatusWindowSizing)[df integerForKey:@"statusWindowSizing"]];
209     [_window buildTextWindowWithString:[bull stringByAppendingString:[titleStrings componentsJoinedByString:end]]];
210         _currentType = StatusWindowUpcomingSongsType;
211     [_window appear:self];
212 }
213
214 - (void)showVolumeWindowWithLevel:(float)level
215 {
216     [_window setImage:[NSImage imageNamed:@"Volume"]];
217     [_window setSizing:(ITTransientStatusWindowSizing)[df integerForKey:@"statusWindowSizing"]];
218     [_window buildMeterWindowWithCharacter:[NSString stringWithUTF8String:"▊"]
219                                       size:18
220                                      count:10
221                                     active:( ceil(level * 100) / 10 )];
222         _currentType = StatusWindowVolumeType;
223     [_window appear:self];
224 }
225
226 - (void)showRatingWindowWithRating:(float)rating
227 {
228     [_window setImage:[NSImage imageNamed:@"Rating"]];
229     [_window setSizing:(ITTransientStatusWindowSizing)[df integerForKey:@"statusWindowSizing"]];
230     [_window buildMeterWindowWithCharacter:[NSString stringWithUTF8String:"★"]
231                                       size:48
232                                      count:5
233                                     active:( ceil(rating * 100) / 20 )];
234     _currentType = StatusWindowRatingType;
235         [_window appear:self];
236 }
237
238 - (void)showShuffleWindow:(BOOL)shuffle
239 {
240     [_window setImage:[NSImage imageNamed:@"Shuffle"]];
241     [_window setSizing:(ITTransientStatusWindowSizing)[df integerForKey:@"statusWindowSizing"]];
242     [_window buildTextWindowWithString:( shuffle ? NSLocalizedString(@"shuffleOn", @"Shuffle On") : NSLocalizedString(@"shuffleOff", @"Shuffle Off"))];
243         _currentType = StatusWindowRatingType;
244     [_window appear:self];
245 }
246
247 - (void)showRepeatWindowWithMode:(StatusWindowRepeatMode)mode
248 {
249     NSString *string = nil;
250     
251     if ( mode == StatusWindowRepeatNone ) {
252         string = NSLocalizedString(@"repeatOff", @"Repeat Off");
253     } else if ( mode == StatusWindowRepeatGroup ) {
254         string = NSLocalizedString(@"repeatPlaylist", @"Repeat Playlist");
255     } else if ( mode == StatusWindowRepeatTrack ) {
256         string = NSLocalizedString(@"repeatOneTrack", @"Repeat One Track");;
257     }
258     
259     [_window setImage:[NSImage imageNamed:@"Repeat"]];
260     [_window setSizing:(ITTransientStatusWindowSizing)[df integerForKey:@"statusWindowSizing"]];
261     [_window buildTextWindowWithString:string];
262         _currentType = StatusWindowRepeatType;
263     [_window appear:self];
264 }
265
266 - (void)showSongShufflabilityWindow:(BOOL)shufflable
267 {
268     [_window setImage:[NSImage imageNamed:@"Shuffle"]];
269     [_window setSizing:(ITTransientStatusWindowSizing)[df integerForKey:@"statusWindowSizing"]];
270     [_window buildTextWindowWithString:( !shufflable ? NSLocalizedString(@"shufflableOn", @"Current Song Skipped When Shuffling") : NSLocalizedString(@"shufflableOff", @"Current Song Not Skipped When Shuffling"))];
271         _currentType = StatusWindowShufflabilityType;
272     [_window appear:self];
273 }
274
275 - (void)showSetupQueryWindow
276 {
277     NSString *message = NSLocalizedString(@"autolaunch_msg", @"Would you like MenuTunes to launch\nautomatically at startup?");
278
279     [_window setImage:[NSImage imageNamed:@"Setup"]];
280     [_window setSizing:(ITTransientStatusWindowSizing)[df integerForKey:@"statusWindowSizing"]];
281     [_window buildDialogWindowWithMessage:message
282                             defaultButton:NSLocalizedString(@"launch_at_startup", @"Launch at Startup")
283                           alternateButton:NSLocalizedString(@"launch_manually", @"Launch Manually")
284                                    target:[PreferencesController sharedPrefs]
285                             defaultAction:@selector(autoLaunchOK)
286                           alternateAction:@selector(autoLaunchCancel)];
287
288         _currentType = StatusWindowSetupType;
289     [_window appear:self];
290     [_window setLocked:YES];
291 }
292
293
294 - (void)showRegistrationQueryWindow
295 {
296     NSString *message = NSLocalizedString(@"trialexpired_msg", @"Your 7-day unlimited trial period has elapsed.\nYou must register to continue using MenuTunes.");
297
298     [_window setImage:[NSImage imageNamed:@"Register"]];
299     [_window setSizing:(ITTransientStatusWindowSizing)[df integerForKey:@"statusWindowSizing"]];
300     [_window buildDialogWindowWithMessage:message
301                             defaultButton:NSLocalizedString(@"registernow", @"Register Now")
302                           alternateButton:NSLocalizedString(@"quitmenutunes", @"Quit MenuTunes")
303                                    target:[MainController sharedController]
304                             defaultAction:@selector(registerNowOK)
305                           alternateAction:@selector(registerNowCancel)];
306
307         _currentType = StatusWindowRegistrationType;
308     [_window appear:self];
309     [_window setLocked:YES];
310 }
311
312 - (void)showReconnectQueryWindow
313 {
314     NSString *message = NSLocalizedString(@"sharedplayeravailable_msg", @"The selected shared player is available again.\nWould you like to reconnect to it?");
315     [_window setLocked:NO];
316     [_window setImage:[NSImage imageNamed:@"Setup"]];
317     [_window setSizing:(ITTransientStatusWindowSizing)[df integerForKey:@"statusWindowSizing"]];
318     [_window buildDialogWindowWithMessage:message
319                             defaultButton:NSLocalizedString(@"reconnect", @"Reconnect")
320                           alternateButton:NSLocalizedString(@"ignore", @"Ignore")
321                                    target:[MainController sharedController]
322                             defaultAction:@selector(reconnect)
323                           alternateAction:@selector(cancelReconnect)];
324
325         _currentType = StatusWindowNetworkType;
326     [_window appear:self];
327     [_window setLocked:YES];
328 }
329
330 - (void)showNetworkErrorQueryWindow
331 {
332     NSString *message = NSLocalizedString(@"sharedplayerunreachable_msg", @"The remote MenuTunes server is unreachable.\nMenuTunes will revert back to the local player.");
333
334     [_window setImage:[NSImage imageNamed:@"Setup"]];
335     [_window setSizing:(ITTransientStatusWindowSizing)[df integerForKey:@"statusWindowSizing"]];
336     [_window buildDialogWindowWithMessage:message
337                             defaultButton:@" OK "
338                           alternateButton:nil
339                                    target:[MainController sharedController]
340                             defaultAction:@selector(cancelReconnect)
341                           alternateAction:nil];
342
343         _currentType = StatusWindowNetworkType;
344     [_window appear:self];
345     [_window setLocked:YES];
346 }
347
348 - (void)showPreferencesUpdateWindow
349 {
350     NSString *message = NSLocalizedString(@"reconfigureprefs_msg", @"The new features in this version of MenuTunes\nrequire you to reconfigure your preferences.");
351
352     [_window setImage:[NSImage imageNamed:@"Setup"]];
353     [_window setSizing:(ITTransientStatusWindowSizing)[df integerForKey:@"statusWindowSizing"]];
354     [_window buildDialogWindowWithMessage:message
355                             defaultButton:NSLocalizedString(@"showpreferences", @"Show Preferences")
356                           alternateButton:@"OK"
357                                    target:[MainController sharedController]
358                             defaultAction:@selector(showPreferencesAndClose)
359                           alternateAction:@selector(cancelReconnect)];
360
361         _currentType = StatusWindowPreferencesType;
362     [_window appear:self];
363     [_window setLocked:YES];
364 }
365
366 - (void)showDebugModeEnabledWindow
367 {
368         [_window setImage:[NSImage imageNamed:@"Setup"]];
369     [_window setSizing:(ITTransientStatusWindowSizing)[df integerForKey:@"statusWindowSizing"]];
370     [_window buildDialogWindowWithMessage:NSLocalizedString(@"debugmodeenabled", @"Debug Mode Enabled")
371                             defaultButton:@"OK"
372                           alternateButton:nil
373                                    target:[MainController sharedController]
374                             defaultAction:@selector(cancelReconnect)
375                           alternateAction:nil];
376
377         _currentType = StatusWindowDebugType;
378     [_window appear:self];
379         [_window setLocked:YES];
380 }
381
382 - (StatusWindowType)currentStatusWindowType
383 {
384         return _currentType;
385 }
386
387 - (void)updateTime:(NSString *)time
388 {
389         if (time && [time length]) {
390                 [_window updateTime:time range:_timeRange];
391         }
392 }
393
394 @end