26 - (NSString *)cssString
28 NSColor *srgbColor = [
self colorUsingColorSpace:NSColorSpace.sRGBColorSpace];
29 return [NSString stringWithFormat:@"rgba(%g,%g,%g,%g)",
30 255 * srgbColor.redComponent,
31 255 * srgbColor.greenComponent,
32 255 * srgbColor.blueComponent,
33 srgbColor.alphaComponent];
41 @property (retain, nonatomic) NSString *mostRecentText;
48 if (
self = [super init])
50 self.linkTextAttributes = @{ NSForegroundColorAttributeName:NSColor.secondaryLabelColor };
56 - (NSSize)intrinsicContentSize
58 [
self.layoutManager ensureLayoutForTextContainer:self.textContainer];
59 return [
self.layoutManager usedRectForTextContainer:self.textContainer].size;
64 [
super didChangeText];
65 [
self invalidateIntrinsicContentSize];
68 - (void)viewDidChangeEffectiveAppearance
70 [
self setText:self.mostRecentText];
73 - (void)setText:(NSString *)text
76 NSMutableString *s = [NSMutableString new];
77 [s appendString:@"<meta http-equiv='Content-Type' content='text/html; charset=utf-8'>"];
79 NSString *headingCSS = NSColor.labelColor.cssString;
80 NSString *subheadingCSS = NSColor.tertiaryLabelColor.cssString;
81 NSString *bodyCSS = NSColor.secondaryLabelColor.cssString;
83 [s appendFormat:@"<style> \
84 body { font: 10pt 'Helvetica Neue'; color: %@; } \
85 p { padding-top: 30px; } \
86 h1 { padding-top: 30px; } \
87 .name { font-size: 28pt; font-weight: 300; } \
88 .version { color: %@; font-size: 22pt; font-weight: 200; } \
89 .copyright { color: %@; } \
90 .description { font-weight: bold; } \
91 .licenses { color: %@; font-size: 8pt; } \
92 pre { font: 'Monaco'; } \
98 [s appendString:text];
99 NSAttributedString *as = [[NSAttributedString alloc] initWithHTML:[s dataUsingEncoding:NSUTF8StringEncoding] documentAttributes:nil];
101 self.textStorage.mutableString.string =
@"";
102 [
self.textStorage appendAttributedString:as];
116 - (void)cancelOperation:(
id)sender
133 if (
self = [super initWithContentRect:NSMakeRect(0, 0, 820, 500)
134 styleMask:NSWindowStyleMaskTitled|NSWindowStyleMaskClosable|NSWindowStyleMaskFullSizeContentView
135 backing:NSBackingStoreBuffered
138 self.releasedWhenClosed = NO;
139 self.titlebarAppearsTransparent = YES;
142 NSBox *leftColumn = [NSBox new];
143 leftColumn.translatesAutoresizingMaskIntoConstraints = NO;
144 leftColumn.boxType = NSBoxCustom;
145 leftColumn.borderWidth = 0;
146 leftColumn.fillColor = NSColor.controlBackgroundColor;
150 NSImageView *icon = [NSImageView new];
151 icon.translatesAutoresizingMaskIntoConstraints = NO;
152 icon.image = NSApplication.sharedApplication.applicationIconImage;
153 [leftColumn addSubview:icon];
156 poweredByVuo.translatesAutoresizingMaskIntoConstraints = NO;
157 poweredByVuo.borderVisible = NO;
158 [leftColumn addSubview:poweredByVuo];
160 NSDictionary *views = NSDictionaryOfVariableBindings(icon, poweredByVuo);
161 [NSLayoutConstraint activateConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-[icon(160)]-|" options:0 metrics:nil views:views]];
162 [NSLayoutConstraint activateConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-[poweredByVuo]-|" options:0 metrics:nil views:views]];
163 [NSLayoutConstraint activateConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-[icon(160)]-(>=0)-[poweredByVuo(120)]-|" options:0 metrics:nil views:views]];
165 [poweredByVuo release];
170 NSBox *rightColumn = [NSBox new];
171 rightColumn.translatesAutoresizingMaskIntoConstraints = NO;
172 rightColumn.boxType = NSBoxCustom;
173 rightColumn.borderWidth = 0;
174 rightColumn.fillColor = NSColor.windowBackgroundColor;
180 description.frame = NSMakeRect(0, 0, 560, 0);
181 description.translatesAutoresizingMaskIntoConstraints = NO;
182 description.drawsBackground = NO;
184 NSMutableString *s = [NSMutableString new];
186 NSString *name = NSBundle.mainBundle.infoDictionary[@"CFBundleName"];
188 name = NSProcessInfo.processInfo.processName;
189 [s appendFormat:@"<span class='name'>%@</span>", name];
191 NSString *version = NSBundle.mainBundle.infoDictionary[@"CFBundleShortVersionString"];
193 [s appendFormat:@" <span class='version'>%@</span>", version];
195 NSString *copyright = NSBundle.mainBundle.infoDictionary[@"NSHumanReadableCopyright"];
197 [s appendFormat:@"<p class='copyright'>%@</p>", copyright];
199 NSString *desc = NSBundle.mainBundle.infoDictionary[@"description"];
201 [s appendFormat:@"<div class='description'>%@</div><br>", desc];
203 NSString *homepage = NSBundle.mainBundle.infoDictionary[@"homepageURL"];
204 NSString *documentation = NSBundle.mainBundle.infoDictionary[@"documentationURL"];
206 [s appendFormat:@"<p><a href='%@'>%@</a></p>", homepage, homepage];
208 [s appendFormat:@"<p><a href='%@'>%@</a></p>", documentation, documentation];
210 [description setText:s];
213 [rightColumn addSubview:description];
219 NSMutableString *s = [NSMutableString new];
220 [s appendString:@"<div class='licenses'>"];
221 [s appendString:[
self licenseString]];
222 [s appendString:@"</div>"];
223 [licenses setText:s];
226 NSScrollView *licensesScroller = [[NSScrollView alloc] initWithFrame:NSMakeRect(20, 20, 560, 280)];
227 licensesScroller.hasVerticalScroller = YES;
228 licensesScroller.documentView = licenses;
229 licenses.frame = NSMakeRect(0, 0, licensesScroller.contentSize.width, licensesScroller.contentSize.height);
230 [rightColumn addSubview:licensesScroller];
233 NSDictionary *views = NSDictionaryOfVariableBindings(description, licensesScroller);
234 [NSLayoutConstraint activateConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-[description]-|" options:0 metrics:nil views:views]];
235 [NSLayoutConstraint activateConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-[licensesScroller]-|" options:0 metrics:nil views:views]];
236 [NSLayoutConstraint activateConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-[description][licensesScroller]-|" options:0 metrics:nil views:views]];
239 [licensesScroller release];
240 [description release];
245 self.contentView = c;
246 [c addSubview:leftColumn];
247 [c addSubview:rightColumn];
249 NSDictionary *views = NSDictionaryOfVariableBindings(leftColumn,rightColumn);
250 [NSLayoutConstraint activateConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[leftColumn][rightColumn]|" options:0 metrics:nil views:views]];
251 [NSLayoutConstraint activateConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[leftColumn]|" options:0 metrics:nil views:views]];
252 [NSLayoutConstraint activateConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[rightColumn]|" options:0 metrics:nil views:views]];
255 [leftColumn release];
256 [rightColumn release];
265 - (NSString *)licenseString
267 NSMutableString *s = [NSMutableString new];
270 [s appendString:@"<p>This app may include software and resources licensed under the following terms:</p>"];
274 char licensesPath[PATH_MAX+1];
279 strncat(licensesPath,
"/Vuo.framework/Versions/" VUO_FRAMEWORK_VERSION_STRING
"/Documentation/Licenses", PATH_MAX);
284 strncat(licensesPath,
"/VuoRunner.framework/Versions/" VUO_FRAMEWORK_VERSION_STRING
"/Documentation/Licenses", PATH_MAX);
288 bool foundLicenses =
false;
289 DIR *dirp = licensesPath[0] ? opendir(licensesPath) : NULL;
293 while ((dp = readdir(dirp)) != NULL)
295 if (dp->d_name[0] ==
'.')
298 char *nameWithoutExtension = strdup(dp->d_name);
299 nameWithoutExtension[strlen(nameWithoutExtension) - 4] = 0;
300 [s appendString:[NSString stringWithFormat:@"<h2>%s</h2>", nameWithoutExtension]];
301 free(nameWithoutExtension);
303 size_t pathSize = strlen(licensesPath) + dp->d_namlen + 2;
304 char licensePath[pathSize];
305 strlcpy(licensePath, licensesPath, pathSize);
306 strlcat(licensePath,
"/", pathSize);
307 strlcat(licensePath, dp->d_name, pathSize);
309 [s appendString:[NSString stringWithFormat:@"<pre>%@</pre><br>",
310 [NSString stringWithContentsOfFile:[NSString stringWithUTF8String:licensePath] usedEncoding:nil error:nil]]];
312 foundLicenses =
true;
318 [s appendString:@"<p>(No license information found.)</p>"];
320 return [s autorelease];
329 @property(nonatomic, strong) NSWindow *window;
337 - (void)displayAboutPanel:(
id)sender
342 [_window makeKeyAndOrderFront:sender];