xref: /haiku/src/apps/aboutsystem/AboutSystem.cpp (revision 03187b607b2b5eec7ee059f1ead09bdba14991fb)
1 /*
2  * Copyright (c) 2005-2009, Haiku, Inc.
3  * Distributed under the terms of the MIT license.
4  *
5  * Authors:
6  *		DarkWyrm <bpmagic@columbus.rr.com>
7  *		René Gollent
8  */
9 
10 #include <ctype.h>
11 #include <stdio.h>
12 #include <sys/utsname.h>
13 #include <time.h>
14 #include <unistd.h>
15 
16 #include <map>
17 #include <string>
18 
19 #include <AppFileInfo.h>
20 #include <Application.h>
21 #include <Bitmap.h>
22 #include <File.h>
23 #include <FindDirectory.h>
24 #include <Font.h>
25 #include <fs_attr.h>
26 #include <LayoutBuilder.h>
27 #include <MessageRunner.h>
28 #include <Messenger.h>
29 #include <OS.h>
30 #include <Path.h>
31 #include <Resources.h>
32 #include <Screen.h>
33 #include <ScrollView.h>
34 #include <String.h>
35 #include <StringView.h>
36 #include <TranslationUtils.h>
37 #include <TranslatorFormats.h>
38 #include <View.h>
39 #include <Volume.h>
40 #include <VolumeRoster.h>
41 #include <Window.h>
42 
43 #include <AppMisc.h>
44 #include <AutoDeleter.h>
45 #include <cpu_type.h>
46 
47 #include "HyperTextActions.h"
48 #include "HyperTextView.h"
49 #include "Utilities.h"
50 
51 
52 #ifndef LINE_MAX
53 #define LINE_MAX 2048
54 #endif
55 
56 #define SCROLL_CREDITS_VIEW 'mviv'
57 
58 
59 static const char *UptimeToString(char string[], size_t size);
60 static const char *MemUsageToString(char string[], size_t size,
61 	system_info *info);
62 
63 static const rgb_color kDarkGrey = { 100, 100, 100, 255 };
64 static const rgb_color kHaikuGreen = { 42, 131, 36, 255 };
65 static const rgb_color kHaikuOrange = { 255, 69, 0, 255 };
66 static const rgb_color kHaikuYellow = { 255, 176, 0, 255 };
67 static const rgb_color kLinkBlue = { 80, 80, 200, 255 };
68 
69 
70 class AboutApp : public BApplication {
71 public:
72 								AboutApp();
73 };
74 
75 class AboutWindow : public BWindow {
76 public:
77 							AboutWindow();
78 
79 	virtual	bool			QuitRequested();
80 };
81 
82 class LogoView : public BView {
83 public:
84 							LogoView();
85 	virtual					~LogoView();
86 
87 	virtual	BSize			MinSize();
88 	virtual	BSize			MaxSize();
89 
90 	virtual void			Draw(BRect updateRect);
91 
92 private:
93 			BBitmap*		fLogo;
94 };
95 
96 class CropView : public BView {
97 public:
98 							CropView(BView* target, int32 left, int32 top,
99 								int32 right, int32 bottom);
100 	virtual					~CropView();
101 
102 	virtual	BSize			MinSize();
103 	virtual	BSize			MaxSize();
104 
105 	virtual void			DoLayout();
106 
107 private:
108 			BView*			fTarget;
109 			int32			fCropLeft;
110 			int32			fCropTop;
111 			int32			fCropRight;
112 			int32			fCropBottom;
113 };
114 
115 class AboutView : public BView {
116 public:
117 							AboutView();
118 							~AboutView();
119 
120 	virtual void			AttachedToWindow();
121 	virtual void			Pulse();
122 
123 	virtual void			MessageReceived(BMessage* msg);
124 	virtual void			MouseDown(BPoint point);
125 
126 			void			AddCopyrightEntry(const char* name,
127 								const char* text,
128 								const StringVector& licenses,
129 								const char* url);
130 			void			AddCopyrightEntry(const char* name,
131 								const char* text, const char* url = NULL);
132 			void			PickRandomHaiku();
133 
134 
135 private:
136 	typedef std::map<std::string, PackageCredit*> PackageCreditMap;
137 
138 private:
139 			BView*			_CreateLabel(const char* name, const char* label);
140 			BView*			_CreateCreditsView();
141 			status_t		_GetLicensePath(const char* license,
142 								BPath& path);
143 			void			_AddCopyrightsFromAttribute();
144 			void			_AddPackageCredit(const PackageCredit& package);
145 			void			_AddPackageCreditEntries();
146 
147 			BStringView*	fMemView;
148 			BTextView*		fUptimeView;
149 			BView*			fInfoView;
150 			HyperTextView*	fCreditsView;
151 
152 			BBitmap*		fLogo;
153 
154 			bigtime_t		fLastActionTime;
155 			BMessageRunner*	fScrollRunner;
156 			PackageCreditMap fPackageCredits;
157 };
158 
159 
160 //	#pragma mark -
161 
162 
163 AboutApp::AboutApp()
164 	: BApplication("application/x-vnd.Haiku-About")
165 {
166 	AboutWindow* window = new AboutWindow();
167 	window->Show();
168 }
169 
170 
171 //	#pragma mark -
172 
173 
174 AboutWindow::AboutWindow()
175 	: BWindow(BRect(0, 0, 500, 300), "About This System", B_TITLED_WINDOW,
176 			B_AUTO_UPDATE_SIZE_LIMITS | B_NOT_ZOOMABLE)
177 {
178 	SetLayout(new BGroupLayout(B_VERTICAL));
179 	AddChild(new AboutView());
180 
181 	// Make sure we take the minimal window size into account when centering
182 	BSize size = GetLayout()->MinSize();
183 	ResizeTo(max_c(size.width, Bounds().Width()),
184 		max_c(size.height, Bounds().Height()));
185 
186 	MoveTo((BScreen().Frame().Width() - Bounds().Width()) / 2,
187 		(BScreen().Frame().Height() - Bounds().Height()) / 2 );
188 }
189 
190 
191 bool
192 AboutWindow::QuitRequested()
193 {
194 	be_app->PostMessage(B_QUIT_REQUESTED);
195 	return true;
196 }
197 
198 
199 //	#pragma mark - LogoView
200 
201 
202 LogoView::LogoView()
203 	: BView("logo", B_WILL_DRAW)
204 {
205 	fLogo = BTranslationUtils::GetBitmap(B_PNG_FORMAT, "haikulogo.png");
206 	SetViewColor(255, 255, 255);
207 }
208 
209 
210 LogoView::~LogoView()
211 {
212 	delete fLogo;
213 }
214 
215 
216 BSize
217 LogoView::MinSize()
218 {
219 	if (fLogo == NULL)
220 		return BSize(0, 0);
221 
222 	return BSize(fLogo->Bounds().Width(), fLogo->Bounds().Height());
223 }
224 
225 
226 BSize
227 LogoView::MaxSize()
228 {
229 	if (fLogo == NULL)
230 		return BSize(0, 0);
231 
232 	return BSize(B_SIZE_UNLIMITED, fLogo->Bounds().Height());
233 }
234 
235 
236 void
237 LogoView::Draw(BRect updateRect)
238 {
239 	if (fLogo != NULL) {
240 		DrawBitmap(fLogo,
241 			BPoint((Bounds().Width() - fLogo->Bounds().Width()) / 2, 0));
242 	}
243 }
244 
245 
246 //	#pragma mark - CropView
247 
248 
249 CropView::CropView(BView* target, int32 left, int32 top, int32 right,
250 		int32 bottom)
251 	: BView("crop view", 0),
252 	fTarget(target),
253 	fCropLeft(left),
254 	fCropTop(top),
255 	fCropRight(right),
256 	fCropBottom(bottom)
257 {
258 	AddChild(target);
259 }
260 
261 
262 CropView::~CropView()
263 {
264 }
265 
266 
267 BSize
268 CropView::MinSize()
269 {
270 	if (fTarget == NULL)
271 		return BSize();
272 
273 	BSize size = fTarget->MinSize();
274 	if (size.width != B_SIZE_UNSET)
275 		size.width -= fCropLeft + fCropRight;
276 	if (size.height != B_SIZE_UNSET)
277 		size.height -= fCropTop + fCropBottom;
278 
279 	return size;
280 }
281 
282 
283 BSize
284 CropView::MaxSize()
285 {
286 	if (fTarget == NULL)
287 		return BSize();
288 
289 	BSize size = fTarget->MaxSize();
290 	if (size.width != B_SIZE_UNSET)
291 		size.width -= fCropLeft + fCropRight;
292 	if (size.height != B_SIZE_UNSET)
293 		size.height -= fCropTop + fCropBottom;
294 
295 	return size;
296 }
297 
298 
299 void
300 CropView::DoLayout()
301 {
302 	BView::DoLayout();
303 
304 	if (fTarget == NULL)
305 		return;
306 
307 	fTarget->MoveTo(-fCropLeft, -fCropTop);
308 	fTarget->ResizeTo(Bounds().Width() + fCropLeft + fCropRight,
309 		Bounds().Height() + fCropTop + fCropBottom);
310 }
311 
312 
313 //	#pragma mark - AboutView
314 
315 
316 AboutView::AboutView()
317 	: BView("aboutview", B_WILL_DRAW | B_PULSE_NEEDED),
318 	fLastActionTime(system_time()),
319 	fScrollRunner(NULL)
320 {
321 	// Begin Construction of System Information controls
322 
323 	system_info systemInfo;
324 	get_system_info(&systemInfo);
325 
326 	// Create all the various labels for system infomation
327 
328 	// OS Version
329 
330 	char string[1024];
331 	strcpy(string, "Unknown");
332 
333 	// the version is stored in the BEOS:APP_VERSION attribute of libbe.so
334 	BPath path;
335 	if (find_directory(B_BEOS_LIB_DIRECTORY, &path) == B_OK) {
336 		path.Append("libbe.so");
337 
338 		BAppFileInfo appFileInfo;
339 		version_info versionInfo;
340 		BFile file;
341 		if (file.SetTo(path.Path(), B_READ_ONLY) == B_OK
342 			&& appFileInfo.SetTo(&file) == B_OK
343 			&& appFileInfo.GetVersionInfo(&versionInfo,
344 				B_APP_VERSION_KIND) == B_OK
345 			&& versionInfo.short_info[0] != '\0')
346 			strcpy(string, versionInfo.short_info);
347 	}
348 
349 	// Add revision from uname() info
350 	utsname unameInfo;
351 	if (uname(&unameInfo) == 0) {
352 		long revision;
353 		if (sscanf(unameInfo.version, "r%ld", &revision) == 1) {
354 			char version[16];
355 			snprintf(version, sizeof(version), "%ld", revision);
356 			strlcat(string, " (Revision ", sizeof(string));
357 			strlcat(string, version, sizeof(string));
358 			strlcat(string, ")", sizeof(string));
359 		}
360 	}
361 
362 	BStringView* versionView = new BStringView("ostext", string);
363 	versionView->SetExplicitAlignment(BAlignment(B_ALIGN_LEFT,
364 		B_ALIGN_VERTICAL_UNSET));
365 
366 	// GCC version
367 #if __GNUC__ != 2
368 	snprintf(string, sizeof(string), "GCC %d", __GNUC__);
369 
370 	BStringView* gccView = new BStringView("gcctext", string);
371 	gccView->SetExplicitAlignment(BAlignment(B_ALIGN_LEFT,
372 		B_ALIGN_VERTICAL_UNSET));
373 #endif
374 
375 	// CPU count, type and clock speed
376 	char processorLabel[256];
377 	if (systemInfo.cpu_count > 1) {
378 		snprintf(processorLabel, sizeof(processorLabel), "%ld Processors:",
379 			systemInfo.cpu_count);
380 	} else
381 		strlcpy(processorLabel, "Processor:", sizeof(processorLabel));
382 
383 	BString cpuType;
384 	cpuType << get_cpu_vendor_string(systemInfo.cpu_type)
385 		<< " " << get_cpu_model_string(&systemInfo);
386 
387 	BStringView* cpuView = new BStringView("cputext", cpuType.String());
388 	cpuView->SetExplicitAlignment(BAlignment(B_ALIGN_LEFT,
389 		B_ALIGN_VERTICAL_UNSET));
390 
391 	int32 clockSpeed = get_rounded_cpu_speed();
392 	if (clockSpeed < 1000)
393 		sprintf(string,"%ld MHz", clockSpeed);
394 	else
395 		sprintf(string,"%.2f GHz", clockSpeed / 1000.0f);
396 
397 	BStringView* frequencyView = new BStringView("frequencytext", string);
398 	frequencyView->SetExplicitAlignment(BAlignment(B_ALIGN_LEFT,
399 		B_ALIGN_VERTICAL_UNSET));
400 
401 	// RAM
402 	fMemView = new BStringView("ramtext",
403 		MemUsageToString(string, sizeof(string), &systemInfo));
404 	fMemView->SetExplicitAlignment(BAlignment(B_ALIGN_LEFT,
405 		B_ALIGN_VERTICAL_UNSET));
406 
407 	// Kernel build time/date
408 	snprintf(string, sizeof(string), "%s %s",
409 		systemInfo.kernel_build_date, systemInfo.kernel_build_time);
410 
411 	BStringView* kernelView = new BStringView("kerneltext", string);
412 	kernelView->SetExplicitAlignment(BAlignment(B_ALIGN_LEFT,
413 		B_ALIGN_VERTICAL_UNSET));
414 
415 	// Uptime
416 	fUptimeView = new BTextView("uptimetext");
417 	fUptimeView->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
418 	fUptimeView->MakeEditable(false);
419 	fUptimeView->MakeSelectable(false);
420 	fUptimeView->SetWordWrap(true);
421 
422 	fUptimeView->SetText(UptimeToString(string, sizeof(string)));
423 
424 	const float offset = 5;
425 
426 	SetLayout(new BGroupLayout(B_HORIZONTAL));
427 
428 	BLayoutBuilder::Group<>((BGroupLayout*)GetLayout())
429 		.AddGroup(B_VERTICAL)
430 			.Add(new LogoView())
431 			.AddGroup(B_VERTICAL)
432 				.Add(_CreateLabel("oslabel", "Version:"))
433 				.Add(versionView)
434 #if __GNUC__ != 2
435 				.Add(gccView)
436 #endif
437 				.AddStrut(offset)
438 				.Add(_CreateLabel("cpulabel", processorLabel))
439 				.Add(cpuView)
440 				.Add(frequencyView)
441 				.AddStrut(offset)
442 				.Add(_CreateLabel("memlabel", "Memory:"))
443 				.Add(fMemView)
444 				.AddStrut(offset)
445 				.Add(_CreateLabel("kernellabel", "Kernel:"))
446 				.Add(kernelView)
447 				.AddStrut(offset)
448 				.Add(_CreateLabel("uptimelabel", "Time Running:"))
449 				.Add(fUptimeView)
450 				.SetInsets(5, 5, 5, 5)
451 			.End()
452 			// TODO: investigate: adding this causes the time to be cut
453 			//.AddGlue()
454 		.End()
455 		.Add(_CreateCreditsView());
456 
457 	float min = fMemView->MinSize().width * 1.1f;
458 	fCreditsView->SetExplicitMinSize(BSize(min, min));
459 }
460 
461 
462 AboutView::~AboutView()
463 {
464 	delete fScrollRunner;
465 }
466 
467 
468 void
469 AboutView::AttachedToWindow()
470 {
471 	BView::AttachedToWindow();
472 	Window()->SetPulseRate(500000);
473 	SetEventMask(B_POINTER_EVENTS);
474 }
475 
476 
477 void
478 AboutView::MouseDown(BPoint point)
479 {
480 	BRect r(92, 26, 105, 31);
481 	if (r.Contains(point)) {
482 		printf("Easter Egg\n");
483 		PickRandomHaiku();
484 	}
485 
486 	if (Bounds().Contains(point)) {
487 		fLastActionTime = system_time();
488 		delete fScrollRunner;
489 		fScrollRunner = NULL;
490 	}
491 }
492 
493 
494 void
495 AboutView::Pulse()
496 {
497 	char string[255];
498 	system_info info;
499 	get_system_info(&info);
500 	fUptimeView->SetText(UptimeToString(string, sizeof(string)));
501 	fMemView->SetText(MemUsageToString(string, sizeof(string), &info));
502 
503 	if (fScrollRunner == NULL && system_time() > fLastActionTime + 10000000) {
504 		BMessage message(SCROLL_CREDITS_VIEW);
505 		//fScrollRunner = new BMessageRunner(this, &message, 25000, -1);
506 	}
507 }
508 
509 
510 void
511 AboutView::MessageReceived(BMessage *msg)
512 {
513 	switch (msg->what) {
514 		case SCROLL_CREDITS_VIEW:
515 		{
516 			BScrollBar *scrollBar = fCreditsView->ScrollBar(B_VERTICAL);
517 			if (scrollBar == NULL)
518 				break;
519 			float max, min;
520 			scrollBar->GetRange(&min, &max);
521 			if (scrollBar->Value() < max)
522 				fCreditsView->ScrollBy(0, 1);
523 
524 			break;
525 		}
526 
527 		default:
528 			BView::MessageReceived(msg);
529 			break;
530 	}
531 }
532 
533 
534 void
535 AboutView::AddCopyrightEntry(const char *name, const char *text,
536 	const char *url)
537 {
538 	AddCopyrightEntry(name, text, StringVector(), url);
539 }
540 
541 
542 void
543 AboutView::AddCopyrightEntry(const char *name, const char *text,
544 	const StringVector& licenses, const char *url)
545 {
546 	BFont font(be_bold_font);
547 	//font.SetSize(be_bold_font->Size());
548 	font.SetFace(B_BOLD_FACE | B_ITALIC_FACE);
549 
550 	fCreditsView->SetFontAndColor(&font, B_FONT_ALL, &kHaikuYellow);
551 	fCreditsView->Insert(name);
552 	fCreditsView->Insert("\n");
553 	fCreditsView->SetFontAndColor(be_plain_font, B_FONT_ALL, &kDarkGrey);
554 	fCreditsView->Insert(text);
555 	fCreditsView->Insert("\n");
556 
557 	if (licenses.CountStrings() > 0) {
558 		if (licenses.CountStrings() > 1)
559 			fCreditsView->Insert("Licenses: ");
560 		else
561 			fCreditsView->Insert("License: ");
562 
563 		for (int32 i = 0; i < licenses.CountStrings(); i++) {
564 			const char* license = licenses.StringAt(i);
565 
566 			if (i > 0)
567 				fCreditsView->Insert(", ");
568 
569 			BPath licensePath;
570 			if (_GetLicensePath(license, licensePath) == B_OK) {
571 				fCreditsView->InsertHyperText(license,
572 					new OpenFileAction(licensePath.Path()));
573 			} else
574 				fCreditsView->Insert(license);
575 		}
576 
577 		fCreditsView->Insert("\n");
578 	}
579 
580 	if (url) {
581 		fCreditsView->SetFontAndColor(be_plain_font, B_FONT_ALL, &kLinkBlue);
582 		fCreditsView->InsertHyperText(url, new URLAction(url));
583 		fCreditsView->Insert("\n");
584 	}
585 	fCreditsView->Insert("\n");
586 }
587 
588 
589 void
590 AboutView::PickRandomHaiku()
591 {
592 	BFile fortunes(
593 #ifdef __HAIKU__
594 		"/etc/fortunes/Haiku",
595 #else
596 		"data/etc/fortunes/Haiku",
597 #endif
598 		B_READ_ONLY);
599 	struct stat st;
600 	if (fortunes.InitCheck() < B_OK)
601 		return;
602 	if (fortunes.GetStat(&st) < B_OK)
603 		return;
604 	char *buff = (char *)malloc((size_t)st.st_size + 1);
605 	if (!buff)
606 		return;
607 	buff[(size_t)st.st_size] = '\0';
608 	BList haikuList;
609 	if (fortunes.Read(buff, (size_t)st.st_size) == (ssize_t)st.st_size) {
610 		char *p = buff;
611 		while (p && *p) {
612 			char *e = strchr(p, '%');
613 			BString *s = new BString(p, e ? (e - p) : -1);
614 			haikuList.AddItem(s);
615 			p = e;
616 			if (p && (*p == '%'))
617 				p++;
618 			if (p && (*p == '\n'))
619 				p++;
620 		}
621 	}
622 	free(buff);
623 	if (haikuList.CountItems() < 1)
624 		return;
625 	BString *s = (BString *)haikuList.ItemAt(rand() % haikuList.CountItems());
626 	BFont font(be_bold_font);
627 	font.SetSize(be_bold_font->Size());
628 	font.SetFace(B_BOLD_FACE | B_ITALIC_FACE);
629 	fCreditsView->SelectAll();
630 	fCreditsView->Delete();
631 	fCreditsView->SetFontAndColor(&font, B_FONT_ALL, &kDarkGrey);
632 	fCreditsView->Insert(s->String());
633 	fCreditsView->Insert("\n");
634 	while ((s = (BString *)haikuList.RemoveItem((int32)0))) {
635 		delete s;
636 	}
637 }
638 
639 
640 BView*
641 AboutView::_CreateLabel(const char* name, const char* label)
642 {
643 	BStringView* labelView = new BStringView(name, label);
644 	labelView->SetExplicitAlignment(BAlignment(B_ALIGN_LEFT,
645 		B_ALIGN_VERTICAL_UNSET));
646 	labelView->SetFont(be_bold_font);
647 	return labelView;
648 }
649 
650 
651 BView*
652 AboutView::_CreateCreditsView()
653 {
654 	// Begin construction of the credits view
655 	fCreditsView = new HyperTextView("credits");
656 	fCreditsView->SetFlags(fCreditsView->Flags() | B_FRAME_EVENTS);
657 	fCreditsView->SetStylable(true);
658 	fCreditsView->MakeEditable(false);
659 	fCreditsView->SetWordWrap(true);
660 	fCreditsView->SetInsets(5, 5, 5, 5);
661 
662 	BScrollView* creditsScroller = new BScrollView("creditsScroller",
663 		fCreditsView, B_WILL_DRAW | B_FRAME_EVENTS, false, true,
664 		B_PLAIN_BORDER);
665 
666 	// Haiku copyright
667 	BFont font(be_bold_font);
668 	font.SetSize(font.Size() + 4);
669 
670 	fCreditsView->SetFontAndColor(&font, B_FONT_ALL, &kHaikuGreen);
671 	fCreditsView->Insert("Haiku\n");
672 
673 	char string[1024];
674 	time_t time = ::time(NULL);
675 	struct tm* tm = localtime(&time);
676 	int32 year = tm->tm_year + 1900;
677 	if (year < 2008)
678 		year = 2008;
679 	snprintf(string, sizeof(string),
680 		COPYRIGHT_STRING "2001-%ld The Haiku project. ", year);
681 
682 	fCreditsView->SetFontAndColor(be_plain_font, B_FONT_ALL, &kDarkGrey);
683 	fCreditsView->Insert(string);
684 
685 	fCreditsView->SetFontAndColor(be_plain_font, B_FONT_ALL, &kDarkGrey);
686 	fCreditsView->Insert("The copyright to the Haiku code is property of "
687 		"Haiku, Inc. or of the respective authors where expressly noted "
688 		"in the source."
689 		"\n\n");
690 
691 	fCreditsView->SetFontAndColor(be_plain_font, B_FONT_ALL, &kLinkBlue);
692 	fCreditsView->InsertHyperText("http://www.haiku-os.org",
693 		new URLAction("http://www.haiku-os.org"));
694 	fCreditsView->Insert("\n\n");
695 
696 	font.SetSize(be_bold_font->Size());
697 	font.SetFace(B_BOLD_FACE | B_ITALIC_FACE);
698 
699 	fCreditsView->SetFontAndColor(&font, B_FONT_ALL, &kHaikuOrange);
700 	fCreditsView->Insert("Current Maintainers:\n");
701 
702 	fCreditsView->SetFontAndColor(be_plain_font, B_FONT_ALL, &kDarkGrey);
703 	fCreditsView->Insert(
704 		"Ithamar R. Adema\n"
705 		"Bruno G. Albuquerque\n"
706 		"Stephan Aßmus\n"
707 		"Salvatore Benedetto\n"
708 		"Stefano Ceccherini\n"
709 		"Rudolf Cornelissen\n"
710 		"Alexandre Deckner\n"
711 		"Adrien Destugues\n"
712 		"Oliver Ruiz Dorantes\n"
713 		"Axel Dörfler\n"
714 		"Jérôme Duval\n"
715 		"René Gollent\n"
716 		"Bryce Groff\n"
717 		"Karsten Heimrich\n"
718 		"Philippe Houdoin\n"
719 		"Maurice Kalinowski\n"
720 		"Euan Kirkhope\n"
721 		"Ryan Leavengood\n"
722 		"Michael Lotz\n"
723 		"David McPaul\n"
724 		"Fredrik Modéen\n"
725 		"Marcus Overhagen\n"
726 		"Michael Pfeiffer\n"
727 		"François Revol\n"
728 		"Philippe Saint-Pierre\n"
729 		"Andrej Spielmann\n"
730 		"Jonas Sundström\n"
731 		"Oliver Tappe\n"
732 		"Gerasim Troeglazov\n"
733 		"Ingo Weinhold\n"
734 		"Artur Wyszynski\n"
735 		"Clemens Zeidler\n"
736 		"Siarzhuk Zharski\n"
737 		"\n");
738 
739 	fCreditsView->SetFontAndColor(&font, B_FONT_ALL, &kHaikuOrange);
740 	fCreditsView->Insert("Past Maintainers:\n");
741 
742 	fCreditsView->SetFontAndColor(be_plain_font, B_FONT_ALL, &kDarkGrey);
743 	fCreditsView->Insert(
744 		"Andrew Bachmann\n"
745 		"Tyler Dauwalder\n"
746 		"Daniel Furrer\n"
747 		"Andre Alves Garzia\n"
748 		"Erik Jaesler\n"
749 		"Marcin Konicki\n"
750 		"Waldemar Kornewald\n"
751 		"Thomas Kurschel\n"
752 		"Frans Van Nispen\n"
753 		"Adi Oanca\n"
754 		"Michael Phipps\n"
755 		"Niels Sascha Reedijk\n"
756 		"David Reid\n"
757 		"Hugo Santos\n"
758 		"Alexander G. M. Smith\n"
759 		"Bryan Varner\n"
760 		"Nathan Whitehorn\n"
761 		"Michael Wilber\n"
762 		"Jonathan Yoder\n"
763 		"Gabe Yoder\n"
764 		"\n");
765 
766 	fCreditsView->SetFontAndColor(&font, B_FONT_ALL, &kHaikuOrange);
767 	fCreditsView->Insert("Website, Marketing & Documentation:\n");
768 
769 	fCreditsView->SetFontAndColor(be_plain_font, B_FONT_ALL, &kDarkGrey);
770 	fCreditsView->Insert(
771 		"Phil Greenway\n"
772 		"Gavin James\n"
773 		"Matt Madia\n"
774 		"Jorge G. Mare (aka Koki)\n"
775 		"Urias McCullough\n"
776 		"Niels Sascha Reedijk\n"
777 		"Joachim Seemer (Humdinger)\n"
778 		"Jonathan Yoder\n"
779 		"\n");
780 
781 	fCreditsView->SetFontAndColor(&font, B_FONT_ALL, &kHaikuOrange);
782 	fCreditsView->Insert("Contributors:\n");
783 
784 	fCreditsView->SetFontAndColor(be_plain_font, B_FONT_ALL, &kDarkGrey);
785 	fCreditsView->Insert(
786 		"Andrea Anzani\n"
787 		"Andre Braga\n"
788 		"Bruce Cameron\n"
789 		"Greg Crain\n"
790 		"David Dengg\n"
791 		"John Drinkwater\n"
792 		"Cian Duffy\n"
793 		"Fredrik Ekdahl\n"
794 		"Mark Erben\n"
795 		"Christian Fasshauer\n"
796 		"Andreas Färber\n"
797 		"Marc Flerackers\n"
798 		"Michele Frau (zuMi)\n"
799 		"Matthijs Hollemans\n"
800 		"Mathew Hounsell\n"
801 		"Morgan Howe\n"
802 		"Ma Jie\n"
803 		"Carwyn Jones\n"
804 		"Vasilis Kaoutsis\n"
805 		"James Kim\n"
806 		"Shintaro Kinugawa\n"
807 		"Jan Klötzke\n"
808 		"Kurtis Kopf\n"
809 		"Tomáš Kučera\n"
810 		"Luboš Kulič\n"
811 		"Elad Lahav\n"
812 		"Anthony Lee\n"
813 		"Santiago Lema\n"
814 		"Raynald Lesieur\n"
815 		"Oscar Lesta\n"
816 		"Jerome Leveque\n"
817 		"Christof Lutteroth\n"
818 		"Graham MacDonald\n"
819 		"Brecht Machiels\n"
820 		"Jan Matějek\n"
821 		"Brian Matzon\n"
822 		"Christopher ML Zumwalt May\n"
823 		"Andrew McCall\n"
824 		"Scott McCreary\n"
825 		"Marius Middelthon\n"
826 		"Marco Minutoli\n"
827 		"Misza\n"
828 		"MrSiggler\n"
829 		"Alan Murta\n"
830 		"Raghuram Nagireddy\n"
831 		"Jeroen Oortwijn (idefix)\n"
832 		"Pahtz\n"
833 		"Michael Paine\n"
834 		"Adrian Panasiuk\n"
835 		"Francesco Piccinno\n"
836 		"David Powell\n"
837 		"Jeremy Rand\n"
838 		"Hartmut Reh\n"
839 		"Daniel Reinhold\n"
840 		"Chris Roberts\n"
841 		"Samuel Rodriguez Perez\n"
842 		"Thomas Roell\n"
843 		"Rafael Romo\n"
844 		"Ralf Schülke\n"
845 		"Reznikov Sergei\n"
846 		"Zousar Shaker\n"
847 		"Daniel Switkin\n"
848 		"Atsushi Takamatsu\n"
849 		"James Urquhart\n"
850 		"Jason Vandermark\n"
851 		"Sandor Vroemisse\n"
852 		"Denis Washington\n"
853 		"Ulrich Wimboeck\n"
854 		"Johannes Wischert\n"
855 		"James Woodcock\n"
856 		"Gerald Zajac\n"
857 		"Łukasz Zemczak\n"
858 		"JiSheng Zhang\n"
859 		"Zhao Shuai\n"
860 		"\n" B_UTF8_ELLIPSIS " and probably some more we forgot to mention "
861 		"(sorry!)"
862 		"\n\n");
863 
864 	fCreditsView->SetFontAndColor(&font, B_FONT_ALL, &kHaikuOrange);
865 	fCreditsView->Insert("Special Thanks To:\n");
866 
867 	fCreditsView->SetFontAndColor(be_plain_font, B_FONT_ALL, &kDarkGrey);
868 	fCreditsView->Insert("Travis Geiselbrecht (and his NewOS kernel)\n");
869 	fCreditsView->Insert("Michael Phipps (project founder)\n\n");
870 	fCreditsView->Insert("The Haiku-Ports Team\n");
871 	fCreditsView->Insert("The Haikuware Team and their Bounty Program\n");
872 	fCreditsView->Insert("The BeGeistert Team\n\n");
873 	fCreditsView->Insert("... and the many community members making "
874 		"donations!\n\n");
875 
876 	// copyrights for various projects we use
877 
878 	BPath mitPath;
879 	_GetLicensePath("MIT", mitPath);
880 
881 	font.SetSize(be_bold_font->Size() + 4);
882 	font.SetFace(B_BOLD_FACE);
883 	fCreditsView->SetFontAndColor(&font, B_FONT_ALL, &kHaikuGreen);
884 	fCreditsView->Insert("\nCopyrights\n\n");
885 
886 	fCreditsView->SetFontAndColor(be_plain_font, B_FONT_ALL, &kDarkGrey);
887 	fCreditsView->Insert("[Click a license name to read the respective "
888 		"license.]\n\n");
889 
890 	// Haiku license
891 	fCreditsView->Insert("The code that is unique to Haiku, especially the "
892 		"kernel and all code that applications may link against, is "
893 		"distributed under the terms of the ");
894 	fCreditsView->InsertHyperText("MIT license",
895 		new OpenFileAction(mitPath.Path()));
896 	fCreditsView->Insert(". Some system libraries contain third party code "
897 		"distributed under the LGPL license. You can find the copyrights "
898 		"to third party code below.\n\n");
899 
900 	// GNU copyrights
901 	AddCopyrightEntry("The GNU Project",
902 		"Contains software from the GNU Project, "
903 		"released under the GPL and LGPL licences:\n"
904 		"GNU C Library, "
905 		"GNU coretools, diffutils, findutils, "
906 		"sharutils, gawk, bison, m4, make, "
907 		"gdb, wget, ncurses, termcap, "
908 		"Bourne Again Shell.\n"
909 		COPYRIGHT_STRING "The Free Software Foundation.",
910 		StringVector("GNU LGPL v2.1", "GNU GPL v2", "GNU GPL v3", NULL),
911 		"http://www.gnu.org");
912 
913 	// FreeBSD copyrights
914 	AddCopyrightEntry("The FreeBSD Project",
915 		"Contains software from the FreeBSD Project, "
916 		"released under the BSD licence:\n"
917 		"cal, ftpd, ping, telnet, "
918 		"telnetd, traceroute\n"
919 		COPYRIGHT_STRING "1994-2008 The FreeBSD Project.  "
920 		"All rights reserved.",
921 		"http://www.freebsd.org");
922 			// TODO: License!
923 
924 	// NetBSD copyrights
925 	AddCopyrightEntry("The NetBSD Project",
926 		"Contains software developed by the NetBSD, "
927 		"Foundation, Inc. and its contributors:\n"
928 		"ftp, tput\n"
929 		COPYRIGHT_STRING "1996-2008 The NetBSD Foundation, Inc.  "
930 		"All rights reserved.",
931 		"http://www.netbsd.org");
932 			// TODO: License!
933 
934 	// FFMpeg copyrights
935 	_AddPackageCredit(PackageCredit("FFMpeg libavcodec")
936 		.SetCopyright(COPYRIGHT_STRING "2000-2007 Fabrice Bellard, et al.")
937 		.SetURL("http://www.ffmpeg.org"));
938 			// TODO: License!
939 
940 	// AGG copyrights
941 	_AddPackageCredit(PackageCredit("AntiGrain Geometry")
942 		.SetCopyright(COPYRIGHT_STRING "2002-2006 Maxim Shemanarev (McSeem).")
943 		.SetURL("http://www.antigrain.com"));
944 			// TODO: License!
945 
946 	// PDFLib copyrights
947 	_AddPackageCredit(PackageCredit("PDFLib")
948 		.SetCopyright(COPYRIGHT_STRING "1997-2006 PDFlib GmbH and Thomas Merz. "
949 			"All rights reserved.\n"
950 			"PDFlib and PDFlib logo are registered trademarks of PDFlib GmbH.")
951 		.SetURL("http://www.pdflib.com"));
952 			// TODO: License!
953 
954 	// FreeType copyrights
955 	_AddPackageCredit(PackageCredit("FreeType2")
956 		.SetCopyright("Portions of this software are copyright "
957 			B_UTF8_COPYRIGHT " 1996-2006 "
958 			"The FreeType Project.  All rights reserved.")
959 		.SetURL("http://www.freetype.org"));
960 			// TODO: License!
961 
962 	// Mesa3D (http://www.mesa3d.org) copyrights
963 	_AddPackageCredit(PackageCredit("Mesa")
964 		.SetCopyright(COPYRIGHT_STRING "1999-2006 Brian Paul. "
965 			"Mesa3D project.  All rights reserved.")
966 		.SetURL("http://www.mesa3d.org"));
967 			// TODO: License!
968 
969 	// SGI's GLU implementation copyrights
970 	_AddPackageCredit(PackageCredit("GLU")
971 		.SetCopyright(COPYRIGHT_STRING
972 			"1991-2000 Silicon Graphics, Inc. "
973 			"SGI's Software FreeB license.  All rights reserved."));
974 			// TODO: License!
975 
976 	// GLUT implementation copyrights
977 	_AddPackageCredit(PackageCredit("GLUT")
978 		.SetCopyrights(COPYRIGHT_STRING "1994-1997 Mark Kilgard. "
979 				"All rights reserved.",
980 			COPYRIGHT_STRING "1997 Be Inc.",
981 			COPYRIGHT_STRING "1999 Jake Hamby.",
982 			NULL));
983 			// TODO: License!
984 
985 	// OpenGroup & DEC (BRegion backend) copyright
986 	_AddPackageCredit(PackageCredit("BRegion backend (XFree86)")
987 		.SetCopyrights(COPYRIGHT_STRING "1987, 1988, 1998 The Open Group.",
988 			COPYRIGHT_STRING "1987, 1988 Digital Equipment "
989 				"Corporation, Maynard, Massachusetts.\n"
990 				"All rights reserved.",
991 			NULL));
992 			// TODO: License!
993 
994 	// Konatu font
995 	_AddPackageCredit(PackageCredit("Konatu font")
996 		.SetCopyright(COPYRIGHT_STRING "2002- MASUDA mitiya.\n"
997 			"MIT license. All rights reserved."));
998 			// TODO: License!
999 
1000 	// expat copyrights
1001 	_AddPackageCredit(PackageCredit("expat")
1002 		.SetCopyrights(COPYRIGHT_STRING
1003 				"1998, 1999, 2000 Thai Open Source "
1004 				"Software Center Ltd and Clark Cooper.",
1005 			COPYRIGHT_STRING "2001, 2002, 2003 Expat maintainers.",
1006 			NULL));
1007 			// TODO: License!
1008 
1009 	// zlib copyrights
1010 	_AddPackageCredit(PackageCredit("zlib")
1011 		.SetCopyright(COPYRIGHT_STRING
1012 			"1995-2004 Jean-loup Gailly and Mark Adler."));
1013 			// TODO: License!
1014 
1015 	// zip copyrights
1016 	_AddPackageCredit(PackageCredit("Info-ZIP")
1017 		.SetCopyright(COPYRIGHT_STRING
1018 			"1990-2002 Info-ZIP. All rights reserved."));
1019 			// TODO: License!
1020 
1021 	// bzip2 copyrights
1022 	_AddPackageCredit(PackageCredit("bzip2")
1023 		.SetCopyright(COPYRIGHT_STRING
1024 			"1996-2005 Julian R Seward. All rights reserved."));
1025 			// TODO: License!
1026 
1027 	// VIM copyrights
1028 	_AddPackageCredit(PackageCredit("Vi IMproved")
1029 		.SetCopyright(COPYRIGHT_STRING "Bram Moolenaar et al."));
1030 			// TODO: License!
1031 
1032 	// lp_solve copyrights
1033 	_AddPackageCredit(PackageCredit("lp_solve")
1034 		.SetCopyright(COPYRIGHT_STRING
1035 			"Michel Berkelaar, Kjell Eikland, Peter Notebaert")
1036 		.SetLicense("GNU LGPL v2.1")
1037 		.SetURL("http://lpsolve.sourceforge.net/"));
1038 
1039 	// OpenEXR copyrights
1040 	_AddPackageCredit(PackageCredit("OpenEXR")
1041 		.SetCopyright(COPYRIGHT_STRING "2002-2005 Industrial Light & Magic, "
1042 			"a division of Lucas Digital Ltd. LLC."));
1043 			// TODO: License!
1044 
1045 	// Bullet copyrights
1046 	_AddPackageCredit(PackageCredit("Bullet")
1047 		.SetCopyright(COPYRIGHT_STRING "2003-2008 Erwin Coumans")
1048 		.SetURL("http://www.bulletphysics.com"));
1049 			// TODO: License!
1050 
1051 	// atftp copyrights
1052 	_AddPackageCredit(PackageCredit("atftp")
1053 		.SetCopyright(COPYRIGHT_STRING
1054 			"2000 Jean-Pierre Lefebvre and Remi Lefebvre"));
1055 			// TODO: License!
1056 
1057 	// Netcat copyrights
1058 	_AddPackageCredit(PackageCredit("Netcat")
1059 		.SetCopyright(COPYRIGHT_STRING "1996 Hobbit"));
1060 			// TODO: License!
1061 
1062 	// acpica copyrights
1063 	_AddPackageCredit(PackageCredit("acpica")
1064 		.SetCopyright(COPYRIGHT_STRING "1999-2006 Intel Corp."));
1065 			// TODO: License!
1066 
1067 	// unrar copyrights
1068 	_AddPackageCredit(PackageCredit("unrar")
1069 		.SetCopyright(COPYRIGHT_STRING "2002-2008 Alexander L. Roshal. "
1070 			"All rights reserved.")
1071 		.SetURL("http://www.rarlab.com"));
1072 			// TODO: License!
1073 
1074 	// libpng copyrights
1075 	_AddPackageCredit(PackageCredit("libpng")
1076 		.SetCopyright(COPYRIGHT_STRING "2004, 2006-2008 Glenn "
1077 			"Randers-Pehrson."));
1078 			// TODO: License!
1079 
1080 	// libprint copyrights
1081 	_AddPackageCredit(PackageCredit("libprint")
1082 		.SetCopyright(COPYRIGHT_STRING
1083 			"1999-2000 Y.Takagi. All rights reserved."));
1084 			// TODO: License!
1085 
1086 	// cortex copyrights
1087 	_AddPackageCredit(PackageCredit("Cortex")
1088 		.SetCopyright(COPYRIGHT_STRING "1999-2000 Eric Moon."));
1089 			// TODO: License!
1090 
1091 	// FluidSynth copyrights
1092 	_AddPackageCredit(PackageCredit("FluidSynth")
1093 		.SetCopyright(COPYRIGHT_STRING "2003 Peter Hanappe and others."));
1094 			// TODO: License!
1095 
1096 	// CannaIM copyrights
1097 	_AddPackageCredit(PackageCredit("CannaIM")
1098 		.SetCopyright(COPYRIGHT_STRING "1999 Masao Kawamura."));
1099 			// TODO: License!
1100 
1101 	// libxml2, libxslt, libexslt copyrights
1102 	_AddPackageCredit(PackageCredit("libxml2, libxslt")
1103 		.SetCopyright(COPYRIGHT_STRING
1104 			"1998-2003 Daniel Veillard. All rights reserved."));
1105 			// TODO: License!
1106 
1107 	_AddPackageCredit(PackageCredit("libexslt")
1108 		.SetCopyright(COPYRIGHT_STRING
1109 			"2001-2002 Thomas Broyer, Charlie "
1110 			"Bozeman and Daniel Veillard.  All rights reserved."));
1111 			// TODO: License!
1112 
1113 	// Xiph.org Foundation copyrights
1114 	_AddPackageCredit(PackageCredit("Xiph.org Foundation")
1115 		.SetCopyrights("libvorbis, libogg, libtheora, libspeex",
1116 			COPYRIGHT_STRING "1994-2008 Xiph.Org. "
1117 				"All rights reserved.",
1118 			NULL)
1119 		.SetURL("http://www.xiph.org"));
1120 			// TODO: License!
1121 
1122 	// The Tcpdump Group
1123 	_AddPackageCredit(PackageCredit("The Tcpdump Group")
1124 		.SetCopyright("tcpdump, libpcap")
1125 		.SetURL("http://www.tcpdump.org"));
1126 			// TODO: License!
1127 
1128 	// Matroska
1129 	_AddPackageCredit(PackageCredit("libmatroska")
1130 		.SetCopyright(COPYRIGHT_STRING "2002-2003 Steve Lhomme. "
1131 			"All rights reserved.")
1132 		.SetURL("http://www.matroska.org"));
1133 			// TODO: License!
1134 
1135 	// BColorQuantizer (originally CQuantizer code)
1136 	_AddPackageCredit(PackageCredit("CQuantizer")
1137 		.SetCopyright(COPYRIGHT_STRING "1996-1997 Jeff Prosise. "
1138 			"All rights reserved."));
1139 			// TODO: License!
1140 
1141 	// MAPM (Mike's Arbitrary Precision Math Library) used by DeskCalc
1142 	_AddPackageCredit(PackageCredit("MAPM")
1143 		.SetCopyright(COPYRIGHT_STRING
1144 			"1999-2007 Michael C. Ring. All rights reserved.")
1145 		.SetURL("http://tc.umn.edu/~ringx004"));
1146 			// TODO: License!
1147 
1148 	// MkDepend 1.7 copyright (Makefile dependency generator)
1149 	_AddPackageCredit(PackageCredit("MkDepend")
1150 		.SetCopyright(COPYRIGHT_STRING "1995-2001 Lars Düning. "
1151 			"All rights reserved."));
1152 			// TODO: License!
1153 
1154 	// libhttpd copyright (used as Poorman backend)
1155 	_AddPackageCredit(PackageCredit("libhttpd")
1156 		.SetCopyright(COPYRIGHT_STRING
1157 			"1995,1998,1999,2000,2001 by "
1158 			"Jef Poskanzer. All rights reserved.")
1159 		.SetLicense("LibHTTPd")
1160 		.SetURL("http://www.acme.com/software/thttpd/"));
1161 
1162 #ifdef __INTEL__
1163 	// Udis86 copyrights
1164 	_AddPackageCredit(PackageCredit("Udis86")
1165 		.SetCopyright(COPYRIGHT_STRING "2002, 2003, 2004 Vivek Mohan. "
1166 			"All rights reserved.")
1167 		.SetURL("http://udis86.sourceforge.net"));
1168 			// TODO: License!
1169 #endif
1170 
1171 	_AddCopyrightsFromAttribute();
1172 	_AddPackageCreditEntries();
1173 
1174 	return new CropView(creditsScroller, 0, 1, 1, 1);
1175 }
1176 
1177 
1178 status_t
1179 AboutView::_GetLicensePath(const char* license, BPath& path)
1180 {
1181 	static const directory_which directoryConstants[] = {
1182 		B_USER_DATA_DIRECTORY,
1183 		B_COMMON_DATA_DIRECTORY,
1184 		B_SYSTEM_DATA_DIRECTORY
1185 	};
1186 	static const int dirCount = 3;
1187 
1188 	for (int i = 0; i < dirCount; i++) {
1189 		struct stat st;
1190 		status_t error = find_directory(directoryConstants[i], &path);
1191 		if (error == B_OK && path.Append("licenses") == B_OK
1192 			&& path.Append(license) == B_OK
1193 			&& lstat(path.Path(), &st) == 0) {
1194 			return B_OK;
1195 		}
1196 	}
1197 
1198 	path.Unset();
1199 	return B_ENTRY_NOT_FOUND;
1200 }
1201 
1202 
1203 void
1204 AboutView::_AddCopyrightsFromAttribute()
1205 {
1206 #ifdef __HAIKU__
1207 	// open the app executable file
1208 	char appPath[B_PATH_NAME_LENGTH];
1209 	int appFD;
1210 	if (BPrivate::get_app_path(appPath) != B_OK
1211 		|| (appFD = open(appPath, O_RDONLY)) < 0) {
1212 		return;
1213 	}
1214 
1215 	// open the attribute
1216 	int attrFD = fs_fopen_attr(appFD, "COPYRIGHTS", B_STRING_TYPE, O_RDONLY);
1217 	close(appFD);
1218 	if (attrFD < 0)
1219 		return;
1220 
1221 	// attach it to a FILE
1222 	FILE* attrFile = fdopen(attrFD, "r");
1223 	if (attrFile == NULL) {
1224 		close(attrFD);
1225 		return;
1226 	}
1227 	CObjectDeleter<FILE, int> _(attrFile, fclose);
1228 
1229 	// read and parse the copyrights
1230 	BMessage package;
1231 	BString fieldName;
1232 	BString fieldValue;
1233 	char lineBuffer[LINE_MAX];
1234 	while (char* line = fgets(lineBuffer, sizeof(lineBuffer), attrFile)) {
1235 		// chop off line break
1236 		size_t lineLen = strlen(line);
1237 		if (lineLen > 0 && line[lineLen - 1] == '\n')
1238 			line[--lineLen] = '\0';
1239 
1240 		// flush previous field, if a new field begins, otherwise append
1241 		if (lineLen == 0 || !isspace(line[0])) {
1242 			// new field -- flush the previous one
1243 			if (fieldName.Length() > 0) {
1244 				fieldValue = trim_string(fieldValue.String(),
1245 					fieldValue.Length());
1246 				package.AddString(fieldName.String(), fieldValue);
1247 				fieldName = "";
1248 			}
1249 		} else if (fieldName.Length() > 0) {
1250 			// append to current field
1251 			fieldValue += line;
1252 			continue;
1253 		} else {
1254 			// bogus line -- ignore
1255 			continue;
1256 		}
1257 
1258 		if (lineLen == 0)
1259 			continue;
1260 
1261 		// parse new field
1262 		char* colon = strchr(line, ':');
1263 		if (colon == NULL) {
1264 			// bogus line -- ignore
1265 			continue;
1266 		}
1267 
1268 		fieldName.SetTo(line, colon - line);
1269 		fieldName = trim_string(line, colon - line);
1270 		if (fieldName.Length() == 0) {
1271 			// invalid field name
1272 			continue;
1273 		}
1274 
1275 		fieldValue = colon + 1;
1276 
1277 		if (fieldName == "Package") {
1278 			// flush the current package
1279 			_AddPackageCredit(PackageCredit(package));
1280 			package.MakeEmpty();
1281 		}
1282 	}
1283 
1284 	// flush current package
1285 	_AddPackageCredit(PackageCredit(package));
1286 #endif
1287 }
1288 
1289 
1290 void
1291 AboutView::_AddPackageCreditEntries()
1292 {
1293 	for (PackageCreditMap::iterator it = fPackageCredits.begin();
1294 		it != fPackageCredits.end(); ++it) {
1295 		PackageCredit* package = it->second;
1296 
1297 		BString text(package->CopyrightAt(0));
1298 		int32 count = package->CountCopyrights();
1299 		for (int32 i = 1; i < count; i++)
1300 			text << "\n" << package->CopyrightAt(i);
1301 
1302 		AddCopyrightEntry(package->PackageName(), text.String(),
1303 			package->Licenses(), package->URL());
1304 	}
1305 }
1306 
1307 
1308 void
1309 AboutView::_AddPackageCredit(const PackageCredit& package)
1310 {
1311 	if (!package.IsValid())
1312 		return;
1313 
1314 	PackageCreditMap::iterator it = fPackageCredits.find(package.PackageName());
1315 	if (it != fPackageCredits.end()) {
1316 		// If the new package credit isn't "better" than the old one, ignore it.
1317 		PackageCredit* oldPackage = it->second;
1318 		if (!package.IsBetterThan(*oldPackage))
1319 			return;
1320 
1321 		// replace the old credit
1322 		fPackageCredits.erase(it);
1323 		delete oldPackage;
1324 	}
1325 
1326 	fPackageCredits[package.PackageName()] = new PackageCredit(package);
1327 }
1328 
1329 
1330 //	#pragma mark -
1331 
1332 
1333 static const char *
1334 MemUsageToString(char string[], size_t size, system_info *info)
1335 {
1336 	snprintf(string, size, "%d MB total, %d MB used (%d%%)",
1337 			int(info->max_pages / 256.0f + 0.5f),
1338 			int(info->used_pages / 256.0f + 0.5f),
1339 			int(100 * info->used_pages / info->max_pages));
1340 
1341 	return string;
1342 }
1343 
1344 
1345 static const char *
1346 UptimeToString(char string[], size_t size)
1347 {
1348 	int64 days, hours, minutes, seconds, remainder;
1349 	int64 systime = system_time();
1350 
1351 	days = systime / 86400000000LL;
1352 	remainder = systime % 86400000000LL;
1353 
1354 	hours = remainder / 3600000000LL;
1355 	remainder = remainder % 3600000000LL;
1356 
1357 	minutes = remainder / 60000000;
1358 	remainder = remainder % 60000000;
1359 
1360 	seconds = remainder / 1000000;
1361 
1362 	char *str = string;
1363 	if (days) {
1364 		str += snprintf(str, size, "%lld day%s",days, days > 1 ? "s" : "");
1365 	}
1366 	if (hours) {
1367 		str += snprintf(str, size - strlen(string), "%s%lld hour%s",
1368 				str != string ? ", " : "",
1369 				hours, hours > 1 ? "s" : "");
1370 	}
1371 	if (minutes) {
1372 		str += snprintf(str, size - strlen(string), "%s%lld minute%s",
1373 				str != string ? ", " : "",
1374 				minutes, minutes > 1 ? "s" : "");
1375 	}
1376 
1377 	if (seconds || str == string) {
1378 		// Haiku would be well-known to boot very fast.
1379 		// Let's be ready to handle below minute uptime, zero second included ;-)
1380 		str += snprintf(str, size - strlen(string), "%s%lld second%s",
1381 				str != string ? ", " : "",
1382 				seconds, seconds > 1 ? "s" : "");
1383 	}
1384 
1385 	return string;
1386 }
1387 
1388 
1389 int
1390 main()
1391 {
1392 	AboutApp app;
1393 	app.Run();
1394 	return 0;
1395 }
1396 
1397