1/*
2 * Copyright 2002-2017, Haiku, Inc.
3 * Distributed under the terms of the MIT license.
4 *
5 * Authors:
6 *		Oliver Siebenmarck
7 *		Andrew McCall, mccall@digitalparadise.co.uk
8 *		Michael Wilber
9 *		Maxime Simon
10 */
11
12
13#include "DataTranslationsWindow.h"
14
15#include <algorithm>
16
17#include <math.h>
18#include <stdio.h>
19
20#include <Alert.h>
21#include <Alignment.h>
22#include <Application.h>
23#include <Bitmap.h>
24#include <Box.h>
25#include <Catalog.h>
26#include <ControlLook.h>
27#include <Entry.h>
28#include <GroupView.h>
29#include <IconView.h>
30#include <LayoutBuilder.h>
31#include <ListView.h>
32#include <Path.h>
33#include <Screen.h>
34#include <ScrollView.h>
35#include <String.h>
36#include <StringView.h>
37#include <SupportDefs.h>
38#include <TextView.h>
39#include <TranslationDefs.h>
40#include <TranslatorRoster.h>
41
42
43#include "DataTranslations.h"
44#include "DataTranslationsSettings.h"
45#include "TranslatorListView.h"
46
47
48#undef B_TRANSLATION_CONTEXT
49#define B_TRANSLATION_CONTEXT "DataTranslations"
50
51
52const uint32 kMsgTranslatorInfo = 'trin';
53const uint32 kMsgSelectedTranslator = 'trsl';
54
55
56DataTranslationsWindow::DataTranslationsWindow()
57	:
58	BWindow(BRect(0.0f, 0.0f, 597.0f, 368.0f),
59		B_TRANSLATE_SYSTEM_NAME("DataTranslations"),
60		B_TITLED_WINDOW, B_ASYNCHRONOUS_CONTROLS | B_NOT_RESIZABLE
61			| B_NOT_ZOOMABLE | B_AUTO_UPDATE_SIZE_LIMITS),
62	fRelease(NULL)
63{
64	MoveTo(DataTranslationsSettings::Instance()->WindowCorner());
65
66	_SetupViews();
67
68	// Make sure that the window isn't positioned off screen
69	BScreen screen;
70	BRect screenFrame = screen.Frame();
71	if (!screenFrame.Contains(Frame()))
72		CenterOnScreen();
73
74	BTranslatorRoster* roster = BTranslatorRoster::Default();
75	roster->StartWatching(this);
76
77	Show();
78}
79
80
81DataTranslationsWindow::~DataTranslationsWindow()
82{
83	BTranslatorRoster* roster = BTranslatorRoster::Default();
84	roster->StopWatching(this);
85}
86
87
88// Reads the installed translators and adds them to our BListView
89status_t
90DataTranslationsWindow::_PopulateListView()
91{
92	BTranslatorRoster* roster = BTranslatorRoster::Default();
93
94	// Get all Translators on the system. Gives us the number of translators
95	// installed in num_translators and a reference to the first one
96	int32 numTranslators;
97	translator_id* translators = NULL;
98	roster->GetAllTranslators(&translators, &numTranslators);
99
100	float maxWidth = 0;
101
102	for (int32 i = 0; i < numTranslators; i++) {
103		// Getting the first three Infos: Name, Info & Version
104		int32 version;
105		const char* name;
106		const char* info;
107		roster->GetTranslatorInfo(translators[i], &name, &info, &version);
108		fTranslatorListView->AddItem(new TranslatorItem(translators[i], name));
109		maxWidth = std::max(maxWidth, fTranslatorListView->StringWidth(name));
110	}
111
112	fTranslatorListView->SortItems();
113
114	fTranslatorListView->SetExplicitSize(BSize(maxWidth + 20, B_SIZE_UNSET));
115
116	delete[] translators;
117	return B_OK;
118}
119
120
121status_t
122DataTranslationsWindow::_GetTranslatorInfo(int32 id, const char*& name,
123	const char*& info, int32& version, BPath& path)
124{
125	// Returns information about the translator with the given id
126
127	if (id < 0)
128		return B_BAD_VALUE;
129
130	BTranslatorRoster* roster = BTranslatorRoster::Default();
131	if (roster->GetTranslatorInfo(id, &name, &info, &version) != B_OK)
132		return B_ERROR;
133
134	// Get the translator's path
135	entry_ref ref;
136	if (roster->GetRefFor(id, &ref) == B_OK) {
137		BEntry entry(&ref);
138		path.SetTo(&entry);
139	} else
140		path.Unset();
141
142	return B_OK;
143}
144
145
146status_t
147DataTranslationsWindow::_ShowConfigView(int32 id)
148{
149	// Shows the config panel for the translator with the given id
150
151	if (id < 0)
152		return B_BAD_VALUE;
153
154	BTranslatorRoster* roster = BTranslatorRoster::Default();
155
156	if (fConfigView != NULL) {
157		fRightBox->RemoveChild(fConfigView);
158		delete fConfigView;
159		fConfigView = NULL;
160		fInfoText = NULL;
161		if (fRelease != NULL) {
162			fRelease->Release();
163			fRelease = NULL;
164		}
165	}
166
167	BMessage emptyMessage;
168	BRect rect(0.0f, 0.0f, 200.0f, 233.0f);
169	status_t result = roster->MakeConfigurationView(id, &emptyMessage,
170		&fConfigView, &rect);
171
172	if (result != B_OK)
173		return result;
174
175	fConfigView->SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
176		// force config views to all have the same color
177	fRightBox->AddChild(fConfigView);
178
179	// for default 12pt font size: 597 ��� (0.85 * 12 * 49)
180	fConfigView->SetExplicitMinSize(
181		BSize(ceilf(be_control_look->DefaultItemSpacing() * 49)
182			- fTranslatorListView->Frame().Width(), B_SIZE_UNSET));
183
184	// Make sure the translator's image doesn't get unloaded while we are still
185	// showing a config view whose code is in the image
186	fRelease = roster->AcquireTranslator(id);
187
188	return B_OK;
189}
190
191
192void
193DataTranslationsWindow::_ShowInfoView()
194{
195	if (fConfigView != NULL) {
196		fRightBox->RemoveChild(fConfigView);
197		delete fConfigView;
198		fConfigView = NULL;
199		fInfoText = NULL;
200		if (fRelease != NULL) {
201			fRelease->Release();
202			fRelease = NULL;
203		}
204	}
205
206	fInfoText = new BTextView("info text");
207	fInfoText->MakeEditable(false);
208	fInfoText->MakeSelectable(false);
209	fInfoText->SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
210	fInfoText->SetText(B_TRANSLATE(
211		"Use this control panel to set default values for translators, "
212		"to be used when no other settings are specified by an application."));
213	rgb_color textColor = ui_color(B_PANEL_TEXT_COLOR);
214	fInfoText->SetFontAndColor(be_plain_font, B_FONT_ALL, &textColor);
215
216	BGroupView* group = new BGroupView(B_VERTICAL);
217	group->AddChild(fInfoText);
218	float spacing = be_control_look->DefaultItemSpacing();
219	group->GroupLayout()->SetInsets(spacing, spacing, spacing, spacing);
220	fRightBox->AddChild(group);
221	fConfigView = group;
222
223	fConfigView->SetExplicitMinSize(
224		BSize(ceilf(spacing * be_plain_font->Size() * 0.7)
225			- fTranslatorListView->Frame().Width(),
226			ceilf(spacing * be_plain_font->Size() * 0.4)));
227}
228
229
230void
231DataTranslationsWindow::_SetupViews()
232{
233	fInfoText = NULL;
234	fConfigView = NULL;
235	// This is NULL until a translator is
236	// selected from the listview
237
238	// Add the translators list view
239	fTranslatorListView = new TranslatorListView("TransList");
240	fTranslatorListView->SetSelectionMessage(
241		new BMessage(kMsgSelectedTranslator));
242
243	BScrollView* scrollView = new BScrollView("scroll_trans",
244		fTranslatorListView, B_WILL_DRAW | B_FRAME_EVENTS, false,
245		true, B_FANCY_BORDER);
246
247	// Box around the config and info panels
248	fRightBox = new BBox("Right_Side");
249	fRightBox->SetExplicitAlignment(BAlignment(B_ALIGN_USE_FULL_WIDTH,
250		B_ALIGN_USE_FULL_HEIGHT));
251
252	// Add the translator icon view
253	fIconView = new IconView();
254
255	// Add the translator info button
256	fButton = new BButton("info", B_TRANSLATE("Info"),
257		new BMessage(kMsgTranslatorInfo),
258		B_WILL_DRAW | B_FRAME_EVENTS | B_NAVIGABLE);
259	fButton->SetEnabled(false);
260
261	// Populate the translators list view
262	_PopulateListView();
263
264	// Build the layout
265	BLayoutBuilder::Group<>(this, B_HORIZONTAL)
266		.SetInsets(B_USE_WINDOW_SPACING)
267		.Add(scrollView, 3)
268		.AddGroup(B_VERTICAL)
269			.Add(fRightBox)
270			.AddGroup(B_HORIZONTAL)
271				.Add(fIconView)
272				.AddGlue()
273				.Add(fButton)
274				.End()
275			.End()
276		.End();
277
278	fTranslatorListView->MakeFocus();
279	_ShowInfoView();
280}
281
282
283bool
284DataTranslationsWindow::QuitRequested()
285{
286	BPoint pt(Frame().LeftTop());
287	DataTranslationsSettings::Instance()->SetWindowCorner(pt);
288	be_app->PostMessage(B_QUIT_REQUESTED);
289	return true;
290}
291
292
293void
294DataTranslationsWindow::_ShowInfoAlert(int32 id)
295{
296	const char* name = NULL;
297	const char* info = NULL;
298	BPath path;
299	int32 version = 0;
300	_GetTranslatorInfo(id, name, info, version, path);
301
302	const char* labels[] = { B_TRANSLATE("Name:"), B_TRANSLATE("Version:"),
303		B_TRANSLATE("Info:"), B_TRANSLATE("Path:"), NULL };
304	int offsets[4];
305
306	BString message;
307	BString temp;
308
309	offsets[0] = 0;
310	temp.SetToFormat("%s %s\n", labels[0], name);
311
312	message.Append(temp);
313
314	offsets[1] = message.Length();
315	// Convert the version number into a readable format
316	temp.SetToFormat("%s %" B_PRId32 ".%" B_PRId32 ".%" B_PRId32 "\n\n", labels[1],
317		B_TRANSLATION_MAJOR_VERSION(version),
318		B_TRANSLATION_MINOR_VERSION(version),
319		B_TRANSLATION_REVISION_VERSION(version));
320
321	message.Append(temp);
322
323	offsets[2] = message.Length();
324	temp.SetToFormat("%s\n%s\n\n", labels[2], info);
325
326	message.Append(temp);
327
328	offsets[3] = message.Length();
329	temp.SetToFormat("%s %s\n", labels[3], path.Path());
330
331	message.Append(temp);
332
333	BAlert* alert = new BAlert(B_TRANSLATE("Info"), message.String(),
334		B_TRANSLATE("OK"));
335	BTextView* view = alert->TextView();
336	BFont font;
337
338	view->SetStylable(true);
339
340	view->GetFont(&font);
341	font.SetFace(B_BOLD_FACE);
342
343	for (int32 i = 0; labels[i]; i++) {
344		view->SetFontAndColor(offsets[i], offsets[i] + strlen(labels[i]), &font);
345	}
346
347	alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
348	alert->Go();
349}
350
351
352void
353DataTranslationsWindow::MessageReceived(BMessage* message)
354{
355	switch (message->what) {
356		case kMsgTranslatorInfo:
357		{
358			int32 selected = fTranslatorListView->CurrentSelection(0);
359			if (selected < 0)
360				break;
361
362			TranslatorItem* item = fTranslatorListView->TranslatorAt(selected);
363			if (item != NULL)
364				_ShowInfoAlert(item->ID());
365			break;
366		}
367
368		case kMsgSelectedTranslator:
369		{
370			// Update the icon and translator info panel
371			// to match the new selection
372
373			int32 selected = fTranslatorListView->CurrentSelection(0);
374			if (selected < 0) {
375				// If none selected, clear the old one
376				fIconView->DrawIcon(false);
377				fButton->SetEnabled(false);
378				fRightBox->RemoveChild(fConfigView);
379				_ShowInfoView();
380				break;
381			}
382
383			TranslatorItem* item = fTranslatorListView->TranslatorAt(selected);
384			if (item == NULL)
385				break;
386
387			_ShowConfigView(item->ID());
388
389			const char* name = NULL;
390			const char* info = NULL;
391			int32 version = 0;
392			BPath path;
393			_GetTranslatorInfo(item->ID(), name, info, version, path);
394			fIconView->SetIcon(path);
395			fButton->SetEnabled(true);
396			break;
397		}
398
399		case B_COLORS_UPDATED:
400		{
401			if (fInfoText == NULL || fInfoText->Parent() == NULL)
402				break;
403
404			rgb_color color;
405			if (message->FindColor(ui_color_name(B_PANEL_TEXT_COLOR), &color)
406					== B_OK) {
407				fInfoText->SetFontAndColor(be_plain_font, B_FONT_ALL, &color);
408			}
409			break;
410		}
411
412		case B_TRANSLATOR_ADDED:
413		{
414			int32 index = 0;
415			int32 id;
416			while (message->FindInt32("translator_id", index++, &id) == B_OK) {
417				const char* name;
418				const char* info;
419				int32 version;
420				BPath path;
421				if (_GetTranslatorInfo(id, name, info, version, path) == B_OK)
422					fTranslatorListView->AddItem(new TranslatorItem(id, name));
423			}
424
425			fTranslatorListView->SortItems();
426			break;
427		}
428
429		case B_TRANSLATOR_REMOVED:
430		{
431			int32 index = 0;
432			int32 id;
433			while (message->FindInt32("translator_id", index++, &id) == B_OK) {
434				for (int32 i = 0; i < fTranslatorListView->CountItems(); i++) {
435					TranslatorItem* item = fTranslatorListView->TranslatorAt(i);
436
437					if (item == NULL)
438						continue;
439
440					if (item->ID() == (translator_id)id) {
441						fTranslatorListView->RemoveItem(i);
442						delete item;
443						break;
444					}
445				}
446			}
447			break;
448		}
449
450		default:
451			BWindow::MessageReceived(message);
452			break;
453	}
454}
455