Release the bar color after creating it. Fixes a leak that I found using leaks.
2 // GrowliCalWindowView.m
5 // Created by Nelson Elhage on Wed Jun 09 2004.
6 // Name changed from KABubbleWindowView.m by Justin Burns on Fri Nov 05 2004.
7 // Adapted for iCal by Takumi Murayama on Thu Aug 17 2006.
8 // Copyright (c) 2004-2006 The Growl Project. All rights reserved.
11 #import "GrowliCalWindowView.h"
12 #import "GrowlDefinesInternal.h"
13 #import "GrowliCalDefines.h"
14 #import "GrowlImageAdditions.h"
15 #import "GrowlBezierPathAdditions.h"
16 #import "NSMutableAttributedStringAdditions.h"
17 #import <WebKit/WebPreferences.h>
19 /* to get the limit pref */
20 #import "GrowliCalPrefsController.h"
22 /* Hardcoded geometry values */
23 #define PANEL_WIDTH_PX 270.0f /*!< Total width of the panel, including border */
24 #define BORDER_WIDTH_PX 1.0f
25 #define BORDER_RADIUS_PX 6.0f
26 #define PANEL_VSPACE_PX 1.0f /*!< Vertical padding from bounds to content area */
27 #define PANEL_HSPACE_PX 6.0f /*!< Horizontal padding from bounds to content area */
28 #define ICON_SIZE_PX 32.0f /*!< The width and height of the (square) icon */
29 #define ICON_SIZE_LARGE_PX 48.0f /*!< The width and height of the (square) icon */
30 #define ICON_HSPACE_PX 3.0f /*!< Horizontal space between icon and title/description */
31 #define TITLE_VSPACE_PX 5.0f /*!< Vertical space between title and description */
32 #define TITLE_FONT_SIZE_PTS 11.0f
33 #define DESCR_FONT_SIZE_PTS 11.0f
34 #define MAX_TEXT_ROWS 5 /*!< The maximum number of rows of text, used only if the limit preference is set. */
35 #define MIN_TEXT_HEIGHT (PANEL_VSPACE_PX + PANEL_VSPACE_PX + iconSize + 1)
36 #define TEXT_AREA_WIDTH (PANEL_WIDTH_PX - PANEL_HSPACE_PX - PANEL_HSPACE_PX - iconSize - ICON_HSPACE_PX)
38 static void GrowliCalShadeInterpolate( void *info, const float *inData, float *outData ) {
39 float *colors = (float *) info;
41 register float a = inData[0];
42 register float a_coeff = 1.0f - a;
44 // SIMD could come in handy here
45 // outData[0..3] = a_coeff * colors[4..7] + a * colors[0..3]
46 outData[0] = (a_coeff * colors[4]) + (a * colors[0]);
47 outData[1] = (a_coeff * colors[5]) + (a * colors[1]);
48 outData[2] = (a_coeff * colors[6]) + (a * colors[2]);
49 outData[3] = (a_coeff * colors[7]) + (a * colors[3]);
52 static void addTopRoundedRectToPath(CGContextRef context, CGRect rect, float radius) {
53 float minX = CGRectGetMinX(rect);
54 float minY = CGRectGetMinY(rect);
55 float maxX = CGRectGetMaxX(rect);
56 float maxY = CGRectGetMaxY(rect);
57 float midX = CGRectGetMidX(rect);
58 float midY = CGRectGetMidY(rect);
60 CGContextBeginPath(context);
61 CGContextMoveToPoint(context, maxX, midY);
62 CGContextAddArcToPoint(context, maxX, maxY, midX, maxY, 0); // Bottom Right
63 CGContextAddArcToPoint(context, minX, maxY, minX, midY, 0); // Bottom Left
64 CGContextAddArcToPoint(context, minX, minY, midX, minY, radius);// Top Left
65 CGContextAddArcToPoint(context, maxX, minY, maxX, midY, radius);// Top Right
66 CGContextClosePath(context);
71 @implementation GrowliCalWindowView
73 - (id) initWithFrame:(NSRect) frame {
74 if ((self = [super initWithFrame:frame])) {
75 titleFont = [[NSFont systemFontOfSize:TITLE_FONT_SIZE_PTS] retain];
76 textFont = [[NSFont systemFontOfSize:DESCR_FONT_SIZE_PTS] retain];
77 textLayoutManager = [[NSLayoutManager alloc] init];
78 titleLayoutManager = [[NSLayoutManager alloc] init];
79 lineHeight = [textLayoutManager defaultLineHeightForFont:textFont];
81 int size = GrowliCalSizePrefDefault;
82 READ_GROWL_PREF_INT(GrowliCalSizePref, GrowliCalPrefDomain, &size);
83 if (size == GrowliCalSizeLarge) {
84 iconSize = ICON_SIZE_LARGE_PX;
86 iconSize = ICON_SIZE_PX;
99 [borderColor release];
100 [textStorage release];
101 [titleStorage release];
102 [textLayoutManager release];
103 [titleLayoutManager release];
108 - (float) titleHeight {
109 return haveTitle ? titleHeight : 0.0f;
113 - (void) drawRect:(NSRect) rect {
115 //Make sure that we don't draw in the main thread
116 //if ([super dispatchDrawingToThread:rect]) {
117 NSRect b = [self bounds];
118 CGRect bounds = CGRectMake(b.origin.x, b.origin.y, b.size.width, b.size.height);
119 CGRect shape = CGRectInset(bounds, BORDER_WIDTH_PX*0.5f, BORDER_WIDTH_PX*0.5f);
121 CGContextRef context = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
123 // Create a path with enough room to strike the border and remain inside our frame.
124 // Since the path is in the middle of the line, this means we must inset it by half the border width.
125 addRoundedRectToPath(context, shape, BORDER_RADIUS_PX);
126 CGContextSetLineWidth(context, BORDER_WIDTH_PX);
128 CGContextSaveGState(context);
129 CGContextClip(context);
131 // Create a callback function to generate the
132 // fill clipped graphics context with our gradient
133 struct CGFunctionCallbacks callbacks = { 0U, GrowliCalShadeInterpolate, NULL };
136 [lightColor getRed:&colors[0]
141 [bgColor getRed:&colors[4]
146 CGFunctionRef function = CGFunctionCreate( (void *) colors,
152 CGColorSpaceRef cspace = CGColorSpaceCreateDeviceRGB();
155 src.x = CGRectGetMaxX(bounds);
156 src.y = CGRectGetMaxY(bounds);
157 dst.x = CGRectGetMinX(bounds);
159 CGShadingRef shading = CGShadingCreateAxial(cspace, dst, src,
160 function, false, false);
162 CGContextDrawShading(context, shading);
164 CGShadingRelease(shading);
165 CGFunctionRelease(function);
167 CGContextRestoreGState(context);
170 tbcolor[0] = [borderColor redComponent];
171 tbcolor[1] = [borderColor greenComponent];
172 tbcolor[2] = [borderColor blueComponent];
173 tbcolor[3] = [borderColor alphaComponent];
174 CGColorRef barcolor = CGColorCreate(cspace,tbcolor);
176 CGContextSetFillColorWithColor(context,barcolor);
179 CGRect titlebar = CGRectMake(0,CGRectGetMinY(shape),CGRectGetWidth(shape),15);
180 addTopRoundedRectToPath(context,titlebar,BORDER_RADIUS_PX);
181 CGContextFillPath(context);
182 CGColorSpaceRelease(cspace);
184 addRoundedRectToPath(context, shape, BORDER_RADIUS_PX);
185 CGContextSetLineWidth(context, BORDER_WIDTH_PX);
187 CGContextStrokePath(context);
190 drawRect.origin.x = CGRectGetMaxX(shape) - iconSize - ICON_HSPACE_PX;
191 drawRect.origin.y = PANEL_VSPACE_PX;
192 drawRect.size.width = iconSize;
193 drawRect.size.height = iconSize;
195 [icon setFlipped:YES];
196 [icon drawScaledInRect:drawRect
197 operation:NSCompositeSourceOver
200 drawRect.origin.x = PANEL_HSPACE_PX;
203 [titleLayoutManager drawGlyphsForGlyphRange:titleRange atPoint:drawRect.origin];
204 drawRect.origin.y += titleHeight + TITLE_VSPACE_PX;
208 [textLayoutManager drawGlyphsForGlyphRange:textRange atPoint:drawRect.origin];
210 [[self window] invalidateShadow];
211 [super drawRect:rect];
217 - (void) setPriority:(int)priority {
218 /* What is going on here? setPriority is the preference reader and completely ignores the priority? */
219 /* This method is completely ridiculous */
220 float backgroundAlpha = 95.0f;
221 READ_GROWL_PREF_FLOAT(GrowliCalOpacity, GrowliCalPrefDomain, &backgroundAlpha);
222 backgroundAlpha *= 0.01f;
225 textColor = [[NSColor whiteColor] retain];
228 [lightColor release];
229 [borderColor release];
231 GrowliCalColorType color = GrowliCalPurple;
232 READ_GROWL_PREF_INT(GrowliCalColor, GrowliCalPrefDomain, &color);
234 case GrowliCalPurple:
235 bgColor = [NSColor colorWithCalibratedRed:0.4000f green:0.1804f blue:0.7569f alpha:backgroundAlpha];
236 lightColor = [NSColor colorWithCalibratedRed:0.6863f green:0.5294f blue:0.9765f alpha:backgroundAlpha];
237 borderColor = [NSColor colorWithCalibratedRed:0.3216f green:0.0588f blue:0.6902f alpha:backgroundAlpha];
241 bgColor = [NSColor colorWithCalibratedRed:0.7804f green:0.1098f blue:0.7725f alpha:backgroundAlpha];
242 lightColor = [NSColor colorWithCalibratedRed:0.8157f green:0.2471f blue:0.8078f alpha:backgroundAlpha];
243 borderColor = [NSColor colorWithCalibratedRed:0.7412f green:0.0000f blue:0.7294f alpha:backgroundAlpha];
247 bgColor = [NSColor colorWithCalibratedRed:0.1490f green:0.7333f blue:0.0000f alpha:backgroundAlpha];
248 lightColor = [NSColor colorWithCalibratedRed:0.3765f green:0.8039f blue:0.2549f alpha:backgroundAlpha];
249 borderColor = [NSColor colorWithCalibratedRed:0.0000f green:0.6824f blue:0.0000f alpha:backgroundAlpha];
253 bgColor = [NSColor colorWithCalibratedRed:0.1255f green:0.3765f blue:0.9529f alpha:backgroundAlpha];
254 lightColor = [NSColor colorWithCalibratedRed:0.3529f green:0.5647f blue:1.0000f alpha:backgroundAlpha];
255 borderColor = [NSColor colorWithCalibratedRed:0.0588f green:0.2784f blue:0.9137f alpha:backgroundAlpha];
258 case GrowliCalOrange:
259 bgColor = [NSColor colorWithCalibratedRed:1.0000f green:0.4510f blue:0.0000f alpha:backgroundAlpha];
260 lightColor = [NSColor colorWithCalibratedRed:1.0000f green:0.6235f blue:0.0941f alpha:backgroundAlpha];
261 borderColor = [NSColor colorWithCalibratedRed:1.0000f green:0.4314f blue:0.0000f alpha:backgroundAlpha];
265 bgColor = [NSColor colorWithCalibratedRed:1.0000f green:0.0000f blue:0.0000f alpha:backgroundAlpha];
266 lightColor = [NSColor colorWithCalibratedRed:1.0000f green:0.2941f blue:0.3137f alpha:backgroundAlpha];
267 borderColor = [NSColor colorWithCalibratedRed:0.9529f green:0.0000f blue:0.0000f alpha:backgroundAlpha];
272 /* When could this ever happen?!? -eds */
273 if (priority == -2) {
274 bgColor = [NSColor colorWithCalibratedRed:0.4000f green:0.1804f blue:0.7569f alpha:backgroundAlpha];
275 lightColor = [NSColor colorWithCalibratedRed:0.6863f green:0.5294f blue:0.9765f alpha:backgroundAlpha];
276 borderColor = [NSColor colorWithCalibratedRed:0.3216f green:0.0588f blue:0.6902f alpha:backgroundAlpha];
277 } else if (priority == -1) {
278 bgColor = [NSColor colorWithCalibratedRed:0.1490f green:0.7333f blue:0.0000f alpha:backgroundAlpha];
279 lightColor = [NSColor colorWithCalibratedRed:0.3765f green:0.8039f blue:0.2549f alpha:backgroundAlpha];
280 borderColor = [NSColor colorWithCalibratedRed:0.0000f green:0.6824f blue:0.0000f alpha:backgroundAlpha];
281 } else if (priority == 1) {
282 bgColor = [NSColor colorWithCalibratedRed:1.0000f green:0.4510f blue:0.0000f alpha:backgroundAlpha];
283 lightColor = [NSColor colorWithCalibratedRed:1.0000f green:0.6235f blue:0.0941f alpha:backgroundAlpha];
284 borderColor = [NSColor colorWithCalibratedRed:1.0000f green:0.4314f blue:0.0000f alpha:backgroundAlpha];
285 } else if (priority == 2) {
286 bgColor = [NSColor colorWithCalibratedRed:1.0000f green:0.0000f blue:0.0000f alpha:backgroundAlpha];
287 lightColor = [NSColor colorWithCalibratedRed:1.0000f green:0.2941f blue:0.3137f alpha:backgroundAlpha];
288 borderColor = [NSColor colorWithCalibratedRed:0.9529f green:0.0000f blue:0.0000f alpha:backgroundAlpha];
290 bgColor = [NSColor colorWithCalibratedRed:0.1255f green:0.3765f blue:0.9529f alpha:backgroundAlpha];
291 lightColor = [NSColor colorWithCalibratedRed:0.3529f green:0.5647f blue:1.0000f alpha:backgroundAlpha];
292 borderColor = [NSColor colorWithCalibratedRed:0.0588f green:0.2784f blue:0.9137f alpha:backgroundAlpha];
299 [borderColor retain];
302 - (void) setIcon:(NSImage *) anIcon {
304 icon = [anIcon retain];
305 [self setNeedsDisplay:YES];
308 - (void) setTitle:(NSString *) aTitle {
309 haveTitle = [aTitle length] != 0;
312 [self setNeedsDisplay:YES];
317 NSSize containerSize;
318 containerSize.width = TEXT_AREA_WIDTH;
319 containerSize.height = FLT_MAX;
320 titleStorage = [[NSTextStorage alloc] init];
321 titleContainer = [[NSTextContainer alloc] initWithContainerSize:containerSize];
322 [titleLayoutManager addTextContainer:titleContainer]; // retains textContainer
323 [titleContainer release];
324 [titleStorage addLayoutManager:titleLayoutManager]; // retains layoutManager
325 [titleContainer setLineFragmentPadding:0.0f];
328 NSMutableParagraphStyle *paragraphStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy];
329 [paragraphStyle setLineBreakMode:NSLineBreakByTruncatingTail];
330 NSDictionary *defaultAttributes = [[NSDictionary alloc] initWithObjectsAndKeys:
331 titleFont, NSFontAttributeName,
332 textColor, NSForegroundColorAttributeName,
333 paragraphStyle, NSParagraphStyleAttributeName,
335 [paragraphStyle release];
337 [[titleStorage mutableString] setString:aTitle];
338 [titleStorage setAttributes:defaultAttributes range:NSMakeRange(0, [titleStorage length])];
340 [defaultAttributes release];
342 titleRange = [titleLayoutManager glyphRangeForTextContainer:titleContainer]; // force layout
343 titleHeight = [titleLayoutManager usedRectForTextContainer:titleContainer].size.height;
345 [self setNeedsDisplay:YES];
348 - (void) setText:(NSString *) aText {
349 haveText = [aText length] != 0;
352 [self setNeedsDisplay:YES];
357 NSSize containerSize;
358 BOOL limitPref = YES;
359 READ_GROWL_PREF_BOOL(GrowliCalLimitPref, GrowliCalPrefDomain, &limitPref);
360 containerSize.width = TEXT_AREA_WIDTH;
362 containerSize.height = lineHeight * MAX_TEXT_ROWS;
364 containerSize.height = FLT_MAX;
365 textStorage = [[NSTextStorage alloc] init];
366 textContainer = [[NSTextContainer alloc] initWithContainerSize:containerSize];
367 [textLayoutManager addTextContainer:textContainer]; // retains textContainer
368 [textContainer release];
369 [textStorage addLayoutManager:textLayoutManager]; // retains layoutManager
370 [textContainer setLineFragmentPadding:0.0f];
373 NSDictionary *defaultAttributes = [[NSDictionary alloc] initWithObjectsAndKeys:
374 textFont, NSFontAttributeName,
375 textColor, NSForegroundColorAttributeName,
378 [[textStorage mutableString] setString:aText];
379 [textStorage setAttributes:defaultAttributes range:NSMakeRange(0, [textStorage length])];
381 [defaultAttributes release];
383 textRange = [textLayoutManager glyphRangeForTextContainer:textContainer]; // force layout
384 textHeight = [textLayoutManager usedRectForTextContainer:textContainer].size.height;
386 [self setNeedsDisplay:YES];
390 float height = PANEL_VSPACE_PX + PANEL_VSPACE_PX + [self titleHeight] + [self descriptionHeight];
391 if (haveTitle && haveText)
392 height += TITLE_VSPACE_PX;
393 if (height < MIN_TEXT_HEIGHT)
394 height = MIN_TEXT_HEIGHT;
396 // resize the window so that it contains the tracking rect
397 NSWindow *window = [self window];
398 NSRect windowRect = [window frame];
399 windowRect.origin.y -= height - windowRect.size.height;
400 windowRect.size.height = height;
401 [window setFrame:windowRect display:NO];
404 [self removeTrackingRect:trackingRectTag];
405 trackingRectTag = [self addTrackingRect:[self frame] owner:self userData:NULL assumeInside:NO];
409 // Coordinates are based on top left corner
413 - (float) descriptionHeight {
414 return haveText ? textHeight : 0.0f;
417 - (int) descriptionRowCount {
418 int rowCount = textHeight / lineHeight;
419 BOOL limitPref = YES;
420 READ_GROWL_PREF_BOOL(GrowliCalLimitPref, GrowliCalPrefDomain, &limitPref);
422 return MIN(rowCount, MAX_TEXT_ROWS);