Vuo 2.4.4
Loading...
Searching...
No Matches
VuoAppAboutBox.m
Go to the documentation of this file.
1
10#import "VuoAppAboutBox.h"
11
12#import <libgen.h>
13#import <dirent.h>
14
15#import "VuoAppSplashView.h"
16
20@interface NSColor (VuoAppAboutBox)
21@end
22
23@implementation NSColor (VuoAppAboutBox)
24- (NSString *)cssString
25{
26 NSColor *srgbColor = [self colorUsingColorSpace:NSColorSpace.sRGBColorSpace];
27 return [NSString stringWithFormat:@"rgba(%g,%g,%g,%g)",
28 255 * srgbColor.redComponent,
29 255 * srgbColor.greenComponent,
30 255 * srgbColor.blueComponent,
31 srgbColor.alphaComponent];
32}
33@end
34
38@interface VuoAppAboutTextView : NSTextView
39@property (retain, nonatomic) NSString *mostRecentText;
40@end
41
42@implementation VuoAppAboutTextView
43
44- (instancetype)init
45{
46 if (self = [super init])
47 {
48 self.linkTextAttributes = @{ NSForegroundColorAttributeName:NSColor.secondaryLabelColor };
49 self.editable = NO;
50 }
51 return self;
52}
53
54- (NSSize)intrinsicContentSize
55{
56 [self.layoutManager ensureLayoutForTextContainer:self.textContainer];
57 return [self.layoutManager usedRectForTextContainer:self.textContainer].size;
58}
59
60- (void)didChangeText
61{
62 [super didChangeText];
63 [self invalidateIntrinsicContentSize];
64}
65
66- (void)viewDidChangeEffectiveAppearance
67{
68 [self setText:self.mostRecentText];
69}
70
71- (void)setText:(NSString *)text
72{
73 self.mostRecentText = text;
74 NSMutableString *s = [NSMutableString new];
75 [s appendString:@"<meta http-equiv='Content-Type' content='text/html; charset=utf-8'>"];
76
77 NSString *headingCSS = NSColor.labelColor.cssString;
78 NSString *subheadingCSS = NSColor.tertiaryLabelColor.cssString;
79 NSString *bodyCSS = NSColor.secondaryLabelColor.cssString;
80
81 [s appendFormat:@"<style> \
82 body { font: 10pt 'Helvetica Neue'; color: %@; } \
83 p { padding-top: 30px; } \
84 h1 { padding-top: 30px; } \
85 .name { font-size: 28pt; font-weight: 300; } \
86 .version { color: %@; font-size: 22pt; font-weight: 200; } \
87 .copyright { color: %@; } \
88 .description { font-weight: bold; } \
89 .licenses { color: %@; font-size: 8pt; } \
90 pre { font: 'Monaco'; } \
91 </style>",
92 headingCSS,
93 subheadingCSS,
94 subheadingCSS,
95 bodyCSS];
96 [s appendString:text];
97 NSAttributedString *as = [[NSAttributedString alloc] initWithHTML:[s dataUsingEncoding:NSUTF8StringEncoding] documentAttributes:nil];
98 [s release];
99 self.textStorage.mutableString.string = @"";
100 [self.textStorage appendAttributedString:as];
101 [as release];
102}
103
104@end
105
106
110@interface VuoAppAboutContentView : NSView
111@end
112
113@implementation VuoAppAboutContentView
114- (void)cancelOperation:(id)sender
115{
116 [self.window close];
117}
118@end
119
120
124@interface VuoAppAboutWindow : NSWindow
125@end
126
127@implementation VuoAppAboutWindow
128
129- (instancetype)init
130{
131 if (self = [super initWithContentRect:NSMakeRect(0, 0, 820, 500)
132 styleMask:NSWindowStyleMaskTitled|NSWindowStyleMaskClosable|NSWindowStyleMaskFullSizeContentView
133 backing:NSBackingStoreBuffered
134 defer:NO])
135 {
136 self.releasedWhenClosed = NO;
137 self.titlebarAppearsTransparent = YES;
138
139
140 NSBox *leftColumn = [NSBox new];
141 leftColumn.translatesAutoresizingMaskIntoConstraints = NO;
142 leftColumn.boxType = NSBoxCustom;
143 leftColumn.borderWidth = 0;
144 leftColumn.fillColor = NSColor.controlBackgroundColor;
145
146 // Contents of left column.
147 {
148 NSImageView *icon = [NSImageView new];
149 icon.translatesAutoresizingMaskIntoConstraints = NO;
150 icon.image = NSApplication.sharedApplication.applicationIconImage;
151 [leftColumn addSubview:icon];
152
153 VuoAppSplashView *poweredByVuo = [VuoAppSplashView new];
154 poweredByVuo.translatesAutoresizingMaskIntoConstraints = NO;
155 poweredByVuo.borderVisible = NO;
156 [leftColumn addSubview:poweredByVuo];
157
158 NSDictionary *views = NSDictionaryOfVariableBindings(icon, poweredByVuo);
159 [NSLayoutConstraint activateConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-[icon(160)]-|" options:0 metrics:nil views:views]];
160 [NSLayoutConstraint activateConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-[poweredByVuo]-|" options:0 metrics:nil views:views]];
161 [NSLayoutConstraint activateConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-[icon(160)]-(>=0)-[poweredByVuo(120)]-|" options:0 metrics:nil views:views]];
162
163 [poweredByVuo release];
164 [icon release];
165 }
166
167
168 NSBox *rightColumn = [NSBox new];
169 rightColumn.translatesAutoresizingMaskIntoConstraints = NO;
170 rightColumn.boxType = NSBoxCustom;
171 rightColumn.borderWidth = 0;
172 rightColumn.fillColor = NSColor.windowBackgroundColor;
173
174 // Contents of right column.
175 {
176 // Description.
177 VuoAppAboutTextView *description = [VuoAppAboutTextView new];
178 description.frame = NSMakeRect(0, 0, 560, 0);
179 description.translatesAutoresizingMaskIntoConstraints = NO;
180 description.drawsBackground = NO;
181 {
182 NSMutableString *s = [NSMutableString new];
183
184 NSString *name = NSBundle.mainBundle.infoDictionary[@"CFBundleName"];
185 if (!name)
186 name = NSProcessInfo.processInfo.processName;
187 [s appendFormat:@"<span class='name'>%@</span>", name];
188
189 NSString *version = NSBundle.mainBundle.infoDictionary[@"CFBundleShortVersionString"];
190 if (version.length)
191 [s appendFormat:@" <span class='version'>%@</span>", version];
192
193 NSString *copyright = NSBundle.mainBundle.infoDictionary[@"NSHumanReadableCopyright"];
194 if (copyright)
195 [s appendFormat:@"<p class='copyright'>%@</p>", copyright];
196
197 NSString *desc = NSBundle.mainBundle.infoDictionary[@"description"];
198 if (desc)
199 [s appendFormat:@"<div class='description'>%@</div><br>", desc];
200
201 NSString *homepage = NSBundle.mainBundle.infoDictionary[@"homepageURL"];
202 NSString *documentation = NSBundle.mainBundle.infoDictionary[@"documentationURL"];
203 if (homepage)
204 [s appendFormat:@"<p><a href='%@'>%@</a></p>", homepage, homepage];
205 if (documentation)
206 [s appendFormat:@"<p><a href='%@'>%@</a></p>", documentation, documentation];
207
208 [description setText:s];
209 [s release];
210 }
211 [rightColumn addSubview:description];
212
213
214 // Licenses.
216 {
217 NSMutableString *s = [NSMutableString new];
218 [s appendString:@"<div class='licenses'>"];
219 [s appendString:[self licenseString]];
220 [s appendString:@"</div>"];
221 [licenses setText:s];
222 [s release];
223 }
224 NSScrollView *licensesScroller = [[NSScrollView alloc] initWithFrame:NSMakeRect(20, 20, 560, 280)];
225 licensesScroller.hasVerticalScroller = YES;
226 licensesScroller.documentView = licenses;
227 licenses.frame = NSMakeRect(0, 0, licensesScroller.contentSize.width, licensesScroller.contentSize.height);
228 [rightColumn addSubview:licensesScroller];
229
230
231 NSDictionary *views = NSDictionaryOfVariableBindings(description, licensesScroller);
232 [NSLayoutConstraint activateConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-[description]-|" options:0 metrics:nil views:views]];
233 [NSLayoutConstraint activateConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-[licensesScroller]-|" options:0 metrics:nil views:views]];
234 [NSLayoutConstraint activateConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-[description][licensesScroller]-|" options:0 metrics:nil views:views]];
235
236 [licenses release];
237 [licensesScroller release];
238 [description release];
239 }
240
241
243 self.contentView = c;
244 [c addSubview:leftColumn];
245 [c addSubview:rightColumn];
246
247 NSDictionary *views = NSDictionaryOfVariableBindings(leftColumn,rightColumn);
248 [NSLayoutConstraint activateConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[leftColumn][rightColumn]|" options:0 metrics:nil views:views]];
249 [NSLayoutConstraint activateConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[leftColumn]|" options:0 metrics:nil views:views]];
250 [NSLayoutConstraint activateConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[rightColumn]|" options:0 metrics:nil views:views]];
251
252 [c release];
253 [leftColumn release];
254 [rightColumn release];
255 }
256
257 return self;
258}
259
263- (NSString *)licenseString
264{
265 NSMutableString *s = [NSMutableString new];
266
267
268 [s appendString:@"<p>This app may include software and resources licensed under the following terms:</p>"];
269
270
271 // Derive the path of "Licenses" directory.
272 char licensesPath[PATH_MAX+1];
273 licensesPath[0] = 0;
274 const char *frameworkPath = VuoGetFrameworkOrRunnerFrameworkPath();
275 if (frameworkPath)
276 {
277 strncpy(licensesPath, frameworkPath, PATH_MAX);
278 strncat(licensesPath, "/Versions/" VUO_FRAMEWORK_VERSION_STRING "/Documentation/Licenses", PATH_MAX);
279 }
280
281
282 bool foundLicenses = false;
283 DIR *dirp = licensesPath[0] ? opendir(licensesPath) : NULL;
284 if (dirp)
285 {
286 struct dirent *dp;
287 while ((dp = readdir(dirp)) != NULL)
288 {
289 if (dp->d_name[0] == '.')
290 continue;
291
292 char *nameWithoutExtension = strdup(dp->d_name);
293 nameWithoutExtension[strlen(nameWithoutExtension) - 4] = 0;
294 [s appendString:[NSString stringWithFormat:@"<h2>%s</h2>", nameWithoutExtension]];
295 free(nameWithoutExtension);
296
297 size_t pathSize = strlen(licensesPath) + dp->d_namlen + 2;
298 char licensePath[pathSize];
299 strlcpy(licensePath, licensesPath, pathSize);
300 strlcat(licensePath, "/", pathSize);
301 strlcat(licensePath, dp->d_name, pathSize);
302
303 [s appendString:[NSString stringWithFormat:@"<pre>%@</pre><br>",
304 [NSString stringWithContentsOfFile:[NSString stringWithUTF8String:licensePath] usedEncoding:nil error:nil]]];
305
306 foundLicenses = true;
307 }
308 closedir(dirp);
309 }
310
311 if (!foundLicenses)
312 [s appendString:@"<p>(No license information found.)</p>"];
313
314 return [s autorelease];
315}
316
317@end
318
322@interface VuoAppAboutBox ()
323@property(nonatomic, strong) NSWindow *window;
324@end
325
326@implementation VuoAppAboutBox
327
331- (void)displayAboutPanel:(id)sender
332{
333 if (!_window)
334 _window = [VuoAppAboutWindow new];
335 [_window center];
336 [_window makeKeyAndOrderFront:sender];
337}
338
339@end