|
boredzo@2402
|
1 |
//
|
|
boredzo@2402
|
2 |
// GrowlBubblesWindowView.m
|
|
boredzo@2402
|
3 |
// Growl
|
|
boredzo@2402
|
4 |
//
|
|
boredzo@2402
|
5 |
// Created by Nelson Elhage on Wed Jun 09 2004.
|
|
boredzo@2402
|
6 |
// Name changed from KABubbleWindowView.m by Justin Burns on Fri Nov 05 2004.
|
|
ingmarstein@3040
|
7 |
// Copyright (c) 2004-2006 The Growl Project. All rights reserved.
|
|
boredzo@2402
|
8 |
//
|
|
boredzo@2402
|
9 |
|
|
boredzo@2402
|
10 |
#import "GrowlBubblesWindowView.h"
|
|
boredzo@2402
|
11 |
#import "GrowlDefinesInternal.h"
|
|
boredzo@2402
|
12 |
#import "GrowlBubblesDefines.h"
|
|
boredzo@2402
|
13 |
#import "GrowlImageAdditions.h"
|
|
boredzo@2402
|
14 |
#import "GrowlBezierPathAdditions.h"
|
|
boredzo@2402
|
15 |
#import "NSMutableAttributedStringAdditions.h"
|
|
boredzo@2402
|
16 |
#import <WebKit/WebPreferences.h>
|
|
boredzo@2402
|
17 |
|
|
boredzo@2402
|
18 |
/* to get the limit pref */
|
|
boredzo@2402
|
19 |
#import "GrowlBubblesPrefsController.h"
|
|
boredzo@2402
|
20 |
|
|
boredzo@2402
|
21 |
/* Hardcoded geometry values */
|
|
Rudy@4246
|
22 |
#define PANEL_WIDTH_PX 270.0 /*!< Total width of the panel, including border */
|
|
Rudy@4246
|
23 |
#define BORDER_WIDTH_PX 4.0
|
|
Rudy@4246
|
24 |
#define BORDER_RADIUS_PX 9.0
|
|
Rudy@4246
|
25 |
#define PANEL_VSPACE_PX 10.0 /*!< Vertical padding from bounds to content area */
|
|
Rudy@4246
|
26 |
#define PANEL_HSPACE_PX 15.0 /*!< Horizontal padding from bounds to content area */
|
|
Rudy@4246
|
27 |
#define ICON_SIZE_PX 32.0 /*!< The width and height of the (square) icon */
|
|
Rudy@4246
|
28 |
#define ICON_SIZE_LARGE_PX 48.0 /*!< The width and height of the (square) icon */
|
|
Rudy@4246
|
29 |
#define ICON_HSPACE_PX 8.0 /*!< Horizontal space between icon and title/description */
|
|
Rudy@4246
|
30 |
#define TITLE_VSPACE_PX 5.0 /*!< Vertical space between title and description */
|
|
Rudy@4246
|
31 |
#define TITLE_FONT_SIZE_PTS 13.0
|
|
Rudy@4246
|
32 |
#define DESCR_FONT_SIZE_PTS 11.0
|
|
boredzo@2402
|
33 |
#define MAX_TEXT_ROWS 5 /*!< The maximum number of rows of text, used only if the limit preference is set. */
|
|
boredzo@2402
|
34 |
#define MIN_TEXT_HEIGHT (PANEL_VSPACE_PX + PANEL_VSPACE_PX + iconSize)
|
|
boredzo@2402
|
35 |
#define TEXT_AREA_WIDTH (PANEL_WIDTH_PX - PANEL_HSPACE_PX - PANEL_HSPACE_PX - iconSize - ICON_HSPACE_PX)
|
|
boredzo@2402
|
36 |
|
|
Rudy@4246
|
37 |
static void GrowlBubblesShadeInterpolate( void *info, const CGFloat *inData, CGFloat *outData ) {
|
|
Rudy@4246
|
38 |
CGFloat *colors = (CGFloat *) info;
|
|
Rudy@4246
|
39 |
|
|
Rudy@4246
|
40 |
register CGFloat a = inData[0];
|
|
Rudy@4246
|
41 |
register CGFloat a_coeff = 1.0 - a;
|
|
boredzo@2402
|
42 |
|
|
boredzo@2402
|
43 |
// SIMD could come in handy here
|
|
boredzo@2402
|
44 |
// outData[0..3] = a_coeff * colors[4..7] + a * colors[0..3]
|
|
boredzo@2402
|
45 |
outData[0] = a_coeff * colors[4] + a * colors[0];
|
|
boredzo@2402
|
46 |
outData[1] = a_coeff * colors[5] + a * colors[1];
|
|
boredzo@2402
|
47 |
outData[2] = a_coeff * colors[6] + a * colors[2];
|
|
boredzo@2402
|
48 |
outData[3] = a_coeff * colors[7] + a * colors[3];
|
|
boredzo@2402
|
49 |
}
|
|
boredzo@2402
|
50 |
|
|
boredzo@2402
|
51 |
#pragma mark -
|
|
boredzo@2402
|
52 |
|
|
boredzo@2402
|
53 |
@implementation GrowlBubblesWindowView
|
|
rudy@2782
|
54 |
|
|
boredzo@2402
|
55 |
- (id) initWithFrame:(NSRect) frame {
|
|
boredzo@2402
|
56 |
if ((self = [super initWithFrame:frame])) {
|
|
boredzo@2402
|
57 |
titleFont = [[NSFont boldSystemFontOfSize:TITLE_FONT_SIZE_PTS] retain];
|
|
boredzo@2402
|
58 |
textFont = [[NSFont messageFontOfSize:DESCR_FONT_SIZE_PTS] retain];
|
|
Rudy@4246
|
59 |
borderColor = [[NSColor colorWithCalibratedWhite:0.0 alpha:0.5] retain];
|
|
Rudy@4246
|
60 |
highlightColor = [[NSColor colorWithCalibratedWhite:0.0 alpha:0.75] retain];
|
|
boredzo@2402
|
61 |
textLayoutManager = [[NSLayoutManager alloc] init];
|
|
boredzo@2402
|
62 |
titleLayoutManager = [[NSLayoutManager alloc] init];
|
|
boredzo@2402
|
63 |
lineHeight = [textLayoutManager defaultLineHeightForFont:textFont];
|
|
boredzo@2402
|
64 |
|
|
boredzo@2402
|
65 |
int size = GrowlBubblesSizePrefDefault;
|
|
boredzo@2402
|
66 |
READ_GROWL_PREF_INT(GrowlBubblesSizePref, GrowlBubblesPrefDomain, &size);
|
|
boredzo@2402
|
67 |
if (size == GrowlBubblesSizeLarge) {
|
|
boredzo@2402
|
68 |
iconSize = ICON_SIZE_LARGE_PX;
|
|
boredzo@2402
|
69 |
} else {
|
|
boredzo@2402
|
70 |
iconSize = ICON_SIZE_PX;
|
|
boredzo@2402
|
71 |
}
|
|
boredzo@2402
|
72 |
}
|
|
boredzo@2402
|
73 |
return self;
|
|
boredzo@2402
|
74 |
}
|
|
boredzo@2402
|
75 |
|
|
boredzo@2402
|
76 |
- (void) dealloc {
|
|
boredzo@2402
|
77 |
[titleFont release];
|
|
boredzo@2402
|
78 |
[textFont release];
|
|
boredzo@2402
|
79 |
[icon release];
|
|
boredzo@2402
|
80 |
[textColor release];
|
|
boredzo@2402
|
81 |
[bgColor release];
|
|
boredzo@2402
|
82 |
[lightColor release];
|
|
boredzo@2402
|
83 |
[borderColor release];
|
|
boredzo@2402
|
84 |
[highlightColor release];
|
|
boredzo@2402
|
85 |
[textStorage release];
|
|
boredzo@2402
|
86 |
[titleStorage release];
|
|
boredzo@2402
|
87 |
[textLayoutManager release];
|
|
boredzo@2402
|
88 |
[titleLayoutManager release];
|
|
boredzo@2402
|
89 |
|
|
boredzo@2402
|
90 |
[super dealloc];
|
|
boredzo@2402
|
91 |
}
|
|
boredzo@2402
|
92 |
|
|
Rudy@4246
|
93 |
- (CGFloat) titleHeight {
|
|
Rudy@4246
|
94 |
return haveTitle ? titleHeight : 0.0;
|
|
boredzo@2402
|
95 |
}
|
|
boredzo@2402
|
96 |
|
|
rudy@2782
|
97 |
|
|
boredzo@2402
|
98 |
- (void) drawRect:(NSRect) rect {
|
|
evands@3503
|
99 |
#pragma unused (rect)
|
|
ofri@2708
|
100 |
//Make sure that we don't draw in the main thread
|
|
rudy@2782
|
101 |
//if ([super dispatchDrawingToThread:rect]) {
|
|
ofri@2708
|
102 |
NSRect b = [self bounds];
|
|
ofri@2708
|
103 |
CGRect bounds = CGRectMake(b.origin.x, b.origin.y, b.size.width, b.size.height);
|
|
Rudy@4246
|
104 |
CGRect shape = CGRectInset(bounds, BORDER_WIDTH_PX*0.5, BORDER_WIDTH_PX*0.5);
|
|
ingmarstein@2721
|
105 |
|
|
ofri@2708
|
106 |
CGContextRef context = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
|
|
ingmarstein@2721
|
107 |
|
|
ofri@2708
|
108 |
// Create a path with enough room to strike the border and remain inside our frame.
|
|
ofri@2708
|
109 |
// Since the path is in the middle of the line, this means we must inset it by half the border width.
|
|
ofri@2708
|
110 |
addRoundedRectToPath(context, shape, BORDER_RADIUS_PX);
|
|
ofri@2708
|
111 |
CGContextSetLineWidth(context, BORDER_WIDTH_PX);
|
|
ingmarstein@2721
|
112 |
|
|
ofri@2708
|
113 |
CGContextSaveGState(context);
|
|
ofri@2708
|
114 |
CGContextClip(context);
|
|
ingmarstein@2721
|
115 |
|
|
ofri@2708
|
116 |
// Create a callback function to generate the
|
|
ofri@2708
|
117 |
// fill clipped graphics context with our gradient
|
|
ofri@2708
|
118 |
struct CGFunctionCallbacks callbacks = { 0U, GrowlBubblesShadeInterpolate, NULL };
|
|
Rudy@4246
|
119 |
CGFloat colors[8];
|
|
ingmarstein@2721
|
120 |
|
|
ofri@2708
|
121 |
[lightColor getRed:&colors[0]
|
|
ofri@2708
|
122 |
green:&colors[1]
|
|
ofri@2708
|
123 |
blue:&colors[2]
|
|
ofri@2708
|
124 |
alpha:&colors[3]];
|
|
ingmarstein@2721
|
125 |
|
|
ofri@2708
|
126 |
[bgColor getRed:&colors[4]
|
|
ofri@2708
|
127 |
green:&colors[5]
|
|
ofri@2708
|
128 |
blue:&colors[6]
|
|
ofri@2708
|
129 |
alpha:&colors[7]];
|
|
ingmarstein@2721
|
130 |
|
|
ofri@2708
|
131 |
CGFunctionRef function = CGFunctionCreate( (void *) colors,
|
|
ofri@2708
|
132 |
1U,
|
|
ofri@2708
|
133 |
/*domain*/ NULL,
|
|
ofri@2708
|
134 |
4U,
|
|
ofri@2708
|
135 |
/*range*/ NULL,
|
|
ofri@2708
|
136 |
&callbacks );
|
|
ofri@2708
|
137 |
CGColorSpaceRef cspace = CGColorSpaceCreateDeviceRGB();
|
|
ingmarstein@2721
|
138 |
|
|
ofri@2708
|
139 |
CGPoint src, dst;
|
|
ofri@2708
|
140 |
src.x = CGRectGetMinX(bounds);
|
|
ofri@2708
|
141 |
src.y = CGRectGetMaxY(bounds);
|
|
ofri@2708
|
142 |
dst.x = src.x;
|
|
ofri@2708
|
143 |
dst.y = CGRectGetMinY(bounds);
|
|
ofri@2708
|
144 |
CGShadingRef shading = CGShadingCreateAxial(cspace, src, dst,
|
|
ofri@2708
|
145 |
function, false, false);
|
|
ingmarstein@2721
|
146 |
|
|
ofri@2708
|
147 |
CGContextDrawShading(context, shading);
|
|
ingmarstein@2721
|
148 |
|
|
ofri@2708
|
149 |
CGShadingRelease(shading);
|
|
ofri@2708
|
150 |
CGColorSpaceRelease(cspace);
|
|
ofri@2708
|
151 |
CGFunctionRelease(function);
|
|
ingmarstein@2721
|
152 |
|
|
ofri@2708
|
153 |
CGContextRestoreGState(context);
|
|
ingmarstein@2721
|
154 |
|
|
ofri@2708
|
155 |
addRoundedRectToPath(context, shape, BORDER_RADIUS_PX);
|
|
ofri@2708
|
156 |
CGContextSetLineWidth(context, BORDER_WIDTH_PX);
|
|
ofri@2708
|
157 |
if (mouseOver)
|
|
ofri@2708
|
158 |
[highlightColor set];
|
|
ofri@2708
|
159 |
else
|
|
ofri@2708
|
160 |
[borderColor set];
|
|
ofri@2708
|
161 |
CGContextStrokePath(context);
|
|
ingmarstein@2721
|
162 |
|
|
ofri@2708
|
163 |
NSRect drawRect;
|
|
ofri@2708
|
164 |
drawRect.origin.x = PANEL_HSPACE_PX;
|
|
ofri@2708
|
165 |
drawRect.origin.y = PANEL_VSPACE_PX;
|
|
ofri@2708
|
166 |
drawRect.size.width = iconSize;
|
|
ofri@2708
|
167 |
drawRect.size.height = iconSize;
|
|
ingmarstein@2721
|
168 |
|
|
ofri@2708
|
169 |
[icon setFlipped:YES];
|
|
ofri@2708
|
170 |
[icon drawScaledInRect:drawRect
|
|
ofri@2708
|
171 |
operation:NSCompositeSourceOver
|
|
Rudy@4246
|
172 |
fraction:1.0];
|
|
ingmarstein@2721
|
173 |
|
|
ofri@2708
|
174 |
drawRect.origin.x += iconSize + ICON_HSPACE_PX;
|
|
ingmarstein@2721
|
175 |
|
|
ofri@2708
|
176 |
if (haveTitle) {
|
|
ofri@2708
|
177 |
[titleLayoutManager drawGlyphsForGlyphRange:titleRange atPoint:drawRect.origin];
|
|
ofri@2708
|
178 |
drawRect.origin.y += titleHeight + TITLE_VSPACE_PX;
|
|
ofri@2708
|
179 |
}
|
|
ingmarstein@2721
|
180 |
|
|
ofri@2708
|
181 |
if (haveText)
|
|
ofri@2708
|
182 |
[textLayoutManager drawGlyphsForGlyphRange:textRange atPoint:drawRect.origin];
|
|
ingmarstein@2721
|
183 |
|
|
ofri@2708
|
184 |
[[self window] invalidateShadow];
|
|
bgannin@3983
|
185 |
[super drawRect:rect];
|
|
rudy@2782
|
186 |
//}
|
|
boredzo@2402
|
187 |
}
|
|
boredzo@2402
|
188 |
|
|
boredzo@2402
|
189 |
#pragma mark -
|
|
boredzo@2402
|
190 |
|
|
boredzo@2402
|
191 |
- (void) setPriority:(int)priority {
|
|
boredzo@2402
|
192 |
NSString *key;
|
|
boredzo@2402
|
193 |
NSString *textKey;
|
|
boredzo@2402
|
194 |
NSString *topKey;
|
|
boredzo@2402
|
195 |
|
|
boredzo@2402
|
196 |
switch (priority) {
|
|
boredzo@2402
|
197 |
case -2:
|
|
boredzo@2402
|
198 |
key = GrowlBubblesVeryLowColor;
|
|
boredzo@2402
|
199 |
textKey = GrowlBubblesVeryLowTextColor;
|
|
boredzo@2402
|
200 |
topKey = GrowlBubblesVeryLowTopColor;
|
|
boredzo@2402
|
201 |
break;
|
|
boredzo@2402
|
202 |
case -1:
|
|
boredzo@2402
|
203 |
key = GrowlBubblesModerateColor;
|
|
boredzo@2402
|
204 |
textKey = GrowlBubblesModerateTextColor;
|
|
boredzo@2402
|
205 |
topKey = GrowlBubblesModerateTopColor;
|
|
boredzo@2402
|
206 |
break;
|
|
boredzo@2402
|
207 |
case 1:
|
|
boredzo@2402
|
208 |
key = GrowlBubblesHighColor;
|
|
boredzo@2402
|
209 |
textKey = GrowlBubblesHighTextColor;
|
|
boredzo@2402
|
210 |
topKey = GrowlBubblesHighTopColor;
|
|
boredzo@2402
|
211 |
break;
|
|
boredzo@2402
|
212 |
case 2:
|
|
boredzo@2402
|
213 |
key = GrowlBubblesEmergencyColor;
|
|
boredzo@2402
|
214 |
textKey = GrowlBubblesEmergencyTextColor;
|
|
boredzo@2402
|
215 |
topKey = GrowlBubblesEmergencyTopColor;
|
|
boredzo@2402
|
216 |
break;
|
|
boredzo@2402
|
217 |
case 0:
|
|
boredzo@2402
|
218 |
default:
|
|
boredzo@2402
|
219 |
key = GrowlBubblesNormalColor;
|
|
boredzo@2402
|
220 |
textKey = GrowlBubblesNormalTextColor;
|
|
boredzo@2402
|
221 |
topKey = GrowlBubblesNormalTopColor;
|
|
boredzo@2402
|
222 |
break;
|
|
boredzo@2402
|
223 |
}
|
|
boredzo@2402
|
224 |
|
|
boredzo@2402
|
225 |
NSData *data = nil;
|
|
boredzo@2402
|
226 |
|
|
Rudy@4246
|
227 |
CGFloat backgroundAlpha = 95.0;
|
|
boredzo@2402
|
228 |
READ_GROWL_PREF_FLOAT(GrowlBubblesOpacity, GrowlBubblesPrefDomain, &backgroundAlpha);
|
|
Rudy@4246
|
229 |
backgroundAlpha *= 0.01;
|
|
Rudy@4259
|
230 |
|
|
boredzo@2402
|
231 |
Class NSDataClass = [NSData class];
|
|
boredzo@2402
|
232 |
READ_GROWL_PREF_VALUE(key, GrowlBubblesPrefDomain, NSData *, &data);
|
|
Rudy@4259
|
233 |
if(data)
|
|
Rudy@4259
|
234 |
CFMakeCollectable(data);
|
|
boredzo@2402
|
235 |
if (data && [data isKindOfClass:NSDataClass]) {
|
|
Rudy@4259
|
236 |
bgColor = [NSUnarchiver unarchiveObjectWithData:data];
|
|
Rudy@4259
|
237 |
bgColor = [bgColor colorWithAlphaComponent:backgroundAlpha];
|
|
boredzo@2402
|
238 |
} else {
|
|
Rudy@4246
|
239 |
bgColor = [NSColor colorWithCalibratedRed:0.69412
|
|
Rudy@4246
|
240 |
green:0.83147
|
|
Rudy@4246
|
241 |
blue:0.96078
|
|
boredzo@2402
|
242 |
alpha:backgroundAlpha];
|
|
boredzo@2402
|
243 |
}
|
|
boredzo@2402
|
244 |
[bgColor retain];
|
|
boredzo@2402
|
245 |
[data release];
|
|
boredzo@2402
|
246 |
|
|
boredzo@2402
|
247 |
data = nil;
|
|
boredzo@2402
|
248 |
READ_GROWL_PREF_VALUE(textKey, GrowlBubblesPrefDomain, NSData *, &data);
|
|
Rudy@4259
|
249 |
if(data)
|
|
Rudy@4259
|
250 |
CFMakeCollectable(data);
|
|
boredzo@2402
|
251 |
if (data && [data isKindOfClass:NSDataClass]) {
|
|
boredzo@2402
|
252 |
textColor = [NSUnarchiver unarchiveObjectWithData:data];
|
|
boredzo@2402
|
253 |
} else {
|
|
boredzo@2402
|
254 |
textColor = [NSColor controlTextColor];
|
|
boredzo@2402
|
255 |
}
|
|
boredzo@2402
|
256 |
[textColor retain];
|
|
boredzo@2402
|
257 |
[data release];
|
|
boredzo@2402
|
258 |
|
|
boredzo@2402
|
259 |
data = nil;
|
|
boredzo@2402
|
260 |
READ_GROWL_PREF_VALUE(topKey, GrowlBubblesPrefDomain, NSData *, &data);
|
|
Rudy@4259
|
261 |
if(data)
|
|
Rudy@4259
|
262 |
CFMakeCollectable(data);
|
|
boredzo@2402
|
263 |
if (data && [data isKindOfClass:NSDataClass]) {
|
|
boredzo@2402
|
264 |
lightColor = [NSUnarchiver unarchiveObjectWithData:data];
|
|
boredzo@2402
|
265 |
lightColor = [lightColor colorWithAlphaComponent:backgroundAlpha];
|
|
boredzo@2402
|
266 |
} else {
|
|
Rudy@4246
|
267 |
lightColor = [NSColor colorWithCalibratedRed:0.93725
|
|
Rudy@4246
|
268 |
green:0.96863
|
|
Rudy@4246
|
269 |
blue:0.99216
|
|
boredzo@2402
|
270 |
alpha:backgroundAlpha];
|
|
boredzo@2402
|
271 |
}
|
|
boredzo@2402
|
272 |
[lightColor retain];
|
|
boredzo@2402
|
273 |
[data release];
|
|
boredzo@2402
|
274 |
}
|
|
boredzo@2402
|
275 |
|
|
boredzo@2402
|
276 |
- (void) setIcon:(NSImage *) anIcon {
|
|
boredzo@2402
|
277 |
[icon release];
|
|
boredzo@2402
|
278 |
icon = [anIcon retain];
|
|
boredzo@2402
|
279 |
[self setNeedsDisplay:YES];
|
|
boredzo@2402
|
280 |
}
|
|
boredzo@2402
|
281 |
|
|
bgannin@3404
|
282 |
- (void) setTitle:(NSString *) aTitle {
|
|
boredzo@2402
|
283 |
haveTitle = [aTitle length] != 0;
|
|
boredzo@2402
|
284 |
|
|
boredzo@2402
|
285 |
if (!haveTitle) {
|
|
boredzo@2402
|
286 |
[self setNeedsDisplay:YES];
|
|
boredzo@2402
|
287 |
return;
|
|
boredzo@2402
|
288 |
}
|
|
boredzo@2402
|
289 |
|
|
boredzo@2402
|
290 |
if (!titleStorage) {
|
|
boredzo@2402
|
291 |
NSSize containerSize;
|
|
boredzo@2402
|
292 |
containerSize.width = TEXT_AREA_WIDTH;
|
|
boredzo@2402
|
293 |
containerSize.height = FLT_MAX;
|
|
boredzo@2402
|
294 |
titleStorage = [[NSTextStorage alloc] init];
|
|
boredzo@2402
|
295 |
titleContainer = [[NSTextContainer alloc] initWithContainerSize:containerSize];
|
|
boredzo@2402
|
296 |
[titleLayoutManager addTextContainer:titleContainer]; // retains textContainer
|
|
boredzo@2402
|
297 |
[titleContainer release];
|
|
boredzo@2402
|
298 |
[titleStorage addLayoutManager:titleLayoutManager]; // retains layoutManager
|
|
Rudy@4246
|
299 |
[titleContainer setLineFragmentPadding:0.0];
|
|
boredzo@2402
|
300 |
}
|
|
boredzo@2402
|
301 |
|
|
boredzo@2402
|
302 |
NSMutableParagraphStyle *paragraphStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy];
|
|
boredzo@2402
|
303 |
[paragraphStyle setLineBreakMode:NSLineBreakByTruncatingTail];
|
|
boredzo@2402
|
304 |
NSDictionary *defaultAttributes = [[NSDictionary alloc] initWithObjectsAndKeys:
|
|
boredzo@2402
|
305 |
titleFont, NSFontAttributeName,
|
|
boredzo@2402
|
306 |
textColor, NSForegroundColorAttributeName,
|
|
boredzo@2402
|
307 |
paragraphStyle, NSParagraphStyleAttributeName,
|
|
boredzo@2402
|
308 |
nil];
|
|
boredzo@2402
|
309 |
[paragraphStyle release];
|
|
boredzo@2402
|
310 |
|
|
bgannin@3404
|
311 |
[[titleStorage mutableString] setString:aTitle];
|
|
bgannin@3404
|
312 |
[titleStorage setAttributes:defaultAttributes range:NSMakeRange(0, [titleStorage length])];
|
|
boredzo@2402
|
313 |
|
|
boredzo@2402
|
314 |
[defaultAttributes release];
|
|
boredzo@2402
|
315 |
|
|
boredzo@2402
|
316 |
titleRange = [titleLayoutManager glyphRangeForTextContainer:titleContainer]; // force layout
|
|
boredzo@2402
|
317 |
titleHeight = [titleLayoutManager usedRectForTextContainer:titleContainer].size.height;
|
|
boredzo@2402
|
318 |
|
|
boredzo@2402
|
319 |
[self setNeedsDisplay:YES];
|
|
boredzo@2402
|
320 |
}
|
|
boredzo@2402
|
321 |
|
|
bgannin@3404
|
322 |
- (void) setText:(NSString *) aText {
|
|
boredzo@2402
|
323 |
haveText = [aText length] != 0;
|
|
boredzo@2402
|
324 |
|
|
boredzo@2402
|
325 |
if (!haveText) {
|
|
boredzo@2402
|
326 |
[self setNeedsDisplay:YES];
|
|
boredzo@2402
|
327 |
return;
|
|
boredzo@2402
|
328 |
}
|
|
boredzo@2402
|
329 |
|
|
boredzo@2402
|
330 |
if (!textStorage) {
|
|
boredzo@2402
|
331 |
NSSize containerSize;
|
|
boredzo@2402
|
332 |
BOOL limitPref = YES;
|
|
boredzo@2402
|
333 |
READ_GROWL_PREF_BOOL(GrowlBubblesLimitPref, GrowlBubblesPrefDomain, &limitPref);
|
|
boredzo@2402
|
334 |
containerSize.width = TEXT_AREA_WIDTH;
|
|
boredzo@2402
|
335 |
if (limitPref)
|
|
boredzo@2402
|
336 |
containerSize.height = lineHeight * MAX_TEXT_ROWS;
|
|
boredzo@2402
|
337 |
else
|
|
boredzo@2402
|
338 |
containerSize.height = FLT_MAX;
|
|
boredzo@2402
|
339 |
textStorage = [[NSTextStorage alloc] init];
|
|
boredzo@2402
|
340 |
textContainer = [[NSTextContainer alloc] initWithContainerSize:containerSize];
|
|
boredzo@2402
|
341 |
[textLayoutManager addTextContainer:textContainer]; // retains textContainer
|
|
boredzo@2402
|
342 |
[textContainer release];
|
|
boredzo@2402
|
343 |
[textStorage addLayoutManager:textLayoutManager]; // retains layoutManager
|
|
Rudy@4246
|
344 |
[textContainer setLineFragmentPadding:0.0];
|
|
boredzo@2402
|
345 |
}
|
|
boredzo@2402
|
346 |
|
|
boredzo@2402
|
347 |
NSDictionary *defaultAttributes = [[NSDictionary alloc] initWithObjectsAndKeys:
|
|
boredzo@2402
|
348 |
textFont, NSFontAttributeName,
|
|
boredzo@2402
|
349 |
textColor, NSForegroundColorAttributeName,
|
|
boredzo@2402
|
350 |
nil];
|
|
boredzo@2402
|
351 |
|
|
bgannin@3404
|
352 |
[[textStorage mutableString] setString:aText];
|
|
bgannin@3404
|
353 |
[textStorage setAttributes:defaultAttributes range:NSMakeRange(0, [textStorage length])];
|
|
bgannin@3404
|
354 |
|
|
boredzo@2402
|
355 |
[defaultAttributes release];
|
|
boredzo@2402
|
356 |
|
|
boredzo@2402
|
357 |
textRange = [textLayoutManager glyphRangeForTextContainer:textContainer]; // force layout
|
|
boredzo@2402
|
358 |
textHeight = [textLayoutManager usedRectForTextContainer:textContainer].size.height;
|
|
boredzo@2402
|
359 |
|
|
boredzo@2402
|
360 |
[self setNeedsDisplay:YES];
|
|
boredzo@2402
|
361 |
}
|
|
boredzo@2402
|
362 |
|
|
boredzo@2402
|
363 |
- (void) sizeToFit {
|
|
Rudy@4246
|
364 |
CGFloat height = PANEL_VSPACE_PX + PANEL_VSPACE_PX + [self titleHeight] + [self descriptionHeight];
|
|
boredzo@2402
|
365 |
if (haveTitle && haveText)
|
|
ingmarstein@2580
|
366 |
height += TITLE_VSPACE_PX;
|
|
ingmarstein@2580
|
367 |
if (height < MIN_TEXT_HEIGHT)
|
|
ingmarstein@2580
|
368 |
height = MIN_TEXT_HEIGHT;
|
|
boredzo@2402
|
369 |
|
|
boredzo@2402
|
370 |
// resize the window so that it contains the tracking rect
|
|
ingmarstein@2580
|
371 |
NSWindow *window = [self window];
|
|
ingmarstein@2580
|
372 |
NSRect windowRect = [window frame];
|
|
ingmarstein@2580
|
373 |
windowRect.origin.y -= height - windowRect.size.height;
|
|
ingmarstein@2580
|
374 |
windowRect.size.height = height;
|
|
evands@3582
|
375 |
[window setFrame:windowRect display:YES animate:YES];
|
|
boredzo@2402
|
376 |
|
|
boredzo@2402
|
377 |
if (trackingRectTag)
|
|
boredzo@2402
|
378 |
[self removeTrackingRect:trackingRectTag];
|
|
ingmarstein@2580
|
379 |
trackingRectTag = [self addTrackingRect:[self frame] owner:self userData:NULL assumeInside:NO];
|
|
boredzo@2402
|
380 |
}
|
|
boredzo@2402
|
381 |
|
|
ingmarstein@2548
|
382 |
- (BOOL) isFlipped {
|
|
boredzo@2402
|
383 |
// Coordinates are based on top left corner
|
|
boredzo@2402
|
384 |
return YES;
|
|
boredzo@2402
|
385 |
}
|
|
boredzo@2402
|
386 |
|
|
Rudy@4246
|
387 |
- (CGFloat) descriptionHeight {
|
|
Rudy@4246
|
388 |
return haveText ? textHeight : 0.0;
|
|
Rudy@4246
|
389 |
}
|
|
Rudy@4246
|
390 |
|
|
Rudy@4246
|
391 |
- (NSInteger) descriptionRowCount {
|
|
Rudy@4246
|
392 |
NSInteger rowCount = textHeight / lineHeight;
|
|
boredzo@2402
|
393 |
BOOL limitPref = YES;
|
|
boredzo@2402
|
394 |
READ_GROWL_PREF_BOOL(GrowlBubblesLimitPref, GrowlBubblesPrefDomain, &limitPref);
|
|
boredzo@2402
|
395 |
if (limitPref)
|
|
boredzo@2402
|
396 |
return MIN(rowCount, MAX_TEXT_ROWS);
|
|
boredzo@2402
|
397 |
else
|
|
boredzo@2402
|
398 |
return rowCount;
|
|
boredzo@2402
|
399 |
}
|
|
rudy@3298
|
400 |
|
|
boredzo@2402
|
401 |
@end
|