root/src/apps/mediaplayer/interface/VolumeSlider.cpp
/*
 * 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());
}