1/*
2 * Copyright 2007-2012 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 */
9
10
11#include <AboutWindow.h>
12
13#include <stdarg.h>
14#include <time.h>
15
16#include <Alert.h>
17#include <AppFileInfo.h>
18#include <Bitmap.h>
19#include <File.h>
20#include <Font.h>
21#include <GroupLayoutBuilder.h>
22#include <GroupView.h>
23#include <LayoutBuilder.h>
24#include <Point.h>
25#include <Roster.h>
26#include <Screen.h>
27#include <ScrollView.h>
28#include <Size.h>
29#include <String.h>
30#include <StringView.h>
31#include <SystemCatalog.h>
32#include <TextView.h>
33#include <View.h>
34#include <Window.h>
35
36
37static const float kStripeWidth = 30.0;
38
39using BPrivate::gSystemCatalog;
40
41#undef B_TRANSLATION_CONTEXT
42#define B_TRANSLATION_CONTEXT "AboutWindow"
43
44
45class StripeView : public BView {
46 public:
47							StripeView(BBitmap* icon);
48	virtual					~StripeView();
49
50	virtual	void			Draw(BRect updateRect);
51
52 private:
53			BBitmap*		fIcon;
54};
55
56
57class AboutView : public BGroupView {
58 public:
59							AboutView(const char* name,
60								const char* signature);
61	virtual					~AboutView();
62
63			BTextView*		InfoView() const { return fInfoView; };
64
65 protected:
66			const char*		AppVersion(const char* signature);
67			BBitmap*		AppIcon(const char* signature);
68
69 private:
70			BStringView*	fNameView;
71			BStringView*	fVersionView;
72			BTextView*		fInfoView;
73};
74
75
76//	#pragma mark -
77
78
79StripeView::StripeView(BBitmap* icon)
80	:
81	BView("StripeView", B_WILL_DRAW),
82	fIcon(icon)
83{
84	float width = 0.0f;
85	if (icon != NULL)
86		width += icon->Bounds().Width() + 32.0f;
87
88	SetExplicitMinSize(BSize(width, B_SIZE_UNSET));
89	SetExplicitPreferredSize(BSize(width, B_SIZE_UNLIMITED));
90	SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
91}
92
93
94StripeView::~StripeView()
95{
96}
97
98
99void
100StripeView::Draw(BRect updateRect)
101{
102	if (fIcon == NULL)
103		return;
104
105	SetHighColor(ViewColor());
106	FillRect(updateRect);
107
108	BRect stripeRect = Bounds();
109	stripeRect.right = kStripeWidth;
110	SetHighColor(tint_color(ViewColor(), B_DARKEN_1_TINT));
111	FillRect(stripeRect);
112
113	SetDrawingMode(B_OP_ALPHA);
114	SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_OVERLAY);
115	DrawBitmapAsync(fIcon, BPoint(15.0f, 10.0f));
116}
117
118
119//	#pragma mark -
120
121
122AboutView::AboutView(const char* appName, const char* signature)
123	:
124	BGroupView("AboutView", B_VERTICAL)
125{
126	fNameView = new BStringView("name", appName);
127	BFont font;
128	fNameView->GetFont(&font);
129	font.SetFace(B_BOLD_FACE);
130	font.SetSize(font.Size() * 2.0);
131	fNameView->SetFont(&font, B_FONT_FAMILY_AND_STYLE | B_FONT_SIZE
132		| B_FONT_FLAGS);
133
134	fVersionView = new BStringView("version", AppVersion(signature));
135
136	fInfoView = new BTextView("info", B_WILL_DRAW);
137	fInfoView->SetExplicitMinSize(BSize(210.0, 160.0));
138	fInfoView->MakeEditable(false);
139	fInfoView->SetWordWrap(true);
140	fInfoView->SetInsets(5.0, 5.0, 5.0, 5.0);
141	fInfoView->SetViewColor(ui_color(B_DOCUMENT_BACKGROUND_COLOR));
142	fInfoView->SetHighColor(ui_color(B_DOCUMENT_TEXT_COLOR));
143
144	BScrollView* infoViewScroller = new BScrollView(
145		"infoViewScroller", fInfoView, B_WILL_DRAW | B_FRAME_EVENTS,
146		false, true, B_PLAIN_BORDER);
147
148	GroupLayout()->SetSpacing(0);
149	BLayoutBuilder::Group<>(this)
150		.AddGroup(B_HORIZONTAL, 0)
151			.Add(new StripeView(AppIcon(signature)))
152			.AddGroup(B_VERTICAL, B_USE_SMALL_SPACING)
153				.SetInsets(0, B_USE_DEFAULT_SPACING,
154					B_USE_DEFAULT_SPACING, B_USE_DEFAULT_SPACING)
155				.Add(fNameView)
156				.Add(fVersionView)
157				.Add(infoViewScroller)
158				.End()
159			.AddGlue()
160			.End();
161}
162
163
164AboutView::~AboutView()
165{
166}
167
168
169const char*
170AboutView::AppVersion(const char* signature)
171{
172	if (signature == NULL)
173		return NULL;
174
175	entry_ref ref;
176	if (be_roster->FindApp(signature, &ref) != B_OK)
177		return NULL;
178
179	BFile file(&ref, B_READ_ONLY);
180	BAppFileInfo appMime(&file);
181	if (appMime.InitCheck() != B_OK)
182		return NULL;
183
184	version_info versionInfo;
185	if (appMime.GetVersionInfo(&versionInfo, B_APP_VERSION_KIND) == B_OK) {
186		if (versionInfo.major == 0 && versionInfo.middle == 0
187			&& versionInfo.minor == 0) {
188			return NULL;
189		}
190
191		const char* version = B_TRANSLATE_MARK("Version");
192		version = gSystemCatalog.GetString(version, "AboutWindow");
193		BString appVersion(version);
194		appVersion << " " << versionInfo.major << "." << versionInfo.middle;
195		if (versionInfo.minor > 0)
196			appVersion << "." << versionInfo.minor;
197
198		// Add the version variety
199		const char* variety = NULL;
200		switch (versionInfo.variety) {
201			case B_DEVELOPMENT_VERSION:
202				variety = B_TRANSLATE_MARK("development");
203				break;
204			case B_ALPHA_VERSION:
205				variety = B_TRANSLATE_MARK("alpha");
206				break;
207			case B_BETA_VERSION:
208				variety = B_TRANSLATE_MARK("beta");
209				break;
210			case B_GAMMA_VERSION:
211				variety = B_TRANSLATE_MARK("gamma");
212				break;
213			case B_GOLDEN_MASTER_VERSION:
214				variety = B_TRANSLATE_MARK("gold master");
215				break;
216		}
217
218		if (variety != NULL) {
219			variety = gSystemCatalog.GetString(variety, "AboutWindow");
220			appVersion << "-" << variety;
221		}
222
223		return appVersion.String();
224	}
225
226	return NULL;
227}
228
229
230BBitmap*
231AboutView::AppIcon(const char* signature)
232{
233	if (signature == NULL)
234		return NULL;
235
236	entry_ref ref;
237	if (be_roster->FindApp(signature, &ref) != B_OK)
238		return NULL;
239
240	BFile file(&ref, B_READ_ONLY);
241	BAppFileInfo appMime(&file);
242	if (appMime.InitCheck() != B_OK)
243		return NULL;
244
245	BBitmap* icon = new BBitmap(BRect(0.0, 0.0, 127.0, 127.0), B_RGBA32);
246	if (appMime.GetIcon(icon, (icon_size)128) == B_OK)
247		return icon;
248
249	delete icon;
250	return NULL;
251}
252
253
254//	#pragma mark -
255
256
257BAboutWindow::BAboutWindow(const char* appName, const char* signature)
258	:	BWindow(BRect(0.0, 0.0, 200.0, 200.0), appName, B_TITLED_WINDOW,
259		B_ASYNCHRONOUS_CONTROLS | B_NOT_ZOOMABLE | B_NOT_RESIZABLE
260			| B_AUTO_UPDATE_SIZE_LIMITS)
261{
262	SetLayout(new BGroupLayout(B_VERTICAL));
263
264	const char* about = B_TRANSLATE_MARK("About");
265	about = gSystemCatalog.GetString(about, "AboutWindow");
266
267	BString title(about);
268	title << " " << appName;
269	SetTitle(title.String());
270
271	fAboutView = new AboutView(appName, signature);
272	AddChild(fAboutView);
273
274	MoveTo(AboutPosition(Frame().Width(), Frame().Height()));
275}
276
277
278BAboutWindow::~BAboutWindow()
279{
280	fAboutView->RemoveSelf();
281	delete fAboutView;
282	fAboutView = NULL;
283}
284
285
286bool
287BAboutWindow::QuitRequested()
288{
289	Hide();
290
291	return false;
292}
293
294
295//	#pragma mark -
296
297
298BPoint
299BAboutWindow::AboutPosition(float width, float height)
300{
301	BPoint result(100, 100);
302
303	BWindow* window =
304		dynamic_cast<BWindow*>(BLooper::LooperForThread(find_thread(NULL)));
305
306	BScreen screen(window);
307 	BRect screenFrame(0, 0, 640, 480);
308 	if (screen.IsValid())
309 		screenFrame = screen.Frame();
310
311	// Horizontally, we're smack in the middle
312	result.x = screenFrame.left + (screenFrame.Width() / 2.0) - (width / 2.0);
313
314	// This is probably sooo wrong, but it looks right on 1024 x 768
315	result.y = screenFrame.top + (screenFrame.Height() / 4.0)
316		- ceil(height / 3.0);
317
318	return result;
319}
320
321
322void
323BAboutWindow::AddDescription(const char* description)
324{
325	if (description == NULL)
326		return;
327
328	const char* appDesc = B_TRANSLATE_MARK(description);
329	appDesc = gSystemCatalog.GetString(appDesc, "AboutWindow");
330
331	BString desc("");
332	if (fAboutView->InfoView()->TextLength() > 0)
333		desc << "\n\n";
334
335	desc << appDesc;
336
337	fAboutView->InfoView()->Insert(desc.String());
338}
339
340
341void
342BAboutWindow::AddCopyright(int32 firstCopyrightYear,
343	const char* copyrightHolder, const char** extraCopyrights)
344{
345	BString copyright(B_UTF8_COPYRIGHT " %years% %holder%");
346
347	// Get current year
348	time_t tp;
349	time(&tp);
350	char currentYear[5];
351	strftime(currentYear, 5, "%Y", localtime(&tp));
352	BString copyrightYears;
353	copyrightYears << firstCopyrightYear;
354	if (copyrightYears != currentYear)
355		copyrightYears << "-" << currentYear;
356
357	BString text("");
358	if (fAboutView->InfoView()->TextLength() > 0)
359		text << "\n\n";
360
361	text << copyright;
362
363	// Fill out the copyright year placeholder
364	text.ReplaceAll("%years%", copyrightYears.String());
365
366	// Fill in the copyright holder placeholder
367	text.ReplaceAll("%holder%", copyrightHolder);
368
369	// Add extra copyright strings
370	if (extraCopyrights != NULL) {
371		// Add optional extra copyright information
372		for (int32 i = 0; extraCopyrights[i]; i++)
373			text << "\n" << B_UTF8_COPYRIGHT << " " << extraCopyrights[i];
374	}
375
376	const char* allRightsReserved = B_TRANSLATE_MARK("All Rights Reserved.");
377	allRightsReserved = gSystemCatalog.GetString(allRightsReserved,
378		"AboutWindow");
379	text << "\n    " << allRightsReserved;
380
381	fAboutView->InfoView()->Insert(text.String());
382}
383
384
385void
386BAboutWindow::AddAuthors(const char** authors)
387{
388	if (authors == NULL)
389		return;
390
391	const char* writtenBy = B_TRANSLATE_MARK("Written by:");
392	writtenBy = gSystemCatalog.GetString(writtenBy, "AboutWindow");
393
394	BString text("");
395	if (fAboutView->InfoView()->TextLength() > 0)
396		text << "\n\n";
397
398	text << writtenBy;
399	text << "\n";
400	for (int32 i = 0; authors[i]; i++)
401		text << "    " << authors[i] << "\n";
402
403	fAboutView->InfoView()->Insert(text.String());
404}
405
406
407void
408BAboutWindow::AddSpecialThanks(const char** thanks)
409{
410	if (thanks == NULL)
411		return;
412
413	const char* specialThanks = B_TRANSLATE_MARK("Special Thanks:");
414	specialThanks = gSystemCatalog.GetString(specialThanks, "AboutWindow");
415
416	BString text("");
417	if (fAboutView->InfoView()->TextLength() > 0)
418		text << "\n\n";
419
420	text << specialThanks << "\n";
421	for (int32 i = 0; thanks[i]; i++)
422		text << "    " << thanks[i] << "\n";
423
424	fAboutView->InfoView()->Insert(text.String());
425}
426
427
428void
429BAboutWindow::AddVersionHistory(const char** history)
430{
431	if (history == NULL)
432		return;
433
434	const char* versionHistory = B_TRANSLATE_MARK("Version history:");
435	versionHistory = gSystemCatalog.GetString(versionHistory, "AboutWindow");
436
437	BString text("");
438	if (fAboutView->InfoView()->TextLength() > 0)
439		text << "\n\n";
440
441	text << versionHistory << "\n";
442	for (int32 i = 0; history[i]; i++)
443		text << "    " << history[i] << "\n";
444
445	fAboutView->InfoView()->Insert(text.String());
446}
447
448
449void
450BAboutWindow::AddExtraInfo(const char* extraInfo)
451{
452	if (extraInfo == NULL)
453		return;
454
455	const char* appExtraInfo = B_TRANSLATE_MARK(extraInfo);
456	appExtraInfo = gSystemCatalog.GetString(extraInfo, "AboutWindow");
457
458	BString extra("");
459	if (fAboutView->InfoView()->TextLength() > 0)
460		extra << "\n\n";
461
462	extra << appExtraInfo;
463
464	fAboutView->InfoView()->Insert(extra.String());
465}
466