xref: /haiku/src/preferences/appearance/LookAndFeelSettingsView.cpp (revision 02354704729d38c3b078c696adc1bbbd33cbcf72)
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 
52 static const int32 kMsgSetDecor = 'deco';
53 static const int32 kMsgDecorInfo = 'idec';
54 
55 static const int32 kMsgSetControlLook = 'ctlk';
56 static const int32 kMsgControlLookInfo = 'iclk';
57 
58 static const int32 kMsgDoubleScrollBarArrows = 'dsba';
59 
60 static const int32 kMsgArrowStyleSingle = 'mass';
61 static const int32 kMsgArrowStyleDouble = 'masd';
62 
63 static const bool kDefaultDoubleScrollBarArrowsSetting = false;
64 
65 
66 //	#pragma mark - LookAndFeelSettingsView
67 
68 
69 LookAndFeelSettingsView::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()->Name();
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(B_TRANSLATE("No effect on running applications"));
105 
106 	fControlLookInfoButton = new BButton(B_TRANSLATE("About"),
107 		new BMessage(kMsgControlLookInfo));
108 
109 	// scroll bar arrow style
110 	BBox* arrowStyleBox = new BBox("arrow style");
111 	arrowStyleBox->SetLabel(B_TRANSLATE("Arrow style"));
112 
113 	fArrowStyleSingle = new FakeScrollBar(true, false,
114 		new BMessage(kMsgArrowStyleSingle));
115 	fArrowStyleDouble = new FakeScrollBar(true, true,
116 		new BMessage(kMsgArrowStyleDouble));
117 
118 	BView* arrowStyleView;
119 	arrowStyleView = BLayoutBuilder::Group<>()
120 		.AddGroup(B_VERTICAL, 1)
121 			.Add(new BStringView("single", B_TRANSLATE("Single:")))
122 			.Add(fArrowStyleSingle)
123 			.AddStrut(B_USE_DEFAULT_SPACING)
124 			.Add(new BStringView("double", B_TRANSLATE("Double:")))
125 			.Add(fArrowStyleDouble)
126 			.SetInsets(B_USE_DEFAULT_SPACING, B_USE_DEFAULT_SPACING,
127 				B_USE_DEFAULT_SPACING, B_USE_DEFAULT_SPACING)
128 			.End()
129 		.View();
130 	arrowStyleBox->AddChild(arrowStyleView);
131 	arrowStyleBox->SetExplicitAlignment(BAlignment(B_ALIGN_LEFT,
132 		B_ALIGN_VERTICAL_CENTER));
133 	arrowStyleBox->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET));
134 
135 	BStringView* scrollBarLabel
136 		= new BStringView("scroll bar", B_TRANSLATE("Scroll bar:"));
137 	scrollBarLabel->SetExplicitAlignment(
138 		BAlignment(B_ALIGN_LEFT, B_ALIGN_TOP));
139 
140 	// control layout
141 	BLayoutBuilder::Grid<>(this, B_USE_DEFAULT_SPACING, B_USE_DEFAULT_SPACING)
142 		.Add(fDecorMenuField->CreateLabelLayoutItem(), 0, 0)
143 		.Add(fDecorMenuField->CreateMenuBarLayoutItem(), 1, 0)
144 		.Add(fDecorInfoButton, 2, 0)
145 		.Add(fControlLookMenuField->CreateLabelLayoutItem(), 0, 1)
146 		.Add(fControlLookMenuField->CreateMenuBarLayoutItem(), 1, 1)
147 		.Add(fControlLookInfoButton, 2, 1)
148 		.Add(scrollBarLabel, 0, 2)
149 		.Add(arrowStyleBox, 1, 2)
150 		.AddGlue(0, 3)
151 		.SetInsets(B_USE_WINDOW_SPACING);
152 
153 	// TODO : Decorator Preview Image?
154 }
155 
156 
157 LookAndFeelSettingsView::~LookAndFeelSettingsView()
158 {
159 }
160 
161 
162 void
163 LookAndFeelSettingsView::AttachedToWindow()
164 {
165 	AdoptParentColors();
166 
167 	if (Parent() == NULL)
168 		SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
169 
170 	fDecorMenu->SetTargetForItems(this);
171 	fDecorInfoButton->SetTarget(this);
172 	fControlLookMenu->SetTargetForItems(this);
173 	fControlLookInfoButton->SetTarget(this);
174 	fArrowStyleSingle->SetTarget(this);
175 	fArrowStyleDouble->SetTarget(this);
176 
177 	if (fSavedDoubleArrowsValue)
178 		fArrowStyleDouble->SetValue(B_CONTROL_ON);
179 	else
180 		fArrowStyleSingle->SetValue(B_CONTROL_ON);
181 }
182 
183 
184 void
185 LookAndFeelSettingsView::MessageReceived(BMessage* message)
186 {
187 	switch (message->what) {
188 		case kMsgSetDecor:
189 		{
190 			BString newDecor;
191 			if (message->FindString("decor", &newDecor) == B_OK)
192 				_SetDecor(newDecor);
193 			break;
194 		}
195 
196 		case kMsgDecorInfo:
197 		{
198 			DecorInfo* decor = fDecorUtility.FindDecorator(fCurrentDecor);
199 			if (decor == NULL)
200 				break;
201 
202 			BString authorsText(decor->Authors().String());
203 			authorsText.ReplaceAll(", ", "\n\t");
204 
205 			BString infoText(B_TRANSLATE("%decorName\n\n"
206 				"Authors:\n\t%decorAuthors\n\n"
207 				"URL: %decorURL\n"
208 				"License: %decorLic\n\n"
209 				"%decorDesc\n"));
210 
211 			infoText.ReplaceFirst("%decorName", decor->Name().String());
212 			infoText.ReplaceFirst("%decorAuthors", authorsText.String());
213 			infoText.ReplaceFirst("%decorLic", decor->LicenseName().String());
214 			infoText.ReplaceFirst("%decorURL", decor->SupportURL().String());
215 			infoText.ReplaceFirst("%decorDesc",
216 				decor->ShortDescription().String());
217 
218 			BAlert* infoAlert = new BAlert(B_TRANSLATE("About decorator"),
219 				infoText.String(), B_TRANSLATE("OK"));
220 			infoAlert->SetFlags(infoAlert->Flags() | B_CLOSE_ON_ESCAPE);
221 			infoAlert->Go();
222 
223 			break;
224 		}
225 
226 		case kMsgSetControlLook:
227 		{
228 			BString controlLook;
229 			if (message->FindString("control_look", &controlLook) == B_OK)
230 				_SetControlLook(controlLook);
231 			break;
232 		}
233 
234 		case kMsgControlLookInfo:
235 		{
236 			BString infoText(B_TRANSLATE("Default Haiku ControlLook"));
237 			BString path;
238 			if (!BPrivate::get_control_look(path))
239 				break;
240 
241 			if (path.Length() > 0) {
242 				BFile file(path.String(), B_READ_ONLY);
243 				if (file.InitCheck() != B_OK)
244 					break;
245 
246 				BAppFileInfo info(&file);
247 				struct version_info version;
248 				if (info.InitCheck() != B_OK
249 					|| info.GetVersionInfo(&version, B_APP_VERSION_KIND) != B_OK)
250 					break;
251 
252 				infoText = version.short_info;
253 				infoText << "\n" << version.long_info;
254 			}
255 
256 			BAlert* infoAlert = new BAlert(B_TRANSLATE("About control look"),
257 				infoText.String(), B_TRANSLATE("OK"));
258 			infoAlert->SetFlags(infoAlert->Flags() | B_CLOSE_ON_ESCAPE);
259 			infoAlert->Go();
260 
261 			break;
262 		}
263 
264 		case kMsgArrowStyleSingle:
265 			_SetDoubleScrollBarArrows(false);
266 			break;
267 
268 		case kMsgArrowStyleDouble:
269 			_SetDoubleScrollBarArrows(true);
270 			break;
271 
272 		default:
273 			BView::MessageReceived(message);
274 			break;
275 	}
276 }
277 
278 
279 //	#pragma mark - LookAndFeelSettingsView private methods
280 
281 
282 void
283 LookAndFeelSettingsView::_SetDecor(const BString& name)
284 {
285 	_SetDecor(fDecorUtility.FindDecorator(name));
286 }
287 
288 
289 void
290 LookAndFeelSettingsView::_SetDecor(DecorInfo* decorInfo)
291 {
292 	if (fDecorUtility.SetDecorator(decorInfo) == B_OK) {
293 		fCurrentDecor = fDecorUtility.CurrentDecorator()->Name();
294 		fDecorMenu->FindItem(_DecorLabel(fCurrentDecor))->SetMarked(true);
295 		Window()->PostMessage(kMsgUpdate);
296 	}
297 }
298 
299 
300 void
301 LookAndFeelSettingsView::_BuildDecorMenu()
302 {
303 	fDecorMenu = new BPopUpMenu(B_TRANSLATE("Choose Decorator"));
304 
305 	// collect the current system decor settings
306 	int32 count = fDecorUtility.CountDecorators();
307 	for (int32 i = 0; i < count; ++i) {
308 		DecorInfo* decorator = fDecorUtility.DecoratorAt(i);
309 		if (decorator == NULL) {
310 			fprintf(stderr, "Decorator : error NULL entry @ %" B_PRId32
311 				" / %" B_PRId32 "\n", i, count);
312 			continue;
313 		}
314 
315 		BString decorName = decorator->Name();
316 		BMessage* message = new BMessage(kMsgSetDecor);
317 		message->AddString("decor", decorName);
318 
319 		BMenuItem* item = new BMenuItem(_DecorLabel(decorName), message);
320 		fDecorMenu->AddItem(item);
321 		if (decorName == fCurrentDecor)
322 			item->SetMarked(true);
323 	}
324 }
325 
326 
327 const char*
328 LookAndFeelSettingsView::_DecorLabel(const BString& name)
329 {
330 	BString label(name);
331 	return label.RemoveLast("Decorator").Trim().String();
332 }
333 
334 
335 void
336 LookAndFeelSettingsView::_SetControlLook(const BString& path)
337 {
338 	fCurrentControlLook = path;
339 	BPrivate::set_control_look(path);
340 
341 	if (path.Length() > 0) {
342 		BEntry entry(path.String());
343 		const char* label = _ControlLookLabel(entry.Name());
344 		fControlLookMenu->FindItem(label)->SetMarked(true);
345 	} else
346 		fControlLookMenu->FindItem(B_TRANSLATE("Default"))->SetMarked(true);
347 
348 	Window()->PostMessage(kMsgUpdate);
349 }
350 
351 
352 void
353 LookAndFeelSettingsView::_BuildControlLookMenu()
354 {
355 	BPathFinder pathFinder;
356 	BStringList paths;
357 	BDirectory dir;
358 
359 	fControlLookMenu = new BPopUpMenu(B_TRANSLATE("Choose ControlLook"));
360 
361 	BMessage* message = new BMessage(kMsgSetControlLook);
362 	message->AddString("control_look", "");
363 
364 	BMenuItem* item = new BMenuItem(B_TRANSLATE("Default"), message);
365 	if (fCurrentControlLook.Length() == 0)
366 		item->SetMarked(true);
367 	fControlLookMenu->AddItem(item);
368 
369 	status_t error = pathFinder.FindPaths(B_FIND_PATH_ADD_ONS_DIRECTORY,
370 		"control_look", paths);
371 
372 	int32 count = paths.CountStrings();
373 	for (int32 i = 0; i < count; ++i) {
374 		if (error != B_OK || dir.SetTo(paths.StringAt(i)) != B_OK)
375 			continue;
376 
377 		BEntry entry;
378 		while (dir.GetNextEntry(&entry) == B_OK) {
379 			BPath path(paths.StringAt(i), entry.Name());
380 			message = new BMessage(kMsgSetControlLook);
381 			message->AddString("control_look", path.Path());
382 
383 			item = new BMenuItem(_ControlLookLabel(entry.Name()), message);
384 			fControlLookMenu->AddItem(item);
385 			if (BString(path.Path()) == fCurrentControlLook)
386 				item->SetMarked(true);
387 		}
388 	}
389 }
390 
391 
392 const char*
393 LookAndFeelSettingsView::_ControlLookLabel(const char* name)
394 {
395 	BString label(name);
396 	return label.RemoveLast("ControlLook").Trim().String();
397 }
398 
399 
400 bool
401 LookAndFeelSettingsView::_DoubleScrollBarArrows()
402 {
403 	scroll_bar_info info;
404 	get_scroll_bar_info(&info);
405 
406 	return info.double_arrows;
407 }
408 
409 
410 void
411 LookAndFeelSettingsView::_SetDoubleScrollBarArrows(bool doubleArrows)
412 {
413 	scroll_bar_info info;
414 	get_scroll_bar_info(&info);
415 
416 	if (info.double_arrows == doubleArrows)
417 		return;
418 
419 	info.double_arrows = doubleArrows;
420 	set_scroll_bar_info(&info);
421 
422 	if (doubleArrows)
423 		fArrowStyleDouble->SetValue(B_CONTROL_ON);
424 	else
425 		fArrowStyleSingle->SetValue(B_CONTROL_ON);
426 
427 	Window()->PostMessage(kMsgUpdate);
428 }
429 
430 
431 bool
432 LookAndFeelSettingsView::IsDefaultable()
433 {
434 	return fCurrentDecor != fDecorUtility.DefaultDecorator()->Name()
435 		|| fCurrentControlLook.Length() != 0
436 		|| _DoubleScrollBarArrows() != false;
437 }
438 
439 
440 void
441 LookAndFeelSettingsView::SetDefaults()
442 {
443 	_SetDecor(fDecorUtility.DefaultDecorator());
444 	_SetControlLook(BString(""));
445 	_SetDoubleScrollBarArrows(false);
446 }
447 
448 
449 bool
450 LookAndFeelSettingsView::IsRevertable()
451 {
452 	return fCurrentDecor != fSavedDecor
453 		|| fCurrentControlLook != fSavedControlLook
454 		|| _DoubleScrollBarArrows() != fSavedDoubleArrowsValue;
455 }
456 
457 
458 void
459 LookAndFeelSettingsView::Revert()
460 {
461 	if (IsRevertable()) {
462 		_SetDecor(fSavedDecor);
463 		_SetControlLook(fSavedControlLook);
464 		_SetDoubleScrollBarArrows(fSavedDoubleArrowsValue);
465 	}
466 }
467