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