xref: /haiku/src/preferences/appearance/LookAndFeelSettingsView.cpp (revision 46b7da1f4f40f7157d74fc7fb26ff9ec7f2416f2)
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 
LookAndFeelSettingsView(const char * name)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()->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 
~LookAndFeelSettingsView()158 LookAndFeelSettingsView::~LookAndFeelSettingsView()
159 {
160 }
161 
162 
163 void
AttachedToWindow()164 LookAndFeelSettingsView::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 
185 void
MessageReceived(BMessage * message)186 LookAndFeelSettingsView::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 
283 void
_SetDecor(const BString & name)284 LookAndFeelSettingsView::_SetDecor(const BString& name)
285 {
286 	_SetDecor(fDecorUtility.FindDecorator(name));
287 }
288 
289 
290 void
_SetDecor(DecorInfo * decorInfo)291 LookAndFeelSettingsView::_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 
302 void
_BuildDecorMenu()303 LookAndFeelSettingsView::_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 
328 const char*
_DecorLabel(const BString & name)329 LookAndFeelSettingsView::_DecorLabel(const BString& name)
330 {
331 	BString label(name);
332 	return label.RemoveLast("Decorator").Trim().String();
333 }
334 
335 
336 void
_SetControlLook(const BString & path)337 LookAndFeelSettingsView::_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 
353 void
_BuildControlLookMenu()354 LookAndFeelSettingsView::_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 
393 const char*
_ControlLookLabel(const char * name)394 LookAndFeelSettingsView::_ControlLookLabel(const char* name)
395 {
396 	BString label(name);
397 	return label.RemoveLast("ControlLook").Trim().String();
398 }
399 
400 
401 bool
_DoubleScrollBarArrows()402 LookAndFeelSettingsView::_DoubleScrollBarArrows()
403 {
404 	scroll_bar_info info;
405 	get_scroll_bar_info(&info);
406 
407 	return info.double_arrows;
408 }
409 
410 
411 void
_SetDoubleScrollBarArrows(bool doubleArrows)412 LookAndFeelSettingsView::_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 
432 bool
IsDefaultable()433 LookAndFeelSettingsView::IsDefaultable()
434 {
435 	return fCurrentDecor != fDecorUtility.DefaultDecorator()->ShortcutName()
436 		|| fCurrentControlLook.Length() != 0
437 		|| _DoubleScrollBarArrows() != false;
438 }
439 
440 
441 void
SetDefaults()442 LookAndFeelSettingsView::SetDefaults()
443 {
444 	_SetDecor(fDecorUtility.DefaultDecorator());
445 	_SetControlLook(BString(""));
446 	_SetDoubleScrollBarArrows(false);
447 }
448 
449 
450 bool
IsRevertable()451 LookAndFeelSettingsView::IsRevertable()
452 {
453 	return fCurrentDecor != fSavedDecor
454 		|| fCurrentControlLook != fSavedControlLook
455 		|| _DoubleScrollBarArrows() != fSavedDoubleArrowsValue;
456 }
457 
458 
459 void
Revert()460 LookAndFeelSettingsView::Revert()
461 {
462 	if (IsRevertable()) {
463 		_SetDecor(fSavedDecor);
464 		_SetControlLook(fSavedControlLook);
465 		_SetDoubleScrollBarArrows(fSavedDoubleArrowsValue);
466 	}
467 }
468