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