1/*
2 * Copyright 2002-2015 Haiku, Inc. All rights reserved.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 *		DarkWyrm, darkwyrm@earthlink.net
7 *		Rene Gollent, rene@gollent.com
8 *		John Scipione, jscipione@gmail.com
9 *		Joseph Groover <looncraz@looncraz.net>
10 */
11
12
13#include "APRView.h"
14
15#include <stdio.h>
16
17#include <Alert.h>
18#include <Catalog.h>
19#include <DefaultColors.h>
20#include <Directory.h>
21#include <Entry.h>
22#include <File.h>
23#include <HSL.h>
24#include <LayoutBuilder.h>
25#include <Locale.h>
26#include <Messenger.h>
27#include <Path.h>
28#include <SpaceLayoutItem.h>
29
30#include "APRWindow.h"
31#include "defs.h"
32#include "ColorPreview.h"
33#include "Colors.h"
34#include "ColorWhichListView.h"
35#include "ColorWhichItem.h"
36
37
38#undef B_TRANSLATION_CONTEXT
39#define B_TRANSLATION_CONTEXT "Colors tab"
40
41#define COLOR_DROPPED 'cldp'
42#define AUTO_ADJUST_CHANGED 'madj'
43
44
45APRView::APRView(const char* name)
46	:
47	BView(name, B_WILL_DRAW)
48{
49	SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
50
51	LoadSettings();
52
53	fAutoSelectCheckBox = new BCheckBox(B_TRANSLATE("Automatically pick secondary colors"),
54		new BMessage(AUTO_ADJUST_CHANGED));
55	fAutoSelectCheckBox->SetValue(true);
56
57	// Set up list of color attributes
58	fAttrList = new ColorWhichListView("AttributeList");
59
60	fScrollView = new BScrollView("ScrollView", fAttrList, 0, false, true);
61	fScrollView->SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
62
63	_CreateItems();
64
65	fColorPreview = new ColorPreview(new BMessage(COLOR_DROPPED), 0);
66	fColorPreview->SetExplicitAlignment(BAlignment(B_ALIGN_HORIZONTAL_CENTER,
67		B_ALIGN_VERTICAL_CENTER));
68
69	fPicker = new BColorControl(B_ORIGIN, B_CELLS_32x8, 8.0,
70		"picker", new BMessage(UPDATE_COLOR));
71
72	BLayoutBuilder::Group<>(this, B_VERTICAL)
73		.Add(fAutoSelectCheckBox)
74		.Add(fScrollView, 10.0)
75		.AddGroup(B_HORIZONTAL, B_USE_DEFAULT_SPACING)
76			.Add(fColorPreview)
77			.AddGlue()
78			.Add(fPicker)
79			.End()
80		.SetInsets(B_USE_WINDOW_SPACING);
81
82	fColorPreview->Parent()->SetExplicitMaxSize(
83		BSize(B_SIZE_UNSET, fPicker->Bounds().Height()));
84	fAttrList->SetSelectionMessage(new BMessage(ATTRIBUTE_CHOSEN));
85}
86
87
88APRView::~APRView()
89{
90}
91
92
93void
94APRView::AttachedToWindow()
95{
96	fAutoSelectCheckBox->SetTarget(this);
97	fPicker->SetTarget(this);
98	fAttrList->SetTarget(this);
99	fColorPreview->SetTarget(this);
100
101	fAttrList->Select(0);
102	SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
103}
104
105
106void
107APRView::MessageReceived(BMessage *msg)
108{
109	switch (msg->what) {
110		case SET_COLOR:
111		{
112			rgb_color* color;
113			ssize_t size;
114			color_which which;
115
116			if (msg->FindData(kRGBColor, B_RGB_COLOR_TYPE,
117					(const void**)&color, &size) == B_OK
118				&& msg->FindUInt32(kWhich, (uint32*)&which) == B_OK) {
119				_SetColor(which, *color);
120				Window()->PostMessage(kMsgUpdate);
121			}
122			break;
123		}
124
125		case SET_CURRENT_COLOR:
126		{
127			rgb_color* color;
128			ssize_t size;
129
130			if (msg->FindData(kRGBColor, B_RGB_COLOR_TYPE,
131					(const void**)&color, &size) == B_OK) {
132				_SetCurrentColor(*color);
133				Window()->PostMessage(kMsgUpdate);
134			}
135			break;
136		}
137
138		case UPDATE_COLOR:
139		{
140			// Received from the color fPicker when its color changes
141			rgb_color color = fPicker->ValueAsColor();
142			_SetCurrentColor(color);
143
144			Window()->PostMessage(kMsgUpdate);
145			break;
146		}
147
148		case ATTRIBUTE_CHOSEN:
149		{
150			// Received when the user chooses a GUI fAttribute from the list
151
152			ColorWhichItem* item = (ColorWhichItem*)
153				fAttrList->ItemAt(fAttrList->CurrentSelection());
154			if (item == NULL)
155				break;
156
157			fWhich = item->ColorWhich();
158			rgb_color color = ui_color(fWhich);
159			_SetCurrentColor(color);
160			break;
161		}
162
163		case AUTO_ADJUST_CHANGED:
164		{
165			_CreateItems();
166			break;
167		}
168
169		default:
170			BView::MessageReceived(msg);
171			break;
172	}
173}
174
175
176void
177APRView::LoadSettings()
178{
179	get_default_colors(&fDefaultColors);
180	get_current_colors(&fCurrentColors);
181	fPrevColors = fCurrentColors;
182}
183
184
185void
186APRView::SetDefaults()
187{
188	_SetUIColors(fDefaultColors);
189	_UpdatePreviews(fDefaultColors);
190
191	// Use a default color that stands out to show errors clearly
192	rgb_color color = fDefaultColors.GetColor(ui_color_name(fWhich),
193		make_color(255, 0, 255));
194
195	fPicker->SetValue(color);
196	fColorPreview->SetColor(color);
197	fColorPreview->Invalidate();
198
199	Window()->PostMessage(kMsgUpdate);
200}
201
202
203void
204APRView::Revert()
205{
206	_SetUIColors(fPrevColors);
207	_UpdatePreviews(fPrevColors);
208
209	rgb_color color = fPrevColors.GetColor(ui_color_name(fWhich),
210		make_color(255, 0, 255));
211	fPicker->SetValue(color);
212	fColorPreview->SetColor(color);
213	fColorPreview->Invalidate();
214
215	Window()->PostMessage(kMsgUpdate);
216}
217
218
219bool
220APRView::IsDefaultable()
221{
222	return !fDefaultColors.HasSameData(fCurrentColors);
223}
224
225
226bool
227APRView::IsRevertable()
228{
229	return !fPrevColors.HasSameData(fCurrentColors);
230}
231
232
233void
234APRView::_CreateItems()
235{
236	while (fAttrList->CountItems() > 0)
237		delete fAttrList->RemoveItem((int32)0);
238
239	const bool autoSelect = fAutoSelectCheckBox->Value();
240	const int32 count = color_description_count();
241	for (int32 i = 0; i < count; i++) {
242		const ColorDescription& description = *get_color_description(i);
243		const color_which which = description.which;
244		if (autoSelect) {
245			if (which != B_PANEL_BACKGROUND_COLOR
246					&& which != B_STATUS_BAR_COLOR
247					&& which != B_WINDOW_TAB_COLOR) {
248				continue;
249			}
250		}
251
252		const char* text = B_TRANSLATE_NOCOLLECT(description.text);
253		fAttrList->AddItem(new ColorWhichItem(text, which, ui_color(which)));
254	}
255
256	fAttrList->Select(0);
257}
258
259
260void
261APRView::_UpdatePreviews(const BMessage& colors)
262{
263	rgb_color color;
264	for (int32 i = color_description_count() - 1; i >= 0; i--) {
265		ColorWhichItem* item = static_cast<ColorWhichItem*>(fAttrList->ItemAt(i));
266		if (item == NULL)
267			continue;
268
269		color = colors.GetColor(ui_color_name(item->ColorWhich()),
270			make_color(255, 0, 255));
271
272		item->SetColor(color);
273		fAttrList->InvalidateItem(i);
274	}
275}
276
277
278void
279APRView::_SetUIColors(const BMessage& colors)
280{
281	set_ui_colors(&colors);
282	fCurrentColors = colors;
283}
284
285
286void
287APRView::_SetCurrentColor(rgb_color color)
288{
289	_SetColor(fWhich, color);
290
291	int32 currentIndex = fAttrList->CurrentSelection();
292	ColorWhichItem* item = (ColorWhichItem*)fAttrList->ItemAt(currentIndex);
293	if (item != NULL) {
294		item->SetColor(color);
295		fAttrList->InvalidateItem(currentIndex);
296	}
297
298	fPicker->SetValue(color);
299	fColorPreview->SetColor(color);
300	fColorPreview->Invalidate();
301}
302
303
304void
305APRView::_SetColor(color_which which, rgb_color color)
306{
307	_SetOneColor(which, color);
308
309	if (!fAutoSelectCheckBox->Value())
310		return;
311
312	// Protect against accidentally overwriting colors.
313	if (ui_color(which) == color)
314		return;
315
316	if (which == B_PANEL_BACKGROUND_COLOR) {
317		const bool isDark = color.IsDark();
318
319		_SetOneColor(B_MENU_BACKGROUND_COLOR, color);
320		_SetOneColor(B_SCROLL_BAR_THUMB_COLOR, color);
321
322		const rgb_color menuSelectedBackground
323			= tint_color(color, isDark ? B_LIGHTEN_2_TINT : B_DARKEN_2_TINT);
324		_SetOneColor(B_MENU_SELECTED_BACKGROUND_COLOR, menuSelectedBackground);
325
326		const rgb_color controlBackground = tint_color(color, 0.25 /* lighten "> 2" */);
327		_SetOneColor(B_CONTROL_BACKGROUND_COLOR, controlBackground);
328
329		const rgb_color controlBorder
330			= tint_color(color, isDark ? 0.4875 : 1.20 /* lighten/darken "1.5" */);
331		_SetOneColor(B_CONTROL_BORDER_COLOR, controlBorder);
332
333		const rgb_color windowBorder = tint_color(color, 0.75);
334		_SetOneColor(B_WINDOW_BORDER_COLOR, windowBorder);
335
336		const rgb_color inactiveWindowBorder = tint_color(color, B_LIGHTEN_1_TINT);
337		_SetOneColor(B_WINDOW_INACTIVE_TAB_COLOR, inactiveWindowBorder);
338		_SetOneColor(B_WINDOW_INACTIVE_BORDER_COLOR, inactiveWindowBorder);
339
340		const rgb_color listSelectedBackground
341			= tint_color(color, isDark ? 0.77 : 1.12 /* lighten/darken "< 1" */ );
342		_SetOneColor(B_LIST_SELECTED_BACKGROUND_COLOR, listSelectedBackground);
343
344		const color_which fromDefaults[] = {
345			B_MENU_ITEM_TEXT_COLOR,
346			B_MENU_SELECTED_ITEM_TEXT_COLOR,
347			B_MENU_SELECTED_BORDER_COLOR,
348			B_PANEL_TEXT_COLOR,
349			B_DOCUMENT_BACKGROUND_COLOR,
350			B_DOCUMENT_TEXT_COLOR,
351			B_CONTROL_TEXT_COLOR,
352			B_NAVIGATION_PULSE_COLOR,
353			B_WINDOW_INACTIVE_TEXT_COLOR,
354			B_LIST_BACKGROUND_COLOR,
355			B_LIST_ITEM_TEXT_COLOR,
356			B_LIST_SELECTED_ITEM_TEXT_COLOR,
357
358			B_SHINE_COLOR,
359			B_SHADOW_COLOR,
360
361			B_LINK_TEXT_COLOR,
362			B_LINK_HOVER_COLOR,
363			B_LINK_ACTIVE_COLOR,
364			B_LINK_VISITED_COLOR,
365		};
366		for (size_t i = 0; i < B_COUNT_OF(fromDefaults); i++)
367			_SetOneColor(fromDefaults[i], BPrivate::GetSystemColor(fromDefaults[i], isDark));
368	} else if (which == B_STATUS_BAR_COLOR) {
369		const hsl_color statusColorHSL = hsl_color::from_rgb(color);
370
371		hsl_color controlHighlight = statusColorHSL;
372		controlHighlight.saturation = max_c(0.2f, controlHighlight.saturation / 2.f);
373		_SetOneColor(B_CONTROL_HIGHLIGHT_COLOR, controlHighlight.to_rgb());
374
375		hsl_color controlMark = statusColorHSL;
376		controlMark.saturation = max_c(0.2f, controlMark.saturation * 0.67f);
377		controlMark.lightness = max_c(0.25f, controlMark.lightness * 0.55f);
378		_SetOneColor(B_CONTROL_MARK_COLOR, controlMark.to_rgb());
379
380		rgb_color keyboardNav; {
381			hsl_color keyboardNavHSL = statusColorHSL;
382			keyboardNavHSL.lightness = max_c(0.2f, keyboardNavHSL.lightness * 0.75f);
383			keyboardNav = keyboardNavHSL.to_rgb();
384
385			// Use primary color channel only.
386			if (keyboardNav.blue >= max_c(keyboardNav.red, keyboardNav.green))
387				keyboardNav.red = keyboardNav.green = 0;
388			else if (keyboardNav.red >= max_c(keyboardNav.green, keyboardNav.blue))
389				keyboardNav.green = keyboardNav.blue = 0;
390			else
391				keyboardNav.red = keyboardNav.blue = 0;
392		}
393		_SetOneColor(B_KEYBOARD_NAVIGATION_COLOR, keyboardNav);
394	} else if (which == B_WINDOW_TAB_COLOR) {
395		const bool isDark = color.IsDark();
396		const hsl_color tabColorHSL = hsl_color::from_rgb(color);
397		const float tabColorSaturation
398			= tabColorHSL.saturation != 0 ? tabColorHSL.saturation : tabColorHSL.lightness;
399
400		_SetOneColor(B_WINDOW_TEXT_COLOR,
401			BPrivate::GetSystemColor(B_WINDOW_TEXT_COLOR, isDark));
402		_SetOneColor(B_TOOL_TIP_TEXT_COLOR,
403			BPrivate::GetSystemColor(B_TOOL_TIP_TEXT_COLOR, isDark));
404
405		const rgb_color toolTipBackground = tint_color(color, isDark ? 1.7 : 0.15);
406		_SetOneColor(B_TOOL_TIP_BACKGROUND_COLOR, toolTipBackground);
407
408		hsl_color success = hsl_color::from_rgb(BPrivate::GetSystemColor(B_SUCCESS_COLOR, isDark));
409		success.saturation = max_c(0.25f, tabColorSaturation * 0.68f);
410		_SetOneColor(B_SUCCESS_COLOR, success.to_rgb());
411
412		hsl_color failure = hsl_color::from_rgb(BPrivate::GetSystemColor(B_FAILURE_COLOR, isDark));
413		failure.saturation = max_c(0.25f, tabColorSaturation);
414		_SetOneColor(B_FAILURE_COLOR, failure.to_rgb());
415	}
416}
417
418
419void
420APRView::_SetOneColor(color_which which, rgb_color color)
421{
422	if (ui_color(which) == color)
423		return;
424
425	set_ui_color(which, color);
426	fCurrentColors.SetColor(ui_color_name(which), color);
427}
428