1/*
2 * Copyright 2007-2015 Haiku, Inc.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 *		Ryan Leavengood <leavengood@gmail.com>
7 *		John Scipione <jscipione@gmail.com>
8 *		Joseph Groover <looncraz@looncraz.net>
9 */
10
11
12#include <AboutWindow.h>
13
14#include <stdarg.h>
15#include <time.h>
16
17#include <Alert.h>
18#include <AppFileInfo.h>
19#include <Bitmap.h>
20#include <Button.h>
21#include <File.h>
22#include <Font.h>
23#include <GroupLayoutBuilder.h>
24#include <GroupView.h>
25#include <InterfaceDefs.h>
26#include <LayoutBuilder.h>
27#include <Message.h>
28#include <MessageFilter.h>
29#include <Point.h>
30#include <Roster.h>
31#include <Screen.h>
32#include <ScrollView.h>
33#include <Size.h>
34#include <String.h>
35#include <StringView.h>
36#include <SystemCatalog.h>
37#include <TextView.h>
38#include <View.h>
39#include <Window.h>
40
41
42static const float kStripeWidth = 30.0;
43
44using BPrivate::gSystemCatalog;
45
46
47#undef B_TRANSLATION_CONTEXT
48#define B_TRANSLATION_CONTEXT "AboutWindow"
49
50
51namespace BPrivate {
52
53class StripeView : public BView {
54public:
55							StripeView(BBitmap* icon);
56	virtual					~StripeView();
57
58	virtual	void			Draw(BRect updateRect);
59
60			BBitmap*		Icon() const { return fIcon; };
61			void			SetIcon(BBitmap* icon);
62
63private:
64			BBitmap*		fIcon;
65};
66
67
68class AboutView : public BGroupView {
69public:
70							AboutView(const char* name,
71								const char* signature);
72	virtual					~AboutView();
73
74	virtual	void			AllAttached();
75
76			BTextView*		InfoView() const { return fInfoView; };
77
78			BBitmap*		Icon();
79			status_t		SetIcon(BBitmap* icon);
80
81			const char*		Name();
82			status_t		SetName(const char* name);
83
84			const char*		Version();
85			status_t		SetVersion(const char* version);
86
87private:
88			BString			_GetVersionFromSignature(const char* signature);
89			BBitmap*		_GetIconFromSignature(const char* signature);
90
91private:
92			BStringView*	fNameView;
93			BStringView*	fVersionView;
94			BTextView*		fInfoView;
95			StripeView*		fStripeView;
96};
97
98
99//	#pragma mark - StripeView
100
101
102StripeView::StripeView(BBitmap* icon)
103	:
104	BView("StripeView", B_WILL_DRAW),
105	fIcon(icon)
106{
107	SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
108
109	float width = 0.0f;
110	if (icon != NULL)
111		width += icon->Bounds().Width() + 24.0f;
112
113	SetExplicitSize(BSize(width, B_SIZE_UNSET));
114	SetExplicitPreferredSize(BSize(width, B_SIZE_UNLIMITED));
115}
116
117
118StripeView::~StripeView()
119{
120}
121
122
123void
124StripeView::Draw(BRect updateRect)
125{
126	if (fIcon == NULL)
127		return;
128
129	SetHighColor(ViewColor());
130	FillRect(updateRect);
131
132	BRect stripeRect = Bounds();
133	stripeRect.right = kStripeWidth;
134	SetHighColor(tint_color(ViewColor(), B_DARKEN_1_TINT));
135	FillRect(stripeRect);
136
137	SetDrawingMode(B_OP_ALPHA);
138	SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_OVERLAY);
139	DrawBitmapAsync(fIcon, BPoint(15.0f, 10.0f));
140}
141
142
143void
144StripeView::SetIcon(BBitmap* icon)
145{
146	if (fIcon != NULL)
147		delete fIcon;
148
149	fIcon = icon;
150
151	float width = 0.0f;
152	if (icon != NULL)
153		width += icon->Bounds().Width() + 24.0f;
154
155	SetExplicitSize(BSize(width, B_SIZE_UNSET));
156	SetExplicitPreferredSize(BSize(width, B_SIZE_UNLIMITED));
157};
158
159
160//	#pragma mark - AboutView
161
162
163AboutView::AboutView(const char* appName, const char* signature)
164	:
165	BGroupView("AboutView", B_VERTICAL)
166{
167	SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
168	fNameView = new BStringView("name", appName);
169	BFont font;
170	fNameView->GetFont(&font);
171	font.SetFace(B_BOLD_FACE);
172	font.SetSize(font.Size() * 2.0);
173	fNameView->SetFont(&font, B_FONT_FAMILY_AND_STYLE | B_FONT_SIZE
174		| B_FONT_FLAGS);
175	fNameView->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET));
176
177	fVersionView = new BStringView("version",
178		_GetVersionFromSignature(signature).String());
179	fVersionView->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET));
180
181	rgb_color documentColor = ui_color(B_DOCUMENT_TEXT_COLOR);
182	fInfoView = new BTextView("info", NULL, &documentColor, B_WILL_DRAW);
183	fInfoView->SetExplicitMinSize(BSize(210.0, 160.0));
184	fInfoView->MakeEditable(false);
185	fInfoView->SetWordWrap(true);
186	fInfoView->SetInsets(5.0, 5.0, 5.0, 5.0);
187	fInfoView->SetStylable(true);
188
189	BScrollView* infoViewScroller = new BScrollView(
190		"infoViewScroller", fInfoView, B_WILL_DRAW | B_FRAME_EVENTS,
191		false, true, B_PLAIN_BORDER);
192
193	fStripeView = new StripeView(_GetIconFromSignature(signature));
194
195	const char* ok = B_TRANSLATE_MARK("OK");
196	BButton* closeButton = new BButton("ok",
197		gSystemCatalog.GetString(ok, "AboutWindow"),
198		new BMessage(B_QUIT_REQUESTED));
199
200	GroupLayout()->SetSpacing(0);
201	BLayoutBuilder::Group<>(this, B_HORIZONTAL, 0)
202		.Add(fStripeView)
203		.AddGroup(B_VERTICAL)
204			.SetInsets(0, B_USE_DEFAULT_SPACING,
205				B_USE_DEFAULT_SPACING, B_USE_DEFAULT_SPACING)
206			.AddGroup(B_VERTICAL, 0)
207				.Add(fNameView)
208				.Add(fVersionView)
209				.AddStrut(B_USE_SMALL_SPACING)
210				.Add(infoViewScroller)
211				.End()
212			.AddGroup(B_HORIZONTAL, 0)
213				.AddGlue()
214				.Add(closeButton)
215				.End()
216			.End()
217		.View()->SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
218}
219
220
221AboutView::~AboutView()
222{
223}
224
225
226void
227AboutView::AllAttached()
228{
229	fNameView->SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
230	fInfoView->SetViewUIColor(B_DOCUMENT_BACKGROUND_COLOR);
231	fVersionView->SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
232}
233
234
235//	#pragma mark - AboutView private methods
236
237
238BString
239AboutView::_GetVersionFromSignature(const char* signature)
240{
241	if (signature == NULL)
242		return NULL;
243
244	entry_ref ref;
245	if (be_roster->FindApp(signature, &ref) != B_OK)
246		return NULL;
247
248	BFile file(&ref, B_READ_ONLY);
249	BAppFileInfo appMime(&file);
250	if (appMime.InitCheck() != B_OK)
251		return NULL;
252
253	version_info versionInfo;
254	if (appMime.GetVersionInfo(&versionInfo, B_APP_VERSION_KIND) == B_OK) {
255		if (versionInfo.major == 0 && versionInfo.middle == 0
256			&& versionInfo.minor == 0) {
257			return NULL;
258		}
259
260		const char* version = B_TRANSLATE_MARK("Version");
261		version = gSystemCatalog.GetString(version, "AboutWindow");
262		BString appVersion(version);
263		appVersion << " " << versionInfo.major << "." << versionInfo.middle;
264		if (versionInfo.minor > 0)
265			appVersion << "." << versionInfo.minor;
266
267		// Add the version variety
268		const char* variety = NULL;
269		switch (versionInfo.variety) {
270			case B_DEVELOPMENT_VERSION:
271				variety = B_TRANSLATE_MARK("development");
272				break;
273			case B_ALPHA_VERSION:
274				variety = B_TRANSLATE_MARK("alpha");
275				break;
276			case B_BETA_VERSION:
277				variety = B_TRANSLATE_MARK("beta");
278				break;
279			case B_GAMMA_VERSION:
280				variety = B_TRANSLATE_MARK("gamma");
281				break;
282			case B_GOLDEN_MASTER_VERSION:
283				variety = B_TRANSLATE_MARK("gold master");
284				break;
285		}
286
287		if (variety != NULL) {
288			variety = gSystemCatalog.GetString(variety, "AboutWindow");
289			appVersion << "-" << variety;
290		}
291
292		return appVersion;
293	}
294
295	return NULL;
296}
297
298
299BBitmap*
300AboutView::_GetIconFromSignature(const char* signature)
301{
302	if (signature == NULL)
303		return NULL;
304
305	entry_ref ref;
306	if (be_roster->FindApp(signature, &ref) != B_OK)
307		return NULL;
308
309	BFile file(&ref, B_READ_ONLY);
310	BAppFileInfo appMime(&file);
311	if (appMime.InitCheck() != B_OK)
312		return NULL;
313
314	BBitmap* icon = new BBitmap(BRect(0.0, 0.0, 63.0, 63.0), B_RGBA32);
315	if (appMime.GetIcon(icon, (icon_size)64) == B_OK)
316		return icon;
317
318	delete icon;
319	return NULL;
320}
321
322
323//	#pragma mark - AboutView public methods
324
325
326BBitmap*
327AboutView::Icon()
328{
329	if (fStripeView == NULL)
330		return NULL;
331
332	return fStripeView->Icon();
333}
334
335
336status_t
337AboutView::SetIcon(BBitmap* icon)
338{
339	if (fStripeView == NULL)
340		return B_NO_INIT;
341
342	fStripeView->SetIcon(icon);
343
344	return B_OK;
345}
346
347
348const char*
349AboutView::Name()
350{
351	return fNameView->Text();
352}
353
354
355status_t
356AboutView::SetName(const char* name)
357{
358	fNameView->SetText(name);
359
360	return B_OK;
361}
362
363
364const char*
365AboutView::Version()
366{
367	return fVersionView->Text();
368}
369
370
371status_t
372AboutView::SetVersion(const char* version)
373{
374	fVersionView->SetText(version);
375
376	return B_OK;
377}
378
379} // namespace BPrivate
380
381
382//	#pragma mark - BAboutWindow
383
384
385BAboutWindow::BAboutWindow(const char* appName, const char* signature)
386	:
387	BWindow(BRect(0.0, 0.0, 400.0, 200.0), appName, B_MODAL_WINDOW,
388		B_ASYNCHRONOUS_CONTROLS | B_NOT_ZOOMABLE
389			| B_AUTO_UPDATE_SIZE_LIMITS | B_CLOSE_ON_ESCAPE)
390{
391	SetLayout(new BGroupLayout(B_VERTICAL));
392
393	const char* about = B_TRANSLATE_MARK("About %app%");
394	about = gSystemCatalog.GetString(about, "AboutWindow");
395
396	BString title(about);
397	title.ReplaceFirst("%app%", appName);
398	SetTitle(title.String());
399
400	fAboutView = new BPrivate::AboutView(appName, signature);
401	AddChild(fAboutView);
402
403	MoveTo(AboutPosition(Frame().Width(), Frame().Height()));
404}
405
406
407BAboutWindow::~BAboutWindow()
408{
409	fAboutView->RemoveSelf();
410	delete fAboutView;
411	fAboutView = NULL;
412}
413
414
415//	#pragma mark - BAboutWindow virtual methods
416
417
418void
419BAboutWindow::Show()
420{
421	if (IsHidden()) {
422		// move to current workspace
423		SetWorkspaces(B_CURRENT_WORKSPACE);
424	}
425
426	BWindow::Show();
427}
428
429
430//	#pragma mark - BAboutWindow public methods
431
432
433BPoint
434BAboutWindow::AboutPosition(float width, float height)
435{
436	BPoint result(100, 100);
437
438	BWindow* window =
439		dynamic_cast<BWindow*>(BLooper::LooperForThread(find_thread(NULL)));
440
441	BScreen screen(window);
442	BRect screenFrame(0, 0, 640, 480);
443	if (screen.IsValid())
444		screenFrame = screen.Frame();
445
446	// Horizontally, we're smack in the middle
447	result.x = screenFrame.left + (screenFrame.Width() / 2.0) - (width / 2.0);
448
449	// This is probably sooo wrong, but it looks right on 1024 x 768
450	result.y = screenFrame.top + (screenFrame.Height() / 4.0)
451		- ceil(height / 3.0);
452
453	return result;
454}
455
456
457void
458BAboutWindow::AddDescription(const char* description)
459{
460	if (description == NULL)
461		return;
462
463	AddText(description);
464}
465
466
467void
468BAboutWindow::AddCopyright(int32 firstCopyrightYear,
469	const char* copyrightHolder, const char** extraCopyrights)
470{
471	BString copyright(B_UTF8_COPYRIGHT " %years% %holder%");
472
473	// Get current year
474	time_t tp;
475	time(&tp);
476	char currentYear[5];
477	strftime(currentYear, 5, "%Y", localtime(&tp));
478	BString copyrightYears;
479	copyrightYears << firstCopyrightYear;
480	if (copyrightYears != currentYear)
481		copyrightYears << "-" << currentYear;
482
483	BString text("");
484	if (fAboutView->InfoView()->TextLength() > 0)
485		text << "\n\n";
486
487	text << copyright;
488
489	// Fill out the copyright year placeholder
490	text.ReplaceAll("%years%", copyrightYears.String());
491
492	// Fill in the copyright holder placeholder
493	text.ReplaceAll("%holder%", copyrightHolder);
494
495	// Add extra copyright strings
496	if (extraCopyrights != NULL) {
497		// Add optional extra copyright information
498		for (int32 i = 0; extraCopyrights[i]; i++)
499			text << "\n" << B_UTF8_COPYRIGHT << " " << extraCopyrights[i];
500	}
501
502	const char* allRightsReserved = B_TRANSLATE_MARK("All Rights Reserved.");
503	allRightsReserved = gSystemCatalog.GetString(allRightsReserved,
504		"AboutWindow");
505	text << "\n    " << allRightsReserved;
506
507	fAboutView->InfoView()->Insert(text.String());
508}
509
510
511void
512BAboutWindow::AddAuthors(const char** authors)
513{
514	if (authors == NULL)
515		return;
516
517	const char* writtenBy = B_TRANSLATE_MARK("Written by:");
518	writtenBy = gSystemCatalog.GetString(writtenBy, "AboutWindow");
519
520	AddText(writtenBy, authors);
521}
522
523
524void
525BAboutWindow::AddSpecialThanks(const char** thanks)
526{
527	if (thanks == NULL)
528		return;
529
530	const char* specialThanks = B_TRANSLATE_MARK("Special Thanks:");
531	specialThanks = gSystemCatalog.GetString(specialThanks, "AboutWindow");
532
533	AddText(specialThanks, thanks);
534}
535
536
537void
538BAboutWindow::AddVersionHistory(const char** history)
539{
540	if (history == NULL)
541		return;
542
543	const char* versionHistory = B_TRANSLATE_MARK("Version history:");
544	versionHistory = gSystemCatalog.GetString(versionHistory, "AboutWindow");
545
546	AddText(versionHistory, history);
547}
548
549
550void
551BAboutWindow::AddExtraInfo(const char* extraInfo)
552{
553	if (extraInfo == NULL)
554		return;
555
556	const char* appExtraInfo = B_TRANSLATE_MARK(extraInfo);
557	appExtraInfo = gSystemCatalog.GetString(extraInfo, "AboutWindow");
558
559	BString extra("");
560	if (fAboutView->InfoView()->TextLength() > 0)
561		extra << "\n\n";
562
563	extra << appExtraInfo;
564
565	fAboutView->InfoView()->Insert(extra.String());
566}
567
568
569void
570BAboutWindow::AddText(const char* header, const char** contents)
571{
572	BTextView* infoView = fAboutView->InfoView();
573	int32 textLength = infoView->TextLength();
574	BString text("");
575
576	if (textLength > 0) {
577		text << "\n\n";
578		textLength += 2;
579	}
580
581	const char* indent = "";
582	if (header != NULL) {
583		indent = "    ";
584		text << header;
585	}
586
587	if (contents != NULL) {
588		text << "\n";
589		for (int32 i = 0; contents[i]; i++)
590			text << indent << contents[i] << "\n";
591	}
592
593	infoView->Insert(text.String());
594
595	if (contents != NULL && header != NULL) {
596		infoView->SetFontAndColor(textLength, textLength + strlen(header),
597			be_bold_font);
598	}
599}
600
601
602BBitmap*
603BAboutWindow::Icon()
604{
605	return fAboutView->Icon();
606}
607
608
609void
610BAboutWindow::SetIcon(BBitmap* icon)
611{
612	fAboutView->SetIcon(icon);
613}
614
615
616const char*
617BAboutWindow::Name()
618{
619	return fAboutView->Name();
620}
621
622
623void
624BAboutWindow::SetName(const char* name)
625{
626	fAboutView->SetName(name);
627}
628
629
630const char*
631BAboutWindow::Version()
632{
633	return fAboutView->Version();
634}
635
636
637void
638BAboutWindow::SetVersion(const char* version)
639{
640	fAboutView->SetVersion(version);
641}
642
643
644// FBC padding
645
646void BAboutWindow::_ReservedAboutWindow20() {}
647void BAboutWindow::_ReservedAboutWindow19() {}
648void BAboutWindow::_ReservedAboutWindow18() {}
649void BAboutWindow::_ReservedAboutWindow17() {}
650void BAboutWindow::_ReservedAboutWindow16() {}
651void BAboutWindow::_ReservedAboutWindow15() {}
652void BAboutWindow::_ReservedAboutWindow14() {}
653void BAboutWindow::_ReservedAboutWindow13() {}
654void BAboutWindow::_ReservedAboutWindow12() {}
655void BAboutWindow::_ReservedAboutWindow11() {}
656void BAboutWindow::_ReservedAboutWindow10() {}
657void BAboutWindow::_ReservedAboutWindow9() {}
658void BAboutWindow::_ReservedAboutWindow8() {}
659void BAboutWindow::_ReservedAboutWindow7() {}
660void BAboutWindow::_ReservedAboutWindow6() {}
661void BAboutWindow::_ReservedAboutWindow5() {}
662void BAboutWindow::_ReservedAboutWindow4() {}
663void BAboutWindow::_ReservedAboutWindow3() {}
664void BAboutWindow::_ReservedAboutWindow2() {}
665void BAboutWindow::_ReservedAboutWindow1() {}
666