Plugins/Displays/Smoke/GrowlSmokeWindowView.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
boredzo@2402
     1
//
boredzo@2402
     2
//  GrowlSmokeWindowView.m
boredzo@2402
     3
//  Display Plugins
boredzo@2402
     4
//
boredzo@2402
     5
//  Created by Matthew Walton on 11/09/2004.
ingmarstein@3040
     6
//  Copyright 2004-2006 The Growl Project. All rights reserved.
boredzo@2402
     7
//
boredzo@2402
     8
boredzo@2402
     9
#import "GrowlSmokeWindowView.h"
boredzo@2402
    10
#import "GrowlSmokeDefines.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 GrowlSmokeTextAreaWidth (GrowlSmokeNotificationWidth - GrowlSmokePadding - iconSize - GrowlSmokeIconTextPadding - GrowlSmokePadding)
boredzo@2402
    18
#define GrowlSmokeMinTextHeight	(GrowlSmokePadding + iconSize + GrowlSmokePadding)
boredzo@2402
    19
boredzo@2402
    20
@interface ISProgressIndicator : NSProgressIndicator {
boredzo@2402
    21
}
boredzo@2402
    22
@end
boredzo@2402
    23
@implementation ISProgressIndicator
boredzo@2402
    24
- (void) startAnimation:(id)sender {
boredzo@2402
    25
#pragma unused(sender)
boredzo@2402
    26
}
boredzo@2402
    27
- (void) stopAnimation:(id)sender {
boredzo@2402
    28
#pragma unused(sender)
boredzo@2402
    29
}
boredzo@2402
    30
- (void) animate:(id)sender {
boredzo@2402
    31
#pragma unused(sender)
boredzo@2402
    32
}
boredzo@2402
    33
@end
boredzo@2402
    34
boredzo@2402
    35
@implementation GrowlSmokeWindowView
boredzo@2402
    36
boredzo@2402
    37
- (id) initWithFrame:(NSRect)frame {
boredzo@2402
    38
	if ((self = [super initWithFrame:frame])) {
boredzo@2402
    39
		textFont = [[NSFont systemFontOfSize:GrowlSmokeTextFontSize] retain];
boredzo@2402
    40
		textLayoutManager = [[NSLayoutManager alloc] init];
boredzo@2402
    41
		titleLayoutManager = [[NSLayoutManager alloc] init];
boredzo@2402
    42
		lineHeight = [textLayoutManager defaultLineHeightForFont:textFont];
boredzo@2402
    43
		textShadow = [[NSShadow alloc] init];
Rudy@4246
    44
		[textShadow setShadowOffset:NSMakeSize(0.0, -2.0)];
Rudy@4246
    45
		[textShadow setShadowBlurRadius:3.0];
boredzo@2402
    46
boredzo@2402
    47
		int size = GrowlSmokeSizePrefDefault;
boredzo@2402
    48
		READ_GROWL_PREF_INT(GrowlSmokeSizePref, GrowlSmokePrefDomain, &size);
boredzo@2402
    49
		if (size == GrowlSmokeSizeLarge)
boredzo@2402
    50
			iconSize = GrowlSmokeIconSizeLarge;
boredzo@2402
    51
		else
boredzo@2402
    52
			iconSize = GrowlSmokeIconSize;
boredzo@2402
    53
	}
rudy@3600
    54
	[self setCloseBoxOrigin:NSMakePoint(2,3)];
boredzo@2402
    55
	return self;
boredzo@2402
    56
}
boredzo@2402
    57
boredzo@2402
    58
