Upped the timeout for the albums and artist method to 600.
[MenuTunes.git] / StatusWindow.m
1 //
2 //  StatusWindow.m
3 //  MenuTunes
4 //
5 //  Created by Matt L. Judy on Sat Feb 22 2003.
6 //  Copyright (c) 2003 NibFile.com. All rights reserved.
7 //
8
9 #import "StatusWindow.h"
10
11
12 #define SW_PAD             24.00
13 #define SW_SPACE           24.00
14 #define SW_MINW           211.00
15 #define SW_BORDER          32.00
16 #define SW_METER_PAD        4.00
17 #define SW_BUTTON_PAD_R    30.00
18 #define SW_BUTTON_PAD_B    24.00
19 #define SW_BUTTON_DIV      12.00
20 #define SW_BUTTON_EXTRA_W   8.00
21 #define SW_SHADOW_SAT       1.25
22
23 @interface StatusWindow (Private)
24 - (NSRect)setupWindowWithDataSize:(NSSize)dataSize;
25 @end
26
27
28 @implementation StatusWindow
29
30
31 /*************************************************************************/
32 #pragma mark -
33 #pragma mark INITIALIZATION / DEALLOCATION METHODS
34 /*************************************************************************/
35
36 - (id)initWithContentView:(NSView *)contentView
37                  exitMode:(ITTransientStatusWindowExitMode)exitMode
38            backgroundType:(ITTransientStatusWindowBackgroundType)backgroundType
39 {
40     if ( ( self = [super initWithContentView:contentView
41                                exitMode:exitMode
42                          backgroundType:backgroundType] ) ) {
43      // Set default values.
44         _image  = [[NSImage imageNamed:@"NSApplicationIcon"] retain];
45         _locked = NO;
46         _sizing = ITTransientStatusWindowRegular;
47     }
48     
49     return self;
50 }
51
52 - (void)dealloc
53 {
54     [_image release];
55     [super dealloc];
56 }
57
58
59 /*************************************************************************/
60 #pragma mark -
61 #pragma mark ACCESSOR METHODS
62 /*************************************************************************/
63
64 - (void)setImage:(NSImage *)newImage
65 {
66     [_image autorelease];
67     _image = [newImage copy];
68 }
69
70 - (void)setLocked:(BOOL)flag
71 {
72     _locked = flag;
73     [self setExitMode:(flag ? ITTransientStatusWindowExitOnCommand : ITTransientStatusWindowExitAfterDelay)];
74 }
75
76 - (void)setSizing:(ITTransientStatusWindowSizing)newSizing
77 {
78     _sizing = newSizing;
79 }
80
81 /*************************************************************************/
82 #pragma mark -
83 #pragma mark INSTANCE METHODS
84 /*************************************************************************/
85
86 - (void)appear:(id)sender
87 {
88     if ( ! _locked ) {
89         [super appear:sender];
90     }
91 }
92
93 - (void)vanish:(id)sender
94 {
95     if ( ! _locked ) {
96         [super vanish:sender];
97     }
98 }
99
100 - (NSRect)setupWindowWithDataSize:(NSSize)dataSize
101 {
102     float        divisor       = 1.0;
103     NSRect       imageRect;
104     float        imageWidth    = 0.0;
105     float        imageHeight   = 0.0;
106     float        dataWidth     = dataSize.width;
107     float        dataHeight    = dataSize.height;
108     float        contentHeight = 0.0;
109     float        windowWidth   = 0.0;
110     float        windowHeight  = 0.0;
111     NSRect       visibleFrame  = [[self screen] visibleFrame];
112     NSPoint      screenOrigin  = visibleFrame.origin;
113     float        screenWidth   = visibleFrame.size.width;
114     float        screenHeight  = visibleFrame.size.height;
115     float        maxWidth      = ( screenWidth  - (SW_BORDER * 2) );
116     float        maxHeight     = ( screenHeight - (SW_BORDER * 2) );
117     float        excessWidth   = 0.0;
118     float        excessHeight  = 0.0;
119     NSPoint      windowOrigin;
120     ITImageView *imageView;
121     BOOL         shouldAnimate = ( ! (([self visibilityState] == ITWindowAppearingState) ||
122                                       ([self visibilityState] == ITWindowVanishingState)) );
123         
124     if ( _sizing == ITTransientStatusWindowSmall ) {
125         divisor = SMALL_DIVISOR;
126     } else if ( _sizing == ITTransientStatusWindowMini ) {
127         divisor = MINI_DIVISOR;
128     }
129
130 //  Get image width and height.
131     imageWidth  = ( [_image size].width  / divisor );
132     imageHeight = ( [_image size].height / divisor );
133     
134 //  Set the content height to the greater of the text and image heights.
135     contentHeight = ( ( imageHeight > dataHeight ) ? imageHeight : dataHeight );
136
137 //  Setup the Window, and remove all its contentview's subviews.
138     windowWidth  = ( (SW_PAD / divisor) + imageWidth + (SW_SPACE / divisor) + dataWidth + (SW_PAD / divisor) );
139     windowHeight = ( (SW_PAD / divisor) + contentHeight + (SW_PAD / divisor) );
140     
141 //  Constrain size to max limits.  Adjust data sizes accordingly.
142     excessWidth  = (windowWidth  - maxWidth );
143     excessHeight = (windowHeight - maxHeight);
144
145     if ( excessWidth > 0.0 ) {
146         windowWidth = maxWidth;
147         dataWidth -= excessWidth;
148     }
149     
150     if ( excessHeight > 0.0 ) {
151         windowHeight = maxHeight;
152         dataHeight -= excessHeight;
153     }
154     
155     if ( [self horizontalPosition] == ITWindowPositionLeft ) {
156         windowOrigin.x = ( SW_BORDER + screenOrigin.x );
157     } else if ( [self horizontalPosition] == ITWindowPositionCenter ) {
158         windowOrigin.x = ( screenOrigin.x + (screenWidth / 2) - (windowWidth / 2) );
159     } else if ( [self horizontalPosition] == ITWindowPositionRight ) {
160         windowOrigin.x = ( screenOrigin.x + screenWidth - (windowWidth + SW_BORDER) );
161     }
162     
163     if ( [self verticalPosition] == ITWindowPositionTop ) {
164         windowOrigin.y = ( screenOrigin.y + screenHeight - (windowHeight + SW_BORDER) );
165     } else if ( [self verticalPosition] == ITWindowPositionMiddle ) {
166 //      Middle-oriented windows should be slightly proud of the screen's middle.
167         windowOrigin.y = ( (screenOrigin.y + (screenHeight / 2) - (windowHeight / 2)) + (screenHeight / 8) );
168     } else if ( [self verticalPosition] == ITWindowPositionBottom ) {
169         windowOrigin.y = ( SW_BORDER + screenOrigin.y );
170     }
171     
172     [self setFrame:NSMakeRect( windowOrigin.x,
173                                windowOrigin.y,
174                                windowWidth,
175                                windowHeight) display:YES animate:shouldAnimate];
176
177     [[[self contentView] subviews] makeObjectsPerformSelector:@selector(removeFromSuperview)];
178     
179 //  Setup, position, fill, and add the image view to the content view.
180     imageRect = NSMakeRect( (SW_PAD / divisor) + 4,
181                             ((SW_PAD / divisor) + ((contentHeight - imageHeight) / 2)),
182                             imageWidth,
183                             imageHeight );
184     imageView = [[[ITImageView alloc] initWithFrame:imageRect] autorelease];
185     [imageView setAutoresizingMask:(NSViewMinYMargin | NSViewMaxYMargin)];
186     [imageView setImage:_image];
187     [imageView setCastsShadow:YES];
188     [[self contentView] addSubview:imageView];
189
190     return NSMakeRect( ((SW_PAD / divisor) + imageWidth + (SW_SPACE / divisor)),
191                        ((SW_PAD / divisor) + ((contentHeight - dataHeight) / 2)),
192                        dataWidth,
193                        dataHeight);
194 }
195
196 - (void)buildTextWindowWithString:(id)text
197 {
198     if ( ! _locked ) {
199
200         float         divisor       = 1.0;
201         float         dataWidth     = 0.0;
202         float         dataHeight    = 0.0;
203         NSRect        dataRect;
204         NSArray      *lines                     = [(([text isKindOfClass:[NSString class]]) ? text : [text mutableString]) componentsSeparatedByString:@"\n"];
205         id                        oneLine       = nil;
206         NSEnumerator *lineEnum          = [lines objectEnumerator];
207         float         baseFontSize  = 18.0;
208         ITTextField  *textField;
209         NSFont       *font;
210         NSDictionary *attr;
211
212         if ( _sizing == ITTransientStatusWindowSmall ) {
213             divisor = SMALL_DIVISOR;
214         } else if ( _sizing == ITTransientStatusWindowMini ) {
215             divisor = MINI_DIVISOR;
216         }
217                 
218                 font = [NSFont fontWithName:@"LucidaGrande-Bold" size:(baseFontSize / divisor)];
219                 attr = [NSDictionary dictionaryWithObject:font forKey:NSFontAttributeName];
220                 
221 //      Iterate over each line to get text width and height
222         while ( (oneLine = [lineEnum nextObject]) ) {
223 //          Get the width of one line, adding 8.0 because Apple sucks donkey rectum.
224             float oneLineWidth = ( [oneLine sizeWithAttributes:attr].width + 8.0 );
225 //          Add the height of this line to the total text height
226             dataHeight += [oneLine sizeWithAttributes:attr].height;
227 //          If this line wider than the last one, set it as the text width.
228             dataWidth = ( ( dataWidth > oneLineWidth ) ? dataWidth : oneLineWidth );
229         }
230         
231 //      Add 4.0 to the final dataHeight to accomodate the shadow.
232         dataHeight += 4.0;
233
234         dataRect = [self setupWindowWithDataSize:NSMakeSize(dataWidth, dataHeight)];
235         
236 //      Create, position, setup, fill, and add the text view to the content view.
237         textField = [[[ITTextField alloc] initWithFrame:dataRect] autorelease];
238         [textField setAutoresizingMask:(NSViewHeightSizable | NSViewWidthSizable)];
239         [textField setEditable:NO];
240         [textField setSelectable:NO];
241         [textField setBordered:NO];
242         [textField setDrawsBackground:NO];
243         [textField setFont:font];
244         [textField setTextColor:[NSColor whiteColor]];
245         [textField setCastsShadow:YES];
246         [[textField cell] setWraps:NO];
247                 
248                 if ([text isKindOfClass:[NSString class]]) {
249                         [textField setStringValue:text];
250                 } else {
251                         [textField setAttributedStringValue:text];
252                 }
253                 
254         [textField setShadowSaturation:SW_SHADOW_SAT];
255         [[self contentView] addSubview:textField];
256         
257 //      Display the window.
258         [[self contentView] setNeedsDisplay:YES];
259                 _textField = textField;
260     }
261 }
262
263 - (void)buildMeterWindowWithCharacter:(NSString *)character
264                                  size:(float)size
265                                 count:(int)count
266                                active:(int)active
267 {
268     if ( ! _locked ) {
269
270         float         divisor     = 1.0;
271         NSFont       *font;
272         NSDictionary *attr;
273         NSSize        charSize;
274         float         cellHeight;
275         float         cellWidth;
276         float         dataWidth;
277         NSRect        dataRect;
278         NSEnumerator *cellEnum    = nil;
279         id            aCell       = nil;
280         int           activeCount = 0;
281         NSColor      *onColor     = [NSColor whiteColor];
282         NSColor      *offColor    = [NSColor colorWithCalibratedWhite:0.15 alpha:0.50];
283         NSMatrix     *volMatrix;
284         
285         if ( _sizing == ITTransientStatusWindowSmall ) {
286             divisor = SMALL_DIVISOR;
287         } else if ( _sizing == ITTransientStatusWindowMini ) {
288             divisor = MINI_DIVISOR;
289         }
290         
291         font        = [NSFont fontWithName:@"AppleGothic" size:( size / divisor )];
292         attr        = [NSDictionary dictionaryWithObject:font forKey:NSFontAttributeName];
293         charSize    = [character sizeWithAttributes:attr];
294         cellHeight  = ( charSize.height + 4.0 );  // Add 4.0 for shadow
295         cellWidth   = ( (charSize.width) + (SW_METER_PAD / divisor) );
296         dataWidth   = ( cellWidth * count );
297         dataRect    = [self setupWindowWithDataSize:NSMakeSize(dataWidth, cellHeight)];
298         volMatrix   = [[[NSMatrix alloc] initWithFrame:dataRect
299                                                   mode:NSHighlightModeMatrix
300                                              cellClass:NSClassFromString(@"ITTextFieldCell")
301                                           numberOfRows:1
302                                        numberOfColumns:count] autorelease];
303         
304         [volMatrix setCellSize:NSMakeSize(cellWidth, cellHeight)];
305         [volMatrix setIntercellSpacing:NSMakeSize(0, 0)];
306         [volMatrix setAutoresizingMask:(NSViewHeightSizable | NSViewWidthSizable)];
307
308         cellEnum = [[volMatrix cells] objectEnumerator];
309
310         while ( (aCell = [cellEnum nextObject]) ) {
311             [aCell setEditable:NO];
312             [aCell setSelectable:NO];
313             [aCell setBordered:NO];
314             [aCell setDrawsBackground:NO];
315             [aCell setAlignment:NSCenterTextAlignment];
316             [aCell setFont:font];
317             [aCell setStringValue:character];
318             [aCell setShadowSaturation:SW_SHADOW_SAT];
319
320             activeCount ++;
321
322             if ( active >= activeCount ) {
323                 [aCell setCastsShadow:YES];
324                 [aCell setTextColor:onColor];
325             } else {
326                 [aCell setCastsShadow:NO];
327                 [aCell setTextColor:offColor];
328             }
329
330         }
331
332         [[self contentView] addSubview:volMatrix];
333         [[self contentView] setNeedsDisplay:YES];
334         
335     }
336 }
337
338 - (void)buildDialogWindowWithMessage:(NSString *)message
339                        defaultButton:(NSString *)defaultTitle
340                      alternateButton:(NSString *)alternateTitle
341                               target:(id)target
342                        defaultAction:(SEL)okAction
343                      alternateAction:(SEL)alternateAction
344 {
345     if ( ! _locked ) {
346
347         float         divisor       = 1.0;
348         float         textWidth     = 0.0;
349         float         textHeight    = 0.0;
350         float         okWidth       = 0.0;
351         float         cancelWidth   = 0.0;
352         float         wideButtonW   = 0.0;
353         float         buttonWidth   = 0.0;
354         float         dataHeight    = 0.0;
355         float         dataWidth     = 0.0;
356         NSRect        dataRect;
357         float         textY         = 0.0;
358         NSRect        textRect;
359         float         textAddBelow  = 32.0;
360         float         dataMinH      = 92.0;
361         float         textMinH      = 48.0;
362         NSArray      *lines         = [message componentsSeparatedByString:@"\n"];
363         id                        oneLine       = nil;
364         NSEnumerator *lineEnum      = [lines objectEnumerator];
365         float         baseFontSize  = 18.0;
366         ITTextField  *textField;
367         ITButton     *okButton;
368         ITButton     *cancelButton;
369         NSColor      *textColor     = [NSColor whiteColor];
370         NSFont       *font;
371         NSDictionary *attr;
372         NSFont       *buttonFont;
373         NSDictionary *buttonAttr;
374         
375         if ( _sizing == ITTransientStatusWindowSmall ) {
376             divisor = SMALL_DIVISOR;
377         } else if ( _sizing == ITTransientStatusWindowMini ) {
378             divisor = MINI_DIVISOR;
379         }
380         
381         font = [NSFont fontWithName:@"LucidaGrande-Bold" size:(baseFontSize / divisor)];
382         attr = [NSDictionary dictionaryWithObject:font forKey:NSFontAttributeName];
383         buttonFont = [NSFont fontWithName:@"LucidaGrande-Bold" size:(14 / divisor)];
384         buttonAttr = [NSDictionary dictionaryWithObjectsAndKeys:
385             buttonFont , NSFontAttributeName,
386             textColor  , NSForegroundColorAttributeName, 
387             nil];
388         
389 //      Iterate over each line to get text width and height
390         while ( (oneLine = [lineEnum nextObject]) ) {
391 //          Get the width of one line, adding 8.0 because Apple sucks donkey rectum.
392             float oneLineWidth = ( [oneLine sizeWithAttributes:attr].width + 8.0 );
393 //          Add the height of this line to the total text height
394             textHeight += [oneLine sizeWithAttributes:attr].height;
395 //          If this line wider than the last one, set it as the text width.
396             textWidth = ( ( textWidth > oneLineWidth ) ? textWidth : oneLineWidth );
397         }
398         
399 //      Add 4.0 to the final dataHeight to accomodate the shadow.
400         textHeight += 4.0;
401         
402 //      Add extra padding below the text
403         dataHeight = (textHeight + textAddBelow);
404         
405 //      Test to see if data height is tall enough
406         if ( dataHeight < dataMinH ) {
407             dataHeight = dataMinH;
408         }
409         
410 //      Make the buttons, set the titles, and size them to fit their titles
411         okButton     = [[[ITButton alloc] initWithFrame:NSMakeRect(0, 0, 300, 24)] autorelease];
412         cancelButton = [[[ITButton alloc] initWithFrame:NSMakeRect(0, 0, 300, 24)] autorelease];
413         [okButton     setTarget:target];
414         [cancelButton setTarget:target];
415         [okButton     setAction:okAction];
416         [cancelButton setAction:alternateAction];
417         [okButton     setBezelStyle:ITGrayRoundedBezelStyle];
418         [cancelButton setBezelStyle:ITGrayRoundedBezelStyle];
419         [okButton     setAlignment:NSRightTextAlignment];
420         [cancelButton setAlignment:NSCenterTextAlignment];
421         [okButton     setImagePosition:NSNoImage];
422         [cancelButton setImagePosition:NSNoImage];
423         [okButton     setAttributedTitle:[[[NSAttributedString alloc] initWithString:defaultTitle
424                                                                           attributes:buttonAttr] autorelease]];
425         [cancelButton setAttributedTitle:[[[NSAttributedString alloc] initWithString:alternateTitle
426                                                                           attributes:buttonAttr] autorelease]];
427         [okButton     sizeToFit];
428         [cancelButton sizeToFit];
429         
430 //      Get the button widths.  Add any extra width here.
431         okWidth     = ([okButton     frame].size.width + SW_BUTTON_EXTRA_W);
432         cancelWidth = ([cancelButton frame].size.width + SW_BUTTON_EXTRA_W);
433         
434 //      Figure out which button is wider.
435         wideButtonW = ( (okWidth > cancelWidth) ? okWidth : cancelWidth );
436
437 //      Get the total width of the buttons. Add the divider space.
438         buttonWidth = ( (wideButtonW * 2) + SW_BUTTON_DIV );
439
440 //      Set the dataWidth to whichever is greater: text width or button width.
441         dataWidth = ( (textWidth > buttonWidth) ? textWidth : buttonWidth);
442         
443 //      Setup the window
444         dataRect = [self setupWindowWithDataSize:NSMakeSize(dataWidth, dataHeight)];
445         
446 //      Set an initial vertical point for the textRect's origin.
447         textY = dataRect.origin.y + textAddBelow;
448         
449 //      Move that point up if the minimimum height of the text area is not occupied.
450         if ( textHeight < textMinH ) {
451             textY += ( (textMinH - textHeight) / 2 );
452         }
453         
454 //      Build the text rect.
455         textRect = NSMakeRect(dataRect.origin.x,
456                               textY,
457                               textWidth,
458                               textHeight);
459         
460 //      Create, position, setup, fill, and add the text view to the content view.
461         textField = [[[ITTextField alloc] initWithFrame:textRect] autorelease];
462         [textField setEditable:NO];
463         [textField setSelectable:NO];
464         [textField setBordered:NO];
465         [textField setDrawsBackground:NO];
466         [textField setFont:font];
467         [textField setTextColor:textColor];
468         [textField setCastsShadow:YES];
469         [textField setStringValue:message];
470         [textField setShadowSaturation:SW_SHADOW_SAT];
471         [[self contentView] addSubview:textField];
472         
473 //      Set the button frames, and add them to the content view.
474         [okButton setFrame:NSMakeRect( ([[self contentView] frame].size.width - (wideButtonW + SW_BUTTON_PAD_R) ),
475                                        SW_BUTTON_PAD_B,
476                                        wideButtonW,
477                                        24.0)];
478         [cancelButton setFrame:NSMakeRect( ([[self contentView] frame].size.width - ((wideButtonW * 2) + SW_BUTTON_DIV + SW_BUTTON_PAD_R) ),
479                                            SW_BUTTON_PAD_B,
480                                            wideButtonW,
481                                            24.0)];
482         [[self contentView] addSubview:okButton];
483         if (alternateTitle) {
484             [[self contentView] addSubview:cancelButton];
485         }
486
487         [self setIgnoresMouseEvents:NO];
488   
489 //      Display the window.
490         [[self contentView] setNeedsDisplay:YES];
491     }
492 }
493
494 - (void)updateTime:(NSString *)time range:(NSRange)range
495 {
496         NSMutableAttributedString *string = [[_textField attributedStringValue] mutableCopy];
497         [string replaceCharactersInRange:range withString:time];
498         [_textField setAttributedStringValue:[string autorelease]];
499         [[self contentView] setNeedsDisplay:YES];
500 }
501
502 - (NSTimeInterval)animationResizeTime:(NSRect)newFrame
503 {
504     return (NSTimeInterval)0.25;
505 }
506
507 @end