Bumping to version 1.0.1, which uses the latest ITKit. This resolves GrowlITTSW bug #4.
[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         if (!newImage) {
64                 newImage = [NSImage imageNamed:@"NSApplicationIcon"];
65         }
66     [_image autorelease];
67     _image = [newImage copy];
68 }
69
70 - (void)setSizing:(ITTransientStatusWindowSizing)newSizing
71 {
72     _sizing = newSizing;
73 }
74
75 /*************************************************************************/
76 #pragma mark -
77 #pragma mark INSTANCE METHODS
78 /*************************************************************************/
79
80 - (void)appear:(id)sender
81 {
82     [super appear:sender];
83 }
84
85 - (void)vanish:(id)sender
86 {
87     [super vanish:sender];
88 }
89
90 - (NSRect)setupWindowWithDataSize:(NSSize)dataSize
91 {
92     float        divisor       = 1.0;
93     NSRect       imageRect;
94     float        imageWidth    = 0.0;
95     float        imageHeight   = 0.0;
96     float        dataWidth     = dataSize.width;
97     float        dataHeight    = dataSize.height;
98     float        contentHeight = 0.0;
99     float        windowWidth   = 0.0;
100     float        windowHeight  = 0.0;
101     NSRect       visibleFrame  = [[self screen] visibleFrame];
102     NSPoint      screenOrigin  = visibleFrame.origin;
103     float        screenWidth   = visibleFrame.size.width;
104     float        screenHeight  = visibleFrame.size.height;
105     float        maxWidth      = ( screenWidth  - (SW_BORDER * 2) );
106     float        maxHeight     = ( screenHeight - (SW_BORDER * 2) );
107     float        excessWidth   = 0.0;
108     float        excessHeight  = 0.0;
109     NSPoint      windowOrigin  = NSZeroPoint;
110     ITImageView *imageView;
111     BOOL         shouldAnimate = ( ! (([self visibilityState] == ITWindowAppearingState) ||
112                                       ([self visibilityState] == ITWindowVanishingState)) );
113         
114     if ( _sizing == ITTransientStatusWindowSmall ) {
115         divisor = SMALL_DIVISOR;
116     } else if ( _sizing == ITTransientStatusWindowMini ) {
117         divisor = MINI_DIVISOR;
118     }
119         
120         //  Get image width and height.
121     imageWidth  = ( [_image size].width  / divisor );
122     imageHeight = ( [_image size].height / divisor );
123     
124         //  Set the content height to the greater of the text and image heights.
125     contentHeight = ( ( imageHeight > dataHeight ) ? imageHeight : dataHeight );
126         
127         //  Setup the Window, and remove all its contentview's subviews.
128     windowWidth  = ( (SW_PAD / divisor) + imageWidth + ((dataWidth > 0) ? (SW_SPACE / divisor) + dataWidth : 0) + (SW_PAD / divisor) );
129     windowHeight = ( (SW_PAD / divisor) + contentHeight + (SW_PAD / divisor) );
130     
131         //  Constrain size to max limits.  Adjust data sizes accordingly.
132     excessWidth  = (windowWidth  - maxWidth );
133     excessHeight = (windowHeight - maxHeight);
134         
135     if ( excessWidth > 0.0 ) {
136         windowWidth = maxWidth;
137         dataWidth -= excessWidth;
138     }
139     
140     if ( excessHeight > 0.0 ) {
141         windowHeight = maxHeight;
142         dataHeight -= excessHeight;
143     }
144     
145     if ( [self horizontalPosition] == ITWindowPositionLeft ) {
146         windowOrigin.x = ( SW_BORDER + screenOrigin.x );
147     } else if ( [self horizontalPosition] == ITWindowPositionCenter ) {
148         windowOrigin.x = ( screenOrigin.x + (screenWidth / 2) - (windowWidth / 2) );
149     } else if ( [self horizontalPosition] == ITWindowPositionRight ) {
150         windowOrigin.x = ( screenOrigin.x + screenWidth - (windowWidth + SW_BORDER) );
151     }
152     
153     if ( [self verticalPosition] == ITWindowPositionTop ) {
154         windowOrigin.y = ( screenOrigin.y + screenHeight - (windowHeight + SW_BORDER) );
155     } else if ( [self verticalPosition] == ITWindowPositionMiddle ) {
156                 //      Middle-oriented windows should be slightly proud of the screen's middle.
157         windowOrigin.y = ( (screenOrigin.y + (screenHeight / 2) - (windowHeight / 2)) + (screenHeight / 8) );
158     } else if ( [self verticalPosition] == ITWindowPositionBottom ) {
159         windowOrigin.y = ( SW_BORDER + screenOrigin.y );
160     }
161     
162     [self setFrame:NSMakeRect( windowOrigin.x,
163                                                           windowOrigin.y,
164                                                           windowWidth,
165                                                           windowHeight) display:YES animate:shouldAnimate];
166         
167     [[[self contentView] subviews] makeObjectsPerformSelector:@selector(removeFromSuperview)];
168     
169         //  Setup, position, fill, and add the image view to the content view.
170     imageRect = NSMakeRect( (SW_PAD / divisor) + ((dataWidth > 0) ? 4 : 0),
171                                                    ((SW_PAD / divisor) + ((contentHeight - imageHeight) / 2)),
172                                                    imageWidth,
173                                                    imageHeight );
174     imageView = [[[ITImageView alloc] initWithFrame:imageRect] autorelease];
175     [imageView setAutoresizingMask:(NSViewMinYMargin | NSViewMaxYMargin)];
176     [imageView setImage:_image];
177     [imageView setCastsShadow:YES];
178     [[self contentView] addSubview:imageView];
179         
180     return NSMakeRect( ((SW_PAD / divisor) + imageWidth + (SW_SPACE / divisor)),
181                                           ((SW_PAD / divisor) + ((contentHeight - dataHeight) / 2)),
182                                           dataWidth,
183                                           dataHeight);
184 }
185
186 - (void)buildTextWindowWithString:(id)text
187 {
188         float         divisor       = 1.0;
189         float         dataWidth     = 0.0;
190         float         dataHeight    = 0.0;
191         NSRect        dataRect;
192         NSArray      *lines                     = [(([text isKindOfClass:[NSString class]]) ? text : [text mutableString]) componentsSeparatedByString:@"\n"];
193         id                        oneLine       = nil;
194         NSEnumerator *lineEnum          = [lines objectEnumerator];
195         float         baseFontSize  = 18.0;
196         ITTextField  *textField;
197         NSFont       *font;
198         NSDictionary *attr;
199         
200         if ( _sizing == ITTransientStatusWindowSmall ) {
201                 divisor = SMALL_DIVISOR;
202         } else if ( _sizing == ITTransientStatusWindowMini ) {
203                 divisor = MINI_DIVISOR;
204         }
205         
206         font = [NSFont fontWithName:@"LucidaGrande-Bold" size:(baseFontSize / divisor)];
207         attr = [NSDictionary dictionaryWithObject:font forKey:NSFontAttributeName];
208         
209         //      Iterate over each line to get text width and height
210         while ( (oneLine = [lineEnum nextObject]) ) {
211                 //          Get the width of one line, adding 8.0 because Apple sucks donkey rectum.
212                 float oneLineWidth = ( [oneLine sizeWithAttributes:attr].width + 8.0 );
213                 //          Add the height of this line to the total text height
214                 dataHeight += [oneLine sizeWithAttributes:attr].height;
215                 //          If this line wider than the last one, set it as the text width.
216                 dataWidth = ( ( dataWidth > oneLineWidth ) ? dataWidth : oneLineWidth );
217         }
218         
219         //      Add 4.0 to the final dataHeight to accomodate the shadow.
220         dataHeight += 4.0;
221         
222         dataRect = [self setupWindowWithDataSize:NSMakeSize(dataWidth, dataHeight)];
223         
224         //      Create, position, setup, fill, and add the text view to the content view.
225         textField = [[[ITTextField alloc] initWithFrame:dataRect] autorelease];
226         [textField setAutoresizingMask:(NSViewHeightSizable | NSViewWidthSizable)];
227         [textField setEditable:NO];
228         [textField setSelectable:NO];
229         [textField setBordered:NO];
230         [textField setDrawsBackground:NO];
231         [textField setFont:font];
232         [textField setTextColor:[NSColor whiteColor]];
233         [textField setCastsShadow:YES];
234         [[textField cell] setWraps:NO];
235         
236         if ([text isKindOfClass:[NSString class]]) {
237                 [textField setStringValue:text];
238         } else {
239                 [textField setAttributedStringValue:text];
240         }
241         
242         [textField setShadowSaturation:SW_SHADOW_SAT];
243         [[self contentView] addSubview:textField];
244         
245         //      Display the window.
246         [[self contentView] setNeedsDisplay:YES];
247         _textField = textField;
248 }
249
250 - (NSTimeInterval)animationResizeTime:(NSRect)newFrame
251 {
252     return (NSTimeInterval)0.25;
253 }
254
255 @end