- (void) setProgress:(NSNumber *)value {
boredzo@2402
    59
	if (value) {
boredzo@2402
    60
		if (!progressIndicator) {
boredzo@2402
    61
			progressIndicator = [[ISProgressIndicator alloc] initWithFrame:NSMakeRect(GrowlSmokePadding, GrowlSmokePadding + iconSize + GrowlSmokeIconProgressPadding, iconSize, NSProgressIndicatorPreferredSmallThickness)];
boredzo@2402
    62
			[progressIndicator setStyle:NSProgressIndicatorBarStyle];
boredzo@2402
    63
			[progressIndicator setControlSize:NSSmallControlSize];
boredzo@2402
    64
			[progressIndicator setBezeled:NO];
boredzo@2402
    65
			[progressIndicator setControlTint:NSDefaultControlTint];
boredzo@2402
    66
			[progressIndicator setIndeterminate:NO];
boredzo@2402
    67
			[self addSubview:progressIndicator];
boredzo@2402
    68
			[progressIndicator release];
boredzo@2402
    69
		}
boredzo@2402
    70
		[progressIndicator setDoubleValue:[value doubleValue]];
boredzo@2402
    71
		[self setNeedsDisplay:YES];
boredzo@2402
    72
	} else if (progressIndicator) {
boredzo@2402
    73
		[progressIndicator removeFromSuperview];
boredzo@2402
    74
		progressIndicator = nil;
boredzo@2402
    75
	}
boredzo@2402
    76
}
boredzo@2402
    77
boredzo@2402
    78
- (void) dealloc {
boredzo@2402
    79
	[textFont           release];
boredzo@2402
    80
	[icon               release];
boredzo@2402
    81
	[bgColor            release];
boredzo@2402
    82
	[textColor          release];
boredzo@2402
    83
	[textShadow         release];
boredzo@2402
    84
	[textStorage        release];
boredzo@2402
    85
	[textLayoutManager  release];
boredzo@2402
    86
	[titleStorage       release];
boredzo@2402
    87
	[titleLayoutManager release];
boredzo@2402
    88
boredzo@2402
    89
	[super dealloc];
boredzo@2402
    90
}
boredzo@2402
    91
boredzo@2402
    92
- (BOOL) isFlipped {
boredzo@2402
    93
	// Coordinates are based on top left corner
boredzo@2402
    94
    return YES;
boredzo@2402
    95
}
boredzo@2402
    96
boredzo@2402
    97
