1/*
2 *  Copyright 2010-2020 Haiku, Inc. All rights reserved.
3 *  Distributed under the terms of the MIT license.
4 *
5 *	Authors:
6 *		Stephan A��mus <superstippi@gmx.de>
7 *		Alexander von Gluck <kallisti5@unixzen.com>
8 *		John Scipione <jscipione@gmail.com>
9 *		Ryan Leavengood <leavengood@gmail.com>
10 */
11
12
13#include "LookAndFeelSettingsView.h"
14
15#include <stdio.h>
16#include <stdlib.h>
17
18#include <Alert.h>
19#include <Alignment.h>
20#include <AppFileInfo.h>
21#include <Box.h>
22#include <Button.h>
23#include <Catalog.h>
24#include <CheckBox.h>
25#include <File.h>
26#include <InterfaceDefs.h>
27#include <InterfacePrivate.h>
28#include <LayoutBuilder.h>
29#include <Locale.h>
30#include <MenuField.h>
31#include <MenuItem.h>
32#include <Path.h>
33#include <PathFinder.h>
34#include <PopUpMenu.h>
35#include <ScrollBar.h>
36#include <StringView.h>
37#include <Size.h>
38#include <Slider.h>
39#include <SpaceLayoutItem.h>
40#include <StringView.h>
41#include <TextView.h>
42
43#include "APRWindow.h"
44#include "FakeScrollBar.h"
45
46
47#undef B_TRANSLATION_CONTEXT
48#define B_TRANSLATION_CONTEXT "DecorSettingsView"
49	// This was not renamed to keep from breaking translations
50
51
52static const int32 kMsgSetDecor = 'deco';
53static const int32 kMsgDecorInfo = 'idec';
54
55static const int32 kMsgSetControlLook = 'ctlk';
56static const int32 kMsgControlLookInfo = 'iclk';
57
58static const int32 kMsgDoubleScrollBarArrows = 'dsba';
59
60static const int32 kMsgArrowStyleSingle = 'mass';
61static const int32 kMsgArrowStyleDouble = 'masd';
62
63static const bool kDefaultDoubleScrollBarArrowsSetting = false;
64
65
66//	#pragma mark - LookAndFeelSettingsView
67
68
69LookAndFeelSettingsView::LookAndFeelSettingsView(const char* name)
70	:
71	BView(name, 0),
72	fDecorInfoButton(NULL),
73	fDecorMenuField(NULL),
74	fDecorMenu(NULL),
75	fControlLookInfoButton(NULL),
76	fControlLookMenuField(NULL),
77	fControlLookMenu(NULL),
78	fArrowStyleSingle(NULL),
79	fArrowStyleDouble(NULL),
80	fSavedDecor(NULL),
81	fCurrentDecor(NULL),
82	fSavedControlLook(NULL),
83	fCurrentControlLook(NULL),
84	fSavedDoubleArrowsValue(_DoubleScrollBarArrows())
85{
86	fCurrentDecor = fDecorUtility.CurrentDecorator()->ShortcutName();
87	fSavedDecor = fCurrentDecor;
88
89	// Decorator menu
90	_BuildDecorMenu();
91	fDecorMenuField = new BMenuField("decorator",
92		B_TRANSLATE("Decorator:"), fDecorMenu);
93
94	fDecorInfoButton = new BButton(B_TRANSLATE("About"),
95		new BMessage(kMsgDecorInfo));
96
97	BPrivate::get_control_look(fCurrentControlLook);
98	fSavedControlLook = fCurrentControlLook;
99
100	// ControlLook menu
101	_BuildControlLookMenu();
102	fControlLookMenuField = new BMenuField("controllook",
103		B_TRANSLATE("ControlLook:"), fControlLookMenu);
104	fControlLookMenuField->SetToolTip(
105		B_TRANSLATE("No effect on running applications"));
106
107	fControlLookInfoButton = new BButton(B_TRANSLATE("About"),
108		new BMessage(kMsgControlLookInfo));
109
110	// scroll bar arrow style
111	BBox* arrowStyleBox = new BBox("arrow style");
112	arrowStyleBox->SetLabel(B_TRANSLATE("Arrow style"));
113
114	fArrowStyleSingle = new FakeScrollBar(true, false,
115		new BMessage(kMsgArrowStyleSingle));
116	fArrowStyleDouble = new FakeScrollBar(true, true,
117		new BMessage(kMsgArrowStyleDouble));
118
119	BView* arrowStyleView;
120	arrowStyleView = BLayoutBuilder::Group<>()
121		.AddGroup(B_VERTICAL, 1)
122			.Add(new BStringView("single", B_TRANSLATE("Single:")))
123			.Add(fArrowStyleSingle)
124			.AddStrut(B_USE_DEFAULT_SPACING)
125			.Add(new BStringView("double", B_TRANSLATE("Double:")))
126			.Add(fArrowStyleDouble)
127			.SetInsets(B_USE_DEFAULT_SPACING, B_USE_DEFAULT_SPACING,
128				B_USE_DEFAULT_SPACING, B_USE_DEFAULT_SPACING)
129			.End()
130		.View();
131	arrowStyleBox->AddChild(arrowStyleView);
132	arrowStyleBox->SetExplicitAlignment(BAlignment(B_ALIGN_LEFT,
133		B_ALIGN_VERTICAL_CENTER));
134	arrowStyleBox->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET));
135
136	BStringView* scrollBarLabel
137		= new BStringView("scroll bar", B_TRANSLATE("Scroll bar:"));
138	scrollBarLabel->SetExplicitAlignment(
139		BAlignment(B_ALIGN_LEFT, B_ALIGN_TOP));
140
141	// control layout
142	BLayoutBuilder::Grid<>(this, B_USE_DEFAULT_SPACING, B_USE_DEFAULT_SPACING)
143		.Add(fDecorMenuField->CreateLabelLayoutItem(), 0, 0)
144		.Add(fDecorMenuField->CreateMenuBarLayoutItem(), 1, 0)
145		.Add(fDecorInfoButton, 2, 0)
146		.Add(fControlLookMenuField->CreateLabelLayoutItem(), 0, 1)
147		.Add(fControlLookMenuField->CreateMenuBarLayoutItem(), 1, 1)
148		.Add(fControlLookInfoButton, 2, 1)
149		.Add(scrollBarLabel, 0, 2)
150		.Add(arrowStyleBox, 1, 2)
151		.AddGlue(0, 3)
152		.SetInsets(B_USE_WINDOW_SPACING);
153
154	// TODO : Decorator Preview Image?
155}
156
157
158LookAndFeelSettingsView::~LookAndFeelSettingsView()
159{
160}
161
162
163void
164LookAndFeelSettingsView::AttachedToWindow()
165{
166	AdoptParentColors();
167
168	if (Parent() == NULL)
169		SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
170
171	fDecorMenu->SetTargetForItems(this);
172	fDecorInfoButton->SetTarget(this);
173	fControlLookMenu->SetTargetForItems(this);
174	fControlLookInfoButton->SetTarget(this);
175	fArrowStyleSingle->SetTarget(this);
176	fArrowStyleDouble->SetTarget(this);
177
178	if (fSavedDoubleArrowsValue)
179		fArrowStyleDouble->SetValue(B_CONTROL_ON);
180	else
181		fArrowStyleSingle->SetValue(B_CONTROL_ON);
182}
183
184
185void
186LookAndFeelSettingsView::MessageReceived(BMessage* message)
187{
188	switch (message->what) {
189		case kMsgSetDecor:
190		{
191			BString newDecor;
192			if (message->FindString("decor", &newDecor) == B_OK)
193				_SetDecor(newDecor);
194			break;
195		}
196
197		case kMsgDecorInfo:
198		{
199			DecorInfo* decor = fDecorUtility.FindDecorator(fCurrentDecor);
200			if (decor == NULL)
201				break;
202
203			BString authorsText(decor->Authors().String());
204			authorsText.ReplaceAll(", ", "\n\t");
205
206			BString infoText(B_TRANSLATE("%decorName\n\n"
207				"Authors:\n\t%decorAuthors\n\n"
208				"URL: %decorURL\n"
209				"License: %decorLic\n\n"
210				"%decorDesc\n"));
211
212			infoText.ReplaceFirst("%decorName", decor->Name().String());
213			infoText.ReplaceFirst("%decorAuthors", authorsText.String());
214			infoText.ReplaceFirst("%decorLic", decor->LicenseName().String());
215			infoText.ReplaceFirst("%decorURL", decor->SupportURL().String());
216			infoText.ReplaceFirst("%decorDesc",
217				decor->ShortDescription().String());
218
219			BAlert* infoAlert = new BAlert(B_TRANSLATE("About decorator"),
220				infoText.String(), B_TRANSLATE("OK"));
221			infoAlert->SetFlags(infoAlert->Flags() | B_CLOSE_ON_ESCAPE);
222			infoAlert->Go();
223
224			break;
225		}
226
227		case kMsgSetControlLook:
228		{
229			BString controlLook;
230			if (message->FindString("control_look", &controlLook) == B_OK)
231				_SetControlLook(controlLook);
232			break;
233		}
234
235		case kMsgControlLookInfo:
236		{
237			BString infoText(B_TRANSLATE("Default Haiku ControlLook"));
238			BString path;
239			if (!BPrivate::get_control_look(path))
240				break;
241
242			if (path.Length() > 0) {
243				BFile file(path.String(), B_READ_ONLY);
244				if (file.InitCheck() != B_OK)
245					break;
246
247				BAppFileInfo info(&file);
248				struct version_info version;
249				if (info.InitCheck() != B_OK
250					|| info.GetVersionInfo(&version, B_APP_VERSION_KIND) != B_OK)
251					break;
252
253				infoText = version.short_info;
254				infoText << "\n" << version.long_info;
255			}
256
257			BAlert* infoAlert = new BAlert(B_TRANSLATE("About control look"),
258				infoText.String(), B_TRANSLATE("OK"));
259			infoAlert->SetFlags(infoAlert->Flags() | B_CLOSE_ON_ESCAPE);
260			infoAlert->Go();
261
262			break;
263		}
264
265		case kMsgArrowStyleSingle:
266			_SetDoubleScrollBarArrows(false);
267			break;
268
269		case kMsgArrowStyleDouble:
270			_SetDoubleScrollBarArrows(true);
271			break;
272
273		default:
274			BView::MessageReceived(message);
275			break;
276	}
277}
278
279
280//	#pragma mark - LookAndFeelSettingsView private methods
281
282
283void
284LookAndFeelSettingsView::_SetDecor(const BString& name)
285{
286	_SetDecor(fDecorUtility.FindDecorator(name));
287}
288
289
290void
291LookAndFeelSettingsView::_SetDecor(DecorInfo* decorInfo)
292{
293	if (fDecorUtility.SetDecorator(decorInfo) == B_OK) {
294		fCurrentDecor = fDecorUtility.CurrentDecorator()->ShortcutName();
295		BString decorName = fDecorUtility.CurrentDecorator()->Name();
296		fDecorMenu->FindItem(_DecorLabel(decorName))->SetMarked(true);
297		Window()->PostMessage(kMsgUpdate);
298	}
299}
300
301
302void
303LookAndFeelSettingsView::_BuildDecorMenu()
304{
305	fDecorMenu = new BPopUpMenu(B_TRANSLATE("Choose Decorator"));
306
307	// collect the current system decor settings
308	int32 count = fDecorUtility.CountDecorators();
309	for (int32 i = 0; i < count; ++i) {
310		DecorInfo* decor = fDecorUtility.DecoratorAt(i);
311		if (decor == NULL) {
312			fprintf(stderr, "Decorator : error NULL entry @ %" B_PRId32
313				" / %" B_PRId32 "\n", i, count);
314			continue;
315		}
316
317		BMessage* message = new BMessage(kMsgSetDecor);
318		message->AddString("decor", decor->ShortcutName());
319
320		BMenuItem* item = new BMenuItem(_DecorLabel(decor->Name()), message);
321		fDecorMenu->AddItem(item);
322		if (decor->ShortcutName() == fCurrentDecor)
323			item->SetMarked(true);
324	}
325}
326
327
328const char*
329LookAndFeelSettingsView::_DecorLabel(const BString& name)
330{
331	BString label(name);
332	return label.RemoveLast("Decorator").Trim().String();
333}
334
335
336void
337LookAndFeelSettingsView::_SetControlLook(const BString& path)
338{
339	fCurrentControlLook = path;
340	BPrivate::set_control_look(path);
341
342	if (path.Length() > 0) {
343		BEntry entry(path.String());
344		const char* label = _ControlLookLabel(entry.Name());
345		fControlLookMenu->FindItem(label)->SetMarked(true);
346	} else
347		fControlLookMenu->FindItem(B_TRANSLATE("Default"))->SetMarked(true);
348
349	Window()->PostMessage(kMsgUpdate);
350}
351
352
353void
354LookAndFeelSettingsView::_BuildControlLookMenu()
355{
356	BPathFinder pathFinder;
357	BStringList paths;
358	BDirectory dir;
359
360	fControlLookMenu = new BPopUpMenu(B_TRANSLATE("Choose ControlLook"));
361
362	BMessage* message = new BMessage(kMsgSetControlLook);
363	message->AddString("control_look", "");
364
365	BMenuItem* item = new BMenuItem(B_TRANSLATE("Default"), message);
366	if (fCurrentControlLook.Length() == 0)
367		item->SetMarked(true);
368	fControlLookMenu->AddItem(item);
369
370	status_t error = pathFinder.FindPaths(B_FIND_PATH_ADD_ONS_DIRECTORY,
371		"control_look", paths);
372
373	int32 count = paths.CountStrings();
374	for (int32 i = 0; i < count; ++i) {
375		if (error != B_OK || dir.SetTo(paths.StringAt(i)) != B_OK)
376			continue;
377
378		BEntry entry;
379		while (dir.GetNextEntry(&entry) == B_OK) {
380			BPath path(paths.StringAt(i), entry.Name());
381			message = new BMessage(kMsgSetControlLook);
382			message->AddString("control_look", path.Path());
383
384			item = new BMenuItem(_ControlLookLabel(entry.Name()), message);
385			fControlLookMenu->AddItem(item);
386			if (BString(path.Path()) == fCurrentControlLook)
387				item->SetMarked(true);
388		}
389	}
390}
391
392
393const char*
394LookAndFeelSettingsView::_ControlLookLabel(const char* name)
395{
396	BString label(name);
397	return label.RemoveLast("ControlLook").Trim().String();
398}
399
400
401bool
402LookAndFeelSettingsView::_DoubleScrollBarArrows()
403{
404	scroll_bar_info info;
405	get_scroll_bar_info(&info);
406
407	return info.double_arrows;
408}
409
410
411void
412LookAndFeelSettingsView::_SetDoubleScrollBarArrows(bool doubleArrows)
413{
414	scroll_bar_info info;
415	get_scroll_bar_info(&info);
416
417	if (info.double_arrows == doubleArrows)
418		return;
419
420	info.double_arrows = doubleArrows;
421	set_scroll_bar_info(&info);
422
423	if (doubleArrows)
424		fArrowStyleDouble->SetValue(B_CONTROL_ON);
425	else
426		fArrowStyleSingle->SetValue(B_CONTROL_ON);
427
428	Window()->PostMessage(kMsgUpdate);
429}
430
431
432bool
433LookAndFeelSettingsView::IsDefaultable()
434{
435	return fCurrentDecor != fDecorUtility.DefaultDecorator()->ShortcutName()
436		|| fCurrentControlLook.Length() != 0
437		|| _DoubleScrollBarArrows() != false;
438}
439
440
441void
442LookAndFeelSettingsView::SetDefaults()
443{
444	_SetDecor(fDecorUtility.DefaultDecorator());
445	_SetControlLook(BString(""));
446	_SetDoubleScrollBarArrows(false);
447}
448
449
450bool
451LookAndFeelSettingsView::IsRevertable()
452{
453	return fCurrentDecor != fSavedDecor
454		|| fCurrentControlLook != fSavedControlLook
455		|| _DoubleScrollBarArrows() != fSavedDoubleArrowsValue;
456}
457
458
459void
460LookAndFeelSettingsView::Revert()
461{
462	if (IsRevertable()) {
463		_SetDecor(fSavedDecor);
464		_SetControlLook(fSavedControlLook);
465		_SetDoubleScrollBarArrows(fSavedDoubleArrowsValue);
466	}
467}
468