|
boredzo@2402
|
1 |
//
|
|
boredzo@2402
|
2 |
// GrowlBrushedWindowView.m
|
|
boredzo@2402
|
3 |
// Display Plugins
|
|
boredzo@2402
|
4 |
//
|
|
boredzo@2402
|
5 |
// Created by Ingmar Stein on 12/01/2004.
|
|
ingmarstein@3040
|
6 |
// Copyright 2004-2006 The Growl Project. All rights reserved.
|
|
boredzo@2402
|
7 |
//
|
|
boredzo@2402
|
8 |
|
|
boredzo@2402
|
9 |
#import "GrowlBrushedWindowView.h"
|
|
boredzo@2402
|
10 |
#import "GrowlBrushedDefines.h"
|
|
boredzo@2402
|
11 |
#import "GrowlDefinesInternal.h"
|
|
boredzo@2402
|
12 |
#import "GrowlImageAdditions.h"
|
|
boredzo@2402
|
13 |
#import "GrowlBezierPathAdditions.h"
|
|
boredzo@2402
|
14 |
#import "NSMutableAttributedStringAdditions.h"
|
|
boredzo@2402
|
15 |
#import <WebKit/WebPreferences.h>
|
|
boredzo@2402
|
16 |
|
|
boredzo@2402
|
17 |
#define GrowlBrushedTextAreaWidth (GrowlBrushedNotificationWidth - GrowlBrushedPadding - iconSize - GrowlBrushedIconTextPadding - GrowlBrushedPadding)
|
|
boredzo@2402
|
18 |
#define GrowlBrushedMinTextHeight (GrowlBrushedPadding + iconSize + GrowlBrushedPadding)
|
|
boredzo@2402
|
19 |
|
|
boredzo@2402
|
20 |
@implementation GrowlBrushedWindowView
|
|
boredzo@2402
|
21 |
|
|
boredzo@2402
|
22 |
- (id) initWithFrame:(NSRect) frame {
|
|
boredzo@2402
|
23 |
if ((self = [super initWithFrame:frame])) {
|
|
boredzo@2402
|
24 |
textFont = [[NSFont systemFontOfSize:GrowlBrushedTextFontSize] retain];
|
|
boredzo@2402
|
25 |
textLayoutManager = [[NSLayoutManager alloc] init];
|
|
boredzo@2402
|
26 |
titleLayoutManager = [[NSLayoutManager alloc] init];
|
|
boredzo@2402
|
27 |
lineHeight = [textLayoutManager defaultLineHeightForFont:textFont];
|
|
boredzo@2402
|
28 |
textShadow = [[NSShadow alloc] init];
|
|
Rudy@4246
|
29 |
[textShadow setShadowOffset:NSMakeSize(0.0, -2.0)];
|
|
Rudy@4246
|
30 |
[textShadow setShadowBlurRadius:3.0];
|
|
Rudy@4246
|
31 |
[textShadow setShadowColor:[[[self window] backgroundColor] blendedColorWithFraction:0.5
|
|
boredzo@2402
|
32 |
ofColor:[NSColor blackColor]]];
|
|
boredzo@2402
|
33 |
|
|
boredzo@2402
|
34 |
int size = GrowlBrushedSizePrefDefault;
|
|
boredzo@2402
|
35 |
READ_GROWL_PREF_INT(GrowlBrushedSizePref, GrowlBrushedPrefDomain, &size);
|
|
boredzo@2402
|
36 |
if (size == GrowlBrushedSizeLarge) {
|
|
boredzo@2402
|
37 |
iconSize = GrowlBrushedIconSizeLarge;
|
|
boredzo@2402
|
38 |
} else {
|
|
boredzo@2402
|
39 |
iconSize = GrowlBrushedIconSize;
|
|
boredzo@2402
|
40 |
}
|
|
boredzo@2402
|
41 |
}
|
|
boredzo@2402
|
42 |
|
|
boredzo@2402
|
43 |
return self;
|
|
boredzo@2402
|
44 |
}
|
|
boredzo@2402
|
45 |
|
|
boredzo@2402
|
46 |
- (void) dealloc {
|
|
boredzo@2402
|
47 |
[textFont release];
|
|
boredzo@2402
|
48 |
[icon release];
|
|
boredzo@2402
|
49 |
[textColor release];
|
|
boredzo@2402
|
50 |
[textShadow release];
|
|
boredzo@2402
|
51 |
[textStorage release];
|
|
boredzo@2402
|
52 |
[textLayoutManager release];
|
|
boredzo@2402
|
53 |
[titleStorage release];
|
|
boredzo@2402
|
54 |
[titleLayoutManager release];
|
|
boredzo@2402
|
55 |
|
|
boredzo@2402
|
56 |
[super dealloc];
|
|
boredzo@2402
|
57 |
}
|
|
boredzo@2402
|
58 |
|
|
boredzo@2402
|
59 |
- (BOOL)isFlipped {
|
|
boredzo@2402
|
60 |
// Coordinates are based on top left corner
|
|
boredzo@2402
|
61 |
return YES;
|
|
boredzo@2402
|
62 |
}
|
|
boredzo@2402
|
63 |
|
|
boredzo@2402
|
64 |
- (void) drawRect:(NSRect)rect {
|
|
rudy@2806
|
65 |
#pragma unused(rect)
|
|
ofri@2708
|
66 |
//Make sure that we don't draw in the main thread
|
|
eridius@2773
|
67 |
//if ([super dispatchDrawingToThread:rect]) {
|
|
ofri@2708
|
68 |
NSRect b = [self bounds];
|
|
ofri@2708
|
69 |
CGRect bounds = CGRectMake(b.origin.x, b.origin.y, b.size.width, b.size.height);
|
|
ingmarstein@2721
|
70 |
|
|
ofri@2708
|
71 |
CGContextRef context = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
|
|
ingmarstein@2721
|
72 |
|
|
ofri@2708
|
73 |
// clear the window
|
|
ofri@2708
|
74 |
CGContextClearRect(context, bounds);
|
|
ingmarstein@2721
|
75 |
|
|
ofri@2708
|
76 |
// calculate bounds based on icon-float pref on or off
|
|
ofri@2708
|
77 |
CGRect shadedBounds;
|
|
ofri@2708
|
78 |
BOOL floatIcon = GrowlBrushedFloatIconPrefDefault;
|
|
ofri@2708
|
79 |
READ_GROWL_PREF_BOOL(GrowlBrushedFloatIconPref, GrowlBrushedPrefDomain, &floatIcon);
|
|
ofri@2708
|
80 |
if (floatIcon) {
|
|
Rudy@4246
|
81 |
CGFloat sizeReduction = GrowlBrushedPadding + iconSize + (GrowlBrushedIconTextPadding * 0.5);
|
|
Rudy@4246
|
82 |
|
|
Rudy@4246
|
83 |
shadedBounds = CGRectMake(bounds.origin.x + sizeReduction + 1.0,
|
|
Rudy@4246
|
84 |
bounds.origin.y + 1.0,
|
|
Rudy@4246
|
85 |
bounds.size.width - sizeReduction - 2.0,
|
|
Rudy@4246
|
86 |
bounds.size.height - 2.0);
|
|
ofri@2708
|
87 |
} else {
|
|
Rudy@4246
|
88 |
shadedBounds = CGRectInset(bounds, 1.0, 1.0);
|
|
ofri@2708
|
89 |
}
|
|
ingmarstein@2721
|
90 |
|
|
ofri@2708
|
91 |
// set up path for rounded corners
|
|
ofri@2708
|
92 |
addRoundedRectToPath(context, shadedBounds, GrowlBrushedBorderRadius);
|
|
Rudy@4246
|
93 |
CGContextSetLineWidth(context, 2.0);
|
|
ingmarstein@2721
|
94 |
|
|
ofri@2708
|
95 |
// draw background
|
|
ofri@2708
|
96 |
NSWindow *window = [self window];
|
|
ofri@2708
|
97 |
NSColor *bgColor = [window backgroundColor];
|
|
ofri@2708
|
98 |
CGPathDrawingMode drawingMode;
|
|
ofri@2708
|
99 |
if (mouseOver) {
|
|
ofri@2708
|
100 |
drawingMode = kCGPathFillStroke;
|
|
ofri@2708
|
101 |
[bgColor setFill];
|
|
ofri@2708
|
102 |
[[NSColor keyboardFocusIndicatorColor] setStroke];
|
|
ofri@2708
|
103 |
} else {
|
|
ofri@2708
|
104 |
drawingMode = kCGPathFill;
|
|
ofri@2708
|
105 |
[bgColor set];
|
|
ofri@2708
|
106 |
}
|
|
ofri@2708
|
107 |
CGContextDrawPath(context, drawingMode);
|
|
ingmarstein@2721
|
108 |
|
|
ofri@2708
|
109 |
// draw the title and the text
|
|
ofri@2708
|
110 |
NSRect drawRect;
|
|
ofri@2708
|
111 |
drawRect.origin.x = GrowlBrushedPadding;
|
|
ofri@2708
|
112 |
drawRect.origin.y = GrowlBrushedPadding;
|
|
ofri@2708
|
113 |
drawRect.size.width = iconSize;
|
|
ofri@2708
|
114 |
drawRect.size.height = iconSize;
|
|
ingmarstein@2721
|
115 |
|
|
ofri@2708
|
116 |
[icon setFlipped:YES];
|
|
ofri@2708
|
117 |
[icon drawScaledInRect:drawRect
|
|
ofri@2708
|
118 |
operation:NSCompositeSourceOver
|
|
Rudy@4246
|
119 |
fraction:1.0];
|
|
ingmarstein@2721
|
120 |
|
|
ofri@2708
|
121 |
drawRect.origin.x += iconSize + GrowlBrushedIconTextPadding;
|
|
ingmarstein@2721
|
122 |
|
|
ofri@2708
|
123 |
if (haveTitle) {
|
|
ofri@2708
|
124 |
[titleLayoutManager drawGlyphsForGlyphRange:titleRange atPoint:drawRect.origin];
|
|
ofri@2708
|
125 |
drawRect.origin.y += titleHeight + GrowlBrushedTitleTextPadding;
|
|
ofri@2708
|
126 |
}
|
|
ingmarstein@2721
|
127 |
|
|
ofri@2708
|
128 |
if (haveText)
|
|
ofri@2708
|
129 |
[textLayoutManager drawGlyphsForGlyphRange:textRange atPoint:drawRect.origin];
|
|
ingmarstein@2721
|
130 |
|
|
ofri@2708
|
131 |
[window invalidateShadow];
|
|
bgannin@3983
|
132 |
[super drawRect:rect];
|
|
eridius@2773
|
133 |
//}
|
|
boredzo@2402
|
134 |
}
|
|
boredzo@2402
|
135 |
|
|
boredzo@2402
|
136 |
- (void) setIcon:(NSImage *)anIcon {
|
|
boredzo@2402
|
137 |
[icon release];
|
|
boredzo@2402
|
138 |
icon = [anIcon retain];
|
|
boredzo@2402
|
139 |
[self setNeedsDisplay:YES];
|
|
boredzo@2402
|
140 |
}
|
|
boredzo@2402
|
141 |
|
|
bgannin@3404
|
142 |
- (void) setTitle:(NSString *)aTitle {
|
|
boredzo@2402
|
143 |
haveTitle = [aTitle length] != 0;
|
|
boredzo@2402
|
144 |
|
|
boredzo@2402
|
145 |
if (!haveTitle) {
|
|
boredzo@2402
|
146 |
[self setNeedsDisplay:YES];
|
|
boredzo@2402
|
147 |
return;
|
|
boredzo@2402
|
148 |
}
|
|
boredzo@2402
|
149 |
|
|
boredzo@2402
|
150 |
if (!titleStorage) {
|
|
boredzo@2402
|
151 |
NSSize containerSize;
|
|
boredzo@2402
|
152 |
containerSize.width = GrowlBrushedTextAreaWidth;
|
|
boredzo@2402
|
153 |
containerSize.height = FLT_MAX;
|
|
boredzo@2402
|
154 |
titleStorage = [[NSTextStorage alloc] init];
|
|
boredzo@2402
|
155 |
titleContainer = [[NSTextContainer alloc] initWithContainerSize:containerSize];
|
|
boredzo@2402
|
156 |
[titleLayoutManager addTextContainer:titleContainer]; // retains textContainer
|
|
boredzo@2402
|
157 |
[titleContainer release];
|
|
boredzo@2402
|
158 |
[titleStorage addLayoutManager:titleLayoutManager]; // retains layoutManager
|
|
Rudy@4246
|
159 |
[titleContainer setLineFragmentPadding:0.0];
|
|
boredzo@2402
|
160 |
}
|
|
boredzo@2402
|
161 |
|
|
boredzo@2402
|
162 |
// construct attributes for the title
|
|
boredzo@2402
|
163 |
NSMutableParagraphStyle *paragraphStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy];
|
|
boredzo@2402
|
164 |
NSFont *titleFont = [NSFont boldSystemFontOfSize:GrowlBrushedTitleFontSize];
|
|
boredzo@2402
|
165 |
[paragraphStyle setLineBreakMode:NSLineBreakByTruncatingTail];
|
|
boredzo@2402
|
166 |
NSDictionary *defaultAttributes = [[NSDictionary alloc] initWithObjectsAndKeys:
|
|
boredzo@2402
|
167 |
titleFont, NSFontAttributeName,
|
|
boredzo@2402
|
168 |
textColor, NSForegroundColorAttributeName,
|
|
boredzo@2402
|
169 |
textShadow, NSShadowAttributeName,
|
|
boredzo@2402
|
170 |
paragraphStyle, NSParagraphStyleAttributeName,
|
|
boredzo@2402
|
171 |
nil];
|
|
boredzo@2402
|
172 |
[paragraphStyle release];
|
|
boredzo@2402
|
173 |
|
|
bgannin@3404
|
174 |
[[titleStorage mutableString] setString:aTitle];
|
|
bgannin@3404
|
175 |
[titleStorage setAttributes:defaultAttributes range:NSMakeRange(0, [titleStorage length])];
|
|
boredzo@2402
|
176 |
|
|
boredzo@2402
|
177 |
[defaultAttributes release];
|
|
boredzo@2402
|
178 |
|
|
boredzo@2402
|
179 |
titleRange = [titleLayoutManager glyphRangeForTextContainer:titleContainer]; // force layout
|
|
boredzo@2402
|
180 |
titleHeight = [titleLayoutManager usedRectForTextContainer:titleContainer].size.height;
|
|
boredzo@2402
|
181 |
|
|
boredzo@2402
|
182 |
[self setNeedsDisplay:YES];
|
|
boredzo@2402
|
183 |
}
|
|
boredzo@2402
|
184 |
|
|
bgannin@3404
|
185 |
- (void) setText:(NSString *)aText {
|
|
boredzo@2402
|
186 |
haveText = [aText length] != 0;
|
|
boredzo@2402
|
187 |
|
|
boredzo@2402
|
188 |
if (!haveText) {
|
|
boredzo@2402
|
189 |
[self setNeedsDisplay:YES];
|
|
boredzo@2402
|
190 |
return;
|
|
boredzo@2402
|
191 |
}
|
|
boredzo@2402
|
192 |
|
|
boredzo@2402
|
193 |
if (!textStorage) {
|
|
boredzo@2402
|
194 |
NSSize containerSize;
|
|
boredzo@2402
|
195 |
BOOL limitPref = GrowlBrushedLimitPrefDefault;
|
|
boredzo@2402
|
196 |
READ_GROWL_PREF_BOOL(GrowlBrushedLimitPref, GrowlBrushedPrefDomain, &limitPref);
|
|
boredzo@2402
|
197 |
containerSize.width = GrowlBrushedTextAreaWidth;
|
|
boredzo@2402
|
198 |
if (limitPref)
|
|
boredzo@2402
|
199 |
containerSize.height = lineHeight * GrowlBrushedMaxLines;
|
|
boredzo@2402
|
200 |
else
|
|
boredzo@2402
|
201 |
containerSize.height = FLT_MAX;
|
|
boredzo@2402
|
202 |
textStorage = [[NSTextStorage alloc] init];
|
|
boredzo@2402
|
203 |
textContainer = [[NSTextContainer alloc] initWithContainerSize:containerSize];
|
|
boredzo@2402
|
204 |
[textLayoutManager addTextContainer:textContainer]; // retains textContainer
|
|
boredzo@2402
|
205 |
[textContainer release];
|
|
boredzo@2402
|
206 |
[textStorage addLayoutManager:textLayoutManager]; // retains layoutManager
|
|
Rudy@4246
|
207 |
[textContainer setLineFragmentPadding:0.0];
|
|
boredzo@2402
|
208 |
}
|
|
boredzo@2402
|
209 |
|
|
boredzo@2402
|
210 |
// construct attributes for the description text
|
|
boredzo@2402
|
211 |
NSDictionary *defaultAttributes = [[NSDictionary alloc] initWithObjectsAndKeys:
|
|
boredzo@2402
|
212 |
textFont, NSFontAttributeName,
|
|
boredzo@2402
|
213 |
textColor, NSForegroundColorAttributeName,
|
|
boredzo@2402
|
214 |
textShadow, NSShadowAttributeName,
|
|
boredzo@2402
|
215 |
nil];
|
|
boredzo@2402
|
216 |
|
|
bgannin@3404
|
217 |
[[textStorage mutableString] setString:aText];
|
|
bgannin@3404
|
218 |
[textStorage setAttributes:defaultAttributes range:NSMakeRange(0, [textStorage length])];
|
|
boredzo@2402
|
219 |
|
|
boredzo@2402
|
220 |
[defaultAttributes release];
|
|
boredzo@2402
|
221 |
|
|
boredzo@2402
|
222 |
textRange = [textLayoutManager glyphRangeForTextContainer:textContainer]; // force layout
|
|
boredzo@2402
|
223 |
textHeight = [textLayoutManager usedRectForTextContainer:textContainer].size.height;
|
|
boredzo@2402
|
224 |
|
|
boredzo@2402
|
225 |
[self setNeedsDisplay:YES];
|
|
boredzo@2402
|
226 |
}
|
|
boredzo@2402
|
227 |
|
|
boredzo@2402
|
228 |
- (void) setPriority:(int)priority {
|
|
boredzo@2402
|
229 |
NSString *textKey;
|
|
boredzo@2402
|
230 |
switch (priority) {
|
|
boredzo@2402
|
231 |
case -2:
|
|
boredzo@2402
|
232 |
textKey = GrowlBrushedVeryLowTextColor;
|
|
boredzo@2402
|
233 |
break;
|
|
boredzo@2402
|
234 |
case -1:
|
|
boredzo@2402
|
235 |
textKey = GrowlBrushedModerateTextColor;
|
|
boredzo@2402
|
236 |
break;
|
|
boredzo@2402
|
237 |
case 1:
|
|
boredzo@2402
|
238 |
textKey = GrowlBrushedHighTextColor;
|
|
boredzo@2402
|
239 |
break;
|
|
boredzo@2402
|
240 |
case 2:
|
|
boredzo@2402
|
241 |
textKey = GrowlBrushedEmergencyTextColor;
|
|
boredzo@2402
|
242 |
break;
|
|
boredzo@2402
|
243 |
case 0:
|
|
boredzo@2402
|
244 |
default:
|
|
boredzo@2402
|
245 |
textKey = GrowlBrushedNormalTextColor;
|
|
boredzo@2402
|
246 |
break;
|
|
boredzo@2402
|
247 |
}
|
|
boredzo@2402
|
248 |
NSData *data = nil;
|
|
boredzo@2402
|
249 |
|
|
boredzo@2402
|
250 |
[textColor release];
|
|
boredzo@2402
|
251 |
READ_GROWL_PREF_VALUE(textKey, GrowlBrushedPrefDomain, NSData *, &data);
|
|
Rudy@4259
|
252 |
if(data)
|
|
Rudy@4259
|
253 |
CFMakeCollectable(data);
|
|
boredzo@2402
|
254 |
if (data && [data isKindOfClass:[NSData class]]) {
|
|
Rudy@4259
|
255 |
textColor = [NSUnarchiver unarchiveObjectWithData:data];
|
|
boredzo@2402
|
256 |
} else {
|
|
Rudy@4259
|
257 |
textColor = [NSColor colorWithCalibratedWhite:0.1f alpha:1.0f];
|
|
boredzo@2402
|
258 |
}
|
|
boredzo@2402
|
259 |
[textColor retain];
|
|
boredzo@2402
|
260 |
[data release];
|
|
Rudy@4259
|
261 |
data = nil;
|
|
boredzo@2402
|
262 |
}
|
|
boredzo@2402
|
263 |
|
|
boredzo@2402
|
264 |
- (void) sizeToFit {
|
|
Rudy@4246
|
265 |
CGFloat height = GrowlBrushedPadding + GrowlBrushedPadding + [self titleHeight] + [self descriptionHeight];
|
|
boredzo@2402
|
266 |
if (haveTitle && haveText)
|
|
ingmarstein@2580
|
267 |
height += GrowlBrushedTitleTextPadding;
|
|
ingmarstein@2580
|
268 |
if (height < GrowlBrushedMinTextHeight)
|
|
ingmarstein@2580
|
269 |
height = GrowlBrushedMinTextHeight;
|
|
boredzo@2402
|
270 |
|
|
boredzo@2402
|
271 |
// resize the window so that it contains the tracking rect
|
|
ingmarstein@2580
|
272 |
NSWindow *window = [self window];
|
|
boredzo@2402
|
273 |
NSRect windowRect = [[self window] frame];
|
|
ingmarstein@2580
|
274 |
windowRect.origin.y -= height - windowRect.size.height;
|
|
ingmarstein@2580
|
275 |
windowRect.size.height = height;
|
|
evands@3582
|
276 |
[window setFrame:windowRect display:YES animate:YES];
|
|
boredzo@2402
|
277 |
|
|
boredzo@2402
|
278 |
if (trackingRectTag)
|
|
boredzo@2402
|
279 |
[self removeTrackingRect:trackingRectTag];
|
|
ingmarstein@2580
|
280 |
trackingRectTag = [self addTrackingRect:[self frame] owner:self userData:NULL assumeInside:NO];
|
|
boredzo@2402
|
281 |
}
|
|
boredzo@2402
|
282 |
|
|
Rudy@4246
|
283 |
- (CGFloat) titleHeight {
|
|
Rudy@4246
|
284 |
return haveTitle ? titleHeight : 0.0;
|
|
Rudy@4246
|
285 |
}
|
|
Rudy@4246
|
286 |
|
|
Rudy@4246
|
287 |
- (CGFloat) descriptionHeight {
|
|
Rudy@4246
|
288 |
return haveText ? textHeight : 0.0;
|
|
Rudy@4246
|
289 |
}
|
|
Rudy@4246
|
290 |
|
|
Rudy@4246
|
291 |
- (NSInteger) descriptionRowCount {
|
|
Rudy@4246
|
292 |
NSInteger rowCount = textHeight / lineHeight;
|
|
boredzo@2402
|
293 |
BOOL limitPref = GrowlBrushedLimitPrefDefault;
|
|
boredzo@2402
|
294 |
READ_GROWL_PREF_BOOL(GrowlBrushedLimitPref, GrowlBrushedPrefDomain, &limitPref);
|
|
boredzo@2402
|
295 |
if (limitPref)
|
|
boredzo@2402
|
296 |
return MIN(rowCount, GrowlBrushedMaxLines);
|
|
boredzo@2402
|
297 |
else
|
|
boredzo@2402
|
298 |
return rowCount;
|
|
boredzo@2402
|
299 |
}
|
|
boredzo@2402
|
300 |
|
|
boredzo@2402
|
301 |
@end
|