- (void) drawRect:(NSRect)rect {
ingmarstein@2941
    98
#pragma unused(rect)
ofri@2708
    99
	//Make sure that we don't draw in the main thread
eridius@2774
   100
	//if ([super dispatchDrawingToThread:rect]) {
ofri@2708
   101
		NSRect b = [self bounds];
ofri@2708
   102
		CGRect bounds = CGRectMake(b.origin.x, b.origin.y, b.size.width, b.size.height);
ingmarstein@2721
   103
ofri@2708
   104
		CGContextRef context = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
ingmarstein@2721
   105
ofri@2708
   106
		// calculate bounds based on icon-float pref on or off
ofri@2708
   107
		CGRect shadedBounds;
ofri@2708
   108
		BOOL floatIcon = GrowlSmokeFloatIconPrefDefault;
ofri@2708
   109
		READ_GROWL_PREF_BOOL(GrowlSmokeFloatIconPref, GrowlSmokePrefDomain, &floatIcon);
ofri@2708
   110
		if (floatIcon) {
Rudy@4246
   111
			CGFloat sizeReduction = GrowlSmokePadding + iconSize + (GrowlSmokeIconTextPadding * 0.5);
Rudy@4246
   112
Rudy@4246
   113
			shadedBounds = CGRectMake(bounds.origin.x + sizeReduction + 1.0,
Rudy@4246
   114
									  bounds.origin.y + 1.0,
Rudy@4246
   115
									  bounds.size.width - sizeReduction - 2.0,
Rudy@4246
   116
									  bounds.size.height - 2.0);
ofri@2708
   117
		} else {
Rudy@4246
   118
			shadedBounds = CGRectInset(bounds, 1.0, 1.0);
ofri@2708
   119
		}
ingmarstein@2721
   120
ofri@2708
   121
		// set up bezier path for rounded corners
ofri@2708
   122
		addRoundedRectToPath(context, shadedBounds, GrowlSmokeBorderRadius);
Rudy@4246
   123
		CGContextSetLineWidth(context, 2.0);
ingmarstein@2721
   124
ofri@2708
   125
		// draw background
ofri@2708
   126
		CGPathDrawingMode drawingMode;
ofri@2708
   127
		if (mouseOver) {
ofri@2708
   128
			drawingMode = kCGPathFillStroke;
ofri@2708
   129
			[bgColor setFill];
ofri@2708
   130
			[textColor setStroke];
ofri@2708
   131
		} else {
ofri@2708
   132
			drawingMode = kCGPathFill;
ofri@2708
   133
			[bgColor set];
ofri@2708
   134
		}
ofri@2708
   135
		CGContextDrawPath(context, drawingMode);
ingmarstein@2721
   136
ofri@2708
   137
		// draw the title and the text
ofri@2708
   138
		NSRect drawRect;
ofri@2708
   139
		drawRect.origin.x = GrowlSmokePadding;
ofri@2708
   140
		drawRect.origin.y = GrowlSmokePadding;
ofri@2708
   141
		drawRect.size.width = iconSize;
ofri@2708
   142
		drawRect.size.height = iconSize;
ingmarstein@2721
   143
ofri@2708
   144
		[icon setFlipped:YES];
ofri@2708
   145
		[icon drawScaledInRect:drawRect
ofri@2708
   146
					 operation:NSCompositeSourceOver
Rudy@4246
   147
					  fraction:1.0];
ingmarstein@2721
   148
ofri@2708
   149
		drawRect.origin.x += iconSize + GrowlSmokeIconTextPadding;
ingmarstein@2721
   150
ofri@2708
   151
		if (haveTitle) {
ofri@2708
   152
			[titleLayoutManager drawGlyphsForGlyphRange:titleRange atPoint:drawRect.origin];
ofri@2708
   153
			drawRect.origin.y += titleHeight + GrowlSmokeTitleTextPadding;
ofri@2708
   154
		}
ingmarstein@2721
   155
ofri@2708
   156
		if (haveText)
ofri@2708
   157
			[textLayoutManager drawGlyphsForGlyphRange:textRange atPoint:drawRect.origin];
ingmarstein@2721
   158
ofri@2708
   159
		[[self window] invalidateShadow];
bgannin@3983
   160
		[super drawRect:rect];
eridius@2774
   161
	//}
boredzo@2402
   162
}
boredzo@2402
   163
boredzo@2402
   164
- (void) setIcon:(NSImage *)anIcon {
boredzo@2402
   165
	[icon release];
boredzo@2402
   166
	icon = [anIcon retain];
boredzo@2402
   167
	[self setNeedsDisplay:YES];
boredzo@2402
   168
}
boredzo@2402
   169
bgannin@3404
   170
- (void) setTitle:(NSString *)aTitle {
boredzo@2402
   171
	haveTitle = [aTitle length] != 0;
boredzo@2402
   172
boredzo@2402
   173
	if (!haveTitle) {
boredzo@2402
   174
		[self setNeedsDisplay:YES];
boredzo@2402
   175
		return;
boredzo@2402
   176
	}
boredzo@2402
   177
boredzo@2402
   178
	if (!titleStorage) {
boredzo@2402
   179
		NSSize containerSize;
boredzo@2402
   180
		containerSize.width = GrowlSmokeTextAreaWidth;
boredzo@2402
   181
		containerSize.height = FLT_MAX;
boredzo@2402
   182
		titleStorage = [[NSTextStorage alloc] init];
boredzo@2402
   183
		titleContainer = [[NSTextContainer alloc] initWithContainerSize:containerSize];
boredzo@2402
   184
		[titleLayoutManager addTextContainer:titleContainer];	// retains textContainer
boredzo@2402
   185
		[titleContainer release];
boredzo@2402
   186
		[titleStorage addLayoutManager:titleLayoutManager];	// retains layoutManager
Rudy@4246
   187
		[titleContainer setLineFragmentPadding:0.0];
boredzo@2402
   188
	}
boredzo@2402
   189
boredzo@2402
   190
	// construct attributes for the title
boredzo@2402
   191
	NSMutableParagraphStyle *paragraphStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy];
boredzo@2402
   192
	[paragraphStyle setLineBreakMode:NSLineBreakByTruncatingTail];
