xref: /haiku/src/apps/aboutsystem/AboutSystem.cpp (revision 96490327ca254c5b2e989fb3fc20c4d36c2c6f93)
1 /*
2  * Copyright (c) 2005-2008, 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 
15 #include <AppFileInfo.h>
16 #include <Application.h>
17 #include <Bitmap.h>
18 #include <File.h>
19 #include <FindDirectory.h>
20 #include <Font.h>
21 #include <fs_attr.h>
22 #include <MessageRunner.h>
23 #include <Messenger.h>
24 #include <OS.h>
25 #include <Path.h>
26 #include <Query.h>
27 #include <Resources.h>
28 #include <Screen.h>
29 #include <ScrollView.h>
30 #include <String.h>
31 #include <StringView.h>
32 #include <TranslationUtils.h>
33 #include <TranslatorFormats.h>
34 #include <View.h>
35 #include <Volume.h>
36 #include <VolumeRoster.h>
37 #include <Window.h>
38 
39 #include <AppMisc.h>
40 #include <AutoDeleter.h>
41 #include <cpu_type.h>
42 
43 #include "HyperTextActions.h"
44 #include "HyperTextView.h"
45 #include "Utilities.h"
46 
47 
48 #define SCROLL_CREDITS_VIEW 'mviv'
49 #define READ_APP_QUERY_ENT 'raqe'
50 
51 
52 static const char *UptimeToString(char string[], size_t size);
53 static const char *MemUsageToString(char string[], size_t size,
54 	system_info *info);
55 
56 static const rgb_color kDarkGrey = { 100, 100, 100, 255 };
57 static const rgb_color kHaikuGreen = { 42, 131, 36, 255 };
58 static const rgb_color kHaikuOrange = { 255, 69, 0, 255 };
59 static const rgb_color kHaikuYellow = { 255, 176, 0, 255 };
60 static const rgb_color kLinkBlue = { 80, 80, 200, 255 };
61 
62 
63 class AboutApp : public BApplication {
64 	public:
65 		AboutApp(void);
66 };
67 
68 class AboutWindow : public BWindow {
69 	public:
70 				AboutWindow(void);
71 		bool	QuitRequested(void);
72 };
73 
74 class AboutView : public BView {
75 	public:
76 				AboutView(const BRect &r);
77 				~AboutView(void);
78 
79 		virtual void AttachedToWindow();
80 		virtual void Pulse();
81 
82 		virtual void FrameResized(float width, float height);
83 		virtual void Draw(BRect update);
84 		virtual void MessageReceived(BMessage *msg);
85 		virtual void MouseDown(BPoint pt);
86 
87 		void	AddCopyrightEntry(const char *name, const char *text,
88 					const Licenses& licenses, const char *url);
89 		void	AddCopyrightEntry(const char *name, const char *text,
90 					const char *url = NULL);
91 		void	AddCopyrightEntry(const BMessage& packageDescription);
92 		void	PickRandomHaiku();
93 
94 	private:
95 		void	_AddCopyrightsFromAttribute();
96 
97 		BStringView		*fMemView;
98 		BStringView		*fUptimeView;
99 		BView			*fInfoView;
100 		HyperTextView	*fCreditsView;
101 
102 		BBitmap			*fLogo;
103 
104 		BPoint			fDrawPoint;
105 
106 		bigtime_t		fLastActionTime;
107 		BMessageRunner	*fScrollRunner;
108 		BQuery			fAppsQuery;
109 };
110 
111 
112 //	#pragma mark -
113 
114 
115 AboutApp::AboutApp(void)
116 	: BApplication("application/x-vnd.Haiku-About")
117 {
118 	AboutWindow *window = new AboutWindow();
119 	window->Show();
120 }
121 
122 
123 //	#pragma mark -
124 
125 
126 AboutWindow::AboutWindow()
127 	: BWindow(BRect(0, 0, 500, 300), "About This System", B_TITLED_WINDOW,
128 			B_NOT_RESIZABLE | B_NOT_ZOOMABLE)
129 {
130 	AboutView *view = new AboutView(Bounds());
131 	AddChild(view);
132 
133 	// start reading from the app query
134 	BMessage msg(READ_APP_QUERY_ENT);
135 	BMessenger msgr(view);
136 	msgr.SendMessage(&msg);
137 
138 	MoveTo((BScreen().Frame().Width() - Bounds().Width()) / 2,
139 		(BScreen().Frame().Height() - Bounds().Height()) / 2 );
140 }
141 
142 
143 bool
144 AboutWindow::QuitRequested()
145 {
146 	be_app->PostMessage(B_QUIT_REQUESTED);
147 	return true;
148 }
149 
150 
151 AboutView::AboutView(const BRect &rect)
152 	: BView(rect, "aboutview", B_FOLLOW_ALL, B_WILL_DRAW | B_PULSE_NEEDED),
153 	fLastActionTime(system_time()),
154 	fScrollRunner(NULL)
155 {
156 	fLogo = BTranslationUtils::GetBitmap(B_PNG_FORMAT, "haikulogo.png");
157 	if (fLogo) {
158 		fDrawPoint.x = (225-fLogo->Bounds().Width()) / 2;
159 		fDrawPoint.y = 0;
160 	}
161 
162 	// Begin Construction of System Information controls
163 
164 	font_height height;
165 	float labelHeight, textHeight;
166 
167 	system_info systemInfo;
168 	get_system_info(&systemInfo);
169 
170 	be_plain_font->GetHeight(&height);
171 	textHeight = height.ascent + height.descent + height.leading;
172 
173 	be_bold_font->GetHeight(&height);
174 	labelHeight = height.ascent + height.descent + height.leading;
175 
176 	BRect r(0, 0, 225, Bounds().bottom);
177 	if (fLogo)
178 		r.OffsetBy(0, fLogo->Bounds().Height());
179 
180 	fInfoView = new BView(r, "infoview", B_FOLLOW_LEFT | B_FOLLOW_TOP_BOTTOM,
181 		B_WILL_DRAW);
182 	fInfoView->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
183 	fInfoView->SetLowColor(fInfoView->ViewColor());
184 	fInfoView->SetHighColor(ui_color(B_PANEL_TEXT_COLOR));
185 	AddChild(fInfoView);
186 
187 	// Add all the various labels for system infomation
188 
189 	BStringView *stringView;
190 
191 	// OS Version
192 	r.Set(5, 5, 224, labelHeight + 5);
193 	stringView = new BStringView(r, "oslabel", "Version:");
194 	stringView->SetFont(be_bold_font);
195 	fInfoView->AddChild(stringView);
196 	stringView->ResizeToPreferred();
197 
198 	// we update "labelHeight" to the actual height of the string view
199 	labelHeight = stringView->Bounds().Height();
200 
201 	r.OffsetBy(0, labelHeight);
202 	r.bottom = r.top + textHeight;
203 
204 	char string[256];
205 	strcpy(string, "Unknown");
206 
207 	// the version is stored in the BEOS:APP_VERSION attribute of libbe.so
208 	BPath path;
209 	if (find_directory(B_BEOS_LIB_DIRECTORY, &path) == B_OK) {
210 		path.Append("libbe.so");
211 
212 		BAppFileInfo appFileInfo;
213 		version_info versionInfo;
214 		BFile file;
215 		if (file.SetTo(path.Path(), B_READ_ONLY) == B_OK
216 			&& appFileInfo.SetTo(&file) == B_OK
217 			&& appFileInfo.GetVersionInfo(&versionInfo,
218 				B_APP_VERSION_KIND) == B_OK
219 			&& versionInfo.short_info[0] != '\0')
220 			strcpy(string, versionInfo.short_info);
221 	}
222 
223 	// Add revision from uname() info
224 	utsname unameInfo;
225 	if (uname(&unameInfo) == 0) {
226 		long revision;
227 		if (sscanf(unameInfo.version, "r%ld", &revision) == 1) {
228 			char version[16];
229 			snprintf(version, sizeof(version), "%ld", revision);
230 			strlcat(string, " (Revision ", sizeof(string));
231 			strlcat(string, version, sizeof(string));
232 			strlcat(string, ")", sizeof(string));
233 		}
234 	}
235 
236 	stringView = new BStringView(r, "ostext", string);
237 	fInfoView->AddChild(stringView);
238 	stringView->ResizeToPreferred();
239 
240 	// GCC version
241 #if __GNUC__ != 2
242 	r.OffsetBy(0, textHeight);
243 	r.bottom = r.top + textHeight;
244 
245 	snprintf(string, sizeof(string), "GCC %d", __GNUC__);
246 
247 	stringView = new BStringView(r, "gcctext", string);
248 	fInfoView->AddChild(stringView);
249 	stringView->ResizeToPreferred();
250 #endif
251 
252 	// CPU count, type and clock speed
253 	r.OffsetBy(0, textHeight * 1.5);
254 	r.bottom = r.top + labelHeight;
255 
256 	if (systemInfo.cpu_count > 1)
257 		sprintf(string, "%ld Processors:", systemInfo.cpu_count);
258 	else
259 		strcpy(string, "Processor:");
260 
261 	stringView = new BStringView(r, "cpulabel", string);
262 	stringView->SetFont(be_bold_font);
263 	fInfoView->AddChild(stringView);
264 	stringView->ResizeToPreferred();
265 
266 	BString cpuType;
267 	cpuType << get_cpu_vendor_string(systemInfo.cpu_type)
268 		<< " " << get_cpu_model_string(&systemInfo);
269 
270 	r.OffsetBy(0, labelHeight);
271 	r.bottom = r.top + textHeight;
272 	stringView = new BStringView(r, "cputext", cpuType.String());
273 	fInfoView->AddChild(stringView);
274 	stringView->ResizeToPreferred();
275 
276 	r.OffsetBy(0, textHeight);
277 	r.bottom = r.top + textHeight;
278 
279 	int32 clockSpeed = get_rounded_cpu_speed();
280 	if (clockSpeed < 1000)
281 		sprintf(string,"%ld MHz", clockSpeed);
282 	else
283 		sprintf(string,"%.2f GHz", clockSpeed / 1000.0f);
284 
285 	stringView = new BStringView(r, "mhztext", string);
286 	fInfoView->AddChild(stringView);
287 	stringView->ResizeToPreferred();
288 
289 	// RAM
290 	r.OffsetBy(0, textHeight * 1.5);
291 	r.bottom = r.top + labelHeight;
292 	stringView = new BStringView(r, "ramlabel", "Memory:");
293 	stringView->SetFont(be_bold_font);
294 	fInfoView->AddChild(stringView);
295 	stringView->ResizeToPreferred();
296 
297 	r.OffsetBy(0, labelHeight);
298 	r.bottom = r.top + textHeight;
299 
300 	fMemView = new BStringView(r, "ramtext", "");
301 	fInfoView->AddChild(fMemView);
302 	fMemView->SetText(MemUsageToString(string, sizeof(string), &systemInfo));
303 
304 	// Kernel build time/date
305 	r.OffsetBy(0, textHeight * 1.5);
306 	r.bottom = r.top + labelHeight;
307 	stringView = new BStringView(r, "kernellabel", "Kernel:");
308 	stringView->SetFont(be_bold_font);
309 	fInfoView->AddChild(stringView);
310 	stringView->ResizeToPreferred();
311 
312 	r.OffsetBy(0, labelHeight);
313 	r.bottom = r.top + textHeight;
314 
315 	snprintf(string, sizeof(string), "%s %s",
316 		systemInfo.kernel_build_date, systemInfo.kernel_build_time);
317 
318 	stringView = new BStringView(r, "kerneltext", string);
319 	fInfoView->AddChild(stringView);
320 	stringView->ResizeToPreferred();
321 
322 	// Uptime
323 	r.OffsetBy(0, textHeight * 1.5);
324 	r.bottom = r.top + labelHeight;
325 	stringView = new BStringView(r, "uptimelabel", "Time Running:");
326 	stringView->SetFont(be_bold_font);
327 	fInfoView->AddChild(stringView);
328 	stringView->ResizeToPreferred();
329 
330 	r.OffsetBy(0, labelHeight);
331 	r.bottom = r.top + textHeight;
332 
333 	fUptimeView = new BStringView(r, "uptimetext", "");
334 	fInfoView->AddChild(fUptimeView);
335 	// string width changes, so we don't do ResizeToPreferred()
336 
337 	fUptimeView->SetText(UptimeToString(string, sizeof(string)));
338 
339 	// Begin construction of the credits view
340 	r = Bounds();
341 	r.left += fInfoView->Bounds().right + 1;
342 	r.right -= B_V_SCROLL_BAR_WIDTH;
343 
344 	fCreditsView = new HyperTextView(r, "credits",
345 		r.OffsetToCopy(0, 0).InsetByCopy(5, 5), B_FOLLOW_ALL);
346 	fCreditsView->SetFlags(fCreditsView->Flags() | B_FRAME_EVENTS );
347 	fCreditsView->SetStylable(true);
348 	fCreditsView->MakeEditable(false);
349 	fCreditsView->MakeSelectable(false);
350 	fCreditsView->SetWordWrap(true);
351 
352 	BScrollView *creditsScroller = new BScrollView("creditsScroller",
353 		fCreditsView, B_FOLLOW_ALL, B_WILL_DRAW | B_FRAME_EVENTS, false, true,
354 		B_PLAIN_BORDER);
355 	AddChild(creditsScroller);
356 
357 	BFont font(be_bold_font);
358 	font.SetSize(font.Size() + 4);
359 
360 	fCreditsView->SetFontAndColor(&font, B_FONT_ALL, &kHaikuGreen);
361 	fCreditsView->Insert("Haiku\n");
362 
363 	font.SetSize(be_bold_font->Size());
364 	font.SetFace(B_BOLD_FACE | B_ITALIC_FACE);
365 
366 	time_t time = ::time(NULL);
367 	struct tm* tm = localtime(&time);
368 	int32 year = tm->tm_year + 1900;
369 	if (year < 2007)
370 		year = 2007;
371 	snprintf(string, sizeof(string),
372 		"Copyright " B_UTF8_COPYRIGHT "2001-%ld Haiku, Inc.\n", year);
373 
374 	fCreditsView->SetFontAndColor(be_plain_font, B_FONT_ALL, &kDarkGrey);
375 	fCreditsView->Insert(string);
376 
377 	fCreditsView->SetFontAndColor(be_plain_font, B_FONT_ALL, &kLinkBlue);
378 	fCreditsView->InsertHyperText("http://haiku-os.org",
379 		new URLAction("http://haiku-os.org"));
380 	fCreditsView->Insert("\n\n");
381 
382 	fCreditsView->SetFontAndColor(&font, B_FONT_ALL, &kHaikuOrange);
383 	fCreditsView->Insert("Team Leads:\n");
384 
385 	fCreditsView->SetFontAndColor(be_plain_font, B_FONT_ALL, &kDarkGrey);
386 	fCreditsView->Insert(
387 		"Axel Dörfler\n"
388 		"Phil Greenway\n"
389 		"Philippe Houdoin\n"
390 		"Marcus Overhagen\n"
391 		"Ingo Weinhold\n"
392 		"Jonathan Yoder\n"
393 		"\n");
394 
395 	fCreditsView->SetFontAndColor(&font, B_FONT_ALL, &kHaikuOrange);
396 	fCreditsView->Insert("Developers:\n");
397 
398 	fCreditsView->SetFontAndColor(be_plain_font, B_FONT_ALL, &kDarkGrey);
399 	fCreditsView->Insert(
400 		"Ithamar R. Adema\n"
401 		"Bruno G. Albuquerque\n"
402 		"Stephan Aßmus\n"
403 		"Andrew Bachmann\n"
404 		"Salvatore Benedetto\n"
405 		"Stefano Ceccherini\n"
406 		"Rudolf Cornelissen\n"
407 		"Alexandre Deckner\n"
408 		"Oliver Ruiz Dorantes\n"
409 		"Jérôme Duval\n"
410 		"Andre Alves Garzia\n"
411 		"René Gollent\n"
412 		"Karsten Heimrich\n"
413 		"Euan Kirkhope\n"
414 		"Waldemar Kornewald\n"
415 		"Ryan Leavengood\n"
416 		"Michael Lotz\n"
417 		"David McPaul\n"
418 		"Michael Pfeiffer\n"
419 		"Niels Sascha Reedijk\n"
420 		"Jonas Sundström\n"
421 		"François Revol\n"
422 		"Hugo Santos\n"
423 		"Gerasim Troeglazov\n"
424 		"Bryan Varner\n"
425 		"Siarzhuk Zharski\n"
426 		"\n");
427 
428 	fCreditsView->SetFontAndColor(&font, B_FONT_ALL, &kHaikuOrange);
429 	fCreditsView->Insert("Contributors:\n");
430 
431 	fCreditsView->SetFontAndColor(be_plain_font, B_FONT_ALL, &kDarkGrey);
432 	fCreditsView->Insert(
433 		"Andrea Anzani\n"
434 		"Andre Braga\n"
435 		"Bruce Cameron\n"
436 		"Greg Crain\n"
437 		"Tyler Dauwalder\n"
438 		"David Dengg\n"
439 		"John Drinkwater\n"
440 		"Cian Duffy\n"
441 		"Christian Fasshauer\n"
442 		"Andreas Färber\n"
443 		"Marc Flerackers\n"
444 		"Daniel Furrer\n"
445 		"Matthijs Hollemans\n"
446 		"Morgan Howe\n"
447 		"Erik Jaesler\n"
448 		"Carwyn Jones\n"
449 		"Maurice Kalinowski\n"
450 		"Vasilis Kaoutsis\n"
451 		"James Kim\n"
452 		"Shintaro Kinugawa\n"
453 		"Jan Klötzke\n"
454 		"Marcin Konicki\n"
455 		"Kurtis Kopf\n"
456 		"Waldemar Kornewald\n"
457 		"Tomáš Kučera\n"
458 		"Luboš Kulič\n"
459 		"Thomas Kurschel\n"
460 		"Elad Lahav\n"
461 		"Anthony Lee\n"
462 		"Santiago Lema\n"
463 		"Raynald Lesieur\n"
464 		"Oscar Lesta\n"
465 		"Jerome Leveque\n"
466 		"Christof Lutteroth\n"
467 		"Graham MacDonald\n"
468 		"Marco Minutoli\n"
469 		"Jan Matějek\n"
470 		"Brian Matzon\n"
471 		"Christopher ML Zumwalt May\n"
472 		"Andrew McCall\n"
473 		"Scott McCreary\n"
474 		"Urias McCullough\n"
475 		"Michele (zuMi)\n"
476 		"Marius Middelthon\n"
477 		"Misza\n"
478 		"Fredrik Modéen\n"
479 		"MrSiggler\n"
480 		"Alan Murta\n"
481 		"Frans Van Nispen\n"
482 		"Adi Oanca\n"
483 		"Pahtz\n"
484 		"Michael Paine\n"
485 		"Michael Phipps\n"
486 		"Jeremy Rand\n"
487 		"Hartmut Reh\n"
488 		"David Reid\n"
489 		"Daniel Reinhold\n"
490 		"Samuel Rodriguez Perez\n"
491 		"Thomas Roell\n"
492 		"Rafael Romo\n"
493 		"Philippe Saint-Pierre\n"
494 		"Reznikov Sergei\n"
495 		"Zousar Shaker\n"
496 		"Alexander G. M. Smith\n"
497 		"Daniel Switkin\n"
498 		"Atsushi Takamatsu\n"
499 		"Oliver Tappe\n"
500 		"James Urquhart\n"
501 		"Jason Vandermark\n"
502 		"Sandor Vroemisse\n"
503 		"Denis Washington\n"
504 		"Nathan Whitehorn\n"
505 		"Michael Wilber\n"
506 		"Ulrich Wimboeck\n"
507 		"James Woodcock\n"
508 		"Artur Wyszynski\n"
509 		"Gabe Yoder\n"
510 		"Gerald Zajac\n"
511 		"Łukasz Zemczak\n"
512 		"JiSheng Zhang\n"
513 		"\n" B_UTF8_ELLIPSIS " and probably some more we forgot to mention "
514 		"(sorry!)"
515 		"\n\n");
516 
517 	fCreditsView->SetFontAndColor(&font, B_FONT_ALL, &kHaikuOrange);
518 	fCreditsView->Insert("Special Thanks To:\n");
519 
520 	fCreditsView->SetFontAndColor(be_plain_font, B_FONT_ALL, &kDarkGrey);
521 	fCreditsView->Insert("Travis Geiselbrecht (and his NewOS kernel)\n");
522 	fCreditsView->Insert("Michael Phipps (project founder)\n\n");
523 
524 	// copyrights for various projects we use
525 
526 	font.SetSize(be_bold_font->Size() + 4);
527 	font.SetFace(B_BOLD_FACE);
528 	fCreditsView->SetFontAndColor(&font, B_FONT_ALL, &kHaikuGreen);
529 	fCreditsView->Insert("\nCopyrights\n\n");
530 
531 	// GNU copyrights
532 	AddCopyrightEntry("The GNU Project",
533 		"Contains software from the GNU Project, "
534 		"released under the GPL and LGPL licences:\n"
535 		"GNU C Library, "
536 		"GNU coretools, diffutils, findutils, "
537 		"sharutils, gawk, bison, m4, make, "
538 		"gdb, wget, ncurses, termcap, "
539 		"Bourne Again Shell.\n"
540 		"Copyright " B_UTF8_COPYRIGHT " The Free Software Foundation.",
541 		Licenses("GNU LGPL v2.1", "GNU GPL v2", "GNU GPL v3", NULL),
542 		"http://www.gnu.org");
543 
544 	// FreeBSD copyrights
545 	AddCopyrightEntry("The FreeBSD Project",
546 		"Contains software from the FreeBSD Project, "
547 		"released under the BSD licence:\n"
548 		"cal, ftpd, ping, telnet, "
549 		"telnetd, traceroute\n"
550 		"Copyright " B_UTF8_COPYRIGHT " 1994-2008 The FreeBSD Project.  "
551 		"All rights reserved.",
552 		"http://www.freebsd.org");
553 
554 	// NetBSD copyrights
555 	AddCopyrightEntry("The NetBSD Project",
556 		"Contains software developed by the NetBSD, "
557 		"Foundation, Inc. and its contributors:\n"
558 		"ftp, tput\n"
559 		"Copyright " B_UTF8_COPYRIGHT " 1996-2008 The NetBSD Foundation, Inc.  "
560 		"All rights reserved.",
561 		"http://www.netbsd.org");
562 
563 	// FFMpeg copyrights
564 	AddCopyrightEntry("FFMpeg libavcodec",
565 		"Copyright " B_UTF8_COPYRIGHT " 2000-2007 Fabrice Bellard, et al.",
566 		"http://www.ffmpeg.org");
567 
568 	// AGG copyrights
569 	AddCopyrightEntry("AntiGrain Geometry",
570 		"Copyright " B_UTF8_COPYRIGHT " 2002-2006 Maxim Shemanarev (McSeem).",
571 		"http://www.antigrain.com");
572 
573 	// PDFLib copyrights
574 	AddCopyrightEntry("PDFLib",
575 		"Copyright " B_UTF8_COPYRIGHT " 1997-2006 PDFlib GmbH and Thomas Merz. "
576 		"All rights reserved.\n"
577 		"PDFlib and PDFlib logo are registered trademarks of PDFlib GmbH.",
578 		"http://www.pdflib.com");
579 
580 	// FreeType copyrights
581 	AddCopyrightEntry("FreeType2",
582 		"Portions of this software are copyright " B_UTF8_COPYRIGHT " 1996-2006 "
583 		"The FreeType Project.  All rights reserved.",
584 		"http://www.freetype.org");
585 
586 	// Mesa3D (http://www.mesa3d.org) copyrights
587 	AddCopyrightEntry("Mesa",
588 		"Copyright " B_UTF8_COPYRIGHT " 1999-2006 Brian Paul. "
589 		"Mesa3D project.  All rights reserved.",
590 		"http://www.mesa3d.org");
591 
592 	// SGI's GLU implementation copyrights
593 	AddCopyrightEntry("GLU",
594 		"Copyright " B_UTF8_COPYRIGHT " 1991-2000 Silicon Graphics, Inc. "
595 		"SGI's Software FreeB license.  All rights reserved.");
596 
597 	// GLUT implementation copyrights
598 	AddCopyrightEntry("GLUT",
599 		"Copyright " B_UTF8_COPYRIGHT " 1994-1997 Mark Kilgard. "
600 		"All rights reserved.\n"
601 		"Copyright " B_UTF8_COPYRIGHT " 1997 Be Inc.\n"
602 		"Copyright " B_UTF8_COPYRIGHT " 1999 Jake Hamby.");
603 
604 	// OpenGroup & DEC (BRegion backend) copyright
605 	AddCopyrightEntry("BRegion backend (XFree86)",
606 		"Copyright " B_UTF8_COPYRIGHT " 1987, 1988, 1998  The Open Group.\n"
607 		"Copyright " B_UTF8_COPYRIGHT " 1987, 1988 Digital Equipment "
608 		"Corporation, Maynard, Massachusetts.\n"
609 		"All rights reserved.");
610 
611 	// Konatu font
612 	AddCopyrightEntry("Konatu font",
613 		"Copyright " B_UTF8_COPYRIGHT " 2002- MASUDA mitiya.\n"
614 		"MIT license. All rights reserved.");
615 
616 	// expat copyrights
617 	AddCopyrightEntry("expat",
618 		"Copyright " B_UTF8_COPYRIGHT " 1998, 1999, 2000 Thai Open Source "
619 		"Software Center Ltd and Clark Cooper.\n"
620 		"Copyright " B_UTF8_COPYRIGHT " 2001, 2002, 2003 Expat maintainers.");
621 
622 	// zlib copyrights
623 	AddCopyrightEntry("zlib",
624 		"Copyright " B_UTF8_COPYRIGHT " 1995-2004 Jean-loup Gailly and Mark "
625 		"Adler.");
626 
627 	// zip copyrights
628 	AddCopyrightEntry("Info-ZIP",
629 		"Copyright " B_UTF8_COPYRIGHT " 1990-2002 Info-ZIP. All rights reserved.");
630 
631 	// bzip2 copyrights
632 	AddCopyrightEntry("bzip2",
633 		"Copyright " B_UTF8_COPYRIGHT " 1996-2005 Julian R Seward. All rights "
634 		"reserved.");
635 
636 	// VIM copyrights
637 	AddCopyrightEntry("Vi IMproved",
638 		"Copyright " B_UTF8_COPYRIGHT " Bram Moolenaar et al.");
639 
640 	// lp_solve copyrights
641 	AddCopyrightEntry("lp_solve",
642 		"Copyright " B_UTF8_COPYRIGHT
643 			" Michel Berkelaar, Kjell Eikland, Peter Notebaert",
644 		"http://lpsolve.sourceforge.net/");
645 			// license: LGPL
646 
647 	// OpenEXR copyrights
648 	AddCopyrightEntry("OpenEXR",
649 		"Copyright " B_UTF8_COPYRIGHT " 2002-2005 Industrial Light & Magic, "
650 		"a division of Lucas Digital Ltd. LLC.");
651 
652 	// Bullet copyrights
653 	AddCopyrightEntry("Bullet",
654 		"Copyright " B_UTF8_COPYRIGHT " 2003-2008 Erwin Coumans",
655 		"http://www.bulletphysics.com");
656 
657 	// atftp copyrights
658 	AddCopyrightEntry("atftp",
659 		"Copyright " B_UTF8_COPYRIGHT " 2000 Jean-Pierre Lefebvre and Remi "
660 		"Lefebvre");
661 
662 	// Netcat copyrights
663 	AddCopyrightEntry("Netcat",
664 		"Copyright " B_UTF8_COPYRIGHT " 1996 Hobbit");
665 
666 	// acpica copyrights
667 	AddCopyrightEntry("acpica",
668 		"Copyright " B_UTF8_COPYRIGHT " 1999-2006 Intel Corp.");
669 
670 	// unrar copyrights
671 	AddCopyrightEntry("unrar",
672 		"Copyright " B_UTF8_COPYRIGHT " 2002-2008 Alexander L. Roshal. "
673 		"All rights reserved.",
674 		"http://www.rarlab.com");
675 
676 // p7zip copyrights
677 //	AddCopyrightEntry("p7zip",
678 //		"Copyright " B_UTF8_COPYRIGHT " 2008 Igor Pavlov. "
679 //		"All rights reserved.");
680 
681 	// libpng copyrights
682 	AddCopyrightEntry("libpng",
683 		"Copyright " B_UTF8_COPYRIGHT " 2004, 2006-2008 Glenn "
684 		"Randers-Pehrson.");
685 
686 	// libprint copyrights
687 	AddCopyrightEntry("libprint",
688 		"Copyright " B_UTF8_COPYRIGHT " 1999-2000 Y.Takagi. All rights "
689 		"reserved.");
690 
691 	// cortex copyrights
692 	AddCopyrightEntry("Cortex",
693 		"Copyright " B_UTF8_COPYRIGHT " 1999-2000 Eric Moon.");
694 
695 	// FluidSynth copyrights
696 	AddCopyrightEntry("FluidSynth",
697 		"Copyright " B_UTF8_COPYRIGHT " 2003 Peter Hanappe and others.");
698 
699 	// CannaIM copyrights
700 	AddCopyrightEntry("CannaIM",
701 		"Copyright " B_UTF8_COPYRIGHT " 1999 Masao Kawamura.");
702 
703 	// libxml2, libxslt, libexslt copyrights
704 	AddCopyrightEntry("libxml2, libxslt",
705 		"Copyright " B_UTF8_COPYRIGHT " 1998-2003 Daniel Veillard. "
706 		"All rights reserved.");
707 
708 	AddCopyrightEntry("libexslt",
709 		"Copyright " B_UTF8_COPYRIGHT " 2001-2002 Thomas Broyer, Charlie "
710 		"Bozeman and Daniel Veillard.  All rights reserved.");
711 
712 	// Xiph.org Foundation copyrights
713 	AddCopyrightEntry("Xiph.org Foundation",
714 		"libvorbis, libogg, libtheora, libspeex"
715 		"Copyright " B_UTF8_COPYRIGHT " 1994-2008 Xiph.Org. "
716 		"All rights reserved.",
717 		"http://www.xiph.org");
718 
719 	// The Tcpdump Group
720 	AddCopyrightEntry("The Tcpdump Group",
721 		"tcpdump, libpcap",
722 		"http://www.tcpdump.org");
723 
724 	// Matroska
725 	AddCopyrightEntry("libmatroska",
726 		"Copyright " B_UTF8_COPYRIGHT " 2002-2003 Steve Lhomme. "
727 		"All rights reserved.",
728 		"http://www.matroska.org");
729 
730 // OpenSound
731 //	AddCopyrightEntry("OpenSound",
732 //		"Copyright " B_UTF8_COPYRIGHT " 1996-2008 4Front Technologies ",
733 //		"http://www.opensound.com");
734 // BSD license
735 
736 	_AddCopyrightsFromAttribute();
737 
738 	// Build a list of installed applications and show their
739 	// long version info. Well-behaved apps usually give
740 	// copyright info there.
741 
742 	font.SetSize(be_bold_font->Size() + 4);
743 	font.SetFace(B_BOLD_FACE);
744 	fCreditsView->SetFontAndColor(&font, B_FONT_ALL, &kHaikuGreen);
745 	fCreditsView->Insert("\nInstalled applications\n\n");
746 
747 	BVolume bootVolume;
748 	BVolumeRoster().GetBootVolume(&bootVolume);
749 	fAppsQuery.SetVolume(&bootVolume);
750 	if (fAppsQuery.SetPredicate(
751 			"((BEOS:APP_SIG==\"**\")&&(name!=\"*.so\")&&(name!=\"*.rsrc\")&&"
752 			"(BEOS:TYPE==\"application/x-vnd.Be-elfexecutable\"))") >= B_OK) {
753 		fAppsQuery.Fetch();
754 	}
755 }
756 
757 
758 AboutView::~AboutView(void)
759 {
760 	delete fScrollRunner;
761 }
762 
763 
764 void
765 AboutView::AttachedToWindow(void)
766 {
767 	BView::AttachedToWindow();
768 	Window()->SetPulseRate(500000);
769 	SetEventMask(B_POINTER_EVENTS);
770 }
771 
772 
773 void
774 AboutView::MouseDown(BPoint pt)
775 {
776 	BRect r(92, 26, 105, 31);
777 	if (r.Contains(pt)) {
778 		printf("Easter Egg\n");
779 		PickRandomHaiku();
780 	}
781 
782 	if (Bounds().Contains(pt)) {
783 		fLastActionTime = system_time();
784 		delete fScrollRunner;
785 		fScrollRunner = NULL;
786 	}
787 }
788 
789 
790 void
791 AboutView::FrameResized(float width, float height)
792 {
793 	BRect r = fCreditsView->Bounds();
794 	r.OffsetTo(B_ORIGIN);
795 	r.InsetBy(3, 3);
796 	fCreditsView->SetTextRect(r);
797 }
798 
799 
800 void
801 AboutView::Draw(BRect update)
802 {
803 	if (fLogo)
804 		DrawBitmap(fLogo, fDrawPoint);
805 }
806 
807 
808 void
809 AboutView::Pulse(void)
810 {
811 	char string[255];
812 	system_info info;
813 	get_system_info(&info);
814 	fUptimeView->SetText(UptimeToString(string, sizeof(string)));
815 	fMemView->SetText(MemUsageToString(string, sizeof(string), &info));
816 
817 	if (fScrollRunner == NULL && (system_time() > fLastActionTime + 10000000)) {
818 		BMessage message(SCROLL_CREDITS_VIEW);
819 		//fScrollRunner = new BMessageRunner(this, &message, 300000, -1);
820 	}
821 }
822 
823 
824 void
825 AboutView::MessageReceived(BMessage *msg)
826 {
827 	switch (msg->what) {
828 		case SCROLL_CREDITS_VIEW:
829 		{
830 			BScrollBar *scrollBar = fCreditsView->ScrollBar(B_VERTICAL);
831 			if (scrollBar == NULL)
832 				break;
833 			float max, min;
834 			scrollBar->GetRange(&min, &max);
835 			if (scrollBar->Value() < max)
836 				fCreditsView->ScrollBy(0, 5);
837 
838 			break;
839 		}
840 
841 		case READ_APP_QUERY_ENT:
842 		{
843 			BEntry ent;
844 			if (fAppsQuery.GetNextEntry(&ent) < B_OK) {
845 				fAppsQuery.Clear();
846 				fCreditsView->MakeSelectable(true);
847 				break;
848 			}
849 			BFile file;
850 			BPath path;
851 			if (ent.Exists() &&
852 				ent.GetPath(&path) >= B_OK &&
853 				file.SetTo(&ent, B_READ_ONLY) >= B_OK) {
854 				/* filter only apps */
855 				if (strncmp(path.Path(), "/boot/apps", 10) == 0) {
856 					BAppFileInfo appFileInfo(&file);
857 					uint32 flags;
858 					version_info version;
859 					if (appFileInfo.InitCheck() >= B_OK &&
860 						appFileInfo.GetAppFlags(&flags) >= B_OK &&
861 						appFileInfo.GetVersionInfo(&version,
862 							B_APP_VERSION_KIND) >= B_OK) {
863 						//printf("AppFileInfo for %s :\n", path.Path());
864 						//printf("flags: %08x\n", flags);
865 						BString name;
866 						BString info;
867 						name << path.Leaf();
868 						if (strlen(version.short_info) &&
869 							strcmp(version.short_info, path.Leaf()))
870 							name << " (" << version.short_info << ")";
871 						/*
872 						info << "\tVersion: ";
873 						info << version.major << ".";
874 						info << version.middle << ".";
875 						info << version.minor;
876 						char varieties[] = "dabgmf";
877 						if (version.variety > B_FINAL_VERSION)
878 							info << "?";
879 						else
880 							info << varieties[version.variety];
881 						info << version.internal;
882 						info << "\n";
883 						*/
884 						info << version.long_info;
885 						AddCopyrightEntry(name.String(), info.String());
886 
887 					}
888 				}
889 			}
890 			// note for self: read next entry :)
891 			BMessage m(READ_APP_QUERY_ENT);
892 			BMessenger(this).SendMessage(&m);
893 			break;
894 		}
895 
896 		default:
897 			BView::MessageReceived(msg);
898 			break;
899 	}
900 }
901 
902 
903 void
904 AboutView::AddCopyrightEntry(const char *name, const char *text,
905 	const char *url)
906 {
907 	AddCopyrightEntry(name, text, NULL, url);
908 }
909 
910 
911 void
912 AboutView::AddCopyrightEntry(const char *name, const char *text,
913 	const Licenses& licenses, const char *url)
914 {
915 	BFont font(be_bold_font);
916 	//font.SetSize(be_bold_font->Size());
917 	font.SetFace(B_BOLD_FACE | B_ITALIC_FACE);
918 
919 	fCreditsView->SetFontAndColor(&font, B_FONT_ALL, &kHaikuYellow);
920 	fCreditsView->Insert(name);
921 	fCreditsView->Insert("\n");
922 	fCreditsView->SetFontAndColor(be_plain_font, B_FONT_ALL, &kDarkGrey);
923 	fCreditsView->Insert(text);
924 	fCreditsView->Insert("\n");
925 
926 	if (licenses.CountLicenses() > 0) {
927 		if (licenses.CountLicenses() > 1)
928 			fCreditsView->Insert("Licenses: ");
929 		else
930 			fCreditsView->Insert("License: ");
931 
932 		for (int32 i = 0; i < licenses.CountLicenses(); i++) {
933 			const char* license = licenses.LicenseAt(i);
934 			BString licensePath("/etc/licenses/");
935 			licensePath += license;
936 
937 			if (i > 0)
938 				fCreditsView->Insert(", ");
939 
940 			fCreditsView->InsertHyperText(license,
941 				new OpenFileAction(licensePath));
942 		}
943 
944 		fCreditsView->Insert("\n");
945 	}
946 
947 	if (url) {
948 		fCreditsView->SetFontAndColor(be_plain_font, B_FONT_ALL, &kLinkBlue);
949 		fCreditsView->InsertHyperText(url, new URLAction(url));
950 		fCreditsView->Insert("\n");
951 	}
952 	fCreditsView->Insert("\n");
953 }
954 
955 
956 void
957 AboutView::AddCopyrightEntry(const BMessage& packageDescription)
958 {
959 	const char* package;
960 	const char* copyright;
961 	const char* url;
962 
963 	// package and copyright are mandatory
964 	if (packageDescription.FindString("Package", &package) != B_OK
965 		|| packageDescription.FindString("Copyright", &copyright) != B_OK) {
966 		return;
967 	}
968 
969 	// URL is optional
970 	if (packageDescription.FindString("URL", &url) != B_OK)
971 		url = NULL;
972 
973 	BString copyrightLine("Copyright " B_UTF8_COPYRIGHT " ");
974 	copyrightLine += copyright;
975 
976 	AddCopyrightEntry(package, copyrightLine.String(), packageDescription, url);
977 }
978 
979 
980 void
981 AboutView::PickRandomHaiku()
982 {
983 	BFile fortunes(
984 #ifdef __HAIKU__
985 		"/etc/fortunes/Haiku",
986 #else
987 		"data/etc/fortunes/Haiku",
988 #endif
989 		B_READ_ONLY);
990 	struct stat st;
991 	if (fortunes.InitCheck() < B_OK)
992 		return;
993 	if (fortunes.GetStat(&st) < B_OK)
994 		return;
995 	char *buff = (char *)malloc((size_t)st.st_size + 1);
996 	if (!buff)
997 		return;
998 	buff[(size_t)st.st_size] = '\0';
999 	BList haikuList;
1000 	if (fortunes.Read(buff, (size_t)st.st_size) == (ssize_t)st.st_size) {
1001 		char *p = buff;
1002 		while (p && *p) {
1003 			char *e = strchr(p, '%');
1004 			BString *s = new BString(p, e ? (e - p) : -1);
1005 			haikuList.AddItem(s);
1006 			p = e;
1007 			if (p && (*p == '%'))
1008 				p++;
1009 			if (p && (*p == '\n'))
1010 				p++;
1011 		}
1012 	}
1013 	free(buff);
1014 	if (haikuList.CountItems() < 1)
1015 		return;
1016 	BString *s = (BString *)haikuList.ItemAt(rand() % haikuList.CountItems());
1017 	BFont font(be_bold_font);
1018 	font.SetSize(be_bold_font->Size());
1019 	font.SetFace(B_BOLD_FACE | B_ITALIC_FACE);
1020 	fCreditsView->SelectAll();
1021 	fCreditsView->Delete();
1022 	fCreditsView->SetFontAndColor(&font, B_FONT_ALL, &kDarkGrey);
1023 	fCreditsView->Insert(s->String());
1024 	fCreditsView->Insert("\n");
1025 	while ((s = (BString *)haikuList.RemoveItem((int32)0))) {
1026 		delete s;
1027 	}
1028 }
1029 
1030 
1031 void
1032 AboutView::_AddCopyrightsFromAttribute()
1033 {
1034 	// open the app executable file
1035 	char appPath[B_PATH_NAME_LENGTH];
1036 	int appFD;
1037 	if (BPrivate::get_app_path(appPath) != B_OK
1038 		|| (appFD = open(appPath, O_RDONLY)) < 0) {
1039 		return;
1040 	}
1041 
1042 	// open the attribute
1043 	int attrFD = fs_open_attr(appFD, "COPYRIGHTS", B_STRING_TYPE, O_RDONLY);
1044 	close(appFD);
1045 	if (attrFD < 0)
1046 		return;
1047 
1048 	// attach it to a FILE
1049 	FILE* attrFile = fdopen(attrFD, "r");
1050 	if (attrFile == NULL) {
1051 		close(attrFD);
1052 		return;
1053 	}
1054 	CObjectDeleter<FILE, int> _(attrFile, fclose);
1055 
1056 	// read and parse the copyrights
1057 	BMessage package;
1058 	BString fieldName;
1059 	BString fieldValue;
1060 	char lineBuffer[LINE_MAX];
1061 	while (char* line = fgets(lineBuffer, sizeof(lineBuffer), attrFile)) {
1062 		// chop off line break
1063 		size_t lineLen = strlen(line);
1064 		if (lineLen > 0 && line[lineLen - 1] == '\n')
1065 			line[--lineLen] = '\0';
1066 
1067 		// flush previous field, if a new field begins, otherwise append
1068 		if (lineLen == 0 || !isspace(line[0])) {
1069 			// new field -- flush the previous one
1070 			if (fieldName.Length() > 0) {
1071 				fieldValue = trim_string(fieldValue.String(),
1072 					fieldValue.Length());
1073 				package.AddString(fieldName.String(), fieldValue);
1074 				fieldName = "";
1075 			}
1076 		} else if (fieldName.Length() > 0) {
1077 			// append to current field
1078 			fieldValue += line;
1079 			continue;
1080 		} else {
1081 			// bogus line -- ignore
1082 			continue;
1083 		}
1084 
1085 		if (lineLen == 0)
1086 			continue;
1087 
1088 		// parse new field
1089 		char* colon = strchr(line, ':');
1090 		if (colon == NULL) {
1091 			// bogus line -- ignore
1092 			continue;
1093 		}
1094 
1095 		fieldName.SetTo(line, colon - line);
1096 		fieldName = trim_string(line, colon - line);
1097 		if (fieldName.Length() == 0) {
1098 			// invalid field name
1099 			continue;
1100 		}
1101 
1102 		fieldValue = colon + 1;
1103 
1104 		if (fieldName == "Package") {
1105 			// flush the current package
1106 			AddCopyrightEntry(package);
1107 			package.MakeEmpty();
1108 		}
1109 	}
1110 
1111 	// flush current package
1112 	AddCopyrightEntry(package);
1113 }
1114 
1115 
1116 //	#pragma mark -
1117 
1118 
1119 static const char *
1120 MemUsageToString(char string[], size_t size, system_info *info)
1121 {
1122 	snprintf(string, size, "%d MB total, %d MB used (%d%%)",
1123 			int(info->max_pages / 256.0f + 0.5f),
1124 			int(info->used_pages / 256.0f + 0.5f),
1125 			int(100 * info->used_pages / info->max_pages));
1126 
1127 	return string;
1128 }
1129 
1130 
1131 static const char *
1132 UptimeToString(char string[], size_t size)
1133 {
1134 	int64 days, hours, minutes, seconds, remainder;
1135 	int64 systime = system_time();
1136 
1137 	days = systime / 86400000000LL;
1138 	remainder = systime % 86400000000LL;
1139 
1140 	hours = remainder / 3600000000LL;
1141 	remainder = remainder % 3600000000LL;
1142 
1143 	minutes = remainder / 60000000;
1144 	remainder = remainder % 60000000;
1145 
1146 	seconds = remainder / 1000000;
1147 
1148 	char *str = string;
1149 	if (days) {
1150 		str += snprintf(str, size, "%lld day%s",days, days > 1 ? "s" : "");
1151 	}
1152 	if (hours) {
1153 		str += snprintf(str, size - strlen(string), "%s%lld hour%s",
1154 				str != string ? ", " : "",
1155 				hours, hours > 1 ? "s" : "");
1156 	}
1157 	if (minutes) {
1158 		str += snprintf(str, size - strlen(string), "%s%lld minute%s",
1159 				str != string ? ", " : "",
1160 				minutes, minutes > 1 ? "s" : "");
1161 	}
1162 
1163 	if (seconds || str == string) {
1164 		// Haiku would be well-known to boot very fast.
1165 		// Let's be ready to handle below minute uptime, zero second included ;-)
1166 		str += snprintf(str, size - strlen(string), "%s%lld second%s",
1167 				str != string ? ", " : "",
1168 				seconds, seconds > 1 ? "s" : "");
1169 	}
1170 
1171 	return string;
1172 }
1173 
1174 
1175 int
1176 main()
1177 {
1178 	AboutApp app;
1179 	app.Run();
1180 	return 0;
1181 }
1182 
1183