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