Plugins/Displays/Brushed/GrowlBrushedWindowView.m
author Rudy Richter
Sat Aug 01 20:43:39 2009 -0400 (2009-08-01)
changeset 4259 0e9b6b0b1e25
parent 4246 4f52d1d98978
child 4541 9a290d3de636
permissions -rw-r--r--
Plugins: clang warnings
     1 //
     2 //  GrowlBrushedWindowView.m
     3 //  Display Plugins
     4 //
     5 //  Created by Ingmar Stein on 12/01/2004.
     6 //  Copyright 2004-2006 The Growl Project. All rights reserved.
     7 //
     8 
     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>
    16 
    17 #define GrowlBrushedTextAreaWidth	(GrowlBrushedNotificationWidth - GrowlBrushedPadding - iconSize - GrowlBrushedIconTextPadding - GrowlBrushedPadding)
    18 #define GrowlBrushedMinTextHeight	(GrowlBrushedPadding + iconSize + GrowlBrushedPadding)
    19 
    20 @implementation GrowlBrushedWindowView
    21 
    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]]];
    33 
    34 		int size = GrowlBrushedSizePrefDefault;
    35 		READ_GROWL_PREF_INT(GrowlBrushedSizePref, GrowlBrushedPrefDomain, &size);
    36 		if (size == GrowlBrushedSizeLarge) {
    37 			iconSize = GrowlBrushedIconSizeLarge;
    38 		} else {
    39 			iconSize = GrowlBrushedIconSize;
    40 		}
    41 	}
    42 
    43 	return self;
    44 }
    45 
    46 - (void) dealloc {
    47 	[textFont           release];
    48 	[icon               release];
    49 	[textColor          release];
    50 	[textShadow         release];
    51 	[textStorage        release];
    52 	[textLayoutManager  release];
    53 	[titleStorage       release];
    54 	[titleLayoutManager release];
    55 
    56 	[super dealloc];
    57 }
    58 
    59 - (BOOL)isFlipped {
    60 	// Coordinates are based on top left corner
    61     return YES;
    62 }
    63 
    64 - (void) drawRect:(NSRect)rect {
    65 #pragma unused(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);
    70 
    71 		CGContextRef context = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
    72 
    73 		// clear the window
    74 		CGContextClearRect(context, bounds);
    75 
    76 		// calculate bounds based on icon-float pref on or off
    77 		CGRect shadedBounds;
    78 		BOOL floatIcon = GrowlBrushedFloatIconPrefDefault;
    79 		READ_GROWL_PREF_BOOL(GrowlBrushedFloatIconPref, GrowlBrushedPrefDomain, &floatIcon);
    80 		if (floatIcon) {
    81 			CGFloat sizeReduction = GrowlBrushedPadding + iconSize + (GrowlBrushedIconTextPadding * 0.5);
    82 
    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);
    87 		} else {
    88 			shadedBounds = CGRectInset(bounds, 1.0, 1.0);
    89 		}
    90 
    91 		// set up path for rounded corners
    92 		addRoundedRectToPath(context, shadedBounds, GrowlBrushedBorderRadius);
    93 		CGContextSetLineWidth(context, 2.0);
    94 
    95 		// draw background
    96 		NSWindow *window = [self window];
    97 		NSColor *bgColor = [window backgroundColor];
    98 		CGPathDrawingMode drawingMode;
    99 		if (mouseOver) {
   100 			drawingMode = kCGPathFillStroke;
   101 			[bgColor setFill];
   102 			[[NSColor keyboardFocusIndicatorColor] setStroke];
   103 		} else {
   104 			drawingMode = kCGPathFill;
   105 			[bgColor set];
   106 		}
   107 		CGContextDrawPath(context, drawingMode);
   108 
   109 		// draw the title and the text
   110 		NSRect drawRect;
   111 		drawRect.origin.x = GrowlBrushedPadding;
   112 		drawRect.origin.y = GrowlBrushedPadding;
   113 		drawRect.size.width = iconSize;
   114 		drawRect.size.height = iconSize;
   115 
   116 		[icon setFlipped:YES];
   117 		[icon drawScaledInRect:drawRect
   118 					 operation:NSCompositeSourceOver
   119 					  fraction:1.0];
   120 
   121 		drawRect.origin.x += iconSize + GrowlBrushedIconTextPadding;
   122 
   123 		if (haveTitle) {
   124 			[titleLayoutManager drawGlyphsForGlyphRange:titleRange atPoint:drawRect.origin];
   125 			drawRect.origin.y += titleHeight + GrowlBrushedTitleTextPadding;
   126 		}
   127 
   128 		if (haveText)
   129 			[textLayoutManager drawGlyphsForGlyphRange:textRange atPoint:drawRect.origin];
   130 
   131 		[window invalidateShadow];
   132 		[super drawRect:rect];
   133 	//}
   134 }
   135 
   136 - (void) setIcon:(NSImage *)anIcon {
   137 	[icon release];
   138 	icon = [anIcon retain];
   139 	[self setNeedsDisplay:YES];
   140 }
   141 
   142 - (void) setTitle:(NSString *)aTitle {
   143 	haveTitle = [aTitle length] != 0;
   144 
   145 	if (!haveTitle) {
   146 		[self setNeedsDisplay:YES];
   147 		return;
   148 	}
   149 
   150 	if (!titleStorage) {
   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];
   160 	}
   161 
   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,
   171 		nil];
   172 	[paragraphStyle release];
   173 
   174 	[[titleStorage mutableString] setString:aTitle];
   175 	[titleStorage setAttributes:defaultAttributes range:NSMakeRange(0, [titleStorage length])];
   176 
   177 	[defaultAttributes release];
   178 
   179 	titleRange = [titleLayoutManager glyphRangeForTextContainer:titleContainer];	// force layout
   180 	titleHeight = [titleLayoutManager usedRectForTextContainer:titleContainer].size.height;
   181 
   182 	[self setNeedsDisplay:YES];
   183 }
   184 
   185 - (void) setText:(NSString *)aText {
   186 	haveText = [aText length] != 0;
   187 
   188 	if (!haveText) {
   189 		[self setNeedsDisplay:YES];
   190 		return;
   191 	}
   192 
   193 	if (!textStorage) {
   194 		NSSize containerSize;
   195 		BOOL limitPref = GrowlBrushedLimitPrefDefault;
   196 		READ_GROWL_PREF_BOOL(GrowlBrushedLimitPref, GrowlBrushedPrefDomain, &limitPref);
   197 		containerSize.width = GrowlBrushedTextAreaWidth;
   198 		if (limitPref)
   199 			containerSize.height = lineHeight * GrowlBrushedMaxLines;
   200 		else
   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];
   208 	}
   209 
   210 	// construct attributes for the description text
   211 	NSDictionary *defaultAttributes = [[NSDictionary alloc] initWithObjectsAndKeys:
   212 		textFont,   NSFontAttributeName,
   213 		textColor,  NSForegroundColorAttributeName,
   214 		textShadow, NSShadowAttributeName,
   215 		nil];
   216 
   217 	[[textStorage mutableString] setString:aText];
   218 	[textStorage setAttributes:defaultAttributes range:NSMakeRange(0, [textStorage length])];
   219 
   220 	[defaultAttributes release];
   221 
   222 	textRange = [textLayoutManager glyphRangeForTextContainer:textContainer];	// force layout
   223 	textHeight = [textLayoutManager usedRectForTextContainer:textContainer].size.height;
   224 
   225 	[self setNeedsDisplay:YES];
   226 }
   227 
   228 - (void) setPriority:(int)priority {
   229 	NSString *textKey;
   230 	switch (priority) {
   231 		case -2:
   232 			textKey = GrowlBrushedVeryLowTextColor;
   233 			break;
   234 		case -1:
   235 			textKey = GrowlBrushedModerateTextColor;
   236 			break;
   237 		case 1:
   238 			textKey = GrowlBrushedHighTextColor;
   239 			break;
   240 		case 2:
   241 			textKey = GrowlBrushedEmergencyTextColor;
   242 			break;
   243 		case 0:
   244 		default:
   245 			textKey = GrowlBrushedNormalTextColor;
   246 			break;
   247 	}
   248 	NSData *data = nil;
   249 
   250 	[textColor release];
   251 	READ_GROWL_PREF_VALUE(textKey, GrowlBrushedPrefDomain, NSData *, &data);
   252 	if(data)
   253 		CFMakeCollectable(data);		
   254 	if (data && [data isKindOfClass:[NSData class]]) {
   255 			textColor = [NSUnarchiver unarchiveObjectWithData:data];
   256 	} else {
   257 		textColor = [NSColor colorWithCalibratedWhite:0.1f alpha:1.0f];
   258 	}
   259 	[textColor retain];
   260 	[data release];
   261 	data = nil;
   262 }
   263 
   264 - (void) sizeToFit {
   265 	CGFloat height = GrowlBrushedPadding + GrowlBrushedPadding + [self titleHeight] + [self descriptionHeight];
   266 	if (haveTitle && haveText)
   267 		height += GrowlBrushedTitleTextPadding;
   268 	if (height < GrowlBrushedMinTextHeight)
   269 		height = GrowlBrushedMinTextHeight;
   270 
   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];
   277 
   278 	if (trackingRectTag)
   279 		[self removeTrackingRect:trackingRectTag];
   280 	trackingRectTag = [self addTrackingRect:[self frame] owner:self userData:NULL assumeInside:NO];
   281 }
   282 
   283 - (CGFloat) titleHeight {
   284 	return haveTitle ? titleHeight : 0.0;
   285 }
   286 
   287 - (CGFloat) descriptionHeight {
   288 	return haveText ? textHeight : 0.0;
   289 }
   290 
   291 - (NSInteger) descriptionRowCount {
   292 	NSInteger rowCount = textHeight / lineHeight;
   293 	BOOL limitPref = GrowlBrushedLimitPrefDefault;
   294 	READ_GROWL_PREF_BOOL(GrowlBrushedLimitPref, GrowlBrushedPrefDomain, &limitPref);
   295 	if (limitPref)
   296 		return MIN(rowCount, GrowlBrushedMaxLines);
   297 	else
   298 		return rowCount;
   299 }
   300 
   301 @end