boredzo@2402
   193
	NSFont *titleFont = [NSFont boldSystemFontOfSize:GrowlSmokeTitleFontSize];
boredzo@2402
   194
	NSDictionary *defaultAttributes = [[NSDictionary alloc] initWithObjectsAndKeys:
boredzo@2402
   195
		titleFont,		NSFontAttributeName,
boredzo@2402
   196
		textColor,		NSForegroundColorAttributeName,
boredzo@2402
   197
		textShadow,     NSShadowAttributeName,
boredzo@2402
   198
		paragraphStyle, NSParagraphStyleAttributeName,
boredzo@2402
   199
		nil];
boredzo@2402
   200
	[paragraphStyle release];
boredzo@2402
   201
bgannin@3404
   202
	[[titleStorage mutableString] setString:aTitle];
bgannin@3404
   203
	[titleStorage setAttributes:defaultAttributes range:NSMakeRange(0U, [aTitle length])];
boredzo@2402
   204
boredzo@2402
   205
	[defaultAttributes release];
boredzo@2402
   206
boredzo@2402
   207
	titleRange = [titleLayoutManager glyphRangeForTextContainer:titleContainer];	// force layout
boredzo@2402
   208
	titleHeight = [titleLayoutManager usedRectForTextContainer:titleContainer].size.height;
boredzo@2402
   209
boredzo@2402
   210
	[self setNeedsDisplay:YES];
boredzo@2402
   211
}
boredzo@2402
   212
bgannin@3404
   213
- (void) setText:(NSString *)aText {
boredzo@2402
   214
	haveText = [aText length] != 0;
boredzo@2402
   215
boredzo@2402
   216
	if (!haveText) {
boredzo@2402
   217
		[self setNeedsDisplay:YES];
boredzo@2402
   218
		return;
boredzo@2402
   219
	}
boredzo@2402
   220
boredzo@2402
   221
	if (!textStorage) {
boredzo@2402
   222
		NSSize containerSize;
boredzo@2402
   223
		BOOL limitPref = GrowlSmokeLimitPrefDefault;
boredzo@2402
   224
		READ_GROWL_PREF_BOOL(GrowlSmokeLimitPref, GrowlSmokePrefDomain, &limitPref);
boredzo@2402
   225
		containerSize.width = GrowlSmokeTextAreaWidth;
boredzo@2402
   226
		if (limitPref)
boredzo@2402
   227
			containerSize.height = lineHeight * GrowlSmokeMaxLines;
boredzo@2402
   228
		else
boredzo@2402
   229
			containerSize.height = FLT_MAX;
boredzo@2402
   230
		textStorage = [[NSTextStorage alloc] init];
boredzo@2402
   231
		textContainer = [[NSTextContainer alloc] initWithContainerSize:containerSize];
boredzo@2402
   232
		[textLayoutManager addTextContainer:textContainer];	// retains textContainer
boredzo@2402
   233
		[textContainer release];
boredzo@2402
   234
		[textStorage addLayoutManager:textLayoutManager];	// retains layoutManager
Rudy@4246
   235
		[textContainer setLineFragmentPadding:0.0];
boredzo@2402
   236
	}
boredzo@2402
   237
boredzo@2402
   238
	// construct default attributes for the description text
boredzo@2402
   239
	NSDictionary *defaultAttributes = [[NSDictionary alloc] initWithObjectsAndKeys:
boredzo@2402
   240
		textFont,	NSFontAttributeName,
boredzo@2402
   241
		textColor,  NSForegroundColorAttributeName,
boredzo@2402
   242
		textShadow, NSShadowAttributeName,
boredzo@2402
   243
		nil];
boredzo@2402
   244
bgannin@3404
   245
	[[textStorage mutableString] setString:aText];
bgannin@3404
   246
	[textStorage setAttributes:defaultAttributes range:NSMakeRange(0U, [aText length])];
boredzo@2402
   247
boredzo@2402
   248
	[defaultAttributes release];
boredzo@2402
   249
boredzo@2402
   250
	textRange = [textLayoutManager glyphRangeForTextContainer:textContainer];	// force layout
boredzo@2402
   251
	textHeight = [textLayoutManager usedRectForTextContainer:textContainer].size.height;
boredzo@2402
   252
boredzo@2402
   253
	[self setNeedsDisplay:YES];
boredzo@2402
   254
}
boredzo@2402
   255
