2 // GrowlBrushedWindowView.m
5 // Created by Ingmar Stein on 12/01/2004.
6 // Copyright 2004-2006 The Growl Project. All rights reserved.
9 #import "GrowlBrushedWindowView.h"
10 #import "GrowlBrushedDefines.h"
11 #import "GrowlDefinesInternal.h"
12 #import "GrowlImageAdditions.h"
13 #import "GrowlBezierPathAdditions.h"
14 #import "NSMutableAttributedStringAdditions.h"
15 #import <WebKit/WebPreferences.h>
17 #define GrowlBrushedTextAreaWidth (GrowlBrushedNotificationWidth - GrowlBrushedPadding - iconSize - GrowlBrushedIconTextPadding - GrowlBrushedPadding)
18 #define GrowlBrushedMinTextHeight (GrowlBrushedPadding + iconSize + GrowlBrushedPadding)
20 @implementation GrowlBrushedWindowView
22 - (id) initWithFrame:(NSRect) frame {
23 if ((self = [super initWithFrame:frame])) {
24 textFont = [[NSFont systemFontOfSize:GrowlBrushedTextFontSize] retain];
25 textLayoutManager = [[NSLayoutManager alloc] init];
26 titleLayoutManager = [[NSLayoutManager alloc] init];
27 lineHeight = [textLayoutManager defaultLineHeightForFont:textFont];
28 textShadow = [[NSShadow alloc] init];
29 [textShadow setShadowOffset:NSMakeSize(0.0, -2.0)];
30 [textShadow setShadowBlurRadius:3.0];
31 [textShadow setShadowColor:[[[self window] backgroundColor] blendedColorWithFraction:0.5
32 ofColor:[NSColor blackColor]]];
34 int size = GrowlBrushedSizePrefDefault;
35 READ_GROWL_PREF_INT(GrowlBrushedSizePref, GrowlBrushedPrefDomain, &size);
36 if (size == GrowlBrushedSizeLarge) {
37 iconSize = GrowlBrushedIconSizeLarge;
39 iconSize = GrowlBrushedIconSize;
51 [textStorage release];
52 [textLayoutManager release];
53 [titleStorage release];
54 [titleLayoutManager release];
60 // Coordinates are based on top left corner
64 - (void) drawRect:(NSRect)rect {
66 //Make sure that we don't draw in the main thread
67 //if ([super dispatchDrawingToThread:rect]) {
68 NSRect b = [self bounds];
69 CGRect bounds = CGRectMake(b.origin.x, b.origin.y, b.size.width, b.size.height);
71 CGContextRef context = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
74 CGContextClearRect(context, bounds);
76 // calculate bounds based on icon-float pref on or off
78 BOOL floatIcon = GrowlBrushedFloatIconPrefDefault;
79 READ_GROWL_PREF_BOOL(GrowlBrushedFloatIconPref, GrowlBrushedPrefDomain, &floatIcon);
81 CGFloat sizeReduction = GrowlBrushedPadding + iconSize + (GrowlBrushedIconTextPadding * 0.5);
83 shadedBounds = CGRectMake(bounds.origin.x + sizeReduction + 1.0,
84 bounds.origin.y + 1.0,
85 bounds.size.width - sizeReduction - 2.0,
86 bounds.size.height - 2.0);
88 shadedBounds = CGRectInset(bounds, 1.0, 1.0);
91 // set up path for rounded corners
92 addRoundedRectToPath(context, shadedBounds, GrowlBrushedBorderRadius);
93 CGContextSetLineWidth(context, 2.0);
96 NSWindow *window = [self window];
97 NSColor *bgColor = [window backgroundColor];
98 CGPathDrawingMode drawingMode;
100 drawingMode = kCGPathFillStroke;
102 [[NSColor keyboardFocusIndicatorColor] setStroke];
104 drawingMode = kCGPathFill;
107 CGContextDrawPath(context, drawingMode);
109 // draw the title and the text
111 drawRect.origin.x = GrowlBrushedPadding;
112 drawRect.origin.y = GrowlBrushedPadding;
113 drawRect.size.width = iconSize;
114 drawRect.size.height = iconSize;
116 [icon setFlipped:YES];
117 [icon drawScaledInRect:drawRect
118 operation:NSCompositeSourceOver
121 drawRect.origin.x += iconSize + GrowlBrushedIconTextPadding;
124 [titleLayoutManager drawGlyphsForGlyphRange:titleRange atPoint:drawRect.origin];
125 drawRect.origin.y += titleHeight + GrowlBrushedTitleTextPadding;
129 [textLayoutManager drawGlyphsForGlyphRange:textRange atPoint:drawRect.origin];
131 [window invalidateShadow];
132 [super drawRect:rect];
136 - (void) setIcon:(NSImage *)anIcon {
138 icon = [anIcon retain];
139 [self setNeedsDisplay:YES];
142 - (void) setTitle:(NSString *)aTitle {
143 haveTitle = [aTitle length] != 0;
146 [self setNeedsDisplay:YES];
151 NSSize containerSize;
152 containerSize.width = GrowlBrushedTextAreaWidth;
153 containerSize.height = FLT_MAX;
154 titleStorage = [[NSTextStorage alloc] init];
155 titleContainer = [[NSTextContainer alloc] initWithContainerSize:containerSize];
156 [titleLayoutManager addTextContainer:titleContainer]; // retains textContainer
157 [titleContainer release];
158 [titleStorage addLayoutManager:titleLayoutManager]; // retains layoutManager
159 [titleContainer setLineFragmentPadding:0.0];
162 // construct attributes for the title
163 NSMutableParagraphStyle *paragraphStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy];
164 NSFont *titleFont = [NSFont boldSystemFontOfSize:GrowlBrushedTitleFontSize];
165 [paragraphStyle setLineBreakMode:NSLineBreakByTruncatingTail];
166 NSDictionary *defaultAttributes = [[NSDictionary alloc] initWithObjectsAndKeys:
167 titleFont, NSFontAttributeName,
168 textColor, NSForegroundColorAttributeName,
169 textShadow, NSShadowAttributeName,
170 paragraphStyle, NSParagraphStyleAttributeName,
172 [paragraphStyle release];
174 [[titleStorage mutableString] setString:aTitle];
175 [titleStorage setAttributes:defaultAttributes range:NSMakeRange(0, [titleStorage length])];
177 [defaultAttributes release];
179 titleRange = [titleLayoutManager glyphRangeForTextContainer:titleContainer]; // force layout
180 titleHeight = [titleLayoutManager usedRectForTextContainer:titleContainer].size.height;
182 [self setNeedsDisplay:YES];
185 - (void) setText:(NSString *)aText {
186 haveText = [aText length] != 0;
189 [self setNeedsDisplay:YES];
194 NSSize containerSize;
195 BOOL limitPref = GrowlBrushedLimitPrefDefault;
196 READ_GROWL_PREF_BOOL(GrowlBrushedLimitPref, GrowlBrushedPrefDomain, &limitPref);
197 containerSize.width = GrowlBrushedTextAreaWidth;
199 containerSize.height = lineHeight * GrowlBrushedMaxLines;
201 containerSize.height = FLT_MAX;
202 textStorage = [[NSTextStorage alloc] init];
203 textContainer = [[NSTextContainer alloc] initWithContainerSize:containerSize];
204 [textLayoutManager addTextContainer:textContainer]; // retains textContainer
205 [textContainer release];
206 [textStorage addLayoutManager:textLayoutManager]; // retains layoutManager
207 [textContainer setLineFragmentPadding:0.0];
210 // construct attributes for the description text
211 NSDictionary *defaultAttributes = [[NSDictionary alloc] initWithObjectsAndKeys:
212 textFont, NSFontAttributeName,
213 textColor, NSForegroundColorAttributeName,
214 textShadow, NSShadowAttributeName,
217 [[textStorage mutableString] setString:aText];
218 [textStorage setAttributes:defaultAttributes range:NSMakeRange(0, [textStorage length])];
220 [defaultAttributes release];
222 textRange = [textLayoutManager glyphRangeForTextContainer:textContainer]; // force layout
223 textHeight = [textLayoutManager usedRectForTextContainer:textContainer].size.height;
225 [self setNeedsDisplay:YES];
228 - (void) setPriority:(int)priority {
232 textKey = GrowlBrushedVeryLowTextColor;
235 textKey = GrowlBrushedModerateTextColor;
238 textKey = GrowlBrushedHighTextColor;
241 textKey = GrowlBrushedEmergencyTextColor;
245 textKey = GrowlBrushedNormalTextColor;
251 READ_GROWL_PREF_VALUE(textKey, GrowlBrushedPrefDomain, NSData *, &data);
253 CFMakeCollectable(data);
254 if (data && [data isKindOfClass:[NSData class]]) {
255 textColor = [NSUnarchiver unarchiveObjectWithData:data];
257 textColor = [NSColor colorWithCalibratedWhite:0.1f alpha:1.0f];
265 CGFloat height = GrowlBrushedPadding + GrowlBrushedPadding + [self titleHeight] + [self descriptionHeight];
266 if (haveTitle && haveText)
267 height += GrowlBrushedTitleTextPadding;
268 if (height < GrowlBrushedMinTextHeight)
269 height = GrowlBrushedMinTextHeight;
271 // resize the window so that it contains the tracking rect
272 NSWindow *window = [self window];
273 NSRect windowRect = [[self window] frame];
274 windowRect.origin.y -= height - windowRect.size.height;
275 windowRect.size.height = height;
276 [window setFrame:windowRect display:YES animate:YES];
279 [self removeTrackingRect:trackingRectTag];
280 trackingRectTag = [self addTrackingRect:[self frame] owner:self userData:NULL assumeInside:NO];
283 - (CGFloat) titleHeight {
284 return haveTitle ? titleHeight : 0.0;
287 - (CGFloat) descriptionHeight {
288 return haveText ? textHeight : 0.0;
291 - (NSInteger) descriptionRowCount {
292 NSInteger rowCount = textHeight / lineHeight;
293 BOOL limitPref = GrowlBrushedLimitPrefDefault;
294 READ_GROWL_PREF_BOOL(GrowlBrushedLimitPref, GrowlBrushedPrefDomain, &limitPref);
296 return MIN(rowCount, GrowlBrushedMaxLines);