/*
 * Copyright 2010, Stephan Aßmus <superstippi@gmx.de>.
 * Distributed under the terms of the MIT License.
 */
 
 
#include "VolumeSlider.h"
 
#include <GradientLinear.h>
 
#include <stdio.h>
#include <string.h>
 
 
#define KNOB_EMBEDDED 0
#define ROUND_KNOB 0
 
static const rgb_color kGreen = (rgb_color){ 116, 224, 0, 255 };
 
 
// constructor
VolumeSlider::VolumeSlider(const char* name, int32 minValue, int32 maxValue,
		int32 snapValue, BMessage* message)
	:
	BSlider(name, NULL, NULL, minValue, maxValue, B_HORIZONTAL,
		B_BLOCK_THUMB),
	fMuted(false),
	fSnapValue(snapValue),
	fSnapping(false)
{
	SetModificationMessage(message);
	UseFillColor(true, &kGreen);
	SetBarThickness(PreferredBarThickness());
}
 
 
VolumeSlider::~VolumeSlider()
{
}
 
 
void
VolumeSlider::MouseMoved(BPoint where, uint32 transit,
	const BMessage* dragMessage)
{
	if (!IsTracking()) {
		BSlider::MouseMoved(where, transit, dragMessage);
		return;
	}
 
	float cursorPosition = Orientation() == B_HORIZONTAL ? where.x : where.y;
 
	if (fSnapping
		&& cursorPosition >= fMinSnap && cursorPosition <= fMaxSnap) {
		// Don't move the slider, keep the current value for a few
		// more pixels
		return;
	}
 
	fSnapping = false;
 
	int32 oldValue = Value();
	int32 newValue = ValueForPoint(where);
	if (oldValue == newValue) {
		BSlider::MouseMoved(where, transit, dragMessage);
		return;
	}
 
	// Check if there is a 0 dB transition at all
	if ((oldValue < fSnapValue && newValue >= fSnapValue)
		|| (oldValue > fSnapValue && newValue <= fSnapValue)) {
		SetValue(fSnapValue);
		if (ModificationMessage() != NULL)
			Messenger().SendMessage(ModificationMessage());
 
		float snapPoint = _PointForValue(fSnapValue);
		const float kMaxSnapOffset = 6;
		if (oldValue > newValue) {
			// movement from right to left
			fMinSnap = snapPoint - kMaxSnapOffset;
			fMaxSnap = snapPoint + 1;
		} else {
			// movement from left to right
			fMinSnap = snapPoint - 1;
			fMaxSnap = snapPoint + kMaxSnapOffset;
		}
 
		fSnapping = true;
		return;
	}
 
	BSlider::MouseMoved(where, transit, dragMessage);
}
 
 
BRect
VolumeSlider::ThumbFrame() const
{
#if !ROUND_KNOB
	BRect rect = BSlider::ThumbFrame();
	rect.InsetBy(2, 2);
	rect.bottom += 1;
#else
	BRect rect(BarFrame());
#	if KNOB_EMBEDDED
	// Knob embedded in bar frame
	rect.InsetBy(0, 1);
#	else
	// Knob extends outside the bar frame
	rect.InsetBy(0, -1);
#	endif
	rect.InsetBy(rect.Height() / 2, 0);
	rect.left = rect.left + rect.Width() * Position() - rect.Height() / 2;
	rect.right = rect.left + rect.Height();
#endif
 
	return rect;
}
 
 
void
VolumeSlider::DrawThumb()
{
#if ROUND_KNOB
	// Draw a round thumb
	BRect rect(ThumbFrame());
 
	rgb_color base = ui_color(B_PANEL_BACKGROUND_COLOR);
	rgb_color frameLightColor;
	rgb_color frameShadowColor;
	rgb_color shadowColor = (rgb_color){ 0, 0, 0, 60 };
 
	float topTint = 0.49;
	float middleTint1 = 0.62;
	float middleTint2 = 0.76;
	float bottomTint = 0.90;
 
	if (!IsEnabled()) {
		topTint = (topTint + B_NO_TINT) / 2;
		middleTint1 = (middleTint1 + B_NO_TINT) / 2;
		middleTint2 = (middleTint2 + B_NO_TINT) / 2;
		bottomTint = (bottomTint + B_NO_TINT) / 2;
		shadowColor = (rgb_color){ 0, 0, 0, 30 };
	}
 
	// Draw shadow
#if !KNOB_EMBEDDED
	rect.left++;
	rect.top++;
	SetDrawingMode(B_OP_ALPHA);
	SetHighColor(shadowColor);
	FillEllipse(rect);
 
	// Draw thumb shape
	rect.OffsetBy(-1, -1);
#endif
 
	if (IsFocus()) {
		// focused
		frameLightColor = ui_color(B_KEYBOARD_NAVIGATION_COLOR);
		frameShadowColor = frameLightColor;
	} else {
		// figure out the tints to be used
		float frameLightTint;
		float frameShadowTint;
 
		if (!IsEnabled()) {
			frameLightTint = 1.30;
			frameShadowTint = 1.35;
			shadowColor.alpha = 30;
		} else {
			frameLightTint = 1.6;
			frameShadowTint = 1.65;
		}
 
		frameLightColor = tint_color(base, frameLightTint);
		frameShadowColor = tint_color(base, frameShadowTint);
	}
 
	BGradientLinear frameGradient;
	frameGradient.AddColor(frameShadowColor, 0);
	frameGradient.AddColor(frameLightColor, 255);
	frameGradient.SetStart(rect.LeftTop());
	frameGradient.SetEnd(rect.RightBottom());
 
	FillEllipse(rect, frameGradient);
	rect.InsetBy(1, 1);
 
//	frameGradient.MakeEmpty();
//	frameGradient.AddColor(borderColor, 0);
//	frameGradient.AddColor(tint_color(borderColor, 0.8), 255);
//	view->FillEllipse(rect, frameGradient);
//	rect.InsetBy(1, 1);
 
	BGradientLinear gradient;
	if (!IsEnabled()) {
		gradient.AddColor(tint_color(base, topTint), 0);
		gradient.AddColor(tint_color(base, bottomTint), 255);
	} else {
		gradient.AddColor(tint_color(base, topTint), 0);
		gradient.AddColor(tint_color(base, middleTint1), 132);
		gradient.AddColor(tint_color(base, middleTint2), 136);
		gradient.AddColor(tint_color(base, bottomTint), 255);
	}
	gradient.SetStart(rect.LeftTop());
	gradient.SetEnd(rect.LeftBottom());
	FillEllipse(rect, gradient);
#else
	BSlider::DrawThumb();
#endif
}
 
 
BSize
VolumeSlider::MinSize()
{
	BSize size = BSlider::MinSize();
	size.width *= 2;
	return size;
}
 
 
void
VolumeSlider::SetMuted(bool mute)
{
	if (mute == fMuted)
		return;
 
	fMuted = mute;
 
	rgb_color fillColor = kGreen;
	if (fMuted) {
		fillColor = tint_color(ui_color(B_PANEL_BACKGROUND_COLOR),
			B_DARKEN_2_TINT);
	}
 
	UseFillColor(true, &fillColor);
 
	Invalidate();
}
 
 
float
VolumeSlider::PreferredBarThickness() const
{
#if KNOB_EMBEDDED
	return 10.0f;
#else
	return 8.0f;
#endif
}
 
 
float
VolumeSlider::_PointForValue(int32 value) const
{
	int32 min, max;
	GetLimits(&min, &max);
 
	if (Orientation() == B_HORIZONTAL) {
		return ceilf(1.0f * (value - min) / (max - min)
			* (BarFrame().Width() - 2) + BarFrame().left + 1);
	}
 
	return ceilf(BarFrame().top - 1.0f * (value - min) / (max - min)
		* BarFrame().Height());
}
 
 

V730 Not all members of a class are initialized inside the constructor. Consider inspecting: fMinSnap, fMaxSnap.