Initial commit of GrowlITTSW. Currently, all settings are hardcoded.
[GrowlITTSW.git] / GrowlITTSWWindow.m
1 //
2 //  GrowlITTSWWindow.m
3 //  Growl
4 //
5 //  Created by Joseph Spiros on 2/28/09.
6 //  Copyright 2009 iThink Software. All rights reserved.
7 //
8
9 #import "GrowlITTSWWindow.h"
10
11 #define SW_PAD             24.00
12 #define SW_SPACE           24.00
13 #define SW_MINW           211.00
14 #define SW_BORDER          32.00
15 #define SW_METER_PAD        4.00
16 #define SW_BUTTON_PAD_R    30.00
17 #define SW_BUTTON_PAD_B    24.00
18 #define SW_BUTTON_DIV      12.00
19 #define SW_BUTTON_EXTRA_W   8.00
20 #define SW_SHADOW_SAT       1.25
21
22 @interface GrowlITTSWWindow (Private)
23 - (NSRect)setupWindowWithDataSize:(NSSize)dataSize;
24 @end
25
26 @implementation GrowlITTSWWindow
27
28
29 /*************************************************************************/
30 #pragma mark -
31 #pragma mark INITIALIZATION / DEALLOCATION METHODS
32 /*************************************************************************/
33
34 - (id)initWithContentView:(NSView *)contentView
35                  exitMode:(ITTransientStatusWindowExitMode)exitMode
36            backgroundType:(ITTransientStatusWindowBackgroundType)backgroundType
37 {
38     if ( ( self = [super initWithContentView:contentView
39                                                                         exitMode:exitMode
40                                                           backgroundType:backgroundType] ) ) {
41                 // Set default values.
42         _image  = [[NSImage imageNamed:@"NSApplicationIcon"] retain];
43         _sizing = ITTransientStatusWindowRegular;
44     }
45     
46     return self;
47 }
48
49 - (void)dealloc
50 {
51     [_image release];
52     [super dealloc];
53 }
54
55
56 /*************************************************************************/
57 #pragma mark -
58 #pragma mark ACCESSOR METHODS
59 /*************************************************************************/
60
61 - (void)setImage:(NSImage *)newImage
62 {
63     [_image autorelease];
64     _image = [newImage copy];
65 }
66
67 - (void)setSizing:(ITTransientStatusWindowSizing)newSizing
68 {
69     _sizing = newSizing;
70 }
71
72 /*************************************************************************/
73 #pragma mark -
74 #pragma mark INSTANCE METHODS
75 /*************************************************************************/
76
77 - (void)appear:(id)sender
78 {
79     [super appear:sender];
80 }
81
82 - (void)vanish:(id)sender
83 {
84     [super vanish:sender];
85 }
86
87 - (NSRect)setupWindowWithDataSize:(NSSize)dataSize
88 {
89     float        divisor       = 1.0;
90     NSRect       imageRect;
91     float        imageWidth    = 0.0;
92     float        imageHeight   = 0.0;
93     float        dataWidth     = dataSize.width;
94     float        dataHeight    = dataSize.height;
95     float        contentHeight = 0.0;
96     float        windowWidth   = 0.0;
97     float        windowHeight  = 0.0;
98     NSRect       visibleFrame  = [[self screen] visibleFrame];
99     NSPoint      screenOrigin  = visibleFrame.origin;
100     float        screenWidth   = visibleFrame.size.width;
101     float        screenHeight  = visibleFrame.size.height;
102     float        maxWidth      = ( screenWidth  - (SW_BORDER * 2) );
103     float        maxHeight     = ( screenHeight - (SW_BORDER * 2) );
104     float        excessWidth   = 0.0;
105     float        excessHeight  = 0.0;
106     NSPoint      windowOrigin  = NSZeroPoint;
107     ITImageView *imageView;
108     BOOL         shouldAnimate = ( ! (([self visibilityState] == ITWindowAppearingState) ||
109                                       ([self visibilityState] == ITWindowVanishingState)) );
110         
111     if ( _sizing == ITTransientStatusWindowSmall ) {
112         divisor = SMALL_DIVISOR;
113     } else if ( _sizing == ITTransientStatusWindowMini ) {
114         divisor = MINI_DIVISOR;
115     }
116         
117         //  Get image width and height.
118     imageWidth  = ( [_image size].width  / divisor );
119     imageHeight = ( [_image size].height / divisor );
120     
121         //  Set the content height to the greater of the text and image heights.
122     contentHeight = ( ( imageHeight > dataHeight ) ? imageHeight : dataHeight );
123         
124         //  Setup the Window, and remove all its contentview's subviews.
125     windowWidth  = ( (SW_PAD / divisor) + imageWidth + ((dataWidth > 0) ? (SW_SPACE / divisor) + dataWidth : 0) + (SW_PAD / divisor) );
126     windowHeight = ( (SW_PAD / divisor) + contentHeight + (SW_PAD / divisor) );
127     
128         //  Constrain size to max limits.  Adjust data sizes accordingly.
129     excessWidth  = (windowWidth  - maxWidth );
130     excessHeight = (windowHeight - maxHeight);
131         
132     if ( excessWidth > 0.0 ) {
133         windowWidth = maxWidth;
134         dataWidth -= excessWidth;
135     }
136     
137     if ( excessHeight > 0.0 ) {
138         windowHeight = maxHeight;
139         dataHeight -= excessHeight;
140     }
141     
142     if ( [self horizontalPosition] == ITWindowPositionLeft ) {
143         windowOrigin.x = ( SW_BORDER + screenOrigin.x );
144     } else if ( [self horizontalPosition] == ITWindowPositionCenter ) {
145         windowOrigin.x = ( screenOrigin.x + (screenWidth / 2) - (windowWidth / 2) );
146     } else if ( [self horizontalPosition] == ITWindowPositionRight ) {
147         windowOrigin.x = ( screenOrigin.x + screenWidth - (windowWidth + SW_BORDER) );
148     }
149     
150     if ( [self verticalPosition] == ITWindowPositionTop ) {
151         windowOrigin.y = ( screenOrigin.y + screenHeight - (windowHeight + SW_BORDER) );
152     } else if ( [self verticalPosition] == ITWindowPositionMiddle ) {
153                 //      Middle-oriented windows should be slightly proud of the screen's middle.
154         windowOrigin.y = ( (screenOrigin.y + (screenHeight / 2) - (windowHeight / 2)) + (screenHeight / 8) );
155     } else if ( [self verticalPosition] == ITWindowPositionBottom ) {
156         windowOrigin.y = ( SW_BORDER + screenOrigin.y );
157     }
158     
159     [self setFrame:NSMakeRect( windowOrigin.x,
160                                                           windowOrigin.y,
161                                                           windowWidth,
162                                                           windowHeight) display:YES animate:shouldAnimate];
163         
164     [[[self contentView] subviews] makeObjectsPerformSelector:@selector(removeFromSuperview)];
165     
166         //  Setup, position, fill, and add the image view to the content view.
167     imageRect = NSMakeRect( (SW_PAD / divisor) + ((dataWidth > 0) ? 4 : 0),
168                                                    ((SW_PAD / divisor) + ((contentHeight - imageHeight) / 2)),
169                                                    imageWidth,
170                                                    imageHeight );
171     imageView = [[[ITImageView alloc] initWithFrame:imageRect] autorelease];
172     [imageView setAutoresizingMask:(NSViewMinYMargin | NSViewMaxYMargin)];
173     [imageView setImage:_image];
174     [imageView setCastsShadow:YES];
175     [[self contentView] addSubview:imageView];
176         
177     return NSMakeRect( ((SW_PAD / divisor) + imageWidth + (SW_SPACE / divisor)),
178                                           ((SW_PAD / divisor) + ((contentHeight - dataHeight) / 2)),
179                                           dataWidth,
180                                           dataHeight);
181 }
182
183 - (void)buildTextWindowWithString:(id)text
184 {
185         float         divisor       = 1.0;
186         float         dataWidth     = 0.0;
187         float         dataHeight    = 0.0;
188         NSRect        dataRect;
189         NSArray      *lines                     = [(([text isKindOfClass:[NSString class]]) ? text : [text mutableString]) componentsSeparatedByString:@"\n"];
190         id                        oneLine       = nil;
191         NSEnumerator *lineEnum          = [lines objectEnumerator];
192         float         baseFontSize  = 18.0;
193         ITTextField  *textField;
194         NSFont       *font;
195         NSDictionary *attr;
196         
197         if ( _sizing == ITTransientStatusWindowSmall ) {
198                 divisor = SMALL_DIVISOR;
199         } else if ( _sizing == ITTransientStatusWindowMini ) {
200                 divisor = MINI_DIVISOR;
201         }
202         
203         font = [NSFont fontWithName:@"LucidaGrande-Bold" size:(baseFontSize / divisor)];
204         attr = [NSDictionary dictionaryWithObject:font forKey:NSFontAttributeName];
205         
206         //      Iterate over each line to get text width and height
207         while ( (oneLine = [lineEnum nextObject]) ) {
208                 //          Get the width of one line, adding 8.0 because Apple sucks donkey rectum.
209                 float oneLineWidth = ( [oneLine sizeWithAttributes:attr].width + 8.0 );
210                 //          Add the height of this line to the total text height
211                 dataHeight += [oneLine sizeWithAttributes:attr].height;
212                 //          If this line wider than the last one, set it as the text width.
213                 dataWidth = ( ( dataWidth > oneLineWidth ) ? dataWidth : oneLineWidth );
214         }
215         
216         //      Add 4.0 to the final dataHeight to accomodate the shadow.
217         dataHeight += 4.0;
218         
219         dataRect = [self setupWindowWithDataSize:NSMakeSize(dataWidth, dataHeight)];
220         
221         //      Create, position, setup, fill, and add the text view to the content view.
222         textField = [[[ITTextField alloc] initWithFrame:dataRect] autorelease];
223         [textField setAutoresizingMask:(NSViewHeightSizable | NSViewWidthSizable)];
224         [textField setEditable:NO];
225         [textField setSelectable:NO];
226         [textField setBordered:NO];
227         [textField setDrawsBackground:NO];
228         [textField setFont:font];
229         [textField setTextColor:[NSColor whiteColor]];
230         [textField setCastsShadow:YES];
231         [[textField cell] setWraps:NO];
232         
233         if ([text isKindOfClass:[NSString class]]) {
234                 [textField setStringValue:text];
235         } else {
236                 [textField setAttributedStringValue:text];
237         }
238         
239         [textField setShadowSaturation:SW_SHADOW_SAT];
240         [[self contentView] addSubview:textField];
241         
242         //      Display the window.
243         [[self contentView] setNeedsDisplay:YES];
244         _textField = textField;
245 }
246
247 - (NSTimeInterval)animationResizeTime:(NSRect)newFrame
248 {
249     return (NSTimeInterval)0.25;
250 }
251
252 @end