Vuo  2.3.2
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 #import "VuoConfig.h"
17 #import "module.h"
18 
23 @end
24 
25 @implementation NSColor (VuoAppAboutBox)
26 - (NSString *)cssString
27 {
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];
34 }
35 @end
36 
40 @interface VuoAppAboutTextView : NSTextView
41 @property (retain, nonatomic) NSString *mostRecentText;
42 @end
43 
44 @implementation VuoAppAboutTextView
45 
46 - (instancetype)init
47 {
48  if (self = [super init])
49  {
50  self.linkTextAttributes = @{ NSForegroundColorAttributeName:NSColor.secondaryLabelColor };
51  self.editable = NO;
52  }
53  return self;
54 }
55 
56 - (NSSize)intrinsicContentSize
57 {
58  [self.layoutManager ensureLayoutForTextContainer:self.textContainer];
59  return [self.layoutManager usedRectForTextContainer:self.textContainer].size;
60 }
61 
62 - (void)didChangeText
63 {
64  [super didChangeText];
65  [self invalidateIntrinsicContentSize];
66 }
67 
68 - (void)viewDidChangeEffectiveAppearance
69 {
70  [self setText:self.mostRecentText];
71 }
72 
73 - (void)setText:(NSString *)text
74 {
75  self.mostRecentText = text;
76  NSMutableString *s = [NSMutableString new];
77  [s appendString:@"<meta http-equiv='Content-Type' content='text/html; charset=utf-8'>"];
78 
79  NSString *headingCSS = NSColor.labelColor.cssString;
80  NSString *subheadingCSS = NSColor.tertiaryLabelColor.cssString;
81  NSString *bodyCSS = NSColor.secondaryLabelColor.cssString;
82 
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'; } \
93  </style>",
94  headingCSS,
95  subheadingCSS,
96  subheadingCSS,
97  bodyCSS];
98  [s appendString:text];
99  NSAttributedString *as = [[NSAttributedString alloc] initWithHTML:[s dataUsingEncoding:NSUTF8StringEncoding] documentAttributes:nil];
100  [s release];
101  self.textStorage.mutableString.string = @"";
102  [self.textStorage appendAttributedString:as];
103  [as release];
104 }
105 
106 @end
107 
108 
112 @interface VuoAppAboutContentView : NSView
113 @end
114 
115 @implementation VuoAppAboutContentView
116 - (void)cancelOperation:(id)sender
117 {
118  [self.window close];
119 }
120 @end
121 
122 
126 @interface VuoAppAboutWindow : NSWindow
127 @end
128 
129 @implementation VuoAppAboutWindow
130 
131 - (instancetype)init
132 {
133  if (self = [super initWithContentRect:NSMakeRect(0, 0, 820, 500)
134  styleMask:NSWindowStyleMaskTitled|NSWindowStyleMaskClosable|NSWindowStyleMaskFullSizeContentView
135  backing:NSBackingStoreBuffered
136  defer:NO])
137  {
138  self.releasedWhenClosed = NO;
139  self.titlebarAppearsTransparent = YES;
140 
141 
142  NSBox *leftColumn = [NSBox new];
143  leftColumn.translatesAutoresizingMaskIntoConstraints = NO;
144  leftColumn.boxType = NSBoxCustom;
145  leftColumn.borderWidth = 0;
146  leftColumn.fillColor = NSColor.controlBackgroundColor;
147 
148  // Contents of left column.
149  {
150  NSImageView *icon = [NSImageView new];
151  icon.translatesAutoresizingMaskIntoConstraints = NO;
152  icon.image = NSApplication.sharedApplication.applicationIconImage;
153  [leftColumn addSubview:icon];
154 
155  VuoAppSplashView *poweredByVuo = [VuoAppSplashView new];
156  poweredByVuo.translatesAutoresizingMaskIntoConstraints = NO;
157  poweredByVuo.borderVisible = NO;
158  [leftColumn addSubview:poweredByVuo];
159 
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]];
164 
165  [poweredByVuo release];
166  [icon release];
167  }
168 
169 
170  NSBox *rightColumn = [NSBox new];
171  rightColumn.translatesAutoresizingMaskIntoConstraints = NO;
172  rightColumn.boxType = NSBoxCustom;
173  rightColumn.borderWidth = 0;
174  rightColumn.fillColor = NSColor.windowBackgroundColor;
175 
176  // Contents of right column.
177  {
178  // Description.
179  VuoAppAboutTextView *description = [VuoAppAboutTextView new];
180  description.frame = NSMakeRect(0, 0, 560, 0);
181  description.translatesAutoresizingMaskIntoConstraints = NO;
182  description.drawsBackground = NO;
183  {
184  NSMutableString *s = [NSMutableString new];
185 
186  NSString *name = NSBundle.mainBundle.infoDictionary[@"CFBundleName"];
187  if (!name)
188  name = NSProcessInfo.processInfo.processName;
189  [s appendFormat:@"<span class='name'>%@</span>", name];
190 
191  NSString *version = NSBundle.mainBundle.infoDictionary[@"CFBundleShortVersionString"];
192  if (version.length)
193  [s appendFormat:@" <span class='version'>%@</span>", version];
194 
195  NSString *copyright = NSBundle.mainBundle.infoDictionary[@"NSHumanReadableCopyright"];
196  if (copyright)
197  [s appendFormat:@"<p class='copyright'>%@</p>", copyright];
198 
199  NSString *desc = NSBundle.mainBundle.infoDictionary[@"description"];
200  if (desc)
201  [s appendFormat:@"<div class='description'>%@</div><br>", desc];
202 
203  NSString *homepage = NSBundle.mainBundle.infoDictionary[@"homepageURL"];
204  NSString *documentation = NSBundle.mainBundle.infoDictionary[@"documentationURL"];
205  if (homepage)
206  [s appendFormat:@"<p><a href='%@'>%@</a></p>", homepage, homepage];
207  if (documentation)
208  [s appendFormat:@"<p><a href='%@'>%@</a></p>", documentation, documentation];
209 
210  [description setText:s];
211  [s release];
212  }
213  [rightColumn addSubview:description];
214 
215 
216  // Licenses.
217  VuoAppAboutTextView *licenses = [VuoAppAboutTextView new];
218  {
219  NSMutableString *s = [NSMutableString new];
220  [s appendString:@"<div class='licenses'>"];
221  [s appendString:[self licenseString]];
222  [s appendString:@"</div>"];
223  [licenses setText:s];
224  [s release];
225  }
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];
231 
232 
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]];
237 
238  [licenses release];
239  [licensesScroller release];
240  [description release];
241  }
242 
243 
245  self.contentView = c;
246  [c addSubview:leftColumn];
247  [c addSubview:rightColumn];
248 
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]];
253 
254  [c release];
255  [leftColumn release];
256  [rightColumn release];
257  }
258 
259  return self;
260 }
261 
265 - (NSString *)licenseString
266 {
267  NSMutableString *s = [NSMutableString new];
268 
269 
270  [s appendString:@"<p>This app may include software and resources licensed under the following terms:</p>"];
271 
272 
273  // Derive the path of "Licenses" directory.
274  char licensesPath[PATH_MAX+1];
275  licensesPath[0] = 0;
276  if (strlen(VuoGetFrameworkPath()))
277  {
278  strncpy(licensesPath, VuoGetFrameworkPath(), PATH_MAX);
279  strncat(licensesPath, "/Vuo.framework/Versions/" VUO_FRAMEWORK_VERSION_STRING "/Documentation/Licenses", PATH_MAX);
280  }
281  else if (strlen(VuoGetRunnerFrameworkPath()))
282  {
283  strncpy(licensesPath, VuoGetRunnerFrameworkPath(), PATH_MAX);
284  strncat(licensesPath, "/VuoRunner.framework/Versions/" VUO_FRAMEWORK_VERSION_STRING "/Documentation/Licenses", PATH_MAX);
285  }
286 
287 
288  bool foundLicenses = false;
289  DIR *dirp = licensesPath[0] ? opendir(licensesPath) : NULL;
290  if (dirp)
291  {
292  struct dirent *dp;
293  while ((dp = readdir(dirp)) != NULL)
294  {
295  if (dp->d_name[0] == '.')
296  continue;
297 
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);
302 
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);
308 
309  [s appendString:[NSString stringWithFormat:@"<pre>%@</pre><br>",
310  [NSString stringWithContentsOfFile:[NSString stringWithUTF8String:licensePath] usedEncoding:nil error:nil]]];
311 
312  foundLicenses = true;
313  }
314  closedir(dirp);
315  }
316 
317  if (!foundLicenses)
318  [s appendString:@"<p>(No license information found.)</p>"];
319 
320  return [s autorelease];
321 }
322 
323 @end
324 
328 @interface VuoAppAboutBox ()
329 @property(nonatomic, strong) NSWindow *window;
330 @end
331 
332 @implementation VuoAppAboutBox
333 
337 - (void)displayAboutPanel:(id)sender
338 {
339  if (!_window)
340  _window = [VuoAppAboutWindow new];
341  [_window center];
342  [_window makeKeyAndOrderFront:sender];
343 }
344 
345 @end