boredzo@2402
   256
- (void) setPriority:(int)priority {
boredzo@2402
   257
	NSString *key;
boredzo@2402
   258
	NSString *textKey;
boredzo@2402
   259
	switch (priority) {
boredzo@2402
   260
		case -2:
boredzo@2402
   261
			key = GrowlSmokeVeryLowColor;
boredzo@2402
   262
			textKey = GrowlSmokeVeryLowTextColor;
boredzo@2402
   263
			break;
boredzo@2402
   264
		case -1:
boredzo@2402
   265
			key = GrowlSmokeModerateColor;
boredzo@2402
   266
			textKey = GrowlSmokeModerateTextColor;
boredzo@2402
   267
			break;
boredzo@2402
   268
		case 1:
boredzo@2402
   269
			key = GrowlSmokeHighColor;
boredzo@2402
   270
			textKey = GrowlSmokeHighTextColor;
boredzo@2402
   271
			break;
boredzo@2402
   272
		case 2:
boredzo@2402
   273
			key = GrowlSmokeEmergencyColor;
boredzo@2402
   274
			textKey = GrowlSmokeEmergencyTextColor;
boredzo@2402
   275
			break;
boredzo@2402
   276
		case 0:
boredzo@2402
   277
		default:
boredzo@2402
   278
			key = GrowlSmokeNormalColor;
boredzo@2402
   279
			textKey = GrowlSmokeNormalTextColor;
boredzo@2402
   280
			break;
boredzo@2402
   281
	}
boredzo@2402
   282
Rudy@4246
   283
	CGFloat backgroundAlpha = GrowlSmokeAlphaPrefDefault;
boredzo@2402
   284
	READ_GROWL_PREF_FLOAT(GrowlSmokeAlphaPref, GrowlSmokePrefDomain, &backgroundAlpha);
Rudy@4246
   285
	backgroundAlpha *= 0.01;
boredzo@2402
   286
boredzo@2402
   287
	[bgColor release];
boredzo@2402
   288
boredzo@2402
   289
	Class NSDataClass = [NSData class];
boredzo@2402
   290
	NSData *data = nil;
boredzo@2402
   291
boredzo@2402
   292
	READ_GROWL_PREF_VALUE(key, GrowlSmokePrefDomain, NSData *, &data);
Rudy@4259
   293
	if(data)
Rudy@4259
   294
		CFMakeCollectable(data);		
boredzo@2402
   295
	if (data && [data isKindOfClass:NSDataClass]) {
Rudy@4259
   296
			bgColor = [NSUnarchiver unarchiveObjectWithData:data];
Rudy@4259
   297
			bgColor = [bgColor colorWithAlphaComponent:backgroundAlpha];
boredzo@2402
   298
	} else {
Rudy@4246
   299
		bgColor = [NSColor colorWithCalibratedWhite:0.1 alpha:backgroundAlpha];
boredzo@2402
   300
	}
boredzo@2402
   301
	[bgColor retain];
boredzo@2402
   302
	[data release];
boredzo@2402
   303
	data = nil;
boredzo@2402
   304
boredzo@2402
   305
	[textColor release];
boredzo@2402
   306
	READ_GROWL_PREF_VALUE(textKey, GrowlSmokePrefDomain, NSData *, &data);
Rudy@4259
   307
	if(data)
Rudy@4259
   308
		CFMakeCollectable(data);		
Rudy@4259
   309
	if (data && [data isKindOfClass:NSDataClass]) {
Rudy@4259
   310
			textColor = [NSUnarchiver unarchiveObjectWithData:data];
Rudy@4259
   311
	} else {
boredzo@2402
   312
		textColor = [NSColor whiteColor];
Rudy@4259
   313
	}
boredzo@2402
   314
	[textColor retain];
boredzo@2402
   315
	[data release];
Rudy@4259
   316
	data = nil;
Rudy@4259
   317
	
Rudy@4246
   318
	[textShadow setShadowColor:[bgColor blendedColorWithFraction:0.5 ofColor:[NSColor blackColor]]];
boredzo@2402
   319
}
boredzo@2402
   320
