xref: /haiku/src/apps/aboutsystem/AboutSystem.cpp (revision 64b46b706b02d969629415c9a44c394a2a5e4993)
1 /*
2  * Copyright 2005-2018, Haiku, Inc.
3  * Distributed under the terms of the MIT license.
4  *
5  * Authors:
6  *		Augustin Cavalier <waddlesplash>
7  *		DarkWyrm <bpmagic@columbus.rr.com>
8  *		René Gollent
9  *		Wim van der Meer <WPJvanderMeer@gmail.com>
10  */
11 
12 
13 #include <ctype.h>
14 #include <stdio.h>
15 #include <time.h>
16 #include <unistd.h>
17 
18 #include <algorithm>
19 #include <map>
20 #include <string>
21 
22 #include <AppFileInfo.h>
23 #include <Application.h>
24 #include <Bitmap.h>
25 #include <DateTimeFormat.h>
26 #include <DurationFormat.h>
27 #include <File.h>
28 #include <FindDirectory.h>
29 #include <Font.h>
30 #include <fs_attr.h>
31 #include <LayoutBuilder.h>
32 #include <MessageRunner.h>
33 #include <Messenger.h>
34 #include <ObjectList.h>
35 #include <OS.h>
36 #include <Path.h>
37 #include <PathFinder.h>
38 #include <Resources.h>
39 #include <Screen.h>
40 #include <ScrollView.h>
41 #include <String.h>
42 #include <StringFormat.h>
43 #include <StringList.h>
44 #include <StringView.h>
45 #include <TranslationUtils.h>
46 #include <TranslatorFormats.h>
47 #include <View.h>
48 #include <Volume.h>
49 #include <VolumeRoster.h>
50 #include <Window.h>
51 
52 #include <AppMisc.h>
53 #include <AutoDeleter.h>
54 #include <cpu_type.h>
55 #include <parsedate.h>
56 #include <system_revision.h>
57 
58 #include <Catalog.h>
59 #include <Language.h>
60 #include <Locale.h>
61 #include <LocaleRoster.h>
62 
63 #include "HyperTextActions.h"
64 #include "HyperTextView.h"
65 #include "Utilities.h"
66 
67 #include "Credits.h"
68 
69 #ifndef LINE_MAX
70 #define LINE_MAX 2048
71 #endif
72 
73 #define SCROLL_CREDITS_VIEW 'mviv'
74 
75 #undef B_TRANSLATION_CONTEXT
76 #define B_TRANSLATION_CONTEXT "AboutWindow"
77 
78 
79 
80 static const char* UptimeToString(char string[], size_t size);
81 static const char* MemSizeToString(char string[], size_t size,
82 	system_info* info);
83 static const char* MemUsageToString(char string[], size_t size,
84 	system_info* info);
85 
86 
87 static const rgb_color kIdealHaikuGreen = { 42, 131, 36, 255 };
88 static const rgb_color kIdealHaikuOrange = { 255, 69, 0, 255 };
89 static const rgb_color kIdealHaikuYellow = { 255, 176, 0, 255 };
90 static const rgb_color kIdealBeOSBlue = { 0, 0, 200, 255 };
91 static const rgb_color kIdealBeOSRed = { 200, 0, 0, 255 };
92 
93 static const char* kBSDTwoClause = B_TRANSLATE_MARK("BSD (2-clause)");
94 static const char* kBSDThreeClause = B_TRANSLATE_MARK("BSD (3-clause)");
95 static const char* kBSDFourClause = B_TRANSLATE_MARK("BSD (4-clause)");
96 static const char* kGPLv2 = B_TRANSLATE_MARK("GNU GPL v2");
97 static const char* kGPLv3 = B_TRANSLATE_MARK("GNU GPL v3");
98 static const char* kLGPLv2 = B_TRANSLATE_MARK("GNU LGPL v2");
99 static const char* kLGPLv21 = B_TRANSLATE_MARK("GNU LGPL v2.1");
100 #if 0
101 static const char* kPublicDomain = B_TRANSLATE_MARK("Public Domain");
102 #endif
103 #ifdef __i386__
104 static const char* kIntel2xxxFirmware = B_TRANSLATE_MARK("Intel (2xxx firmware)");
105 static const char* kIntelFirmware = B_TRANSLATE_MARK("Intel (firmware)");
106 static const char* kMarvellFirmware = B_TRANSLATE_MARK("Marvell (firmware)");
107 static const char* kRalinkFirmware = B_TRANSLATE_MARK("Ralink (firmware)");
108 #endif
109 
110 
111 static int
112 TranslationComparator(const void* left, const void* right)
113 {
114 	const Translation* leftTranslation = *(const Translation**)left;
115 	const Translation* rightTranslation = *(const Translation**)right;
116 
117 	BLanguage* language;
118 	BString leftName;
119 	if (BLocaleRoster::Default()->GetLanguage(leftTranslation->languageCode,
120 			&language) == B_OK) {
121 		language->GetName(leftName);
122 		delete language;
123 	} else
124 		leftName = leftTranslation->languageCode;
125 
126 	BString rightName;
127 	if (BLocaleRoster::Default()->GetLanguage(rightTranslation->languageCode,
128 			&language) == B_OK) {
129 		language->GetName(rightName);
130 		delete language;
131 	} else
132 		rightName = rightTranslation->languageCode;
133 
134 	BCollator collator;
135 	BLocale::Default()->GetCollator(&collator);
136 	return collator.Compare(leftName.String(), rightName.String());
137 }
138 
139 
140 class AboutApp : public BApplication {
141 public:
142 							AboutApp();
143 			void			MessageReceived(BMessage* message);
144 };
145 
146 
147 class AboutView;
148 
149 class AboutWindow : public BWindow {
150 public:
151 							AboutWindow();
152 
153 	virtual	bool			QuitRequested();
154 
155 			AboutView*		fAboutView;
156 };
157 
158 
159 class LogoView : public BView {
160 public:
161 							LogoView();
162 	virtual					~LogoView();
163 
164 	virtual	BSize			MinSize();
165 	virtual	BSize			MaxSize();
166 
167 	virtual void			Draw(BRect updateRect);
168 
169 private:
170 			BBitmap*		fLogo;
171 };
172 
173 
174 class CropView : public BView {
175 public:
176 							CropView(BView* target, int32 left, int32 top,
177 								int32 right, int32 bottom);
178 	virtual					~CropView();
179 
180 	virtual	BSize			MinSize();
181 	virtual	BSize			MaxSize();
182 
183 	virtual void			DoLayout();
184 
185 private:
186 			BView*			fTarget;
187 			int32			fCropLeft;
188 			int32			fCropTop;
189 			int32			fCropRight;
190 			int32			fCropBottom;
191 };
192 
193 
194 class AboutView : public BView {
195 public:
196 							AboutView();
197 							~AboutView();
198 
199 	virtual void			AttachedToWindow();
200 	virtual	void			AllAttached();
201 	virtual void			Pulse();
202 
203 	virtual void			MessageReceived(BMessage* msg);
204 	virtual void			MouseDown(BPoint point);
205 
206 			void			AddCopyrightEntry(const char* name,
207 								const char* text,
208 								const StringVector& licenses,
209 								const StringVector& sources,
210 								const char* url);
211 			void			AddCopyrightEntry(const char* name,
212 								const char* text, const char* url = NULL);
213 			void			PickRandomHaiku();
214 
215 
216 			void			_AdjustTextColors();
217 private:
218 	typedef std::map<std::string, PackageCredit*> PackageCreditMap;
219 
220 private:
221 			BView*			_CreateLabel(const char* name, const char* label);
222 			BView*			_CreateCreditsView();
223 			status_t		_GetLicensePath(const char* license,
224 								BPath& path);
225 			void			_AddCopyrightsFromAttribute();
226 			void			_AddPackageCredit(const PackageCredit& package);
227 			void			_AddPackageCreditEntries();
228 
229 			BStringView*	fMemView;
230 			BStringView*	fUptimeView;
231 			BView*			fInfoView;
232 			HyperTextView*	fCreditsView;
233 
234 			BObjectList<BView> fTextViews;
235 			BObjectList<BView> fSubTextViews;
236 
237 			BBitmap*		fLogo;
238 
239 			bigtime_t		fLastActionTime;
240 			BMessageRunner*	fScrollRunner;
241 			PackageCreditMap fPackageCredits;
242 
243 private:
244 			rgb_color		fTextColor;
245 			rgb_color		fLinkColor;
246 			rgb_color		fHaikuOrangeColor;
247 			rgb_color		fHaikuGreenColor;
248 			rgb_color		fHaikuYellowColor;
249 			rgb_color		fBeOSRedColor;
250 			rgb_color		fBeOSBlueColor;
251 };
252 
253 
254 //	#pragma mark -
255 
256 
257 AboutApp::AboutApp()
258 	: BApplication("application/x-vnd.Haiku-About")
259 {
260 	B_TRANSLATE_MARK_SYSTEM_NAME_VOID("AboutSystem");
261 
262 	AboutWindow *window = new(std::nothrow) AboutWindow();
263 	if (window)
264 		window->Show();
265 }
266 
267 
268 void
269 AboutApp::MessageReceived(BMessage* message)
270 {
271 	switch (message->what) {
272 		case B_SILENT_RELAUNCH:
273 			WindowAt(0)->Activate();
274 			break;
275 	}
276 
277 	BApplication::MessageReceived(message);
278 }
279 
280 
281 //	#pragma mark -
282 
283 
284 AboutWindow::AboutWindow()
285 	: BWindow(BRect(0, 0, 500, 300), B_TRANSLATE("About this system"),
286 		B_TITLED_WINDOW, B_AUTO_UPDATE_SIZE_LIMITS | B_NOT_ZOOMABLE)
287 {
288 	SetLayout(new BGroupLayout(B_VERTICAL));
289 	fAboutView = new AboutView();
290 	AddChild(fAboutView);
291 
292 	// Make sure we take the minimal window size into account when centering
293 	BSize size = GetLayout()->MinSize();
294 	ResizeTo(max_c(size.width, Bounds().Width()),
295 		max_c(size.height, Bounds().Height()));
296 
297 	CenterOnScreen();
298 }
299 
300 
301 bool
302 AboutWindow::QuitRequested()
303 {
304 	be_app->PostMessage(B_QUIT_REQUESTED);
305 	return true;
306 }
307 
308 
309 //	#pragma mark - LogoView
310 
311 
312 LogoView::LogoView()
313 	: BView("logo", B_WILL_DRAW)
314 {
315 	fLogo = BTranslationUtils::GetBitmap(B_PNG_FORMAT, "logo.png");
316 	SetViewColor(255, 255, 255);
317 }
318 
319 
320 LogoView::~LogoView()
321 {
322 	delete fLogo;
323 }
324 
325 
326 BSize
327 LogoView::MinSize()
328 {
329 	if (fLogo == NULL)
330 		return BSize(0, 0);
331 
332 	return BSize(fLogo->Bounds().Width(), fLogo->Bounds().Height());
333 }
334 
335 
336 BSize
337 LogoView::MaxSize()
338 {
339 	if (fLogo == NULL)
340 		return BSize(0, 0);
341 
342 	return BSize(B_SIZE_UNLIMITED, fLogo->Bounds().Height());
343 }
344 
345 
346 void
347 LogoView::Draw(BRect updateRect)
348 {
349 	if (fLogo != NULL) {
350 		DrawBitmap(fLogo,
351 			BPoint((Bounds().Width() - fLogo->Bounds().Width()) / 2, 0));
352 	}
353 }
354 
355 
356 //	#pragma mark - CropView
357 
358 
359 CropView::CropView(BView* target, int32 left, int32 top, int32 right,
360 		int32 bottom)
361 	: BView("crop view", 0),
362 	fTarget(target),
363 	fCropLeft(left),
364 	fCropTop(top),
365 	fCropRight(right),
366 	fCropBottom(bottom)
367 {
368 	AddChild(target);
369 }
370 
371 
372 CropView::~CropView()
373 {
374 }
375 
376 
377 BSize
378 CropView::MinSize()
379 {
380 	if (fTarget == NULL)
381 		return BSize();
382 
383 	BSize size = fTarget->MinSize();
384 	if (size.width != B_SIZE_UNSET)
385 		size.width -= fCropLeft + fCropRight;
386 	if (size.height != B_SIZE_UNSET)
387 		size.height -= fCropTop + fCropBottom;
388 
389 	return size;
390 }
391 
392 
393 BSize
394 CropView::MaxSize()
395 {
396 	if (fTarget == NULL)
397 		return BSize();
398 
399 	BSize size = fTarget->MaxSize();
400 	if (size.width != B_SIZE_UNSET)
401 		size.width -= fCropLeft + fCropRight;
402 	if (size.height != B_SIZE_UNSET)
403 		size.height -= fCropTop + fCropBottom;
404 
405 	return size;
406 }
407 
408 
409 void
410 CropView::DoLayout()
411 {
412 	BView::DoLayout();
413 
414 	if (fTarget == NULL)
415 		return;
416 
417 	fTarget->MoveTo(-fCropLeft, -fCropTop);
418 	fTarget->ResizeTo(Bounds().Width() + fCropLeft + fCropRight,
419 		Bounds().Height() + fCropTop + fCropBottom);
420 }
421 
422 
423 //	#pragma mark - AboutView
424 
425 #undef B_TRANSLATION_CONTEXT
426 #define B_TRANSLATION_CONTEXT "AboutView"
427 
428 AboutView::AboutView()
429 	: BView("aboutview", B_WILL_DRAW | B_PULSE_NEEDED),
430 	fLastActionTime(system_time()),
431 	fScrollRunner(NULL)
432 {
433 	// Assign the colors, sadly this does not respect live color updates
434 	fTextColor = ui_color(B_DOCUMENT_TEXT_COLOR);
435 	fLinkColor = ui_color(B_LINK_TEXT_COLOR);
436 	fHaikuOrangeColor = mix_color(fTextColor, kIdealHaikuOrange, 191);
437 	fHaikuGreenColor = mix_color(fTextColor, kIdealHaikuGreen, 191);
438 	fHaikuYellowColor = mix_color(fTextColor, kIdealHaikuYellow, 191);
439 	fBeOSRedColor = mix_color(fTextColor, kIdealBeOSRed, 191);
440 	fBeOSBlueColor = mix_color(fTextColor, kIdealBeOSBlue, 191);
441 
442 	// Begin Construction of System Information controls
443 	system_info systemInfo;
444 	get_system_info(&systemInfo);
445 
446 	// Create all the various labels for system infomation
447 
448 	// OS Version
449 
450 	char string[1024];
451 	strlcpy(string, B_TRANSLATE("Unknown"), sizeof(string));
452 
453 	// the version is stored in the BEOS:APP_VERSION attribute of libbe.so
454 	BPath path;
455 	if (find_directory(B_BEOS_LIB_DIRECTORY, &path) == B_OK) {
456 		path.Append("libbe.so");
457 
458 		BAppFileInfo appFileInfo;
459 		version_info versionInfo;
460 		BFile file;
461 		if (file.SetTo(path.Path(), B_READ_ONLY) == B_OK
462 			&& appFileInfo.SetTo(&file) == B_OK
463 			&& appFileInfo.GetVersionInfo(&versionInfo,
464 				B_APP_VERSION_KIND) == B_OK
465 			&& versionInfo.short_info[0] != '\0')
466 			strlcpy(string, versionInfo.short_info, sizeof(string));
467 	}
468 
469 	// Add system revision
470 	const char* haikuRevision = __get_haiku_revision();
471 	if (haikuRevision != NULL) {
472 		strlcat(string, " (", sizeof(string));
473 		strlcat(string, B_TRANSLATE("Revision"), sizeof(string));
474 		strlcat(string, " ", sizeof(string));
475 		strlcat(string, haikuRevision, sizeof(string));
476 		strlcat(string, ")", sizeof(string));
477 	}
478 
479 	BStringView* versionView = new BStringView("ostext", string);
480 	fSubTextViews.AddItem(versionView);
481 	versionView->SetExplicitAlignment(BAlignment(B_ALIGN_LEFT,
482 		B_ALIGN_VERTICAL_UNSET));
483 
484 	BStringView* abiView = new BStringView("abitext", B_HAIKU_ABI_NAME);
485 	fSubTextViews.AddItem(abiView);
486 	abiView->SetExplicitAlignment(BAlignment(B_ALIGN_LEFT,
487 		B_ALIGN_VERTICAL_UNSET));
488 
489 	// CPU count, type and clock speed
490 	static BStringFormat format(B_TRANSLATE_COMMENT(
491 		"{0, plural, one{Processor:} other{# Processors:}}",
492 		"\"Processor:\" or \"2 Processors:\""));
493 
494 	BString processorLabel;
495 	format.Format(processorLabel, systemInfo.cpu_count);
496 
497 	uint32 topologyNodeCount = 0;
498 	cpu_topology_node_info* topology = NULL;
499 	get_cpu_topology_info(NULL, &topologyNodeCount);
500 	if (topologyNodeCount != 0)
501 		topology = new cpu_topology_node_info[topologyNodeCount];
502 	get_cpu_topology_info(topology, &topologyNodeCount);
503 
504 	enum cpu_platform platform = B_CPU_UNKNOWN;
505 	enum cpu_vendor cpuVendor = B_CPU_VENDOR_UNKNOWN;
506 	uint32 cpuModel = 0;
507 	for (uint32 i = 0; i < topologyNodeCount; i++) {
508 		switch (topology[i].type) {
509 			case B_TOPOLOGY_ROOT:
510 				platform = topology[i].data.root.platform;
511 				break;
512 
513 			case B_TOPOLOGY_PACKAGE:
514 				cpuVendor = topology[i].data.package.vendor;
515 				break;
516 
517 			case B_TOPOLOGY_CORE:
518 				cpuModel = topology[i].data.core.model;
519 				break;
520 
521 			default:
522 				break;
523 		}
524 	}
525 
526 	delete[] topology;
527 
528 	BString cpuType;
529 	cpuType << get_cpu_vendor_string(cpuVendor)
530 		<< " " << get_cpu_model_string(platform, cpuVendor, cpuModel);
531 
532 	BStringView* cpuView = new BStringView("cputext", cpuType.String());
533 	fSubTextViews.AddItem(cpuView);
534 	cpuView->SetExplicitAlignment(BAlignment(B_ALIGN_LEFT,
535 		B_ALIGN_VERTICAL_UNSET));
536 
537 	int32 clockSpeed = get_rounded_cpu_speed();
538 	if (clockSpeed < 1000)
539 		snprintf(string, sizeof(string), B_TRANSLATE("%ld MHz"), clockSpeed);
540 	else
541 		snprintf(string, sizeof(string), B_TRANSLATE("%.2f GHz"),
542 			clockSpeed / 1000.0f);
543 
544 	BStringView* frequencyView = new BStringView("frequencytext", string);
545 	fSubTextViews.AddItem(frequencyView);
546 	frequencyView->SetExplicitAlignment(BAlignment(B_ALIGN_LEFT,
547 		B_ALIGN_VERTICAL_UNSET));
548 
549 	// RAM
550 	BStringView *memSizeView = new BStringView("ramsizetext",
551 		MemSizeToString(string, sizeof(string), &systemInfo));
552 	fSubTextViews.AddItem(memSizeView);
553 	memSizeView->SetExplicitAlignment(BAlignment(B_ALIGN_LEFT,
554 		B_ALIGN_VERTICAL_UNSET));
555 
556 	fMemView = new BStringView("ramtext",
557 		MemUsageToString(string, sizeof(string), &systemInfo));
558 	fSubTextViews.AddItem(fMemView);
559 	fMemView->SetExplicitAlignment(BAlignment(B_ALIGN_LEFT,
560 		B_ALIGN_VERTICAL_UNSET));
561 
562 	// Kernel build time/date
563 	BString kernelTimeDate;
564 	kernelTimeDate << systemInfo.kernel_build_date
565 		<< " " << systemInfo.kernel_build_time;
566 	BString buildTimeDate;
567 
568 	time_t buildTimeDateStamp = parsedate(kernelTimeDate, -1);
569 	if (buildTimeDateStamp > 0) {
570 		if (BDateTimeFormat().Format(buildTimeDate, buildTimeDateStamp,
571 			B_LONG_DATE_FORMAT, B_MEDIUM_TIME_FORMAT) != B_OK)
572 			buildTimeDate.SetTo(kernelTimeDate);
573 	} else
574 		buildTimeDate.SetTo(kernelTimeDate);
575 
576 	BStringView* kernelView = new BStringView("kerneltext", buildTimeDate);
577 	fSubTextViews.AddItem(kernelView);
578 	kernelView->SetExplicitAlignment(BAlignment(B_ALIGN_LEFT,
579 		B_ALIGN_VERTICAL_UNSET));
580 
581 	// Uptime
582 	fUptimeView = new BStringView("uptimetext", "...");
583 	fSubTextViews.AddItem(fUptimeView);
584 	fUptimeView->SetText(UptimeToString(string, sizeof(string)));
585 
586 	const float offset = 5;
587 
588 	SetLayout(new BGroupLayout(B_HORIZONTAL, 0));
589 	SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
590 
591 	BLayoutBuilder::Group<>((BGroupLayout*)GetLayout())
592 		.AddGroup(B_VERTICAL, 0)
593 			.Add(new LogoView())
594 			.AddGroup(B_VERTICAL, 0)
595 				.Add(_CreateLabel("oslabel", B_TRANSLATE("Version:")))
596 				.Add(versionView)
597 				.Add(abiView)
598 				.AddStrut(offset)
599 				.Add(_CreateLabel("cpulabel", processorLabel.String()))
600 				.Add(cpuView)
601 				.Add(frequencyView)
602 				.AddStrut(offset)
603 				.Add(_CreateLabel("memlabel", B_TRANSLATE("Memory:")))
604 				.Add(memSizeView)
605 				.Add(fMemView)
606 				.AddStrut(offset)
607 				.Add(_CreateLabel("kernellabel", B_TRANSLATE("Kernel:")))
608 				.Add(kernelView)
609 				.AddStrut(offset)
610 				.Add(_CreateLabel("uptimelabel",
611 					B_TRANSLATE("Time running:")))
612 				.Add(fUptimeView)
613 				.AddGlue()
614 				.SetInsets(5, 5, 5, 5)
615 			.End()
616 			// TODO: investigate: adding this causes the time to be cut
617 			//.AddGlue()
618 		.End()
619 		.Add(_CreateCreditsView());
620 
621 	float min = fMemView->MinSize().width * 1.1f;
622 	fCreditsView->SetExplicitMinSize(BSize(min * 3, min));
623 }
624 
625 
626 AboutView::~AboutView()
627 {
628 	for (PackageCreditMap::iterator it = fPackageCredits.begin();
629 		it != fPackageCredits.end(); it++) {
630 
631 		delete it->second;
632 	}
633 
634 	delete fScrollRunner;
635 }
636 
637 
638 void
639 AboutView::AttachedToWindow()
640 {
641 	BView::AttachedToWindow();
642 	Window()->SetPulseRate(500000);
643 	SetEventMask(B_POINTER_EVENTS);
644 	DoLayout();
645 }
646 
647 
648 void
649 AboutView::AllAttached()
650 {
651 	_AdjustTextColors();
652 }
653 
654 
655 void
656 AboutView::MouseDown(BPoint point)
657 {
658 	BRect r(92, 26, 105, 31);
659 	if (r.Contains(point))
660 		BMessenger(this).SendMessage('eegg');
661 
662 	if (Bounds().Contains(point)) {
663 		fLastActionTime = system_time();
664 		delete fScrollRunner;
665 		fScrollRunner = NULL;
666 	}
667 }
668 
669 
670 void
671 AboutView::Pulse()
672 {
673 	char string[255];
674 	system_info info;
675 	get_system_info(&info);
676 	fUptimeView->SetText(UptimeToString(string, sizeof(string)));
677 	fMemView->SetText(MemUsageToString(string, sizeof(string), &info));
678 
679 	if (fScrollRunner == NULL
680 		&& system_time() > fLastActionTime + 10000000) {
681 		BMessage message(SCROLL_CREDITS_VIEW);
682 		//fScrollRunner = new BMessageRunner(this, &message, 25000, -1);
683 	}
684 }
685 
686 
687 void
688 AboutView::MessageReceived(BMessage* msg)
689 {
690 	switch (msg->what) {
691 		case B_COLORS_UPDATED:
692 		{
693 			if (msg->HasColor(ui_color_name(B_PANEL_TEXT_COLOR)))
694 				_AdjustTextColors();
695 
696 			break;
697 		}
698 		case SCROLL_CREDITS_VIEW:
699 		{
700 			BScrollBar* scrollBar =
701 				fCreditsView->ScrollBar(B_VERTICAL);
702 			if (scrollBar == NULL)
703 				break;
704 			float max, min;
705 			scrollBar->GetRange(&min, &max);
706 			if (scrollBar->Value() < max)
707 				fCreditsView->ScrollBy(0, 1);
708 
709 			break;
710 		}
711 
712 		case 'eegg':
713 		{
714 			printf("Easter egg\n");
715 			PickRandomHaiku();
716 			break;
717 		}
718 
719 		default:
720 			BView::MessageReceived(msg);
721 			break;
722 	}
723 }
724 
725 
726 void
727 AboutView::AddCopyrightEntry(const char* name, const char* text,
728 	const char* url)
729 {
730 	AddCopyrightEntry(name, text, StringVector(), StringVector(), url);
731 }
732 
733 
734 void
735 AboutView::AddCopyrightEntry(const char* name, const char* text,
736 	const StringVector& licenses, const StringVector& sources,
737 	const char* url)
738 {
739 	BFont font(be_bold_font);
740 	//font.SetSize(be_bold_font->Size());
741 	font.SetFace(B_BOLD_FACE | B_ITALIC_FACE);
742 
743 	fCreditsView->SetFontAndColor(&font, B_FONT_ALL, &fHaikuYellowColor);
744 	fCreditsView->Insert(name);
745 	fCreditsView->Insert("\n");
746 	fCreditsView->SetFontAndColor(be_plain_font, B_FONT_ALL, &fTextColor);
747 	fCreditsView->Insert(text);
748 	fCreditsView->Insert("\n");
749 
750 	if (licenses.CountStrings() > 0) {
751 		if (licenses.CountStrings() > 1)
752 			fCreditsView->Insert(B_TRANSLATE("Licenses: "));
753 		else
754 			fCreditsView->Insert(B_TRANSLATE("License: "));
755 
756 		for (int32 i = 0; i < licenses.CountStrings(); i++) {
757 			const char* license = licenses.StringAt(i);
758 
759 			if (i > 0)
760 				fCreditsView->Insert(", ");
761 
762 			BString licenseName;
763 			BString licenseURL;
764 			parse_named_url(license, licenseName, licenseURL);
765 
766 			BPath licensePath;
767 			if (_GetLicensePath(licenseURL, licensePath) == B_OK) {
768 				fCreditsView->InsertHyperText(B_TRANSLATE_NOCOLLECT(licenseName),
769 					new OpenFileAction(licensePath.Path()));
770 			} else
771 				fCreditsView->Insert(licenseName);
772 		}
773 
774 		fCreditsView->Insert("\n");
775 	}
776 
777 	if (sources.CountStrings() > 0) {
778 		fCreditsView->Insert(B_TRANSLATE("Source Code: "));
779 
780 		for (int32 i = 0; i < sources.CountStrings(); i++) {
781 			const char* source = sources.StringAt(i);
782 
783 			if (i > 0)
784 				fCreditsView->Insert(", ");
785 
786 			BString urlName;
787 			BString urlAddress;
788 			parse_named_url(source, urlName, urlAddress);
789 
790 			fCreditsView->SetFontAndColor(be_plain_font, B_FONT_ALL,
791 				&fLinkColor);
792 			fCreditsView->InsertHyperText(urlName,
793 				new URLAction(urlAddress));
794 		}
795 
796 		fCreditsView->Insert("\n");
797 	}
798 
799 	if (url) {
800 		BString urlName;
801 		BString urlAddress;
802 		parse_named_url(url, urlName, urlAddress);
803 
804 		fCreditsView->SetFontAndColor(be_plain_font, B_FONT_ALL,
805 			&fLinkColor);
806 		fCreditsView->InsertHyperText(urlName,
807 			new URLAction(urlAddress));
808 		fCreditsView->Insert("\n");
809 	}
810 	fCreditsView->Insert("\n");
811 }
812 
813 
814 void
815 AboutView::PickRandomHaiku()
816 {
817 	BPath path;
818 	if (find_directory(B_SYSTEM_DATA_DIRECTORY, &path) != B_OK)
819 		path = "/system/data";
820 	path.Append("fortunes");
821 	path.Append("Haiku");
822 
823 	BFile fortunes(path.Path(), B_READ_ONLY);
824 	struct stat st;
825 	if (fortunes.InitCheck() < B_OK)
826 		return;
827 	if (fortunes.GetStat(&st) < B_OK)
828 		return;
829 
830 	char* buff = (char*)malloc((size_t)st.st_size + 1);
831 	if (!buff)
832 		return;
833 	buff[(size_t)st.st_size] = '\0';
834 	BList haikuList;
835 	if (fortunes.Read(buff, (size_t)st.st_size) == (ssize_t)st.st_size) {
836 		char* p = buff;
837 		while (p && *p) {
838 			char* e = strchr(p, '%');
839 			BString* s = new BString(p, e ? (e - p) : -1);
840 			haikuList.AddItem(s);
841 			p = e;
842 			if (p && (*p == '%'))
843 				p++;
844 			if (p && (*p == '\n'))
845 				p++;
846 		}
847 	}
848 	free(buff);
849 	if (haikuList.CountItems() < 1)
850 		return;
851 
852 	BString* s = (BString*)haikuList.ItemAt(rand() % haikuList.CountItems());
853 	BFont font(be_bold_font);
854 	font.SetSize(be_bold_font->Size());
855 	font.SetFace(B_BOLD_FACE | B_ITALIC_FACE);
856 	fCreditsView->SelectAll();
857 	fCreditsView->Delete();
858 	fCreditsView->SetFontAndColor(&font, B_FONT_ALL, &fTextColor);
859 	fCreditsView->Insert(s->String());
860 	fCreditsView->Insert("\n");
861 	while ((s = (BString*)haikuList.RemoveItem((int32)0))) {
862 		delete s;
863 	}
864 }
865 
866 
867 void
868 AboutView::_AdjustTextColors()
869 {
870 	rgb_color textColor = ui_color(B_PANEL_TEXT_COLOR);
871 	rgb_color color = mix_color(ViewColor(), textColor, 192);
872 
873 	BView* view = NULL;
874 	for (int32 index = 0; index < fSubTextViews.CountItems(); ++index) {
875 		view = fSubTextViews.ItemAt(index);
876 		view->SetHighColor(color);
877 		view->Invalidate();
878 	}
879 
880 	// Labels
881 	for (int32 index = 0; index < fTextViews.CountItems(); ++index) {
882 		view = fTextViews.ItemAt(index);
883 		view->SetHighColor(textColor);
884 		view->Invalidate();
885 	}
886 }
887 
888 
889 BView*
890 AboutView::_CreateLabel(const char* name, const char* label)
891 {
892 	BStringView* labelView = new BStringView(name, label);
893 	labelView->SetExplicitAlignment(BAlignment(B_ALIGN_LEFT,
894 		B_ALIGN_VERTICAL_UNSET));
895 	labelView->SetFont(be_bold_font);
896 	fTextViews.AddItem(labelView);
897 	return labelView;
898 }
899 
900 
901 BView*
902 AboutView::_CreateCreditsView()
903 {
904 	// Begin construction of the credits view
905 	fCreditsView = new HyperTextView("credits");
906 	fCreditsView->SetFlags(fCreditsView->Flags() | B_FRAME_EVENTS);
907 	fCreditsView->SetStylable(true);
908 	fCreditsView->MakeEditable(false);
909 	fCreditsView->SetWordWrap(true);
910 	fCreditsView->SetInsets(5, 5, 5, 5);
911 	fCreditsView->SetViewUIColor(B_DOCUMENT_BACKGROUND_COLOR);
912 
913 	BScrollView* creditsScroller = new BScrollView("creditsScroller",
914 		fCreditsView, B_WILL_DRAW | B_FRAME_EVENTS, false, true,
915 		B_PLAIN_BORDER);
916 
917 	// Haiku copyright
918 	BFont font(be_bold_font);
919 	font.SetSize(font.Size() + 4);
920 
921 	fCreditsView->SetFontAndColor(&font, B_FONT_ALL, &fHaikuGreenColor);
922 	fCreditsView->Insert("Haiku\n");
923 
924 	char string[1024];
925 	time_t time = ::time(NULL);
926 	struct tm* tm = localtime(&time);
927 	int32 year = tm->tm_year + 1900;
928 	if (year < 2008)
929 		year = 2008;
930 	snprintf(string, sizeof(string),
931 		B_TRANSLATE(COPYRIGHT_STRING "2001-%" B_PRId32 " The Haiku project. "),
932 		year);
933 
934 	fCreditsView->SetFontAndColor(be_plain_font, B_FONT_ALL, &fTextColor);
935 	fCreditsView->Insert(string);
936 
937 	fCreditsView->SetFontAndColor(be_plain_font, B_FONT_ALL, &fTextColor);
938 	fCreditsView->Insert(B_TRANSLATE("The copyright to the Haiku code is "
939 		"property of Haiku, Inc. or of the respective authors where expressly "
940 		"noted in the source. Haiku" B_UTF8_REGISTERED
941 		" and the HAIKU logo" B_UTF8_REGISTERED
942 		" are registered trademarks of Haiku, Inc."
943 		"\n\n"));
944 
945 	fCreditsView->SetFontAndColor(be_plain_font, B_FONT_ALL, &fLinkColor);
946 	fCreditsView->InsertHyperText("https://www.haiku-os.org",
947 		new URLAction("https://www.haiku-os.org"));
948 	fCreditsView->Insert("\n\n");
949 
950 	font.SetSize(be_bold_font->Size());
951 	font.SetFace(B_BOLD_FACE | B_ITALIC_FACE);
952 
953 	fCreditsView->SetFontAndColor(&font, B_FONT_ALL, &fHaikuOrangeColor);
954 	fCreditsView->Insert(B_TRANSLATE("Current maintainers:\n"));
955 
956 	fCreditsView->SetFontAndColor(be_plain_font, B_FONT_ALL, &fTextColor);
957 	fCreditsView->Insert(kCurrentMaintainers);
958 
959 	fCreditsView->SetFontAndColor(&font, B_FONT_ALL, &fHaikuOrangeColor);
960 	fCreditsView->Insert(B_TRANSLATE("Past maintainers:\n"));
961 
962 	fCreditsView->SetFontAndColor(be_plain_font, B_FONT_ALL, &fTextColor);
963 	fCreditsView->Insert(kPastMaintainers);
964 
965 	fCreditsView->SetFontAndColor(&font, B_FONT_ALL, &fHaikuOrangeColor);
966 	fCreditsView->Insert(B_TRANSLATE("Website & marketing:\n"));
967 
968 	fCreditsView->SetFontAndColor(be_plain_font, B_FONT_ALL, &fTextColor);
969 	fCreditsView->Insert(kWebsiteTeam);
970 
971 	fCreditsView->SetFontAndColor(&font, B_FONT_ALL, &fHaikuOrangeColor);
972 	fCreditsView->Insert(B_TRANSLATE("Past website & marketing:\n"));
973 
974 	fCreditsView->SetFontAndColor(be_plain_font, B_FONT_ALL, &fTextColor);
975 	fCreditsView->Insert(kPastWebsiteTeam);
976 
977 	fCreditsView->SetFontAndColor(&font, B_FONT_ALL, &fHaikuOrangeColor);
978 	fCreditsView->Insert(B_TRANSLATE("Testing and bug triaging:\n"));
979 
980 	fCreditsView->SetFontAndColor(be_plain_font, B_FONT_ALL, &fTextColor);
981 	fCreditsView->Insert(kTestingTeam);
982 
983 	fCreditsView->SetFontAndColor(&font, B_FONT_ALL, &fHaikuOrangeColor);
984 	fCreditsView->Insert(B_TRANSLATE("Contributors:\n"));
985 
986 	fCreditsView->SetFontAndColor(be_plain_font, B_FONT_ALL, &fTextColor);
987 	fCreditsView->Insert(kContributors);
988 	fCreditsView->Insert(
989 		B_TRANSLATE("\n" B_UTF8_ELLIPSIS
990 			"and probably some more we forgot to mention (sorry!)"
991 			"\n\n"));
992 
993 	fCreditsView->SetFontAndColor(&font, B_FONT_ALL, &fHaikuOrangeColor);
994 	fCreditsView->Insert(B_TRANSLATE("Translations:\n"));
995 
996 	BLanguage* lang;
997 	BString langName;
998 
999 	BList sortedTranslations;
1000 	for (uint32 i = 0; i < kNumberOfTranslations; i ++) {
1001 		const Translation* translation = &kTranslations[i];
1002 		sortedTranslations.AddItem((void*)translation);
1003 	}
1004 	sortedTranslations.SortItems(TranslationComparator);
1005 
1006 	for (uint32 i = 0; i < kNumberOfTranslations; i ++) {
1007 		const Translation& translation
1008 			= *(const Translation*)sortedTranslations.ItemAt(i);
1009 
1010 		langName.Truncate(0);
1011 		if (BLocaleRoster::Default()->GetLanguage(translation.languageCode,
1012 				&lang) == B_OK) {
1013 			lang->GetName(langName);
1014 			delete lang;
1015 		} else {
1016 			// We failed to get the localized readable name,
1017 			// go with what we have.
1018 			langName.Append(translation.languageCode);
1019 		}
1020 
1021 		fCreditsView->SetFontAndColor(&font, B_FONT_ALL, &fHaikuGreenColor);
1022 		fCreditsView->Insert("\n");
1023 		fCreditsView->Insert(langName);
1024 		fCreditsView->Insert("\n");
1025 		fCreditsView->SetFontAndColor(be_plain_font, B_FONT_ALL, &fTextColor);
1026 		fCreditsView->Insert(translation.names);
1027 	}
1028 
1029 	fCreditsView->SetFontAndColor(&font, B_FONT_ALL, &fHaikuOrangeColor);
1030 	fCreditsView->Insert(B_TRANSLATE("\n\nSpecial thanks to:\n"));
1031 
1032 	fCreditsView->SetFontAndColor(be_plain_font, B_FONT_ALL, &fTextColor);
1033 	BString beosCredits(B_TRANSLATE(
1034 		"Be Inc. and its developer team, for having created BeOS!\n\n"));
1035 	int32 beosOffset = beosCredits.FindFirst("BeOS");
1036 	fCreditsView->Insert(beosCredits.String(),
1037 		(beosOffset < 0) ? beosCredits.Length() : beosOffset);
1038 	if (beosOffset > -1) {
1039 		fCreditsView->SetFontAndColor(be_plain_font, B_FONT_ALL, &fBeOSBlueColor);
1040 		fCreditsView->Insert("B");
1041 		fCreditsView->SetFontAndColor(be_plain_font, B_FONT_ALL, &fBeOSRedColor);
1042 		fCreditsView->Insert("e");
1043 		fCreditsView->SetFontAndColor(be_plain_font, B_FONT_ALL, &fTextColor);
1044 		beosCredits.Remove(0, beosOffset + 2);
1045 		fCreditsView->Insert(beosCredits.String(), beosCredits.Length());
1046 	}
1047 	fCreditsView->Insert(
1048 		B_TRANSLATE("Travis Geiselbrecht (and his NewOS kernel)\n"));
1049 	fCreditsView->Insert(
1050 		B_TRANSLATE("Michael Phipps (project founder)\n\n"));
1051 	fCreditsView->Insert(
1052 		B_TRANSLATE("The HaikuPorts team\n"));
1053 	fCreditsView->Insert(
1054 		B_TRANSLATE("The Haikuware team and their bounty program\n"));
1055 	fCreditsView->Insert(
1056 		B_TRANSLATE("The BeGeistert team\n"));
1057 	fCreditsView->Insert(
1058 		B_TRANSLATE("Google and their Google Summer of Code and Google Code In "
1059 			"programs\n"));
1060 	fCreditsView->Insert(
1061 		B_TRANSLATE("The University of Auckland and Christof Lutteroth\n\n"));
1062 	fCreditsView->Insert(
1063 		B_TRANSLATE(B_UTF8_ELLIPSIS "and the many people making donations!\n\n"));
1064 
1065 	// copyrights for various projects we use
1066 
1067 	BPath mitPath;
1068 	_GetLicensePath("MIT", mitPath);
1069 	BPath lgplPath;
1070 	_GetLicensePath("GNU LGPL v2.1", lgplPath);
1071 
1072 	font.SetSize(be_bold_font->Size() + 4);
1073 	font.SetFace(B_BOLD_FACE);
1074 	fCreditsView->SetFontAndColor(&font, B_FONT_ALL, &fHaikuGreenColor);
1075 	fCreditsView->Insert(B_TRANSLATE("\nCopyrights\n\n"));
1076 
1077 
1078 	// Haiku license
1079 	BString haikuLicense = B_TRANSLATE_COMMENT("The code that is unique to "
1080 		"Haiku, especially the kernel and all code that applications may link "
1081 		"against, is distributed under the terms of the <MIT license>. "
1082 		"Some system libraries contain third party code distributed under the "
1083 		"<LGPL license>. You can find the copyrights to third party code below."
1084 		"\n\n", "<MIT license> and <LGPL license> aren't variables and can be "
1085 		"translated. However, please, don't remove < and > as they're needed "
1086 		"as placeholders for proper hypertext functionality.");
1087 	int32 licensePart1 = haikuLicense.FindFirst("<");
1088 	int32 licensePart2 = haikuLicense.FindFirst(">");
1089 	int32 licensePart3 = haikuLicense.FindLast("<");
1090 	int32 licensePart4 = haikuLicense.FindLast(">");
1091 	BString part;
1092 	haikuLicense.CopyInto(part, 0, licensePart1);
1093 	fCreditsView->SetFontAndColor(be_plain_font, B_FONT_ALL, &fTextColor);
1094 	fCreditsView->Insert(part);
1095 
1096 	part.Truncate(0);
1097 	haikuLicense.CopyInto(part, licensePart1 + 1, licensePart2 - 1
1098 		- licensePart1);
1099 	fCreditsView->SetFontAndColor(be_plain_font, B_FONT_ALL, &fLinkColor);
1100 	fCreditsView->InsertHyperText(part, new OpenFileAction(mitPath.Path()));
1101 
1102 	part.Truncate(0);
1103 	haikuLicense.CopyInto(part, licensePart2 + 1, licensePart3 - 1
1104 		- licensePart2);
1105 	fCreditsView->SetFontAndColor(be_plain_font, B_FONT_ALL, &fTextColor);
1106 	fCreditsView->Insert(part);
1107 
1108 	part.Truncate(0);
1109 	haikuLicense.CopyInto(part, licensePart3 + 1, licensePart4 - 1
1110 		- licensePart3);
1111 	fCreditsView->SetFontAndColor(be_plain_font, B_FONT_ALL, &fLinkColor);
1112 	fCreditsView->InsertHyperText(part, new OpenFileAction(lgplPath.Path()));
1113 
1114 	part.Truncate(0);
1115 	haikuLicense.CopyInto(part, licensePart4 + 1, haikuLicense.Length() - 1
1116 		- licensePart4);
1117 	fCreditsView->SetFontAndColor(be_plain_font, B_FONT_ALL, &fTextColor);
1118 	fCreditsView->Insert(part);
1119 
1120 	// GNU copyrights
1121 	AddCopyrightEntry("The GNU Project",
1122 		B_TRANSLATE("Contains software from the GNU Project, "
1123 		"released under the GPL and LGPL licenses:\n"
1124 		"GNU C Library, "
1125 		"GNU coretools, diffutils, findutils, "
1126 		"sharutils, gawk, bison, m4, make, "
1127 		"wget, ncurses, termcap, "
1128 		"Bourne Again Shell.\n"
1129 		COPYRIGHT_STRING "The Free Software Foundation."),
1130 		StringVector(kLGPLv21, kGPLv2, kGPLv3, NULL),
1131 		StringVector(),
1132 		"https://www.gnu.org");
1133 
1134 	// FreeBSD copyrights
1135 	AddCopyrightEntry("The FreeBSD Project",
1136 		B_TRANSLATE("Contains software from the FreeBSD Project, "
1137 		"released under the BSD license:\n"
1138 		"ftpd, ping, telnet, telnetd, traceroute\n"
1139 		COPYRIGHT_STRING "1994-2008 The FreeBSD Project. "
1140 		"All rights reserved."),
1141 		StringVector(kBSDTwoClause, kBSDThreeClause, kBSDFourClause,
1142 			NULL),
1143 		StringVector(),
1144 		"https://www.freebsd.org");
1145 
1146 	// FFmpeg copyrights
1147 	_AddPackageCredit(PackageCredit("FFmpeg")
1148 		.SetCopyright(B_TRANSLATE(COPYRIGHT_STRING "2000-2019 Fabrice "
1149 			"Bellard, et al."))
1150 		.SetLicenses(kLGPLv21, kLGPLv2, NULL)
1151 		.SetURL("https://www.ffmpeg.org"));
1152 
1153 	// AGG copyrights
1154 	_AddPackageCredit(PackageCredit("AntiGrain Geometry")
1155 		.SetCopyright(B_TRANSLATE(COPYRIGHT_STRING "2002-2006 Maxim "
1156 			"Shemanarev (McSeem)."))
1157 		.SetLicenses("Anti-Grain Geometry", kBSDThreeClause, NULL)
1158 		.SetURL("http://www.antigrain.com"));
1159 
1160 	// FreeType copyrights
1161 	_AddPackageCredit(PackageCredit("FreeType2")
1162 		.SetCopyrights(B_TRANSLATE(COPYRIGHT_STRING "1996-2002, 2006 "
1163 			"David Turner, Robert Wilhelm and Werner Lemberg."),
1164 			COPYRIGHT_STRING "2014 The FreeType Project. "
1165 			"All rights reserved.",
1166 			NULL)
1167 		.SetLicense("FreeType")
1168 		.SetURL("http://www.freetype.org"));
1169 
1170 	// Mesa3D (http://www.mesa3d.org) copyrights
1171 	_AddPackageCredit(PackageCredit("Mesa")
1172 		.SetCopyright(B_TRANSLATE(COPYRIGHT_STRING "1999-2006 Brian Paul. "
1173 			"Mesa3D Project. All rights reserved."))
1174 		.SetLicense("MIT")
1175 		.SetURL("http://www.mesa3d.org"));
1176 
1177 	// SGI's GLU implementation copyrights
1178 	_AddPackageCredit(PackageCredit("GLU")
1179 		.SetCopyright(B_TRANSLATE(COPYRIGHT_STRING "1991-2000 "
1180 			"Silicon Graphics, Inc. All rights reserved."))
1181 		.SetLicense("SGI Free B")
1182 		.SetURL("http://www.sgi.com/products/software/opengl"));
1183 
1184 	// GLUT implementation copyrights
1185 	_AddPackageCredit(PackageCredit("GLUT")
1186 		.SetCopyrights(B_TRANSLATE(COPYRIGHT_STRING "1994-1997 Mark Kilgard. "
1187 			"All rights reserved."),
1188 			COPYRIGHT_STRING "1997 Be Inc.",
1189 			COPYRIGHT_STRING "1999 Jake Hamby.",
1190 			NULL)
1191 		.SetLicense("MIT")
1192 		.SetURL("http://www.opengl.org/resources/libraries/glut"));
1193 
1194 	// OpenGroup & DEC (BRegion backend) copyright
1195 	_AddPackageCredit(PackageCredit("BRegion backend (XFree86)")
1196 		.SetCopyrights(COPYRIGHT_STRING "1987-1988, 1998 The Open Group.",
1197 			B_TRANSLATE(COPYRIGHT_STRING "1987-1988 Digital Equipment "
1198 			"Corporation, Maynard, Massachusetts.\n"
1199 			"All rights reserved."),
1200 			NULL)
1201 		.SetLicenses("OpenGroup", "DEC", NULL)
1202 		.SetURL("https://xfree86.org"));
1203 
1204 	// Bitstream Charter font
1205 	_AddPackageCredit(PackageCredit("Bitstream Charter font")
1206 		.SetCopyrights(COPYRIGHT_STRING "1989-1992 Bitstream Inc.,"
1207 			"Cambridge, MA.",
1208 			B_TRANSLATE("BITSTREAM CHARTER is a registered trademark of "
1209 				"Bitstream Inc."),
1210 			NULL)
1211 		.SetLicense("Bitstream Charter")
1212 		.SetURL("http://www.bitstream.com/"));
1213 
1214 	// Noto fonts copyright
1215 	_AddPackageCredit(PackageCredit("Noto fonts")
1216 		.SetCopyrights(B_TRANSLATE(COPYRIGHT_STRING
1217 			"2012-2016 Google Internationalization team."),
1218 			NULL)
1219 		.SetLicense("SIL Open Font Licence v1.1")
1220 		.SetURL("http://www.google.com/get/noto/"));
1221 
1222 	// expat copyrights
1223 	_AddPackageCredit(PackageCredit("expat")
1224 		.SetCopyrights(B_TRANSLATE(COPYRIGHT_STRING "1998-2000 Thai "
1225 			"Open Source Software Center Ltd and Clark Cooper."),
1226 			B_TRANSLATE(COPYRIGHT_STRING "2001-2003 Expat maintainers."),
1227 			NULL)
1228 		.SetLicense("Expat")
1229 		.SetURL("http://expat.sourceforge.net"));
1230 
1231 	// zlib copyrights
1232 	_AddPackageCredit(PackageCredit("zlib")
1233 		.SetCopyright(B_TRANSLATE(COPYRIGHT_STRING "1995-2004 Jean-loup "
1234 			"Gailly and Mark Adler."))
1235 		.SetLicense("Zlib")
1236 		.SetURL("http://www.zlib.net"));
1237 
1238 	// zip copyrights
1239 	_AddPackageCredit(PackageCredit("Info-ZIP")
1240 		.SetCopyright(B_TRANSLATE(COPYRIGHT_STRING "1990-2002 Info-ZIP. "
1241 			"All rights reserved."))
1242 		.SetLicense("Info-ZIP")
1243 		.SetURL("http://www.info-zip.org"));
1244 
1245 	// bzip2 copyrights
1246 	_AddPackageCredit(PackageCredit("bzip2")
1247 		.SetCopyright(B_TRANSLATE(COPYRIGHT_STRING "1996-2005 Julian R "
1248 			"Seward. All rights reserved."))
1249 		.SetLicense(kBSDFourClause)
1250 		.SetURL("http://bzip.org"));
1251 
1252 	// OpenEXR copyrights
1253 	_AddPackageCredit(PackageCredit("OpenEXR")
1254 		.SetCopyright(B_TRANSLATE(COPYRIGHT_STRING "2002-2014 Industrial "
1255 			"Light & Magic, a division of Lucas Digital Ltd. LLC."))
1256 		.SetLicense(kBSDThreeClause)
1257 		.SetURL("http://www.openexr.com"));
1258 
1259 	// acpica copyrights
1260 	_AddPackageCredit(PackageCredit("ACPI Component Architecture (ACPICA)")
1261 		.SetCopyright(COPYRIGHT_STRING "1999-2018 Intel Corp.")
1262 		.SetLicense("Intel (ACPICA)")
1263 		.SetURL("https://www.acpica.org"));
1264 
1265 	// libpng copyrights
1266 	_AddPackageCredit(PackageCredit("libpng")
1267 		.SetCopyright(COPYRIGHT_STRING "1995-2017 libpng authors")
1268 		.SetLicense("LibPNG")
1269 		.SetURL("http://www.libpng.org"));
1270 
1271 	// libjpeg copyrights
1272 	_AddPackageCredit(PackageCredit("libjpeg")
1273 		.SetCopyright(B_TRANSLATE(COPYRIGHT_STRING "1994-2009, Thomas G. "
1274 			"Lane, Guido Vollbeding. This software is based in part on the "
1275 			"work of the Independent JPEG Group."))
1276 		.SetLicense("LibJPEG")
1277 		.SetURL("http://www.ijg.org"));
1278 
1279 	// libprint copyrights
1280 	_AddPackageCredit(PackageCredit("libprint")
1281 		.SetCopyright(B_TRANSLATE(COPYRIGHT_STRING "1999-2000 Y.Takagi. "
1282 			"All rights reserved.")));
1283 			// TODO: License!
1284 
1285 	// cortex copyrights
1286 	_AddPackageCredit(PackageCredit("Cortex")
1287 		.SetCopyright(COPYRIGHT_STRING "1999-2000 Eric Moon.")
1288 		.SetLicense(kBSDThreeClause)
1289 		.SetURL("http://cortex.sourceforge.net/documentation"));
1290 
1291 	// FluidSynth copyrights
1292 	_AddPackageCredit(PackageCredit("FluidSynth")
1293 		.SetCopyright(B_TRANSLATE(COPYRIGHT_STRING "2003 Peter Hanappe "
1294 			"and others."))
1295 		.SetLicense(kLGPLv2)
1296 		.SetURL("http://www.fluidsynth.org"));
1297 
1298 	// Xiph.org Foundation copyrights
1299 	_AddPackageCredit(PackageCredit("Xiph.org Foundation")
1300 		.SetCopyrights("libvorbis, libogg, libtheora, libspeex",
1301 			B_TRANSLATE(COPYRIGHT_STRING "1994-2008 Xiph.Org. "
1302 			"All rights reserved."), NULL)
1303 		.SetLicense(kBSDThreeClause)
1304 		.SetURL("http://www.xiph.org"));
1305 
1306 	// Matroska
1307 	_AddPackageCredit(PackageCredit("libmatroska")
1308 		.SetCopyright(B_TRANSLATE(COPYRIGHT_STRING "2002-2003 Steve Lhomme. "
1309 			"All rights reserved."))
1310 		.SetLicense(kLGPLv21)
1311 		.SetURL("http://www.matroska.org"));
1312 
1313 	// BColorQuantizer (originally CQuantizer code)
1314 	_AddPackageCredit(PackageCredit("CQuantizer")
1315 		.SetCopyright(B_TRANSLATE(COPYRIGHT_STRING "1996-1997 Jeff Prosise. "
1316 			"All rights reserved."))
1317 		.SetLicense("CQuantizer")
1318 		.SetURL("http://www.xdp.it"));
1319 
1320 	// MAPM (Mike's Arbitrary Precision Math Library) used by DeskCalc
1321 	_AddPackageCredit(PackageCredit("MAPM")
1322 		.SetCopyright(B_TRANSLATE(COPYRIGHT_STRING "1999-2007 Michael C. "
1323 			"Ring. All rights reserved."))
1324 		.SetLicense("MAPM")
1325 		.SetURL("http://tc.umn.edu/~ringx004"));
1326 
1327 	// MkDepend 1.7 copyright (Makefile dependency generator)
1328 	_AddPackageCredit(PackageCredit("MkDepend")
1329 		.SetCopyright(B_TRANSLATE(COPYRIGHT_STRING "1995-2001 Lars Düning. "
1330 			"All rights reserved."))
1331 		.SetLicense("MIT")
1332 		.SetURL("http://bearnip.com/lars/be"));
1333 
1334 	// libhttpd copyright (used as Poorman backend)
1335 	_AddPackageCredit(PackageCredit("libhttpd")
1336 		.SetCopyright(B_TRANSLATE(COPYRIGHT_STRING "1995, 1998-2001 "
1337 			"Jef Poskanzer. All rights reserved."))
1338 		.SetLicense(kBSDTwoClause)
1339 		.SetURL("http://www.acme.com/software/thttpd/"));
1340 
1341 #ifdef __i386__
1342 	// Udis86 copyrights
1343 	_AddPackageCredit(PackageCredit("Udis86")
1344 		.SetCopyright(B_TRANSLATE(COPYRIGHT_STRING "2002-2004 "
1345 			"Vivek Mohan. All rights reserved."))
1346 		.SetLicense(kBSDTwoClause)
1347 		.SetURL("http://udis86.sourceforge.net"));
1348 
1349 	// Intel PRO/Wireless 2100 & 2200BG firmwares
1350 	_AddPackageCredit(PackageCredit("Intel PRO/Wireless 2100 & 2200BG firmwares")
1351 		.SetCopyright(B_TRANSLATE(COPYRIGHT_STRING "2003-2006 "
1352 			"Intel Corporation. All rights reserved."))
1353 		.SetLicense(kIntel2xxxFirmware)
1354 		.SetURL("http://www.intellinuxwireless.org/"));
1355 
1356 	// Intel wireless firmwares
1357 	_AddPackageCredit(
1358 		PackageCredit("Intel PRO/Wireless network adapter firmwares")
1359 		.SetCopyright(B_TRANSLATE(COPYRIGHT_STRING "2006-2015 "
1360 			"Intel Corporation. All rights reserved."))
1361 		.SetLicense(kIntelFirmware)
1362 		.SetURL("http://www.intellinuxwireless.org/"));
1363 
1364 	// Marvell 88w8363
1365 	_AddPackageCredit(PackageCredit("Marvell 88w8363")
1366 		.SetCopyright(B_TRANSLATE(COPYRIGHT_STRING "2007-2009 "
1367 			"Marvell Semiconductor, Inc. All rights reserved."))
1368 		.SetLicense(kMarvellFirmware)
1369 		.SetURL("http://www.marvell.com/"));
1370 
1371 	// Ralink Firmware RT2501/RT2561/RT2661
1372 	_AddPackageCredit(PackageCredit("Ralink Firmware RT2501/RT2561/RT2661")
1373 		.SetCopyright(B_TRANSLATE(COPYRIGHT_STRING "2007 "
1374 			"Ralink Technology Corporation. All rights reserved."))
1375 		.SetLicense(kRalinkFirmware)
1376 		.SetURL("http://www.ralinktech.com/"));
1377 #endif
1378 
1379 	// Gutenprint
1380 	_AddPackageCredit(PackageCredit("Gutenprint")
1381 		.SetCopyright(B_TRANSLATE(COPYRIGHT_STRING
1382 			"1999-2010 by the authors of Gutenprint. All rights reserved."))
1383 		.SetLicense(kGPLv2)
1384 		.SetURL("http://gutenprint.sourceforge.net/"));
1385 
1386 	// libwebp
1387 	_AddPackageCredit(PackageCredit("libwebp")
1388 		.SetCopyright(B_TRANSLATE(COPYRIGHT_STRING
1389 			"2010-2011 Google Inc. All rights reserved."))
1390 		.SetLicense(kBSDThreeClause)
1391 		.SetURL("http://www.webmproject.org/code/#libwebp_webp_image_library"));
1392 
1393 	// GTF
1394 	_AddPackageCredit(PackageCredit("GTF")
1395 		.SetCopyright(B_TRANSLATE("2001 by Andy Ritger based on the "
1396 			"Generalized Timing Formula"))
1397 		.SetLicense(kBSDThreeClause)
1398 		.SetURL("http://gtf.sourceforge.net/"));
1399 
1400 	// libqrencode
1401 	_AddPackageCredit(PackageCredit("libqrencode")
1402 		.SetCopyright(B_TRANSLATE(COPYRIGHT_STRING "2006-2012 Kentaro Fukuchi"))
1403 		.SetLicense(kLGPLv21)
1404 		.SetURL("http://fukuchi.org/works/qrencode/"));
1405 
1406 	// scrypt
1407 	_AddPackageCredit(PackageCredit("scrypt")
1408 		.SetCopyright(B_TRANSLATE(COPYRIGHT_STRING "2009 Colin Percival"))
1409 		.SetLicense(kBSDTwoClause)
1410 		.SetURL("https://tarsnap.com/scrypt.html"));
1411 
1412 	_AddCopyrightsFromAttribute();
1413 	_AddPackageCreditEntries();
1414 
1415 	return new CropView(creditsScroller, 0, 1, 1, 1);
1416 }
1417 
1418 
1419 status_t
1420 AboutView::_GetLicensePath(const char* license, BPath& path)
1421 {
1422 	BPathFinder pathFinder;
1423 	BStringList paths;
1424 	struct stat st;
1425 
1426 	status_t error = pathFinder.FindPaths(B_FIND_PATH_DATA_DIRECTORY,
1427 		"licenses", paths);
1428 
1429 	for (int i = 0; i < paths.CountStrings(); ++i) {
1430 		if (error == B_OK && path.SetTo(paths.StringAt(i)) == B_OK
1431 			&& path.Append(license) == B_OK
1432 			&& lstat(path.Path(), &st) == 0) {
1433 			return B_OK;
1434 		}
1435 	}
1436 
1437 	path.Unset();
1438 	return B_ENTRY_NOT_FOUND;
1439 }
1440 
1441 
1442 void
1443 AboutView::_AddCopyrightsFromAttribute()
1444 {
1445 #ifdef __HAIKU__
1446 	// open the app executable file
1447 	char appPath[B_PATH_NAME_LENGTH];
1448 	int appFD;
1449 	if (BPrivate::get_app_path(appPath) != B_OK
1450 		|| (appFD = open(appPath, O_RDONLY)) < 0) {
1451 		return;
1452 	}
1453 
1454 	// open the attribute
1455 	int attrFD = fs_fopen_attr(appFD, "COPYRIGHTS", B_STRING_TYPE, O_RDONLY);
1456 	close(appFD);
1457 	if (attrFD < 0)
1458 		return;
1459 
1460 	// attach it to a FILE
1461 	FILE* attrFile = fdopen(attrFD, "r");
1462 	if (attrFile == NULL) {
1463 		close(attrFD);
1464 		return;
1465 	}
1466 	CObjectDeleter<FILE, int> _(attrFile, fclose);
1467 
1468 	// read and parse the copyrights
1469 	BMessage package;
1470 	BString fieldName;
1471 	BString fieldValue;
1472 	char lineBuffer[LINE_MAX];
1473 	while (char* line = fgets(lineBuffer, sizeof(lineBuffer), attrFile)) {
1474 		// chop off line break
1475 		size_t lineLen = strlen(line);
1476 		if (lineLen > 0 && line[lineLen - 1] == '\n')
1477 			line[--lineLen] = '\0';
1478 
1479 		// flush previous field, if a new field begins, otherwise append
1480 		if (lineLen == 0 || !isspace(line[0])) {
1481 			// new field -- flush the previous one
1482 			if (fieldName.Length() > 0) {
1483 				fieldValue = trim_string(fieldValue.String(),
1484 					fieldValue.Length());
1485 				package.AddString(fieldName.String(), fieldValue);
1486 				fieldName = "";
1487 			}
1488 		} else if (fieldName.Length() > 0) {
1489 			// append to current field
1490 			fieldValue += line;
1491 			continue;
1492 		} else {
1493 			// bogus line -- ignore
1494 			continue;
1495 		}
1496 
1497 		if (lineLen == 0)
1498 			continue;
1499 
1500 		// parse new field
1501 		char* colon = strchr(line, ':');
1502 		if (colon == NULL) {
1503 			// bogus line -- ignore
1504 			continue;
1505 		}
1506 
1507 		fieldName.SetTo(line, colon - line);
1508 		fieldName = trim_string(line, colon - line);
1509 		if (fieldName.Length() == 0) {
1510 			// invalid field name
1511 			continue;
1512 		}
1513 
1514 		fieldValue = colon + 1;
1515 
1516 		if (fieldName == "Package") {
1517 			// flush the current package
1518 			_AddPackageCredit(PackageCredit(package));
1519 			package.MakeEmpty();
1520 		}
1521 	}
1522 
1523 	// flush current package
1524 	_AddPackageCredit(PackageCredit(package));
1525 #endif
1526 }
1527 
1528 
1529 void
1530 AboutView::_AddPackageCreditEntries()
1531 {
1532 	// sort the packages case-insensitively
1533 	PackageCredit* packages[fPackageCredits.size()];
1534 	int32 count = 0;
1535 	for (PackageCreditMap::iterator it = fPackageCredits.begin();
1536 			it != fPackageCredits.end(); ++it) {
1537 		packages[count++] = it->second;
1538 	}
1539 
1540 	if (count > 1) {
1541 		std::sort(packages, packages + count,
1542 			&PackageCredit::NameLessInsensitive);
1543 	}
1544 
1545 	// add the credits
1546 	for (int32 i = 0; i < count; i++) {
1547 		PackageCredit* package = packages[i];
1548 
1549 		BString text(package->CopyrightAt(0));
1550 		int32 count = package->CountCopyrights();
1551 		for (int32 i = 1; i < count; i++)
1552 			text << "\n" << package->CopyrightAt(i);
1553 
1554 		AddCopyrightEntry(package->PackageName(), text.String(),
1555 			package->Licenses(), package->Sources(), package->URL());
1556 	}
1557 }
1558 
1559 
1560 void
1561 AboutView::_AddPackageCredit(const PackageCredit& package)
1562 {
1563 	if (!package.IsValid())
1564 		return;
1565 
1566 	PackageCreditMap::iterator it = fPackageCredits.find(package.PackageName());
1567 	if (it != fPackageCredits.end()) {
1568 		// If the new package credit isn't "better" than the old one, ignore it.
1569 		PackageCredit* oldPackage = it->second;
1570 		if (!package.IsBetterThan(*oldPackage))
1571 			return;
1572 
1573 		// replace the old credit
1574 		fPackageCredits.erase(it);
1575 		delete oldPackage;
1576 	}
1577 
1578 	fPackageCredits[package.PackageName()] = new PackageCredit(package);
1579 }
1580 
1581 
1582 //	#pragma mark -
1583 
1584 
1585 static const char*
1586 MemSizeToString(char string[], size_t size, system_info* info)
1587 {
1588 	int inaccessibleMemory = int(info->ignored_pages
1589 		* (B_PAGE_SIZE / 1048576.0f) + 0.5f);
1590 	if (inaccessibleMemory > 0) {
1591 		BString message(B_TRANSLATE("%total MiB total, %inaccessible MiB "
1592 			"inaccessible"));
1593 
1594 		snprintf(string, size, "%d", int((info->max_pages
1595 			+ info->ignored_pages) * (B_PAGE_SIZE / 1048576.0f) + 0.5f));
1596 		message.ReplaceFirst("%total", string);
1597 
1598 		snprintf(string, size, "%d", inaccessibleMemory);
1599 		message.ReplaceFirst("%inaccessible", string);
1600 		strlcpy(string, message.String(), size);
1601 	} else {
1602 		snprintf(string, size, B_TRANSLATE("%d MiB total"),
1603 			int(info->max_pages * (B_PAGE_SIZE / 1048576.0f) + 0.5f));
1604 	}
1605 
1606 	return string;
1607 }
1608 
1609 
1610 static const char*
1611 MemUsageToString(char string[], size_t size, system_info* info)
1612 {
1613 	snprintf(string, size, B_TRANSLATE("%d MiB used (%d%%)"),
1614 		int(info->used_pages * (B_PAGE_SIZE / 1048576.0f) + 0.5f),
1615 		int(100 * info->used_pages / info->max_pages));
1616 
1617 	return string;
1618 }
1619 
1620 
1621 static const char*
1622 UptimeToString(char string[], size_t size)
1623 {
1624 	BDurationFormat formatter;
1625 	BString str;
1626 
1627 	bigtime_t uptime = system_time();
1628 	bigtime_t now = (bigtime_t)time(NULL) * 1000000;
1629 	formatter.Format(str, now - uptime, now);
1630 	str.CopyInto(string, 0, size);
1631 	string[std::min((size_t)str.Length(), size)] = '\0';
1632 
1633 	return string;
1634 }
1635 
1636 
1637 int
1638 main()
1639 {
1640 	AboutApp app;
1641 	app.Run();
1642 	return 0;
1643 }
1644 
1645