Bumping to version 1.0.1, which uses the latest ITKit. This resolves GrowlITTSW bug #4.
[GrowlITTSW.git] / GrowlITTSWController.m
1 //
2 //  GrowlITTSWController.m
3 //  Growl
4 //
5 //  Created by Joseph Spiros on 2/28/09.
6 //  Copyright 2009 iThink Software. All rights reserved.
7 //
8
9 #import "GrowlITTSWController.h"
10 #import "GrowlITTSWPrefs.h"
11
12 #import <ITKit/ITWindowEffect.h>
13 #import <ITKit/ITTSWBackgroundView.h>
14
15 #import "RegexKitLite.h"
16
17 @implementation NSImage (SmoothAdditions)
18
19 - (NSImage *)imageScaledSmoothlyToSize:(NSSize)scaledSize {
20     NSImage *newImage;
21     NSImageRep *rep = [self bestRepresentationForDevice:nil];
22     
23     newImage = [[NSImage alloc] initWithSize:scaledSize];
24     [newImage lockFocus];
25     {
26         [[NSGraphicsContext currentContext] setImageInterpolation:NSImageInterpolationHigh];
27         [[NSGraphicsContext currentContext] setShouldAntialias:YES];
28         [rep drawInRect:NSMakeRect(3, 3, scaledSize.width - 6, scaledSize.height - 6)];
29     }
30     [newImage unlockFocus];
31     return [newImage autorelease];
32 }
33
34 @end
35
36 @interface GrowlITTSWController (Private)
37 - (void)syncWithPrefs;
38 @end
39
40 @implementation GrowlITTSWController
41
42 - (id)init {
43     if ( ( self = [super init] ) ) {
44                 _window = [[GrowlITTSWWindow sharedWindow] retain];
45                 [_window setExitMode:ITTransientStatusWindowExitAfterDelay];
46                 [self syncWithPrefs];
47                 [[NSDistributedNotificationCenter defaultCenter] addObserver:self selector:@selector(syncWithPrefs) name:@"GrowlPreferencesChanged" object:nil];
48     }
49     
50     return self;
51 }
52
53 - (void)dealloc {
54     [_window release];
55     [super dealloc];
56 }
57
58 - (void)syncWithPrefs {
59         _imageSize = [GrowlITTSWPrefs imageSize];
60         _imageNoUpscale = [GrowlITTSWPrefs imageNoUpscale];
61         _wrapNotifications = [GrowlITTSWPrefs wrapNotifications];
62         _wrapColumns = [GrowlITTSWPrefs wrapColumns];
63         
64         NSScreen *screen = [GrowlITTSWPrefs screen];
65         ITHorizontalWindowPosition horizontalPosition = [GrowlITTSWPrefs horizontalPosition];
66         ITVerticalWindowPosition verticalPosition = [GrowlITTSWPrefs verticalPosition];
67         
68         Class appearanceEffect = [GrowlITTSWPrefs appearanceEffect];
69         float appearanceSpeed = [GrowlITTSWPrefs appearanceSpeed];
70         Class vanishEffect = [GrowlITTSWPrefs vanishEffect];
71         float vanishSpeed = [GrowlITTSWPrefs vanishSpeed];
72         float vanishDelay = [GrowlITTSWPrefs vanishDelay];
73         
74         ITTSWBackgroundMode backgroundStyle = [GrowlITTSWPrefs backgroundStyle];
75         NSColor *backgroundColor = [GrowlITTSWPrefs backgroundColor];
76         ITTransientStatusWindowSizing windowSize = [GrowlITTSWPrefs windowSize];
77         
78         if ([_window screen] != screen) {
79                 [_window setScreen:screen];
80         }
81         if ([_window horizontalPosition] != horizontalPosition) {
82                 [_window setHorizontalPosition:horizontalPosition];
83         }
84         if ([_window verticalPosition] != verticalPosition) {
85                 [_window setVerticalPosition:verticalPosition];
86         }
87         
88         if (![[_window entryEffect] isKindOfClass:appearanceEffect]) {
89                 [_window setEntryEffect:[[[appearanceEffect alloc] initWithWindow:_window] autorelease]];
90         }
91         if ([[_window entryEffect] effectTime] != appearanceSpeed) {
92                 [[_window entryEffect] setEffectTime:appearanceSpeed];
93         }
94         if (![[_window exitEffect] isKindOfClass:vanishEffect]) {
95                 [_window setExitEffect:[[[vanishEffect alloc] initWithWindow:_window] autorelease]];
96         }
97         if ([[_window exitEffect] effectTime] != vanishSpeed) {
98                 [[_window exitEffect]  setEffectTime:vanishSpeed];
99         }
100         if ([_window exitDelay] != vanishDelay) {
101                 [_window setExitDelay:vanishDelay];
102         }
103         
104         if ([(ITTSWBackgroundView *)[_window contentView] backgroundMode] != backgroundStyle) {
105                 [(ITTSWBackgroundView *)[_window contentView] setBackgroundMode:backgroundStyle];
106         }
107         if (([(ITTSWBackgroundView *)[_window contentView] backgroundMode] == ITTSWBackgroundColored) && ![[(ITTSWBackgroundView *)[_window contentView] backgroundColor] isEqual:backgroundColor]) {
108                 [(ITTSWBackgroundView *)[_window contentView] setBackgroundColor:backgroundColor];
109         }
110         if ([_window sizing] != windowSize) {
111                 [_window setSizing:windowSize];
112         }
113 }
114
115 - (void)showWindowWithTitle:(NSString *)title desc:(NSString *)desc image:(NSImage *)image {
116         NSString *text = title;
117         
118         if (desc && ![desc isEqualToString:@""] && ![desc isEqualToString:@"\n"]) {
119                 text = [text stringByAppendingFormat:@"\n%@", desc];
120         }
121         
122         NSSize newSize;
123         NSSize oldSize = [image size];
124         BOOL wouldUpscale = ((oldSize.width <= _imageSize) && (oldSize.height <= _imageSize));
125         
126         if (!(wouldUpscale && _imageNoUpscale)) {
127                 if (oldSize.width > oldSize.height) {
128                         newSize = NSMakeSize(_imageSize, (oldSize.height * (_imageSize / oldSize.width)));
129                 } else {
130                         newSize = NSMakeSize((oldSize.width * (_imageSize / oldSize.height)), _imageSize);
131                 }
132                 
133                 image = [[[[NSImage alloc] initWithData:[image TIFFRepresentation]] autorelease] imageScaledSmoothlyToSize:newSize];
134         }
135         
136         if (_wrapNotifications) {
137                 text = [text stringByReplacingOccurrencesOfRegex:[NSString stringWithFormat:@"(\\S\\S{%i,}|.{1,%i})(?:\\s+|$)", _wrapColumns, _wrapColumns] withString:@"$1\n"];
138         }
139         
140         //trim trailing whitespace
141         text = [text stringByReplacingOccurrencesOfRegex:@"\\s+$" withString:@""];
142         
143         NSArray *gothicChars = [NSArray arrayWithObjects:[NSString stringWithUTF8String:"☆"], [NSString stringWithUTF8String:"★"], nil];
144         NSMutableAttributedString *attributedText = [[NSMutableAttributedString alloc] initWithString:text];
145         
146         if (([gothicChars count] > 0) && ([text length] > 0)) {
147                 NSMutableString *gothicRegex = [[NSMutableString alloc] init];
148                 
149                 [gothicRegex appendString:@"[\\n"];
150                 for (NSString *gothicChar in gothicChars) {
151                         [gothicRegex appendString:gothicChar];
152                 }
153                 [gothicRegex appendString:@"]+"];
154                 
155                 NSUInteger endOfLastRange = 0;
156                 NSRange foundRange;
157                 while (endOfLastRange != NSNotFound) {
158                         foundRange = [text rangeOfRegex:gothicRegex inRange:NSMakeRange(endOfLastRange, ([text length] - endOfLastRange))];
159                         if (foundRange.location != NSNotFound) {
160                                 [attributedText setAttributes:[NSDictionary dictionaryWithObjectsAndKeys:[NSFont fontWithName:@"AppleGothic" size:(18.0 / MINI_DIVISOR)], NSFontAttributeName, nil, nil] range:foundRange];
161                                 endOfLastRange = foundRange.location+foundRange.length;
162                                 if (endOfLastRange >= [text length]) {
163                                         endOfLastRange = NSNotFound;
164                                 }
165                         } else {
166                                 endOfLastRange = NSNotFound;
167                         }
168                 }
169         }
170         
171         [_window setImage:image];
172     [_window buildTextWindowWithString:attributedText];
173     [_window appear:self];
174         [attributedText release];
175 }
176
177 @end