boredzo@2402
   321
- (void) sizeToFit {
Rudy@4246
   322
	CGFloat height = GrowlSmokePadding + GrowlSmokePadding + [self titleHeight] + [self descriptionHeight];
boredzo@2402
   323
	if (haveTitle && haveText)
ingmarstein@2580
   324
		height += GrowlSmokeTitleTextPadding;
boredzo@2402
   325
	if (progressIndicator)
ingmarstein@2580
   326
		height += GrowlSmokeIconProgressPadding + [progressIndicator bounds].size.height;
ingmarstein@2580
   327
	if (height < GrowlSmokeMinTextHeight)
ingmarstein@2580
   328
		height = GrowlSmokeMinTextHeight;
boredzo@2402
   329
ingmarstein@3021
   330
	NSRect rect = [self frame];
ingmarstein@3021
   331
	rect.size.height = height;
ingmarstein@3021
   332
	[self setFrame:rect];
ingmarstein@3021
   333
boredzo@2402
   334
	// resize the window so that it contains the tracking rect
ingmarstein@2580
   335
	NSWindow *window = [self window];
ingmarstein@2580
   336
	NSRect windowRect = [window frame];
ingmarstein@2580
   337
	windowRect.origin.y -= height - windowRect.size.height;
ingmarstein@2580
   338
	windowRect.size.height = height;
evands@3582
   339
	[window setFrame:windowRect display:YES animate:YES];
boredzo@2402
   340
boredzo@2402
   341
	if (trackingRectTag)
boredzo@2402
   342
		[self removeTrackingRect:trackingRectTag];
ingmarstein@3021
   343
	trackingRectTag = [self addTrackingRect:rect owner:self userData:NULL assumeInside:NO];
boredzo@2402
   344
}
boredzo@2402
   345
Rudy@4246
   346
- (CGFloat) titleHeight {
Rudy@4246
   347
	return haveTitle ? titleHeight : 0.0;
Rudy@4246
   348
}
Rudy@4246
   349
Rudy@4246
   350
- (CGFloat) descriptionHeight {
Rudy@4246
   351
	return haveText ? textHeight : 0.0;
Rudy@4246
   352
}
Rudy@4246
   353
Rudy@4246
   354
- (NSInteger) descriptionRowCount {
Rudy@4246
   355
	NSInteger rowCount = textHeight / lineHeight;
boredzo@2402
   356
	BOOL limitPref = GrowlSmokeLimitPrefDefault;
boredzo@2402
   357
	READ_GROWL_PREF_BOOL(GrowlSmokeLimitPref, GrowlSmokePrefDomain, &limitPref);
boredzo@2402
   358
	if (limitPref)
boredzo@2402
   359
		return MIN(rowCount, GrowlSmokeMaxLines);
boredzo@2402
   360
	else
boredzo@2402
   361
		return rowCount;
boredzo@2402
   362
}
boredzo@2402
   363
boredzo@2402
   364
#pragma mark -
boredzo@2402
   365
boredzo@2402
   366
- (id) target {
boredzo@2402
   367
	return target;
boredzo@2402
   368
}
boredzo@2402
   369
boredzo@2402
   370
- (void) setTarget:(id) object {
boredzo@2402
   371
	target = object;
boredzo@2402
   372
}
boredzo@2402
   373
boredzo@2402
   374
#pragma mark -
boredzo@2402
   375
boredzo@2402
   376
- (SEL) action {
boredzo@2402
   377
	return action;
boredzo@2402
   378
}
boredzo@2402
   379
boredzo@2402
   380
- (void) setAction:(SEL) selector {
boredzo@2402
   381
	action = selector;
boredzo@2402
   382
}
boredzo@2402
   383
boredzo@2402
   384
@end