1/*
2 * Copyright 2010, Stephan Aßmus <superstippi@gmx.de>.
3 * Distributed under the terms of the MIT License.
4 */
5
6
7#include "VolumeSlider.h"
8
9#include <GradientLinear.h>
10
11#include <stdio.h>
12#include <string.h>
13
14
15#define KNOB_EMBEDDED 0
16#define ROUND_KNOB 0
17
18static const rgb_color kGreen = (rgb_color){ 116, 224, 0, 255 };
19
20
21// constructor
22VolumeSlider::VolumeSlider(const char* name, int32 minValue, int32 maxValue,
23		int32 snapValue, BMessage* message)
24	:
25	BSlider(name, NULL, NULL, minValue, maxValue, B_HORIZONTAL,
26		B_BLOCK_THUMB),
27	fMuted(false),
28	fSnapValue(snapValue),
29	fSnapping(false)
30{
31	SetModificationMessage(message);
32	UseFillColor(true, &kGreen);
33	SetBarThickness(PreferredBarThickness());
34}
35
36
37VolumeSlider::~VolumeSlider()
38{
39}
40
41
42void
43VolumeSlider::MouseMoved(BPoint where, uint32 transit,
44	const BMessage* dragMessage)
45{
46	if (!IsTracking()) {
47		BSlider::MouseMoved(where, transit, dragMessage);
48		return;
49	}
50
51	float cursorPosition = Orientation() == B_HORIZONTAL ? where.x : where.y;
52
53	if (fSnapping
54		&& cursorPosition >= fMinSnap && cursorPosition <= fMaxSnap) {
55		// Don't move the slider, keep the current value for a few
56		// more pixels
57		return;
58	}
59
60	fSnapping = false;
61
62	int32 oldValue = Value();
63	int32 newValue = ValueForPoint(where);
64	if (oldValue == newValue) {
65		BSlider::MouseMoved(where, transit, dragMessage);
66		return;
67	}
68
69	// Check if there is a 0 dB transition at all
70	if ((oldValue < fSnapValue && newValue >= fSnapValue)
71		|| (oldValue > fSnapValue && newValue <= fSnapValue)) {
72		SetValue(fSnapValue);
73		if (ModificationMessage() != NULL)
74			Messenger().SendMessage(ModificationMessage());
75
76		float snapPoint = _PointForValue(fSnapValue);
77		const float kMaxSnapOffset = 6;
78		if (oldValue > newValue) {
79			// movement from right to left
80			fMinSnap = snapPoint - kMaxSnapOffset;
81			fMaxSnap = snapPoint + 1;
82		} else {
83			// movement from left to right
84			fMinSnap = snapPoint - 1;
85			fMaxSnap = snapPoint + kMaxSnapOffset;
86		}
87
88		fSnapping = true;
89		return;
90	}
91
92	BSlider::MouseMoved(where, transit, dragMessage);
93}
94
95
96BRect
97VolumeSlider::ThumbFrame() const
98{
99#if !ROUND_KNOB
100	BRect rect = BSlider::ThumbFrame();
101	rect.InsetBy(2, 2);
102	rect.bottom += 1;
103#else
104	BRect rect(BarFrame());
105#	if KNOB_EMBEDDED
106	// Knob embedded in bar frame
107	rect.InsetBy(0, 1);
108#	else
109	// Knob extends outside the bar frame
110	rect.InsetBy(0, -1);
111#	endif
112	rect.InsetBy(rect.Height() / 2, 0);
113	rect.left = rect.left + rect.Width() * Position() - rect.Height() / 2;
114	rect.right = rect.left + rect.Height();
115#endif
116
117	return rect;
118}
119
120
121void
122VolumeSlider::DrawThumb()
123{
124#if ROUND_KNOB
125	// Draw a round thumb
126	BRect rect(ThumbFrame());
127
128	rgb_color base = ui_color(B_PANEL_BACKGROUND_COLOR);
129	rgb_color frameLightColor;
130	rgb_color frameShadowColor;
131	rgb_color shadowColor = (rgb_color){ 0, 0, 0, 60 };
132
133	float topTint = 0.49;
134	float middleTint1 = 0.62;
135	float middleTint2 = 0.76;
136	float bottomTint = 0.90;
137
138	if (!IsEnabled()) {
139		topTint = (topTint + B_NO_TINT) / 2;
140		middleTint1 = (middleTint1 + B_NO_TINT) / 2;
141		middleTint2 = (middleTint2 + B_NO_TINT) / 2;
142		bottomTint = (bottomTint + B_NO_TINT) / 2;
143		shadowColor = (rgb_color){ 0, 0, 0, 30 };
144	}
145
146	// Draw shadow
147#if !KNOB_EMBEDDED
148	rect.left++;
149	rect.top++;
150	SetDrawingMode(B_OP_ALPHA);
151	SetHighColor(shadowColor);
152	FillEllipse(rect);
153
154	// Draw thumb shape
155	rect.OffsetBy(-1, -1);
156#endif
157
158	if (IsFocus()) {
159		// focused
160		frameLightColor = ui_color(B_KEYBOARD_NAVIGATION_COLOR);
161		frameShadowColor = frameLightColor;
162	} else {
163		// figure out the tints to be used
164		float frameLightTint;
165		float frameShadowTint;
166
167		if (!IsEnabled()) {
168			frameLightTint = 1.30;
169			frameShadowTint = 1.35;
170			shadowColor.alpha = 30;
171		} else {
172			frameLightTint = 1.6;
173			frameShadowTint = 1.65;
174		}
175
176		frameLightColor = tint_color(base, frameLightTint);
177		frameShadowColor = tint_color(base, frameShadowTint);
178	}
179
180	BGradientLinear frameGradient;
181	frameGradient.AddColor(frameShadowColor, 0);
182	frameGradient.AddColor(frameLightColor, 255);
183	frameGradient.SetStart(rect.LeftTop());
184	frameGradient.SetEnd(rect.RightBottom());
185
186	FillEllipse(rect, frameGradient);
187	rect.InsetBy(1, 1);
188
189//	frameGradient.MakeEmpty();
190//	frameGradient.AddColor(borderColor, 0);
191//	frameGradient.AddColor(tint_color(borderColor, 0.8), 255);
192//	view->FillEllipse(rect, frameGradient);
193//	rect.InsetBy(1, 1);
194
195	BGradientLinear gradient;
196	if (!IsEnabled()) {
197		gradient.AddColor(tint_color(base, topTint), 0);
198		gradient.AddColor(tint_color(base, bottomTint), 255);
199	} else {
200		gradient.AddColor(tint_color(base, topTint), 0);
201		gradient.AddColor(tint_color(base, middleTint1), 132);
202		gradient.AddColor(tint_color(base, middleTint2), 136);
203		gradient.AddColor(tint_color(base, bottomTint), 255);
204	}
205	gradient.SetStart(rect.LeftTop());
206	gradient.SetEnd(rect.LeftBottom());
207	FillEllipse(rect, gradient);
208#else
209	BSlider::DrawThumb();
210#endif
211}
212
213
214BSize
215VolumeSlider::MinSize()
216{
217	BSize size = BSlider::MinSize();
218	size.width *= 2;
219	return size;
220}
221
222
223void
224VolumeSlider::SetMuted(bool mute)
225{
226	if (mute == fMuted)
227		return;
228
229	fMuted = mute;
230
231	rgb_color fillColor = kGreen;
232	if (fMuted) {
233		fillColor = tint_color(ui_color(B_PANEL_BACKGROUND_COLOR),
234			B_DARKEN_2_TINT);
235	}
236
237	UseFillColor(true, &fillColor);
238
239	Invalidate();
240}
241
242
243float
244VolumeSlider::PreferredBarThickness() const
245{
246#if KNOB_EMBEDDED
247	return 10.0f;
248#else
249	return 8.0f;
250#endif
251}
252
253
254float
255VolumeSlider::_PointForValue(int32 value) const
256{
257	int32 min, max;
258	GetLimits(&min, &max);
259
260	if (Orientation() == B_HORIZONTAL) {
261		return ceilf(1.0f * (value - min) / (max - min)
262			* (BarFrame().Width() - 2) + BarFrame().left + 1);
263	}
264
265	return ceilf(BarFrame().top - 1.0f * (value - min) / (max - min)
266		* BarFrame().Height());
267}
268
269
270