xref: /haiku/src/apps/aboutsystem/AboutSystem.cpp (revision e0ef64750f3169cd634bb2f7a001e22488b05231)
1 /*
2  * Copyright (c) 2005-2010, Haiku, Inc.
3  * Distributed under the terms of the MIT license.
4  *
5  * Authors:
6  *		DarkWyrm <bpmagic@columbus.rr.com>
7  *		René Gollent
8  *		Wim van der Meer <WPJvanderMeer@gmail.com>
9  */
10 
11 
12 #include <ctype.h>
13 #include <stdio.h>
14 #include <sys/utsname.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 <DurationFormat.h>
26 #include <File.h>
27 #include <FindDirectory.h>
28 #include <Font.h>
29 #include <fs_attr.h>
30 #include <LayoutBuilder.h>
31 #include <MessageRunner.h>
32 #include <Messenger.h>
33 #include <OS.h>
34 #include <Path.h>
35 #include <Resources.h>
36 #include <Screen.h>
37 #include <ScrollView.h>
38 #include <String.h>
39 #include <StringView.h>
40 #include <TranslationUtils.h>
41 #include <TranslatorFormats.h>
42 #include <View.h>
43 #include <Volume.h>
44 #include <VolumeRoster.h>
45 #include <Window.h>
46 
47 #include <AppMisc.h>
48 #include <AutoDeleter.h>
49 #include <cpu_type.h>
50 
51 #include <Catalog.h>
52 #include <Language.h>
53 #include <Locale.h>
54 #include <LocaleRoster.h>
55 
56 #include "HyperTextActions.h"
57 #include "HyperTextView.h"
58 #include "Utilities.h"
59 
60 
61 #ifndef LINE_MAX
62 #define LINE_MAX 2048
63 #endif
64 
65 #define SCROLL_CREDITS_VIEW 'mviv'
66 
67 
68 static const char* UptimeToString(char string[], size_t size);
69 static const char* MemSizeToString(char string[], size_t size,
70 	system_info* info);
71 static const char* MemUsageToString(char string[], size_t size,
72 	system_info* info);
73 
74 
75 static const rgb_color kDarkGrey = { 100, 100, 100, 255 };
76 static const rgb_color kHaikuGreen = { 42, 131, 36, 255 };
77 static const rgb_color kHaikuOrange = { 255, 69, 0, 255 };
78 static const rgb_color kHaikuYellow = { 255, 176, 0, 255 };
79 static const rgb_color kLinkBlue = { 80, 80, 200, 255 };
80 
81 typedef struct
82 {
83 	const char* languageCode;
84 	const char* names;
85 } Translation;
86 
87 static const Translation gTranslations[] =
88 {
89 	{ "bg",
90 		"cssvb94\n"
91 	},
92 	{ "nl",
93 		"Meanwhile\n"
94 	},
95 	{ "eo",
96 		"Travis D. Reed (Dancxjo)\n"
97 	},
98 	{ "fi",
99 		"Jorma Karvonen (Karvjorm)\n"
100 		"Jaakko Leikas (Garjala)\n"
101 	},
102 	{ "fr",
103 		"Jean-Loïc Charroud\n"
104 		"Adrien Destugues (PulkoMandy)\n"
105 	},
106 	{ "da",
107 		"Brian Matzon\n"
108 	},
109 	{ "de",
110 		"Colin Günther\n"
111 		"leszek\n"
112 		"Christian Morgenroth\n"
113 		"Joachim Seemer (Humdinger)\n"
114 		"Matthias Spreiter\n"
115 		"svend\n"
116 	},
117 	{ "hu",
118 		"Zoltán Mizsei (miqlas)\n"
119 		"Zoltán Szabó (Bird)\n"
120 	},
121 	{ "it",
122 		"Andrea Bernardi\n"
123 	},
124 	{ "ja",
125 		"Satoshi Eguchi\n"
126 		"Hironori Ichimiya\n"
127 		"Jorge G. Mare (Koki)\n"
128 		"Takashi Murai\n"
129 		"SHINTA\n"
130 		"Hiroyuki Tsutsumi\n"
131 		"The JPBE.net user group\n"
132 	},
133 	{ "lt",
134 		"Algirdas Buckus\n"
135 	},
136 	{ "pl",
137 		"Hubert Hareńczyk\n"
138 		"Artur Wyszyński\n"
139 	},
140 	{ "pt",
141 		"Marcos Alves (Xeon3D)\n"
142 		"Vasco Costa (gluon)\n"
143 		"Michael Vinícius de Oliveira (michaelvo)\n"
144 	},
145 	{ "ru",
146 		"Tatyana Fursic (iceid)\n"
147 		"StoroZ Gneva\n"
148 		"Rustam Islamov (RISC aka HaikuBot)\n"
149 		"Eugene Katashov (mrNoisy)\n"
150 		"Reznikov Sergei (Diver)\n"
151 		"Michael Smirnov\n"
152 	},
153 	{ "es",
154 		"Nicolás C (CapitanPico)\n"
155 		"Oscar Carballal (oscarcp)\n"
156 		"Miguel Zúñiga González (miguel~1.mx)\n"
157 		"César Ortiz Pantoja (ccortiz)\n"
158 	},
159 	{ "sv",
160 		"Johan Holmberg\n"
161 		"Jimmy Olsson (phalax)\n"
162 		"Victor Widell\n"
163 	},
164 	{ "uk",
165 		"Alex Rudyk (totish)\n"
166 	},
167 	{ "zh",
168 		"Pengfei Han (kurain)\n"
169 	}
170 };
171 
172 #define kNumberOfTranslations (sizeof(gTranslations) / sizeof(Translation))
173 
174 
175 static int
176 TranslationComparator(const void* left, const void* right)
177 {
178 	const Translation* leftTranslation = *(const Translation**)left;
179 	const Translation* rightTranslation = *(const Translation**)right;
180 
181 	BLanguage* language;
182 	BString leftName;
183 	if (be_locale_roster->GetLanguage(leftTranslation->languageCode, &language)
184 			== B_OK) {
185 		language->GetTranslatedName(leftName);
186 		delete language;
187 	} else
188 		leftName = leftTranslation->languageCode;
189 
190 	BString rightName;
191 	if (be_locale_roster->GetLanguage(rightTranslation->languageCode, &language)
192 			== B_OK) {
193 		language->GetTranslatedName(rightName);
194 		delete language;
195 	} else
196 		rightName = rightTranslation->languageCode;
197 
198 	BCollator collator;
199 	be_locale_roster->GetDefaultCollator(&collator);
200 	return collator.Compare(leftName.String(), rightName.String());
201 }
202 
203 
204 class AboutApp : public BApplication {
205 public:
206 							AboutApp();
207 };
208 
209 
210 class AboutWindow : public BWindow {
211 public:
212 							AboutWindow();
213 
214 	virtual	bool			QuitRequested();
215 };
216 
217 
218 class LogoView : public BView {
219 public:
220 							LogoView();
221 	virtual					~LogoView();
222 
223 	virtual	BSize			MinSize();
224 	virtual	BSize			MaxSize();
225 
226 	virtual void			Draw(BRect updateRect);
227 
228 private:
229 			BBitmap*		fLogo;
230 };
231 
232 
233 class CropView : public BView {
234 public:
235 							CropView(BView* target, int32 left, int32 top,
236 								int32 right, int32 bottom);
237 	virtual					~CropView();
238 
239 	virtual	BSize			MinSize();
240 	virtual	BSize			MaxSize();
241 
242 	virtual void			DoLayout();
243 
244 private:
245 			BView*			fTarget;
246 			int32			fCropLeft;
247 			int32			fCropTop;
248 			int32			fCropRight;
249 			int32			fCropBottom;
250 };
251 
252 
253 class AboutView : public BView {
254 public:
255 							AboutView();
256 							~AboutView();
257 
258 	virtual void			AttachedToWindow();
259 	virtual void			Pulse();
260 
261 	virtual void			MessageReceived(BMessage* msg);
262 	virtual void			MouseDown(BPoint point);
263 
264 			void			AddCopyrightEntry(const char* name,
265 								const char* text,
266 								const StringVector& licenses,
267 								const StringVector& sources,
268 								const char* url);
269 			void			AddCopyrightEntry(const char* name,
270 								const char* text, const char* url = NULL);
271 			void			PickRandomHaiku();
272 
273 
274 private:
275 	typedef std::map<std::string, PackageCredit*> PackageCreditMap;
276 
277 private:
278 			BView*			_CreateLabel(const char* name, const char* label);
279 			BView*			_CreateCreditsView();
280 			status_t		_GetLicensePath(const char* license,
281 								BPath& path);
282 			void			_AddCopyrightsFromAttribute();
283 			void			_AddPackageCredit(const PackageCredit& package);
284 			void			_AddPackageCreditEntries();
285 
286 			BStringView*	fMemView;
287 			BTextView*		fUptimeView;
288 			BView*			fInfoView;
289 			HyperTextView*	fCreditsView;
290 
291 			BBitmap*		fLogo;
292 
293 			bigtime_t		fLastActionTime;
294 			BMessageRunner*	fScrollRunner;
295 			PackageCreditMap fPackageCredits;
296 };
297 
298 
299 //	#pragma mark -
300 
301 
302 AboutApp::AboutApp()
303 	: BApplication("application/x-vnd.Haiku-About")
304 {
305 	AboutWindow *window = new(std::nothrow) AboutWindow();
306 	if (window)
307 		window->Show();
308 }
309 
310 
311 //	#pragma mark -
312 
313 
314 #undef B_TRANSLATE_CONTEXT
315 #define B_TRANSLATE_CONTEXT "AboutWindow"
316 
317 AboutWindow::AboutWindow()
318 	: BWindow(BRect(0, 0, 500, 300), B_TRANSLATE("About this system"),
319 		B_TITLED_WINDOW, B_AUTO_UPDATE_SIZE_LIMITS | B_NOT_ZOOMABLE)
320 {
321 	SetLayout(new BGroupLayout(B_VERTICAL));
322 	AddChild(new AboutView());
323 
324 	// Make sure we take the minimal window size into account when centering
325 	BSize size = GetLayout()->MinSize();
326 	ResizeTo(max_c(size.width, Bounds().Width()),
327 		max_c(size.height, Bounds().Height()));
328 
329 	CenterOnScreen();
330 }
331 
332 
333 bool
334 AboutWindow::QuitRequested()
335 {
336 	be_app->PostMessage(B_QUIT_REQUESTED);
337 	return true;
338 }
339 
340 
341 //	#pragma mark - LogoView
342 
343 
344 LogoView::LogoView()
345 	: BView("logo", B_WILL_DRAW)
346 {
347 	fLogo = BTranslationUtils::GetBitmap(B_PNG_FORMAT, "logo.png");
348 	SetViewColor(255, 255, 255);
349 }
350 
351 
352 LogoView::~LogoView()
353 {
354 	delete fLogo;
355 }
356 
357 
358 BSize
359 LogoView::MinSize()
360 {
361 	if (fLogo == NULL)
362 		return BSize(0, 0);
363 
364 	return BSize(fLogo->Bounds().Width(), fLogo->Bounds().Height());
365 }
366 
367 
368 BSize
369 LogoView::MaxSize()
370 {
371 	if (fLogo == NULL)
372 		return BSize(0, 0);
373 
374 	return BSize(B_SIZE_UNLIMITED, fLogo->Bounds().Height());
375 }
376 
377 
378 void
379 LogoView::Draw(BRect updateRect)
380 {
381 	if (fLogo != NULL) {
382 		DrawBitmap(fLogo,
383 			BPoint((Bounds().Width() - fLogo->Bounds().Width()) / 2, 0));
384 	}
385 }
386 
387 
388 //	#pragma mark - CropView
389 
390 
391 CropView::CropView(BView* target, int32 left, int32 top, int32 right,
392 		int32 bottom)
393 	: BView("crop view", 0),
394 	fTarget(target),
395 	fCropLeft(left),
396 	fCropTop(top),
397 	fCropRight(right),
398 	fCropBottom(bottom)
399 {
400 	AddChild(target);
401 }
402 
403 
404 CropView::~CropView()
405 {
406 }
407 
408 
409 BSize
410 CropView::MinSize()
411 {
412 	if (fTarget == NULL)
413 		return BSize();
414 
415 	BSize size = fTarget->MinSize();
416 	if (size.width != B_SIZE_UNSET)
417 		size.width -= fCropLeft + fCropRight;
418 	if (size.height != B_SIZE_UNSET)
419 		size.height -= fCropTop + fCropBottom;
420 
421 	return size;
422 }
423 
424 
425 BSize
426 CropView::MaxSize()
427 {
428 	if (fTarget == NULL)
429 		return BSize();
430 
431 	BSize size = fTarget->MaxSize();
432 	if (size.width != B_SIZE_UNSET)
433 		size.width -= fCropLeft + fCropRight;
434 	if (size.height != B_SIZE_UNSET)
435 		size.height -= fCropTop + fCropBottom;
436 
437 	return size;
438 }
439 
440 
441 void
442 CropView::DoLayout()
443 {
444 	BView::DoLayout();
445 
446 	if (fTarget == NULL)
447 		return;
448 
449 	fTarget->MoveTo(-fCropLeft, -fCropTop);
450 	fTarget->ResizeTo(Bounds().Width() + fCropLeft + fCropRight,
451 		Bounds().Height() + fCropTop + fCropBottom);
452 }
453 
454 
455 //	#pragma mark - AboutView
456 
457 #undef B_TRANSLATE_CONTEXT
458 #define B_TRANSLATE_CONTEXT "AboutView"
459 
460 AboutView::AboutView()
461 	: BView("aboutview", B_WILL_DRAW | B_PULSE_NEEDED),
462 	fLastActionTime(system_time()),
463 	fScrollRunner(NULL)
464 {
465 	// Begin Construction of System Information controls
466 
467 	system_info systemInfo;
468 	get_system_info(&systemInfo);
469 
470 	// Create all the various labels for system infomation
471 
472 	// OS Version
473 
474 	char string[1024];
475 	strcpy(string, B_TRANSLATE("Unknown"));
476 
477 	// the version is stored in the BEOS:APP_VERSION attribute of libbe.so
478 	BPath path;
479 	if (find_directory(B_BEOS_LIB_DIRECTORY, &path) == B_OK) {
480 		path.Append("libbe.so");
481 
482 		BAppFileInfo appFileInfo;
483 		version_info versionInfo;
484 		BFile file;
485 		if (file.SetTo(path.Path(), B_READ_ONLY) == B_OK
486 			&& appFileInfo.SetTo(&file) == B_OK
487 			&& appFileInfo.GetVersionInfo(&versionInfo,
488 				B_APP_VERSION_KIND) == B_OK
489 			&& versionInfo.short_info[0] != '\0')
490 			strcpy(string, versionInfo.short_info);
491 	}
492 
493 	// Add revision from uname() info
494 	utsname unameInfo;
495 	if (uname(&unameInfo) == 0) {
496 		long revision;
497 		if (sscanf(unameInfo.version, "r%ld", &revision) == 1) {
498 			char version[16];
499 			snprintf(version, sizeof(version), "%ld", revision);
500 			strlcat(string, " (", sizeof(string));
501 			strlcat(string, B_TRANSLATE("Revision"), sizeof(string));
502 			strlcat(string, " ", sizeof(string));
503 			strlcat(string, version, sizeof(string));
504 			strlcat(string, ")", sizeof(string));
505 		}
506 	}
507 
508 	BStringView* versionView = new BStringView("ostext", string);
509 	versionView->SetExplicitAlignment(BAlignment(B_ALIGN_LEFT,
510 		B_ALIGN_VERTICAL_UNSET));
511 
512 	// GCC version
513 	BEntry gccFourHybrid("/boot/system/lib/gcc2/libstdc++.r4.so");
514 	BEntry gccTwoHybrid("/boot/system/lib/gcc4/libsupc++.so");
515 	if (gccFourHybrid.Exists() || gccTwoHybrid.Exists())
516 		snprintf(string, sizeof(string), B_TRANSLATE("GCC %d Hybrid"),
517 			__GNUC__);
518 	else
519 		snprintf(string, sizeof(string), "GCC %d", __GNUC__);
520 
521 	BStringView* gccView = new BStringView("gcctext", string);
522 	gccView->SetExplicitAlignment(BAlignment(B_ALIGN_LEFT,
523 		B_ALIGN_VERTICAL_UNSET));
524 
525 	// CPU count, type and clock speed
526 	char processorLabel[256];
527 	if (systemInfo.cpu_count > 1) {
528 		snprintf(processorLabel, sizeof(processorLabel),
529 			B_TRANSLATE("%ld Processors:"), systemInfo.cpu_count);
530 	} else
531 		strlcpy(processorLabel, B_TRANSLATE("Processor:"),
532 			sizeof(processorLabel));
533 
534 	BString cpuType;
535 	cpuType << get_cpu_vendor_string(systemInfo.cpu_type)
536 		<< " " << get_cpu_model_string(&systemInfo);
537 
538 	BStringView* cpuView = new BStringView("cputext", cpuType.String());
539 	cpuView->SetExplicitAlignment(BAlignment(B_ALIGN_LEFT,
540 		B_ALIGN_VERTICAL_UNSET));
541 
542 	int32 clockSpeed = get_rounded_cpu_speed();
543 	if (clockSpeed < 1000)
544 		sprintf(string, B_TRANSLATE("%ld MHz"), clockSpeed);
545 	else
546 		sprintf(string, B_TRANSLATE("%.2f GHz"), clockSpeed / 1000.0f);
547 
548 	BStringView* frequencyView = new BStringView("frequencytext", string);
549 	frequencyView->SetExplicitAlignment(BAlignment(B_ALIGN_LEFT,
550 		B_ALIGN_VERTICAL_UNSET));
551 
552 	// RAM
553 	BStringView *memSizeView = new BStringView("ramsizetext",
554 		MemSizeToString(string, sizeof(string), &systemInfo));
555 	memSizeView->SetExplicitAlignment(BAlignment(B_ALIGN_LEFT,
556 		B_ALIGN_VERTICAL_UNSET));
557 	fMemView = new BStringView("ramtext",
558 		MemUsageToString(string, sizeof(string), &systemInfo));
559 	fMemView->SetExplicitAlignment(BAlignment(B_ALIGN_LEFT,
560 		B_ALIGN_VERTICAL_UNSET));
561 
562 	// Kernel build time/date
563 	snprintf(string, sizeof(string), "%s %s",
564 		systemInfo.kernel_build_date, systemInfo.kernel_build_time);
565 
566 	BStringView* kernelView = new BStringView("kerneltext", string);
567 	kernelView->SetExplicitAlignment(BAlignment(B_ALIGN_LEFT,
568 		B_ALIGN_VERTICAL_UNSET));
569 
570 	// Uptime
571 	fUptimeView = new BTextView("uptimetext");
572 	fUptimeView->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
573 	fUptimeView->MakeEditable(false);
574 	fUptimeView->MakeSelectable(false);
575 	fUptimeView->SetWordWrap(true);
576 
577 	fUptimeView->SetText(UptimeToString(string, sizeof(string)));
578 
579 	const float offset = 5;
580 
581 	SetLayout(new BGroupLayout(B_HORIZONTAL));
582 	SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
583 
584 	BLayoutBuilder::Group<>((BGroupLayout*)GetLayout())
585 		.AddGroup(B_VERTICAL)
586 			.Add(new LogoView())
587 			.AddGroup(B_VERTICAL)
588 				.Add(_CreateLabel("oslabel", B_TRANSLATE("Version:")))
589 				.Add(versionView)
590 				.Add(gccView)
591 				.AddStrut(offset)
592 				.Add(_CreateLabel("cpulabel", processorLabel))
593 				.Add(cpuView)
594 				.Add(frequencyView)
595 				.AddStrut(offset)
596 				.Add(_CreateLabel("memlabel", B_TRANSLATE("Memory:")))
597 				.Add(memSizeView)
598 				.Add(fMemView)
599 				.AddStrut(offset)
600 				.Add(_CreateLabel("kernellabel", B_TRANSLATE("Kernel:")))
601 				.Add(kernelView)
602 				.AddStrut(offset)
603 				.Add(_CreateLabel("uptimelabel",
604 					B_TRANSLATE("Time running:")))
605 				.Add(fUptimeView)
606 				.SetInsets(5, 5, 5, 5)
607 			.End()
608 			// TODO: investigate: adding this causes the time to be cut
609 			//.AddGlue()
610 		.End()
611 		.Add(_CreateCreditsView());
612 
613 	float min = fMemView->MinSize().width * 1.1f;
614 	fCreditsView->SetExplicitMinSize(BSize(min, min));
615 }
616 
617 
618 AboutView::~AboutView()
619 {
620 	delete fScrollRunner;
621 }
622 
623 
624 void
625 AboutView::AttachedToWindow()
626 {
627 	BView::AttachedToWindow();
628 	Window()->SetPulseRate(500000);
629 	SetEventMask(B_POINTER_EVENTS);
630 }
631 
632 
633 void
634 AboutView::MouseDown(BPoint point)
635 {
636 	BRect r(92, 26, 105, 31);
637 	if (r.Contains(point)) {
638 		printf("Easter egg\n");
639 		PickRandomHaiku();
640 	}
641 
642 	if (Bounds().Contains(point)) {
643 		fLastActionTime = system_time();
644 		delete fScrollRunner;
645 		fScrollRunner = NULL;
646 	}
647 }
648 
649 
650 void
651 AboutView::Pulse()
652 {
653 	char string[255];
654 	system_info info;
655 	get_system_info(&info);
656 	fUptimeView->SetText(UptimeToString(string, sizeof(string)));
657 	fMemView->SetText(MemUsageToString(string, sizeof(string), &info));
658 
659 	if (fScrollRunner == NULL
660 		&& system_time() > fLastActionTime + 10000000) {
661 		BMessage message(SCROLL_CREDITS_VIEW);
662 		//fScrollRunner = new BMessageRunner(this, &message, 25000, -1);
663 	}
664 }
665 
666 
667 void
668 AboutView::MessageReceived(BMessage* msg)
669 {
670 	switch (msg->what) {
671 		case SCROLL_CREDITS_VIEW:
672 		{
673 			BScrollBar* scrollBar =
674 				fCreditsView->ScrollBar(B_VERTICAL);
675 			if (scrollBar == NULL)
676 				break;
677 			float max, min;
678 			scrollBar->GetRange(&min, &max);
679 			if (scrollBar->Value() < max)
680 				fCreditsView->ScrollBy(0, 1);
681 
682 			break;
683 		}
684 
685 		default:
686 			BView::MessageReceived(msg);
687 			break;
688 	}
689 }
690 
691 
692 void
693 AboutView::AddCopyrightEntry(const char* name, const char* text,
694 	const char* url)
695 {
696 	AddCopyrightEntry(name, text, StringVector(), StringVector(), url);
697 }
698 
699 
700 void
701 AboutView::AddCopyrightEntry(const char* name, const char* text,
702 	const StringVector& licenses, const StringVector& sources,
703 	const char* url)
704 {
705 	BFont font(be_bold_font);
706 	//font.SetSize(be_bold_font->Size());
707 	font.SetFace(B_BOLD_FACE | B_ITALIC_FACE);
708 
709 	fCreditsView->SetFontAndColor(&font, B_FONT_ALL, &kHaikuYellow);
710 	fCreditsView->Insert(name);
711 	fCreditsView->Insert("\n");
712 	fCreditsView->SetFontAndColor(be_plain_font, B_FONT_ALL, &kDarkGrey);
713 	fCreditsView->Insert(text);
714 	fCreditsView->Insert("\n");
715 
716 	if (licenses.CountStrings() > 0) {
717 		if (licenses.CountStrings() > 1)
718 			fCreditsView->Insert(B_TRANSLATE("Licenses: "));
719 		else
720 			fCreditsView->Insert(B_TRANSLATE("License: "));
721 
722 		for (int32 i = 0; i < licenses.CountStrings(); i++) {
723 			const char* license = licenses.StringAt(i);
724 
725 			if (i > 0)
726 				fCreditsView->Insert(", ");
727 
728 			BString licenseName;
729 			BString licenseURL;
730 			parse_named_url(license, licenseName, licenseURL);
731 
732 			BPath licensePath;
733 			if (_GetLicensePath(licenseURL, licensePath) == B_OK) {
734 				fCreditsView->InsertHyperText(licenseName,
735 					new OpenFileAction(licensePath.Path()));
736 			} else
737 				fCreditsView->Insert(licenseName);
738 		}
739 
740 		fCreditsView->Insert("\n");
741 	}
742 
743 	if (sources.CountStrings() > 0) {
744 		fCreditsView->Insert(B_TRANSLATE("Source Code: "));
745 
746 		for (int32 i = 0; i < sources.CountStrings(); i++) {
747 			const char* source = sources.StringAt(i);
748 
749 			if (i > 0)
750 				fCreditsView->Insert(", ");
751 
752 			BString urlName;
753 			BString urlAddress;
754 			parse_named_url(source, urlName, urlAddress);
755 
756 			fCreditsView->SetFontAndColor(be_plain_font, B_FONT_ALL,
757 				&kLinkBlue);
758 			fCreditsView->InsertHyperText(urlName,
759 				new URLAction(urlAddress));
760 		}
761 
762 		fCreditsView->Insert("\n");
763 	}
764 
765 	if (url) {
766 		BString urlName;
767 		BString urlAddress;
768 		parse_named_url(url, urlName, urlAddress);
769 
770 		fCreditsView->SetFontAndColor(be_plain_font, B_FONT_ALL,
771 			&kLinkBlue);
772 		fCreditsView->InsertHyperText(urlName,
773 			new URLAction(urlAddress));
774 		fCreditsView->Insert("\n");
775 	}
776 	fCreditsView->Insert("\n");
777 }
778 
779 
780 void
781 AboutView::PickRandomHaiku()
782 {
783 	BFile fortunes(
784 #ifdef __HAIKU__
785 		"/etc/fortunes/Haiku",
786 #else
787 		"data/etc/fortunes/Haiku",
788 #endif
789 		B_READ_ONLY);
790 	struct stat st;
791 	if (fortunes.InitCheck() < B_OK)
792 		return;
793 	if (fortunes.GetStat(&st) < B_OK)
794 		return;
795 	char* buff = (char*)malloc((size_t)st.st_size + 1);
796 	if (!buff)
797 		return;
798 	buff[(size_t)st.st_size] = '\0';
799 	BList haikuList;
800 	if (fortunes.Read(buff, (size_t)st.st_size) == (ssize_t)st.st_size) {
801 		char* p = buff;
802 		while (p && *p) {
803 			char* e = strchr(p, '%');
804 			BString* s = new BString(p, e ? (e - p) : -1);
805 			haikuList.AddItem(s);
806 			p = e;
807 			if (p && (*p == '%'))
808 				p++;
809 			if (p && (*p == '\n'))
810 				p++;
811 		}
812 	}
813 	free(buff);
814 	if (haikuList.CountItems() < 1)
815 		return;
816 	BString* s = (BString*)haikuList.ItemAt(rand() % haikuList.CountItems());
817 	BFont font(be_bold_font);
818 	font.SetSize(be_bold_font->Size());
819 	font.SetFace(B_BOLD_FACE | B_ITALIC_FACE);
820 	fCreditsView->SelectAll();
821 	fCreditsView->Delete();
822 	fCreditsView->SetFontAndColor(&font, B_FONT_ALL, &kDarkGrey);
823 	fCreditsView->Insert(s->String());
824 	fCreditsView->Insert("\n");
825 	while ((s = (BString*)haikuList.RemoveItem((int32)0))) {
826 		delete s;
827 	}
828 }
829 
830 
831 BView*
832 AboutView::_CreateLabel(const char* name, const char* label)
833 {
834 	BStringView* labelView = new BStringView(name, label);
835 	labelView->SetExplicitAlignment(BAlignment(B_ALIGN_LEFT,
836 		B_ALIGN_VERTICAL_UNSET));
837 	labelView->SetFont(be_bold_font);
838 	return labelView;
839 }
840 
841 
842 BView*
843 AboutView::_CreateCreditsView()
844 {
845 	// Begin construction of the credits view
846 	fCreditsView = new HyperTextView("credits");
847 	fCreditsView->SetFlags(fCreditsView->Flags() | B_FRAME_EVENTS);
848 	fCreditsView->SetStylable(true);
849 	fCreditsView->MakeEditable(false);
850 	fCreditsView->SetWordWrap(true);
851 	fCreditsView->SetInsets(5, 5, 5, 5);
852 	fCreditsView->SetViewColor(ui_color(B_DOCUMENT_BACKGROUND_COLOR));
853 
854 	BScrollView* creditsScroller = new BScrollView("creditsScroller",
855 		fCreditsView, B_WILL_DRAW | B_FRAME_EVENTS, false, true,
856 		B_PLAIN_BORDER);
857 
858 	// Haiku copyright
859 	BFont font(be_bold_font);
860 	font.SetSize(font.Size() + 4);
861 
862 	fCreditsView->SetFontAndColor(&font, B_FONT_ALL, &kHaikuGreen);
863 	fCreditsView->Insert("Haiku\n");
864 
865 	char string[1024];
866 	time_t time = ::time(NULL);
867 	struct tm* tm = localtime(&time);
868 	int32 year = tm->tm_year + 1900;
869 	if (year < 2008)
870 		year = 2008;
871 	snprintf(string, sizeof(string),
872 		COPYRIGHT_STRING "2001-%ld The Haiku project. ", year);
873 
874 	fCreditsView->SetFontAndColor(be_plain_font, B_FONT_ALL, &kDarkGrey);
875 	fCreditsView->Insert(string);
876 
877 	fCreditsView->SetFontAndColor(be_plain_font, B_FONT_ALL, &kDarkGrey);
878 	fCreditsView->Insert(B_TRANSLATE("The copyright to the Haiku code is "
879 		"property of Haiku, Inc. or of the respective authors where expressly "
880 		"noted in the source. Haiku and the Haiku logo are trademarks of "
881 		"Haiku, Inc."
882 		"\n\n"));
883 
884 	fCreditsView->SetFontAndColor(be_plain_font, B_FONT_ALL, &kLinkBlue);
885 	fCreditsView->InsertHyperText("http://www.haiku-os.org",
886 		new URLAction("http://www.haiku-os.org"));
887 	fCreditsView->Insert("\n\n");
888 
889 	font.SetSize(be_bold_font->Size());
890 	font.SetFace(B_BOLD_FACE | B_ITALIC_FACE);
891 
892 	fCreditsView->SetFontAndColor(&font, B_FONT_ALL, &kHaikuOrange);
893 	fCreditsView->Insert(B_TRANSLATE("Current maintainers:\n"));
894 
895 	fCreditsView->SetFontAndColor(be_plain_font, B_FONT_ALL, &kDarkGrey);
896 	fCreditsView->Insert(
897 		"Ithamar R. Adema\n"
898 		"Bruno G. Albuquerque\n"
899 		"Stephan Aßmus\n"
900 		"Salvatore Benedetto\n"
901 		"Stefano Ceccherini\n"
902 		"Rudolf Cornelissen\n"
903 		"Alexandre Deckner\n"
904 		"Adrien Destugues\n"
905 		"Oliver Ruiz Dorantes\n"
906 		"Axel Dörfler\n"
907 		"Jérôme Duval\n"
908 		"René Gollent\n"
909 		"Bryce Groff\n"
910 		"Colin Günther\n"
911 		"Karsten Heimrich\n"
912 		"Fredrik Holmqvist\n"
913 		"Philippe Houdoin\n"
914 		"Maurice Kalinowski\n"
915 		"Euan Kirkhope\n"
916 		"Ryan Leavengood\n"
917 		"Michael Lotz\n"
918 		"Brecht Machiels\n"
919 		"Matt Madia\n"
920 		"Scott McCreary\n"
921 		"David McPaul\n"
922 		"Wim van der Meer\n"
923 		"Fredrik Modéen\n"
924 		"Marcus Overhagen\n"
925 		"Michael Pfeiffer\n"
926 		"François Revol\n"
927 		"Philippe Saint-Pierre\n"
928 		"Andrej Spielmann\n"
929 		"Jonas Sundström\n"
930 		"Oliver Tappe\n"
931 		"Gerasim Troeglazov\n"
932 		"Ingo Weinhold\n"
933 		"Alex Wilson\n"
934 		"Artur Wyszyński\n"
935 		"Clemens Zeidler\n"
936 		"Siarzhuk Zharski\n"
937 		"\n");
938 
939 	fCreditsView->SetFontAndColor(&font, B_FONT_ALL, &kHaikuOrange);
940 	fCreditsView->Insert(B_TRANSLATE("Past maintainers:\n"));
941 
942 	fCreditsView->SetFontAndColor(be_plain_font, B_FONT_ALL, &kDarkGrey);
943 	fCreditsView->Insert(
944 		"Andrew Bachmann\n"
945 		"Tyler Dauwalder\n"
946 		"Daniel Furrer\n"
947 		"Andre Alves Garzia\n"
948 		"Erik Jaesler\n"
949 		"Marcin Konicki\n"
950 		"Waldemar Kornewald\n"
951 		"Thomas Kurschel\n"
952 		"Frans Van Nispen\n"
953 		"Adi Oanca\n"
954 		"Michael Phipps\n"
955 		"Niels Sascha Reedijk\n"
956 		"David Reid\n"
957 		"Hugo Santos\n"
958 		"Alexander G. M. Smith\n"
959 		"Bryan Varner\n"
960 		"Nathan Whitehorn\n"
961 		"Michael Wilber\n"
962 		"Jonathan Yoder\n"
963 		"Gabe Yoder\n"
964 		"\n");
965 
966 	fCreditsView->SetFontAndColor(&font, B_FONT_ALL, &kHaikuOrange);
967 	fCreditsView->Insert(B_TRANSLATE("Website, marketing & documentation:\n"));
968 
969 	fCreditsView->SetFontAndColor(be_plain_font, B_FONT_ALL, &kDarkGrey);
970 	fCreditsView->Insert(
971 		"Phil Greenway\n"
972 		"Gavin James\n"
973 		"Jorge G. Mare (aka Koki)\n"
974 		"Urias McCullough\n"
975 		"Niels Sascha Reedijk\n"
976 		"Joachim Seemer (Humdinger)\n"
977 		"Jonathan Yoder\n"
978 		"\n");
979 
980 	fCreditsView->SetFontAndColor(&font, B_FONT_ALL, &kHaikuOrange);
981 	fCreditsView->Insert(B_TRANSLATE("Contributors:\n"));
982 
983 	fCreditsView->SetFontAndColor(be_plain_font, B_FONT_ALL, &kDarkGrey);
984 	fCreditsView->Insert(
985 		"Andrea Anzani\n"
986 		"Sean Bartell\n"
987 		"Andre Braga\n"
988 		"Bruce Cameron\n"
989 		"Greg Crain\n"
990 		"David Dengg\n"
991 		"John Drinkwater\n"
992 		"Cian Duffy\n"
993 		"Vincent Duvert\n"
994 		"Fredrik Ekdahl\n"
995 		"Joshua R. Elsasser\n"
996 		"Atis Elsts\n"
997 		"Mark Erben\n"
998 		"Christian Fasshauer\n"
999 		"Andreas Färber\n"
1000 		"Janito Ferreira Filho\n"
1001 		"Marc Flerackers\n"
1002 		"Michele Frau (zuMi)\n"
1003 		"Pete Goodeve\n"
1004 		"Lucian Adrian Grijincu\n"
1005 		"Matthijs Hollemans\n"
1006 		"Mathew Hounsell\n"
1007 		"Morgan Howe\n"
1008 		"Christophe Huriaux\n"
1009 		"Ma Jie\n"
1010 		"Carwyn Jones\n"
1011 		"Vasilis Kaoutsis\n"
1012 		"James Kim\n"
1013 		"Shintaro Kinugawa\n"
1014 		"Jan Klötzke\n"
1015 		"Kurtis Kopf\n"
1016 		"Tomáš Kučera\n"
1017 		"Luboš Kulič\n"
1018 		"Elad Lahav\n"
1019 		"Anthony Lee\n"
1020 		"Santiago Lema\n"
1021 		"Raynald Lesieur\n"
1022 		"Oscar Lesta\n"
1023 		"Jerome Leveque\n"
1024 		"Christof Lutteroth\n"
1025 		"Graham MacDonald\n"
1026 		"Jan Matějek\n"
1027 		"Brian Matzon\n"
1028 		"Christopher ML Zumwalt May\n"
1029 		"Andrew McCall\n"
1030 		"Nathan Mentley\n"
1031 		"Marius Middelthon\n"
1032 		"Marco Minutoli\n"
1033 		"Misza\n"
1034 		"MrSiggler\n"
1035 		"Alan Murta\n"
1036 		"Raghuram Nagireddy\n"
1037 		"Jeroen Oortwijn (idefix)\n"
1038 		"Pahtz\n"
1039 		"Michael Paine\n"
1040 		"Adrian Panasiuk\n"
1041 		"Romain Picard\n"
1042 		"Francesco Piccinno\n"
1043 		"David Powell\n"
1044 		"Jeremy Rand\n"
1045 		"Hartmut Reh\n"
1046 		"Daniel Reinhold\n"
1047 		"Chris Roberts\n"
1048 		"Samuel Rodríguez Pérez\n"
1049 		"Thomas Roell\n"
1050 		"Rafael Romo\n"
1051 		"Ralf Schülke\n"
1052 		"John Scipione\n"
1053 		"Reznikov Sergei\n"
1054 		"Zousar Shaker\n"
1055 		"Caitlin Shaw\n"
1056 		"Daniel Switkin\n"
1057 		"Atsushi Takamatsu\n"
1058 		"James Urquhart\n"
1059 		"Jason Vandermark\n"
1060 		"Sandor Vroemisse\n"
1061 		"Denis Washington\n"
1062 		"Ulrich Wimboeck\n"
1063 		"Johannes Wischert\n"
1064 		"James Woodcock\n"
1065 		"Hong Yul Yang\n"
1066 		"Gerald Zajac\n"
1067 		"Łukasz Zemczak\n"
1068 		"JiSheng Zhang\n"
1069 		"Zhao Shuai\n");
1070 	fCreditsView->Insert(
1071 		B_TRANSLATE("\n" B_UTF8_ELLIPSIS
1072 			" and probably some more we forgot to mention (sorry!)"
1073 			"\n\n"));
1074 
1075 	fCreditsView->SetFontAndColor(&font, B_FONT_ALL, &kHaikuOrange);
1076 	fCreditsView->Insert(B_TRANSLATE("Translations:\n"));
1077 
1078 	BLanguage* lang;
1079 	BString langName;
1080 
1081 	BList sortedTranslations;
1082 	for (uint32 i = 0; i < kNumberOfTranslations; i ++) {
1083 		const Translation* translation = &gTranslations[i];
1084 		sortedTranslations.AddItem((void*)translation);
1085 	}
1086 	sortedTranslations.SortItems(TranslationComparator);
1087 
1088 	for (uint32 i = 0; i < kNumberOfTranslations; i ++) {
1089 		const Translation& translation
1090 			= *(const Translation*)sortedTranslations.ItemAt(i);
1091 
1092 		langName.Truncate(0);
1093 		if (be_locale_roster->GetLanguage(translation.languageCode, &lang)
1094 			== B_OK) {
1095 			lang->GetTranslatedName(langName);
1096 			delete lang;
1097 		} else {
1098 			// We failed to get the localized readable name,
1099 			// go with what we have.
1100 			langName.Append(translation.languageCode);
1101 		}
1102 
1103 		fCreditsView->SetFontAndColor(&font, B_FONT_ALL, &kHaikuGreen);
1104 		fCreditsView->Insert("\n");
1105 		fCreditsView->Insert(langName);
1106 		fCreditsView->Insert("\n");
1107 		fCreditsView->SetFontAndColor(be_plain_font, B_FONT_ALL, &kDarkGrey);
1108 		fCreditsView->Insert(
1109 			translation.names
1110 		);
1111 	}
1112 
1113 	fCreditsView->SetFontAndColor(&font, B_FONT_ALL, &kHaikuOrange);
1114 	fCreditsView->Insert(B_TRANSLATE("\n\nSpecial thanks to:\n"));
1115 
1116 	fCreditsView->SetFontAndColor(be_plain_font, B_FONT_ALL, &kDarkGrey);
1117 	fCreditsView->Insert(
1118 		B_TRANSLATE("Travis Geiselbrecht (and his NewOS kernel)\n"));
1119 	fCreditsView->Insert(
1120 		B_TRANSLATE("Michael Phipps (project founder)\n\n"));
1121 	fCreditsView->Insert(
1122 		B_TRANSLATE("The Haiku-Ports team\n"));
1123 	fCreditsView->Insert(
1124 		B_TRANSLATE("The Haikuware team and their bounty program\n"));
1125 	fCreditsView->Insert(
1126 		B_TRANSLATE("The BeGeistert team\n"));
1127 	fCreditsView->Insert(
1128 		B_TRANSLATE("Google & their Google Summer of Code program\n\n"));
1129 	fCreditsView->Insert(
1130 		B_TRANSLATE("... and the many people making donations!\n\n"));
1131 
1132 	// copyrights for various projects we use
1133 
1134 	BPath mitPath;
1135 	_GetLicensePath("MIT", mitPath);
1136 
1137 	font.SetSize(be_bold_font->Size() + 4);
1138 	font.SetFace(B_BOLD_FACE);
1139 	fCreditsView->SetFontAndColor(&font, B_FONT_ALL, &kHaikuGreen);
1140 	fCreditsView->Insert(B_TRANSLATE("\nCopyrights\n\n"));
1141 
1142 	fCreditsView->SetFontAndColor(be_plain_font, B_FONT_ALL, &kDarkGrey);
1143 	fCreditsView->Insert(B_TRANSLATE("[Click a license name to read the "
1144 		"respective license.]\n\n"));
1145 
1146 	// Haiku license
1147 	BString haikuLicence = B_TRANSLATE("The code that is unique to Haiku, "
1148 		"especially the kernel and all code that applications may link "
1149 		"against, is distributed under the terms of the %MIT licence%. "
1150 		"Some system libraries contain third party code distributed under the "
1151 		"LGPL license. You can find the copyrights to third party code below.\n"
1152 		"\n");
1153 	int32 licencePart1 = haikuLicence.FindFirst("%");
1154 	int32 licencePart2 = haikuLicence.FindLast("%");
1155 	BString part;
1156 	haikuLicence.CopyCharsInto(part, 0, licencePart1 );
1157 	fCreditsView->Insert(part);
1158 
1159 	part.Truncate(0);
1160 	haikuLicence.CopyCharsInto(part, licencePart1 + 1, licencePart2 - 1
1161 		- licencePart1);
1162 	fCreditsView->InsertHyperText(part, new OpenFileAction(mitPath.Path()));
1163 
1164 	part.Truncate(0);
1165 	haikuLicence.CopyCharsInto(part, licencePart2 + 1, haikuLicence.CountChars()
1166 		- licencePart2);
1167 	fCreditsView->Insert(part);
1168 
1169 	// GNU copyrights
1170 	AddCopyrightEntry("The GNU Project",
1171 		"Contains software from the GNU Project, "
1172 		"released under the GPL and LGPL licenses:\n"
1173 		"GNU C Library, "
1174 		"GNU coretools, diffutils, findutils, "
1175 		"sharutils, gawk, bison, m4, make, "
1176 		"gdb, wget, ncurses, termcap, "
1177 		"Bourne Again Shell.\n"
1178 		COPYRIGHT_STRING "The Free Software Foundation.",
1179 		StringVector("GNU LGPL v2.1", "GNU GPL v2", "GNU GPL v3", NULL),
1180 		StringVector(),
1181 		"http://www.gnu.org");
1182 
1183 	// FreeBSD copyrights
1184 	AddCopyrightEntry("The FreeBSD Project",
1185 		"Contains software from the FreeBSD Project, "
1186 		"released under the BSD license:\n"
1187 		"cal, ftpd, ping, telnet, "
1188 		"telnetd, traceroute\n"
1189 		COPYRIGHT_STRING "1994-2008 The FreeBSD Project.  "
1190 		"All rights reserved.",
1191 		"http://www.freebsd.org");
1192 			// TODO: License!
1193 
1194 	// NetBSD copyrights
1195 	AddCopyrightEntry("The NetBSD Project",
1196 		"Contains software developed by the NetBSD, "
1197 		"Foundation, Inc. and its contributors:\n"
1198 		"ftp, tput\n"
1199 		COPYRIGHT_STRING "1996-2008 The NetBSD Foundation, Inc.  "
1200 		"All rights reserved.",
1201 		"http://www.netbsd.org");
1202 			// TODO: License!
1203 
1204 	// FFMpeg copyrights
1205 	_AddPackageCredit(PackageCredit("FFMpeg libavcodec")
1206 		.SetCopyright(COPYRIGHT_STRING "2000-2007 Fabrice Bellard, et al.")
1207 		.SetLicenses("GNU LGPL v2.1", "GNU LGPL v2", NULL)
1208 		.SetURL("http://www.ffmpeg.org"));
1209 
1210 	// AGG copyrights
1211 	_AddPackageCredit(PackageCredit("AntiGrain Geometry")
1212 		.SetCopyright(COPYRIGHT_STRING "2002-2006 Maxim Shemanarev (McSeem).")
1213 		.SetLicenses("Anti-Grain Geometry", "BSD (3-clause)", "GPC", NULL)
1214 		.SetURL("http://www.antigrain.com"));
1215 
1216 	// PDFLib copyrights
1217 	_AddPackageCredit(PackageCredit("PDFLib")
1218 		.SetCopyright(COPYRIGHT_STRING "1997-2006 PDFlib GmbH and Thomas Merz. "
1219 			"All rights reserved.\n"
1220 			"PDFlib and PDFlib logo are registered trademarks of PDFlib GmbH.")
1221 		.SetLicense("PDFlib Lite")
1222 		.SetURL("http://www.pdflib.com"));
1223 
1224 	// FreeType copyrights
1225 	_AddPackageCredit(PackageCredit("FreeType2")
1226 		.SetCopyright("Portions of this software are copyright "
1227 			B_UTF8_COPYRIGHT " 1996-2006 "
1228 			"The FreeType Project.  All rights reserved.")
1229 		.SetLicense("FTL")
1230 		.SetURL("http://www.freetype.org"));
1231 
1232 	// Mesa3D (http://www.mesa3d.org) copyrights
1233 	_AddPackageCredit(PackageCredit("Mesa")
1234 		.SetCopyright(COPYRIGHT_STRING "1999-2006 Brian Paul. "
1235 			"Mesa3D Project.  All rights reserved.")
1236 		.SetLicense("MIT")
1237 		.SetURL("http://www.mesa3d.org"));
1238 
1239 	// SGI's GLU implementation copyrights
1240 	_AddPackageCredit(PackageCredit("GLU")
1241 		.SetCopyright(COPYRIGHT_STRING
1242 			"1991-2000 Silicon Graphics, Inc. "
1243 			"SGI's Software FreeB license.  All rights reserved.")
1244 		.SetLicense("SGI Free B")
1245 		.SetURL("http://www.sgi.com/products/software/opengl"));
1246 
1247 	// GLUT implementation copyrights
1248 	_AddPackageCredit(PackageCredit("GLUT")
1249 		.SetCopyrights(COPYRIGHT_STRING "1994-1997 Mark Kilgard. "
1250 			"All rights reserved.",
1251 			COPYRIGHT_STRING "1997 Be Inc.",
1252 			COPYRIGHT_STRING "1999 Jake Hamby.",
1253 			NULL)
1254 		.SetLicense("GLUT (Mark Kilgard)")
1255 		.SetURL("http://www.opengl.org/resources/libraries/glut"));
1256 
1257 	// OpenGroup & DEC (BRegion backend) copyright
1258 	_AddPackageCredit(PackageCredit("BRegion backend (XFree86)")
1259 		.SetCopyrights(COPYRIGHT_STRING "1987, 1988, 1998 The Open Group.",
1260 			COPYRIGHT_STRING "1987, 1988 Digital Equipment "
1261 			"Corporation, Maynard, Massachusetts.\n"
1262 			"All rights reserved.",
1263 			NULL)
1264 		.SetLicenses("OpenGroup", "DEC", NULL));
1265 			// TODO: URL
1266 
1267 	// VL-Gothic font
1268 	_AddPackageCredit(PackageCredit("VL-Gothic font")
1269 		.SetCopyrights(COPYRIGHT_STRING "1990-2003 Wada Laboratory,"
1270 			" the University of Tokyo", COPYRIGHT_STRING
1271 			"2003-2004 Electronic Font Open Laboratory (/efont/)",
1272 			COPYRIGHT_STRING "2003-2008 M+ FONTS PROJECT",
1273 			COPYRIGHT_STRING "2006-2009 Daisuke SUZUKI",
1274 			COPYRIGHT_STRING "2006-2009 Project Vine",
1275 			"MIT license. All rights reserved.",
1276 			NULL));
1277 			// TODO: License!
1278 
1279 	// expat copyrights
1280 	_AddPackageCredit(PackageCredit("expat")
1281 		.SetCopyrights(COPYRIGHT_STRING
1282 			"1998, 1999, 2000 Thai Open Source "
1283 			"Software Center Ltd and Clark Cooper.",
1284 			COPYRIGHT_STRING "2001, 2002, 2003 Expat maintainers.",
1285 			NULL)
1286 		.SetLicense("Expat")
1287 		.SetURL("http://expat.sourceforge.net"));
1288 
1289 	// zlib copyrights
1290 	_AddPackageCredit(PackageCredit("zlib")
1291 		.SetCopyright(COPYRIGHT_STRING
1292 			"1995-2004 Jean-loup Gailly and Mark Adler.")
1293 		.SetLicense("Zlib")
1294 		.SetURL("http://www.zlib.net"));
1295 
1296 	// zip copyrights
1297 	_AddPackageCredit(PackageCredit("Info-ZIP")
1298 		.SetCopyright(COPYRIGHT_STRING
1299 			"1990-2002 Info-ZIP. All rights reserved.")
1300 		.SetLicense("Info-ZIP")
1301 		.SetURL("http://www.info-zip.org"));
1302 
1303 	// bzip2 copyrights
1304 	_AddPackageCredit(PackageCredit("bzip2")
1305 		.SetCopyright(COPYRIGHT_STRING
1306 			"1996-2005 Julian R Seward. All rights reserved.")
1307 		.SetLicense("BSD (4-clause)")
1308 		.SetURL("http://bzip.org"));
1309 
1310 	// lp_solve copyrights
1311 	_AddPackageCredit(PackageCredit("lp_solve")
1312 		.SetCopyright(COPYRIGHT_STRING
1313 			"Michel Berkelaar, Kjell Eikland, Peter Notebaert")
1314 		.SetLicense("GNU LGPL v2.1")
1315 		.SetURL("http://lpsolve.sourceforge.net/"));
1316 
1317 	// OpenEXR copyrights
1318 	_AddPackageCredit(PackageCredit("OpenEXR")
1319 		.SetCopyright(COPYRIGHT_STRING "2002-2005 Industrial Light & Magic, "
1320 			"a division of Lucas Digital Ltd. LLC.")
1321 		.SetLicense("BSD (3-clause)")
1322 		.SetURL("http://www.openexr.com"));
1323 
1324 	// Bullet copyrights
1325 	_AddPackageCredit(PackageCredit("Bullet")
1326 		.SetCopyright(COPYRIGHT_STRING "2003-2008 Erwin Coumans")
1327 		.SetLicense("Bullet")
1328 		.SetURL("http://www.bulletphysics.com"));
1329 
1330 	// atftp copyrights
1331 	_AddPackageCredit(PackageCredit("atftp")
1332 		.SetCopyright(COPYRIGHT_STRING
1333 			"2000 Jean-Pierre ervbefeL and Remi Lefebvre")
1334 		.SetLicense("GNU GPL v2"));
1335 			// TODO: URL!
1336 
1337 	// Netcat copyrights
1338 	_AddPackageCredit(PackageCredit("Netcat")
1339 		.SetCopyright(COPYRIGHT_STRING "1996 Hobbit"));
1340 			// TODO: License!
1341 
1342 	// acpica copyrights
1343 	_AddPackageCredit(PackageCredit("acpica")
1344 		.SetCopyright(COPYRIGHT_STRING "1999-2006 Intel Corp.")
1345 		.SetLicense("Intel (ACPICA)")
1346 		.SetURL("http://www.acpica.org"));
1347 
1348 	// unrar copyrights
1349 	_AddPackageCredit(PackageCredit("unrar")
1350 		.SetCopyright(COPYRIGHT_STRING "2002-2008 Alexander L. Roshal. "
1351 			"All rights reserved.")
1352 		.SetLicense("UnRAR")
1353 		.SetURL("http://www.rarlab.com"));
1354 
1355 	// libpng copyrights
1356 	_AddPackageCredit(PackageCredit("libpng")
1357 		.SetCopyright(COPYRIGHT_STRING "2004, 2006-2008 Glenn "
1358 			"Randers-Pehrson.")
1359 		.SetLicense("LibPNG")
1360 		.SetURL("http://www.libpng.org"));
1361 
1362 	// libjpeg copyrights
1363 	_AddPackageCredit(PackageCredit("libjpeg")
1364 		.SetCopyright(COPYRIGHT_STRING " 1994-2009, Thomas G. Lane,"
1365 			" Guido Vollbeding. This software is based in part on the "
1366 			"work of the Independent JPEG Group")
1367 		.SetLicense("LibJPEG")
1368 		.SetURL("http://www.ijg.org"));
1369 
1370 	// libprint copyrights
1371 	_AddPackageCredit(PackageCredit("libprint")
1372 		.SetCopyright(COPYRIGHT_STRING
1373 			"1999-2000 Y.Takagi. All rights reserved."));
1374 			// TODO: License!
1375 
1376 	// cortex copyrights
1377 	_AddPackageCredit(PackageCredit("Cortex")
1378 		.SetCopyright(COPYRIGHT_STRING "1999-2000 Eric Moon.")
1379 		.SetLicense("BSD (3-clause)")
1380 		.SetURL("http://cortex.sourceforge.net/documentation"));
1381 
1382 	// FluidSynth copyrights
1383 	_AddPackageCredit(PackageCredit("FluidSynth")
1384 		.SetCopyright(COPYRIGHT_STRING "2003 Peter Hanappe and others.")
1385 		.SetLicense("GNU LGPL v2")
1386 		.SetURL("http://www.fluidsynth.org"));
1387 
1388 	// CannaIM copyrights
1389 	_AddPackageCredit(PackageCredit("CannaIM")
1390 		.SetCopyright(COPYRIGHT_STRING "1999 Masao Kawamura."));
1391 			// TODO: License!
1392 
1393 	// libxml2, libxslt, libexslt copyrights
1394 	_AddPackageCredit(PackageCredit("libxml2, libxslt")
1395 		.SetCopyright(COPYRIGHT_STRING
1396 			"1998-2003 Daniel Veillard. All rights reserved.")
1397 		.SetLicense("MIT (no promotion)")
1398 		.SetURL("http://xmlsoft.org"));
1399 
1400 	_AddPackageCredit(PackageCredit("libexslt")
1401 		.SetCopyright(COPYRIGHT_STRING
1402 			"2001-2002 Thomas Broyer, Charlie "
1403 			"Bozeman and Daniel Veillard.  All rights reserved.")
1404 		.SetLicense("MIT (no promotion)")
1405 		.SetURL("http://xmlsoft.org"));
1406 
1407 	// Xiph.org Foundation copyrights
1408 	_AddPackageCredit(PackageCredit("Xiph.org Foundation")
1409 		.SetCopyrights("libvorbis, libogg, libtheora, libspeex",
1410 			COPYRIGHT_STRING "1994-2008 Xiph.Org. "
1411 			"All rights reserved.",
1412 			NULL)
1413 		.SetLicense("BSD (3-clause)")
1414 		.SetURL("http://www.xiph.org"));
1415 
1416 	// The Tcpdump Group
1417 	_AddPackageCredit(PackageCredit("The Tcpdump Group")
1418 		.SetCopyright("tcpdump, libpcap")
1419 		.SetLicense("BSD (3-clause)")
1420 		.SetURL("http://www.tcpdump.org"));
1421 
1422 	// Matroska
1423 	_AddPackageCredit(PackageCredit("libmatroska")
1424 		.SetCopyright(COPYRIGHT_STRING "2002-2003 Steve Lhomme. "
1425 			"All rights reserved.")
1426 		.SetLicense("GNU LGPL v2.1")
1427 		.SetURL("http://www.matroska.org"));
1428 
1429 	// BColorQuantizer (originally CQuantizer code)
1430 	_AddPackageCredit(PackageCredit("CQuantizer")
1431 		.SetCopyright(COPYRIGHT_STRING "1996-1997 Jeff Prosise. "
1432 			"All rights reserved.")
1433 		.SetLicense("CQuantizer")
1434 		.SetURL("http://www.xdp.it"));
1435 
1436 	// MAPM (Mike's Arbitrary Precision Math Library) used by DeskCalc
1437 	_AddPackageCredit(PackageCredit("MAPM")
1438 		.SetCopyright(COPYRIGHT_STRING
1439 			"1999-2007 Michael C. Ring. All rights reserved.")
1440 		.SetLicense("MAPM")
1441 		.SetURL("http://tc.umn.edu/~ringx004"));
1442 
1443 	// MkDepend 1.7 copyright (Makefile dependency generator)
1444 	_AddPackageCredit(PackageCredit("MkDepend")
1445 		.SetCopyright(COPYRIGHT_STRING "1995-2001 Lars Düning. "
1446 			"All rights reserved.")
1447 		.SetLicense("MkDepend")
1448 		.SetURL("http://bearnip.com/lars/be"));
1449 
1450 	// libhttpd copyright (used as Poorman backend)
1451 	_AddPackageCredit(PackageCredit("libhttpd")
1452 		.SetCopyright(COPYRIGHT_STRING
1453 			"1995,1998,1999,2000,2001 by "
1454 			"Jef Poskanzer. All rights reserved.")
1455 		.SetLicense("LibHTTPd")
1456 		.SetURL("http://www.acme.com/software/thttpd/"));
1457 
1458 #ifdef __INTEL__
1459 	// Udis86 copyrights
1460 	_AddPackageCredit(PackageCredit("Udis86")
1461 		.SetCopyright(COPYRIGHT_STRING "2002, 2003, 2004 Vivek Mohan. "
1462 			"All rights reserved.")
1463 		.SetURL("http://udis86.sourceforge.net"));
1464 			// TODO: License!
1465 #endif
1466 
1467 #ifdef __INTEL__
1468 	// Intel PRO/Wireless 2100 Firmware
1469 	_AddPackageCredit(PackageCredit("Intel PRO/Wireless 2100 Firmware")
1470 		.SetCopyright(COPYRIGHT_STRING
1471 			"2003-2006 by "
1472 			"Intel Corporation. All rights reserved.")
1473 		.SetLicense("Intel (2xxx firmware)")
1474 		.SetURL("http://ipw2100.sourceforge.net/"));
1475 #endif
1476 
1477 #ifdef __INTEL__
1478 	// Intel PRO/Wireless 2200BG Firmware
1479 	_AddPackageCredit(PackageCredit("Intel PRO/Wireless 2200BG Firmware")
1480 		.SetCopyright(COPYRIGHT_STRING
1481 			"2004-2005 by "
1482 			"Intel Corporation. All rights reserved.")
1483 		.SetLicense("Intel (2xxx firmware)")
1484 		.SetURL("http://ipw2200.sourceforge.net/"));
1485 #endif
1486 
1487 #ifdef __INTEL__
1488 	// Intel PRO/Wireless 3945ABG/BG Network Connection Adapter Firmware
1489 	_AddPackageCredit(
1490 		PackageCredit(
1491 			"Intel PRO/Wireless 3945ABG/BG Network Connection Adapter Firmware")
1492 		.SetCopyright(COPYRIGHT_STRING
1493 			"2006 - 2007 by "
1494 			"Intel Corporation. All rights reserved.")
1495 		.SetLicense("Intel (firmware)")
1496 		.SetURL("http://www.intellinuxwireless.org/"));
1497 #endif
1498 #ifdef __INTEL__
1499 	// Intel Wireless WiFi Link 4965AGN Adapter Firmware
1500 	_AddPackageCredit(
1501 		PackageCredit("Intel Wireless WiFi Link 4965AGN Adapter Firmware")
1502 		.SetCopyright(COPYRIGHT_STRING
1503 			"2006 - 2007 by "
1504 			"Intel Corporation. All rights reserved.")
1505 		.SetLicense("Intel (firmware)")
1506 		.SetURL("http://www.intellinuxwireless.org/"));
1507 #endif
1508 
1509 #ifdef __INTEL__
1510 	// Marvell 88w8363
1511 	_AddPackageCredit(PackageCredit("Marvell 88w8363")
1512 		.SetCopyright(COPYRIGHT_STRING
1513 			"2007-2009 by "
1514 			"Marvell Semiconductor, Inc. All rights reserved.")
1515 		.SetLicense("Marvell (firmware)")
1516 		.SetURL("http://www.marvell.com/"));
1517 #endif
1518 
1519 #ifdef __INTEL__
1520 	// Ralink Firmware RT2501/RT2561/RT2661
1521 	_AddPackageCredit(PackageCredit("Ralink Firmware RT2501/RT2561/RT2661")
1522 		.SetCopyright(COPYRIGHT_STRING
1523 			"2007 by "
1524 			"Ralink Technology Corporation. All rights reserved.")
1525 		.SetLicense("Ralink (firmware)")
1526 		.SetURL("http://www.ralinktech.com/"));
1527 #endif
1528 
1529 	_AddCopyrightsFromAttribute();
1530 	_AddPackageCreditEntries();
1531 
1532 	return new CropView(creditsScroller, 0, 1, 1, 1);
1533 }
1534 
1535 
1536 status_t
1537 AboutView::_GetLicensePath(const char* license, BPath& path)
1538 {
1539 	static const directory_which directoryConstants[] = {
1540 		B_USER_DATA_DIRECTORY,
1541 		B_COMMON_DATA_DIRECTORY,
1542 		B_SYSTEM_DATA_DIRECTORY
1543 	};
1544 	static const int dirCount = 3;
1545 
1546 	for (int i = 0; i < dirCount; i++) {
1547 		struct stat st;
1548 		status_t error = find_directory(directoryConstants[i], &path);
1549 		if (error == B_OK && path.Append("licenses") == B_OK
1550 			&& path.Append(license) == B_OK
1551 			&& lstat(path.Path(), &st) == 0) {
1552 			return B_OK;
1553 		}
1554 	}
1555 
1556 	path.Unset();
1557 	return B_ENTRY_NOT_FOUND;
1558 }
1559 
1560 
1561 void
1562 AboutView::_AddCopyrightsFromAttribute()
1563 {
1564 #ifdef __HAIKU__
1565 	// open the app executable file
1566 	char appPath[B_PATH_NAME_LENGTH];
1567 	int appFD;
1568 	if (BPrivate::get_app_path(appPath) != B_OK
1569 		|| (appFD = open(appPath, O_RDONLY)) < 0) {
1570 		return;
1571 	}
1572 
1573 	// open the attribute
1574 	int attrFD = fs_fopen_attr(appFD, "COPYRIGHTS", B_STRING_TYPE, O_RDONLY);
1575 	close(appFD);
1576 	if (attrFD < 0)
1577 		return;
1578 
1579 	// attach it to a FILE
1580 	FILE* attrFile = fdopen(attrFD, "r");
1581 	if (attrFile == NULL) {
1582 		close(attrFD);
1583 		return;
1584 	}
1585 	CObjectDeleter<FILE, int> _(attrFile, fclose);
1586 
1587 	// read and parse the copyrights
1588 	BMessage package;
1589 	BString fieldName;
1590 	BString fieldValue;
1591 	char lineBuffer[LINE_MAX];
1592 	while (char* line = fgets(lineBuffer, sizeof(lineBuffer), attrFile)) {
1593 		// chop off line break
1594 		size_t lineLen = strlen(line);
1595 		if (lineLen > 0 && line[lineLen - 1] == '\n')
1596 			line[--lineLen] = '\0';
1597 
1598 		// flush previous field, if a new field begins, otherwise append
1599 		if (lineLen == 0 || !isspace(line[0])) {
1600 			// new field -- flush the previous one
1601 			if (fieldName.Length() > 0) {
1602 				fieldValue = trim_string(fieldValue.String(),
1603 					fieldValue.Length());
1604 				package.AddString(fieldName.String(), fieldValue);
1605 				fieldName = "";
1606 			}
1607 		} else if (fieldName.Length() > 0) {
1608 			// append to current field
1609 			fieldValue += line;
1610 			continue;
1611 		} else {
1612 			// bogus line -- ignore
1613 			continue;
1614 		}
1615 
1616 		if (lineLen == 0)
1617 			continue;
1618 
1619 		// parse new field
1620 		char* colon = strchr(line, ':');
1621 		if (colon == NULL) {
1622 			// bogus line -- ignore
1623 			continue;
1624 		}
1625 
1626 		fieldName.SetTo(line, colon - line);
1627 		fieldName = trim_string(line, colon - line);
1628 		if (fieldName.Length() == 0) {
1629 			// invalid field name
1630 			continue;
1631 		}
1632 
1633 		fieldValue = colon + 1;
1634 
1635 		if (fieldName == "Package") {
1636 			// flush the current package
1637 			_AddPackageCredit(PackageCredit(package));
1638 			package.MakeEmpty();
1639 		}
1640 	}
1641 
1642 	// flush current package
1643 	_AddPackageCredit(PackageCredit(package));
1644 #endif
1645 }
1646 
1647 
1648 void
1649 AboutView::_AddPackageCreditEntries()
1650 {
1651 	// sort the packages case-insensitively
1652 	PackageCredit* packages[fPackageCredits.size()];
1653 	int32 count = 0;
1654 	for (PackageCreditMap::iterator it = fPackageCredits.begin();
1655 			it != fPackageCredits.end(); ++it) {
1656 		packages[count++] = it->second;
1657 	}
1658 
1659 	if (count > 1) {
1660 		std::sort(packages, packages + count,
1661 			&PackageCredit::NameLessInsensitive);
1662 	}
1663 
1664 	// add the credits
1665 	for (int32 i = 0; i < count; i++) {
1666 		PackageCredit* package = packages[i];
1667 
1668 		BString text(package->CopyrightAt(0));
1669 		int32 count = package->CountCopyrights();
1670 		for (int32 i = 1; i < count; i++)
1671 			text << "\n" << package->CopyrightAt(i);
1672 
1673 		AddCopyrightEntry(package->PackageName(), text.String(),
1674 			package->Licenses(), package->Sources(), package->URL());
1675 	}
1676 }
1677 
1678 
1679 void
1680 AboutView::_AddPackageCredit(const PackageCredit& package)
1681 {
1682 	if (!package.IsValid())
1683 		return;
1684 
1685 	PackageCreditMap::iterator it = fPackageCredits.find(package.PackageName());
1686 	if (it != fPackageCredits.end()) {
1687 		// If the new package credit isn't "better" than the old one, ignore it.
1688 		PackageCredit* oldPackage = it->second;
1689 		if (!package.IsBetterThan(*oldPackage))
1690 			return;
1691 
1692 		// replace the old credit
1693 		fPackageCredits.erase(it);
1694 		delete oldPackage;
1695 	}
1696 
1697 	fPackageCredits[package.PackageName()] = new PackageCredit(package);
1698 }
1699 
1700 
1701 //	#pragma mark -
1702 
1703 
1704 static const char*
1705 MemSizeToString(char string[], size_t size, system_info* info)
1706 {
1707 	int inaccessibleMemory = int(info->ignored_pages
1708 		* (B_PAGE_SIZE / 1048576.0f) + 0.5f);
1709 	if (inaccessibleMemory > 0) {
1710 		BString message(B_TRANSLATE("%total MiB total, %inaccessible MiB "
1711 			"inaccessible"));
1712 
1713 		snprintf(string, size, "%d", int((info->max_pages
1714 			+ info->ignored_pages) * (B_PAGE_SIZE / 1048576.0f) + 0.5f));
1715 		message.ReplaceFirst("%total", string);
1716 
1717 		snprintf(string, size, "%d", inaccessibleMemory);
1718 		message.ReplaceFirst("%inaccessible", string);
1719 		strncpy(string, message.String(), size);
1720 	} else {
1721 		snprintf(string, size, B_TRANSLATE("%d MiB total"),
1722 			int(info->max_pages * (B_PAGE_SIZE / 1048576.0f) + 0.5f));
1723 	}
1724 
1725 	return string;
1726 }
1727 
1728 
1729 static const char*
1730 MemUsageToString(char string[], size_t size, system_info* info)
1731 {
1732 	snprintf(string, size, B_TRANSLATE("%d MiB used (%d%%)"),
1733 		int(info->used_pages * (B_PAGE_SIZE / 1048576.0f) + 0.5f),
1734 		int(100 * info->used_pages / info->max_pages));
1735 
1736 	return string;
1737 }
1738 
1739 
1740 static const char*
1741 UptimeToString(char string[], size_t size)
1742 {
1743 	BDurationFormat formatter;
1744 	BString str;
1745 
1746 	bigtime_t uptime = system_time();
1747 	bigtime_t now = (bigtime_t)time(NULL) * 1000000;
1748 	formatter.Format(now - uptime, now, &str);
1749 	str.CopyInto(string, 0, size);
1750 	string[std::min((size_t)str.Length(), size)] = '\0';
1751 
1752 	return string;
1753 }
1754 
1755 
1756 int
1757 main()
1758 {
1759 	AboutApp app;
1760 	app.Run();
1761 	return 0;
1762 }
1763 
1764