xref: /haiku/src/apps/aboutsystem/AboutSystem.cpp (revision ddac407426cd3b3d0b4589d7a161b300b3539a2a)
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 		"Oliver Ruiz Dorantes\n"
712 		"Axel Dörfler\n"
713 		"Jérôme Duval\n"
714 		"René Gollent\n"
715 		"Karsten Heimrich\n"
716 		"Philippe Houdoin\n"
717 		"Maurice Kalinowski\n"
718 		"Euan Kirkhope\n"
719 		"Ryan Leavengood\n"
720 		"Michael Lotz\n"
721 		"David McPaul\n"
722 		"Fredrik Modéen\n"
723 		"Marcus Overhagen\n"
724 		"Michael Pfeiffer\n"
725 		"François Revol\n"
726 		"Andrej Spielmann\n"
727 		"Oliver Tappe\n"
728 		"Gerasim Troeglazov\n"
729 		"Ingo Weinhold\n"
730 		"Siarzhuk Zharski\n"
731 		"\n");
732 
733 	fCreditsView->SetFontAndColor(&font, B_FONT_ALL, &kHaikuOrange);
734 	fCreditsView->Insert("Past Maintainers:\n");
735 
736 	fCreditsView->SetFontAndColor(be_plain_font, B_FONT_ALL, &kDarkGrey);
737 	fCreditsView->Insert(
738 		"Andrew Bachmann\n"
739 		"Tyler Dauwalder\n"
740 		"Daniel Furrer\n"
741 		"Andre Alves Garzia\n"
742 		"Erik Jaesler\n"
743 		"Marcin Konicki\n"
744 		"Waldemar Kornewald\n"
745 		"Thomas Kurschel\n"
746 		"Frans Van Nispen\n"
747 		"Adi Oanca\n"
748 		"Michael Phipps\n"
749 		"Niels Sascha Reedijk\n"
750 		"David Reid\n"
751 		"Hugo Santos\n"
752 		"Alexander G. M. Smith\n"
753 		"Jonas Sundström\n"
754 		"Bryan Varner\n"
755 		"Nathan Whitehorn\n"
756 		"Michael Wilber\n"
757 		"Jonathan Yoder\n"
758 		"Gabe Yoder\n"
759 		"\n");
760 
761 	fCreditsView->SetFontAndColor(&font, B_FONT_ALL, &kHaikuOrange);
762 	fCreditsView->Insert("Website, Marketing & Documentation:\n");
763 
764 	fCreditsView->SetFontAndColor(be_plain_font, B_FONT_ALL, &kDarkGrey);
765 	fCreditsView->Insert(
766 		"Phil Greenway\n"
767 		"Gavin James\n"
768 		"Urias McCullough\n"
769 		"Niels Sascha Reedijk\n"
770 		"Jonathan Yoder\n"
771 		"\n");
772 
773 	fCreditsView->SetFontAndColor(&font, B_FONT_ALL, &kHaikuOrange);
774 	fCreditsView->Insert("Contributors:\n");
775 
776 	fCreditsView->SetFontAndColor(be_plain_font, B_FONT_ALL, &kDarkGrey);
777 	fCreditsView->Insert(
778 		"Andrea Anzani\n"
779 		"Andre Braga\n"
780 		"Bruce Cameron\n"
781 		"Greg Crain\n"
782 		"David Dengg\n"
783 		"John Drinkwater\n"
784 		"Cian Duffy\n"
785 		"Mark Erben\n"
786 		"Christian Fasshauer\n"
787 		"Andreas Färber\n"
788 		"Marc Flerackers\n"
789 		"Matthijs Hollemans\n"
790 		"Mathew Hounsell\n"
791 		"Morgan Howe\n"
792 		"Carwyn Jones\n"
793 		"Vasilis Kaoutsis\n"
794 		"James Kim\n"
795 		"Shintaro Kinugawa\n"
796 		"Jan Klötzke\n"
797 		"Kurtis Kopf\n"
798 		"Tomáš Kučera\n"
799 		"Luboš Kulič\n"
800 		"Elad Lahav\n"
801 		"Anthony Lee\n"
802 		"Santiago Lema\n"
803 		"Raynald Lesieur\n"
804 		"Oscar Lesta\n"
805 		"Jerome Leveque\n"
806 		"Christof Lutteroth\n"
807 		"Graham MacDonald\n"
808 		"Jan Matějek\n"
809 		"Brian Matzon\n"
810 		"Christopher ML Zumwalt May\n"
811 		"Andrew McCall\n"
812 		"Scott McCreary\n"
813 		"Michele (zuMi)\n"
814 		"Marius Middelthon\n"
815 		"Marco Minutoli\n"
816 		"Misza\n"
817 		"MrSiggler\n"
818 		"Alan Murta\n"
819 		"Pahtz\n"
820 		"Michael Paine\n"
821 		"Adrian Panasiuk\n"
822 		"Francesco Piccinno\n"
823 		"David Powell\n"
824 		"Jeremy Rand\n"
825 		"Hartmut Reh\n"
826 		"Daniel Reinhold\n"
827 		"Samuel Rodriguez Perez\n"
828 		"Thomas Roell\n"
829 		"Rafael Romo\n"
830 		"Philippe Saint-Pierre\n"
831 		"Ralf Schülke\n"
832 		"Reznikov Sergei\n"
833 		"Zousar Shaker\n"
834 		"Daniel Switkin\n"
835 		"Atsushi Takamatsu\n"
836 		"James Urquhart\n"
837 		"Jason Vandermark\n"
838 		"Sandor Vroemisse\n"
839 		"Denis Washington\n"
840 		"Ulrich Wimboeck\n"
841 		"James Woodcock\n"
842 		"Artur Wyszynski\n"
843 		"Gerald Zajac\n"
844 		"Clemens Zeidler\n"
845 		"Łukasz Zemczak\n"
846 		"JiSheng Zhang\n"
847 		"Zhao Shuai\n"
848 		"\n" B_UTF8_ELLIPSIS " and probably some more we forgot to mention "
849 		"(sorry!)"
850 		"\n\n");
851 
852 	fCreditsView->SetFontAndColor(&font, B_FONT_ALL, &kHaikuOrange);
853 	fCreditsView->Insert("Special Thanks To:\n");
854 
855 	fCreditsView->SetFontAndColor(be_plain_font, B_FONT_ALL, &kDarkGrey);
856 	fCreditsView->Insert("Travis Geiselbrecht (and his NewOS kernel)\n");
857 	fCreditsView->Insert("Michael Phipps (project founder)\n\n");
858 
859 	// copyrights for various projects we use
860 
861 	BPath mitPath;
862 	_GetLicensePath("MIT", mitPath);
863 
864 	font.SetSize(be_bold_font->Size() + 4);
865 	font.SetFace(B_BOLD_FACE);
866 	fCreditsView->SetFontAndColor(&font, B_FONT_ALL, &kHaikuGreen);
867 	fCreditsView->Insert("\nCopyrights\n\n");
868 
869 	fCreditsView->SetFontAndColor(be_plain_font, B_FONT_ALL, &kDarkGrey);
870 	fCreditsView->Insert("[Click a license name to read the respective "
871 		"license.]\n\n");
872 
873 	// Haiku license
874 	fCreditsView->Insert("The code that is unique to Haiku, especially the "
875 		"kernel and all code that applications may link against, is "
876 		"distributed under the terms of the ");
877 	fCreditsView->InsertHyperText("MIT license",
878 		new OpenFileAction(mitPath.Path()));
879 	fCreditsView->Insert(". Some system libraries contain third party code "
880 		"distributed under the LGPL license. You can find the copyrights "
881 		"to third party code below.\n\n");
882 
883 	// GNU copyrights
884 	AddCopyrightEntry("The GNU Project",
885 		"Contains software from the GNU Project, "
886 		"released under the GPL and LGPL licences:\n"
887 		"GNU C Library, "
888 		"GNU coretools, diffutils, findutils, "
889 		"sharutils, gawk, bison, m4, make, "
890 		"gdb, wget, ncurses, termcap, "
891 		"Bourne Again Shell.\n"
892 		COPYRIGHT_STRING "The Free Software Foundation.",
893 		StringVector("GNU LGPL v2.1", "GNU GPL v2", "GNU GPL v3", NULL),
894 		"http://www.gnu.org");
895 
896 	// FreeBSD copyrights
897 	AddCopyrightEntry("The FreeBSD Project",
898 		"Contains software from the FreeBSD Project, "
899 		"released under the BSD licence:\n"
900 		"cal, ftpd, ping, telnet, "
901 		"telnetd, traceroute\n"
902 		COPYRIGHT_STRING "1994-2008 The FreeBSD Project.  "
903 		"All rights reserved.",
904 		"http://www.freebsd.org");
905 			// TODO: License!
906 
907 	// NetBSD copyrights
908 	AddCopyrightEntry("The NetBSD Project",
909 		"Contains software developed by the NetBSD, "
910 		"Foundation, Inc. and its contributors:\n"
911 		"ftp, tput\n"
912 		COPYRIGHT_STRING "1996-2008 The NetBSD Foundation, Inc.  "
913 		"All rights reserved.",
914 		"http://www.netbsd.org");
915 			// TODO: License!
916 
917 	// FFMpeg copyrights
918 	_AddPackageCredit(PackageCredit("FFMpeg libavcodec")
919 		.SetCopyright(COPYRIGHT_STRING "2000-2007 Fabrice Bellard, et al.")
920 		.SetURL("http://www.ffmpeg.org"));
921 			// TODO: License!
922 
923 	// AGG copyrights
924 	_AddPackageCredit(PackageCredit("AntiGrain Geometry")
925 		.SetCopyright(COPYRIGHT_STRING "2002-2006 Maxim Shemanarev (McSeem).")
926 		.SetURL("http://www.antigrain.com"));
927 			// TODO: License!
928 
929 	// PDFLib copyrights
930 	_AddPackageCredit(PackageCredit("PDFLib")
931 		.SetCopyright(COPYRIGHT_STRING "1997-2006 PDFlib GmbH and Thomas Merz. "
932 			"All rights reserved.\n"
933 			"PDFlib and PDFlib logo are registered trademarks of PDFlib GmbH.")
934 		.SetURL("http://www.pdflib.com"));
935 			// TODO: License!
936 
937 	// FreeType copyrights
938 	_AddPackageCredit(PackageCredit("FreeType2")
939 		.SetCopyright("Portions of this software are copyright "
940 			B_UTF8_COPYRIGHT " 1996-2006 "
941 			"The FreeType Project.  All rights reserved.")
942 		.SetURL("http://www.freetype.org"));
943 			// TODO: License!
944 
945 	// Mesa3D (http://www.mesa3d.org) copyrights
946 	_AddPackageCredit(PackageCredit("Mesa")
947 		.SetCopyright(COPYRIGHT_STRING "1999-2006 Brian Paul. "
948 			"Mesa3D project.  All rights reserved.")
949 		.SetURL("http://www.mesa3d.org"));
950 			// TODO: License!
951 
952 	// SGI's GLU implementation copyrights
953 	_AddPackageCredit(PackageCredit("GLU")
954 		.SetCopyright(COPYRIGHT_STRING
955 			"1991-2000 Silicon Graphics, Inc. "
956 			"SGI's Software FreeB license.  All rights reserved."));
957 			// TODO: License!
958 
959 	// GLUT implementation copyrights
960 	_AddPackageCredit(PackageCredit("GLUT")
961 		.SetCopyrights(COPYRIGHT_STRING "1994-1997 Mark Kilgard. "
962 				"All rights reserved.",
963 			COPYRIGHT_STRING "1997 Be Inc.",
964 			COPYRIGHT_STRING "1999 Jake Hamby.",
965 			NULL));
966 			// TODO: License!
967 
968 	// OpenGroup & DEC (BRegion backend) copyright
969 	_AddPackageCredit(PackageCredit("BRegion backend (XFree86)")
970 		.SetCopyrights(COPYRIGHT_STRING "1987, 1988, 1998 The Open Group.",
971 			COPYRIGHT_STRING "1987, 1988 Digital Equipment "
972 				"Corporation, Maynard, Massachusetts.\n"
973 				"All rights reserved.",
974 			NULL));
975 			// TODO: License!
976 
977 	// Konatu font
978 	_AddPackageCredit(PackageCredit("Konatu font")
979 		.SetCopyright(COPYRIGHT_STRING "2002- MASUDA mitiya.\n"
980 			"MIT license. All rights reserved."));
981 			// TODO: License!
982 
983 	// expat copyrights
984 	_AddPackageCredit(PackageCredit("expat")
985 		.SetCopyrights(COPYRIGHT_STRING
986 				"1998, 1999, 2000 Thai Open Source "
987 				"Software Center Ltd and Clark Cooper.",
988 			COPYRIGHT_STRING "2001, 2002, 2003 Expat maintainers.",
989 			NULL));
990 			// TODO: License!
991 
992 	// zlib copyrights
993 	_AddPackageCredit(PackageCredit("zlib")
994 		.SetCopyright(COPYRIGHT_STRING
995 			"1995-2004 Jean-loup Gailly and Mark Adler."));
996 			// TODO: License!
997 
998 	// zip copyrights
999 	_AddPackageCredit(PackageCredit("Info-ZIP")
1000 		.SetCopyright(COPYRIGHT_STRING
1001 			"1990-2002 Info-ZIP. All rights reserved."));
1002 			// TODO: License!
1003 
1004 	// bzip2 copyrights
1005 	_AddPackageCredit(PackageCredit("bzip2")
1006 		.SetCopyright(COPYRIGHT_STRING
1007 			"1996-2005 Julian R Seward. All rights reserved."));
1008 			// TODO: License!
1009 
1010 	// VIM copyrights
1011 	_AddPackageCredit(PackageCredit("Vi IMproved")
1012 		.SetCopyright(COPYRIGHT_STRING "Bram Moolenaar et al."));
1013 			// TODO: License!
1014 
1015 	// lp_solve copyrights
1016 	_AddPackageCredit(PackageCredit("lp_solve")
1017 		.SetCopyright(COPYRIGHT_STRING
1018 			"Michel Berkelaar, Kjell Eikland, Peter Notebaert")
1019 		.SetLicense("GNU LGPL v2.1")
1020 		.SetURL("http://lpsolve.sourceforge.net/"));
1021 
1022 	// OpenEXR copyrights
1023 	_AddPackageCredit(PackageCredit("OpenEXR")
1024 		.SetCopyright(COPYRIGHT_STRING "2002-2005 Industrial Light & Magic, "
1025 			"a division of Lucas Digital Ltd. LLC."));
1026 			// TODO: License!
1027 
1028 	// Bullet copyrights
1029 	_AddPackageCredit(PackageCredit("Bullet")
1030 		.SetCopyright(COPYRIGHT_STRING "2003-2008 Erwin Coumans")
1031 		.SetURL("http://www.bulletphysics.com"));
1032 			// TODO: License!
1033 
1034 	// atftp copyrights
1035 	_AddPackageCredit(PackageCredit("atftp")
1036 		.SetCopyright(COPYRIGHT_STRING
1037 			"2000 Jean-Pierre Lefebvre and Remi Lefebvre"));
1038 			// TODO: License!
1039 
1040 	// Netcat copyrights
1041 	_AddPackageCredit(PackageCredit("Netcat")
1042 		.SetCopyright(COPYRIGHT_STRING "1996 Hobbit"));
1043 			// TODO: License!
1044 
1045 	// acpica copyrights
1046 	_AddPackageCredit(PackageCredit("acpica")
1047 		.SetCopyright(COPYRIGHT_STRING "1999-2006 Intel Corp."));
1048 			// TODO: License!
1049 
1050 	// unrar copyrights
1051 	_AddPackageCredit(PackageCredit("unrar")
1052 		.SetCopyright(COPYRIGHT_STRING "2002-2008 Alexander L. Roshal. "
1053 			"All rights reserved.")
1054 		.SetURL("http://www.rarlab.com"));
1055 			// TODO: License!
1056 
1057 	// libpng copyrights
1058 	_AddPackageCredit(PackageCredit("libpng")
1059 		.SetCopyright(COPYRIGHT_STRING "2004, 2006-2008 Glenn "
1060 			"Randers-Pehrson."));
1061 			// TODO: License!
1062 
1063 	// libprint copyrights
1064 	_AddPackageCredit(PackageCredit("libprint")
1065 		.SetCopyright(COPYRIGHT_STRING
1066 			"1999-2000 Y.Takagi. All rights reserved."));
1067 			// TODO: License!
1068 
1069 	// cortex copyrights
1070 	_AddPackageCredit(PackageCredit("Cortex")
1071 		.SetCopyright(COPYRIGHT_STRING "1999-2000 Eric Moon."));
1072 			// TODO: License!
1073 
1074 	// FluidSynth copyrights
1075 	_AddPackageCredit(PackageCredit("FluidSynth")
1076 		.SetCopyright(COPYRIGHT_STRING "2003 Peter Hanappe and others."));
1077 			// TODO: License!
1078 
1079 	// CannaIM copyrights
1080 	_AddPackageCredit(PackageCredit("CannaIM")
1081 		.SetCopyright(COPYRIGHT_STRING "1999 Masao Kawamura."));
1082 			// TODO: License!
1083 
1084 	// libxml2, libxslt, libexslt copyrights
1085 	_AddPackageCredit(PackageCredit("libxml2, libxslt")
1086 		.SetCopyright(COPYRIGHT_STRING
1087 			"1998-2003 Daniel Veillard. All rights reserved."));
1088 			// TODO: License!
1089 
1090 	_AddPackageCredit(PackageCredit("libexslt")
1091 		.SetCopyright(COPYRIGHT_STRING
1092 			"2001-2002 Thomas Broyer, Charlie "
1093 			"Bozeman and Daniel Veillard.  All rights reserved."));
1094 			// TODO: License!
1095 
1096 	// Xiph.org Foundation copyrights
1097 	_AddPackageCredit(PackageCredit("Xiph.org Foundation")
1098 		.SetCopyrights("libvorbis, libogg, libtheora, libspeex",
1099 			COPYRIGHT_STRING "1994-2008 Xiph.Org. "
1100 				"All rights reserved.",
1101 			NULL)
1102 		.SetURL("http://www.xiph.org"));
1103 			// TODO: License!
1104 
1105 	// The Tcpdump Group
1106 	_AddPackageCredit(PackageCredit("The Tcpdump Group")
1107 		.SetCopyright("tcpdump, libpcap")
1108 		.SetURL("http://www.tcpdump.org"));
1109 			// TODO: License!
1110 
1111 	// Matroska
1112 	_AddPackageCredit(PackageCredit("libmatroska")
1113 		.SetCopyright(COPYRIGHT_STRING "2002-2003 Steve Lhomme. "
1114 			"All rights reserved.")
1115 		.SetURL("http://www.matroska.org"));
1116 			// TODO: License!
1117 
1118 	// BColorQuantizer (originally CQuantizer code)
1119 	_AddPackageCredit(PackageCredit("CQuantizer")
1120 		.SetCopyright(COPYRIGHT_STRING "1996-1997 Jeff Prosise. "
1121 			"All rights reserved."));
1122 			// TODO: License!
1123 
1124 	// MAPM (Mike's Arbitrary Precision Math Library) used by DeskCalc
1125 	_AddPackageCredit(PackageCredit("MAPM")
1126 		.SetCopyright(COPYRIGHT_STRING
1127 			"1999-2007 Michael C. Ring. All rights reserved.")
1128 		.SetURL("http://tc.umn.edu/~ringx004"));
1129 			// TODO: License!
1130 
1131 	// MkDepend 1.7 copyright (Makefile dependency generator)
1132 	_AddPackageCredit(PackageCredit("MkDepend")
1133 		.SetCopyright(COPYRIGHT_STRING "1995-2001 Lars Düning. "
1134 			"All rights reserved."));
1135 			// TODO: License!
1136 
1137 	// libhttpd copyright (used as Poorman backend)
1138 	_AddPackageCredit(PackageCredit("libhttpd")
1139 		.SetCopyright(COPYRIGHT_STRING
1140 			"1995,1998,1999,2000,2001 by "
1141 			"Jef Poskanzer. All rights reserved.")
1142 		.SetLicense("LibHTTPd")
1143 		.SetURL("http://www.acme.com/software/thttpd/"));
1144 
1145 #ifdef __INTEL__
1146 	// Udis86 copyrights
1147 	_AddPackageCredit(PackageCredit("Udis86")
1148 		.SetCopyright(COPYRIGHT_STRING "2002, 2003, 2004 Vivek Mohan. "
1149 			"All rights reserved.")
1150 		.SetURL("http://udis86.sourceforge.net"));
1151 			// TODO: License!
1152 #endif
1153 
1154 	_AddCopyrightsFromAttribute();
1155 	_AddPackageCreditEntries();
1156 
1157 	return new CropView(creditsScroller, 0, 1, 1, 1);
1158 }
1159 
1160 
1161 status_t
1162 AboutView::_GetLicensePath(const char* license, BPath& path)
1163 {
1164 	static const directory_which directoryConstants[] = {
1165 		B_USER_DATA_DIRECTORY,
1166 		B_COMMON_DATA_DIRECTORY,
1167 		B_SYSTEM_DATA_DIRECTORY
1168 	};
1169 	static const int dirCount = 3;
1170 
1171 	for (int i = 0; i < dirCount; i++) {
1172 		struct stat st;
1173 		status_t error = find_directory(directoryConstants[i], &path);
1174 		if (error == B_OK && path.Append("licenses") == B_OK
1175 			&& path.Append(license) == B_OK
1176 			&& lstat(path.Path(), &st) == 0) {
1177 			return B_OK;
1178 		}
1179 	}
1180 
1181 	path.Unset();
1182 	return B_ENTRY_NOT_FOUND;
1183 }
1184 
1185 
1186 void
1187 AboutView::_AddCopyrightsFromAttribute()
1188 {
1189 #ifdef __HAIKU__
1190 	// open the app executable file
1191 	char appPath[B_PATH_NAME_LENGTH];
1192 	int appFD;
1193 	if (BPrivate::get_app_path(appPath) != B_OK
1194 		|| (appFD = open(appPath, O_RDONLY)) < 0) {
1195 		return;
1196 	}
1197 
1198 	// open the attribute
1199 	int attrFD = fs_open_attr(appFD, "COPYRIGHTS", B_STRING_TYPE, O_RDONLY);
1200 	close(appFD);
1201 	if (attrFD < 0)
1202 		return;
1203 
1204 	// attach it to a FILE
1205 	FILE* attrFile = fdopen(attrFD, "r");
1206 	if (attrFile == NULL) {
1207 		close(attrFD);
1208 		return;
1209 	}
1210 	CObjectDeleter<FILE, int> _(attrFile, fclose);
1211 
1212 	// read and parse the copyrights
1213 	BMessage package;
1214 	BString fieldName;
1215 	BString fieldValue;
1216 	char lineBuffer[LINE_MAX];
1217 	while (char* line = fgets(lineBuffer, sizeof(lineBuffer), attrFile)) {
1218 		// chop off line break
1219 		size_t lineLen = strlen(line);
1220 		if (lineLen > 0 && line[lineLen - 1] == '\n')
1221 			line[--lineLen] = '\0';
1222 
1223 		// flush previous field, if a new field begins, otherwise append
1224 		if (lineLen == 0 || !isspace(line[0])) {
1225 			// new field -- flush the previous one
1226 			if (fieldName.Length() > 0) {
1227 				fieldValue = trim_string(fieldValue.String(),
1228 					fieldValue.Length());
1229 				package.AddString(fieldName.String(), fieldValue);
1230 				fieldName = "";
1231 			}
1232 		} else if (fieldName.Length() > 0) {
1233 			// append to current field
1234 			fieldValue += line;
1235 			continue;
1236 		} else {
1237 			// bogus line -- ignore
1238 			continue;
1239 		}
1240 
1241 		if (lineLen == 0)
1242 			continue;
1243 
1244 		// parse new field
1245 		char* colon = strchr(line, ':');
1246 		if (colon == NULL) {
1247 			// bogus line -- ignore
1248 			continue;
1249 		}
1250 
1251 		fieldName.SetTo(line, colon - line);
1252 		fieldName = trim_string(line, colon - line);
1253 		if (fieldName.Length() == 0) {
1254 			// invalid field name
1255 			continue;
1256 		}
1257 
1258 		fieldValue = colon + 1;
1259 
1260 		if (fieldName == "Package") {
1261 			// flush the current package
1262 			_AddPackageCredit(PackageCredit(package));
1263 			package.MakeEmpty();
1264 		}
1265 	}
1266 
1267 	// flush current package
1268 	_AddPackageCredit(PackageCredit(package));
1269 #endif
1270 }
1271 
1272 
1273 void
1274 AboutView::_AddPackageCreditEntries()
1275 {
1276 	for (PackageCreditMap::iterator it = fPackageCredits.begin();
1277 		it != fPackageCredits.end(); ++it) {
1278 		PackageCredit* package = it->second;
1279 
1280 		BString text(package->CopyrightAt(0));
1281 		int32 count = package->CountCopyrights();
1282 		for (int32 i = 1; i < count; i++)
1283 			text << "\n" << package->CopyrightAt(i);
1284 
1285 		AddCopyrightEntry(package->PackageName(), text.String(),
1286 			package->Licenses(), package->URL());
1287 	}
1288 }
1289 
1290 
1291 void
1292 AboutView::_AddPackageCredit(const PackageCredit& package)
1293 {
1294 	if (!package.IsValid())
1295 		return;
1296 
1297 	PackageCreditMap::iterator it = fPackageCredits.find(package.PackageName());
1298 	if (it != fPackageCredits.end()) {
1299 		// If the new package credit isn't "better" than the old one, ignore it.
1300 		PackageCredit* oldPackage = it->second;
1301 		if (!package.IsBetterThan(*oldPackage))
1302 			return;
1303 
1304 		// replace the old credit
1305 		fPackageCredits.erase(it);
1306 		delete oldPackage;
1307 	}
1308 
1309 	fPackageCredits[package.PackageName()] = new PackageCredit(package);
1310 }
1311 
1312 
1313 //	#pragma mark -
1314 
1315 
1316 static const char *
1317 MemUsageToString(char string[], size_t size, system_info *info)
1318 {
1319 	snprintf(string, size, "%d MB total, %d MB used (%d%%)",
1320 			int(info->max_pages / 256.0f + 0.5f),
1321 			int(info->used_pages / 256.0f + 0.5f),
1322 			int(100 * info->used_pages / info->max_pages));
1323 
1324 	return string;
1325 }
1326 
1327 
1328 static const char *
1329 UptimeToString(char string[], size_t size)
1330 {
1331 	int64 days, hours, minutes, seconds, remainder;
1332 	int64 systime = system_time();
1333 
1334 	days = systime / 86400000000LL;
1335 	remainder = systime % 86400000000LL;
1336 
1337 	hours = remainder / 3600000000LL;
1338 	remainder = remainder % 3600000000LL;
1339 
1340 	minutes = remainder / 60000000;
1341 	remainder = remainder % 60000000;
1342 
1343 	seconds = remainder / 1000000;
1344 
1345 	char *str = string;
1346 	if (days) {
1347 		str += snprintf(str, size, "%lld day%s",days, days > 1 ? "s" : "");
1348 	}
1349 	if (hours) {
1350 		str += snprintf(str, size - strlen(string), "%s%lld hour%s",
1351 				str != string ? ", " : "",
1352 				hours, hours > 1 ? "s" : "");
1353 	}
1354 	if (minutes) {
1355 		str += snprintf(str, size - strlen(string), "%s%lld minute%s",
1356 				str != string ? ", " : "",
1357 				minutes, minutes > 1 ? "s" : "");
1358 	}
1359 
1360 	if (seconds || str == string) {
1361 		// Haiku would be well-known to boot very fast.
1362 		// Let's be ready to handle below minute uptime, zero second included ;-)
1363 		str += snprintf(str, size - strlen(string), "%s%lld second%s",
1364 				str != string ? ", " : "",
1365 				seconds, seconds > 1 ? "s" : "");
1366 	}
1367 
1368 	return string;
1369 }
1370 
1371 
1372 int
1373 main()
1374 {
1375 	AboutApp app;
1376 	app.Run();
1377 	return 0;
1378 }
1379 
1380