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