/** 
 * @file llmultisldr.cpp
 * @brief LLMultiSlider base class
 *
 * $LicenseInfo:firstyear=2007&license=viewerlgpl$
 * Second Life Viewer Source Code
 * Copyright (C) 2010, Linden Research, Inc.
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation;
 * version 2.1 of the License only.
 * 
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 * 
 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
 * $/LicenseInfo$
 */

#include "linden_common.h"

#include "llmultislider.h"
#include "llui.h"

#include "llgl.h"
#include "llwindow.h"
#include "llfocusmgr.h"
#include "llkeyboard.h"			// for the MASK constants
#include "llcontrol.h"
#include "lluictrlfactory.h"
#include "lluiimage.h"

#include <sstream>

static LLDefaultChildRegistry::Register<LLMultiSlider> r("multi_slider_bar");

const F32 FLOAT_THRESHOLD = 0.00001f;

S32 LLMultiSlider::mNameCounter = 0;

LLMultiSlider::SliderParams::SliderParams()
:	name("name"),
	value("value", 0.f)
{
}

LLMultiSlider::Params::Params()
:	max_sliders("max_sliders", 1),
	allow_overlap("allow_overlap", false),
	loop_overlap("loop_overlap", false),
	orientation("orientation"),
	overlap_threshold("overlap_threshold", 0),
	draw_track("draw_track", true),
	use_triangle("use_triangle", false),
	track_color("track_color"),
	thumb_disabled_color("thumb_disabled_color"),
	thumb_highlight_color("thumb_highlight_color"),
	thumb_outline_color("thumb_outline_color"),
	thumb_center_color("thumb_center_color"),
	thumb_center_selected_color("thumb_center_selected_color"),
	thumb_image("thumb_image"),
	triangle_color("triangle_color"),
	mouse_down_callback("mouse_down_callback"),
	mouse_up_callback("mouse_up_callback"),
	thumb_width("thumb_width"),
	sliders("slider")
{}

LLMultiSlider::LLMultiSlider(const LLMultiSlider::Params& p)
:	LLF32UICtrl(p),
	mMouseOffset( 0 ),
	mMaxNumSliders(p.max_sliders),
	mAllowOverlap(p.allow_overlap),
	mLoopOverlap(p.loop_overlap),
	mDrawTrack(p.draw_track),
	mUseTriangle(p.use_triangle),
	mTrackColor(p.track_color()),
	mThumbOutlineColor(p.thumb_outline_color()),
	mThumbCenterColor(p.thumb_center_color()),
	mThumbCenterSelectedColor(p.thumb_center_selected_color()),
	mDisabledThumbColor(p.thumb_disabled_color()),
	mTriangleColor(p.triangle_color()),
	mThumbWidth(p.thumb_width),
	mOrientation((p.orientation() == "vertical") ? VERTICAL : HORIZONTAL),
	mMouseDownSignal(NULL),
	mMouseUpSignal(NULL)
{
	mValue.emptyMap();
	mCurSlider = LLStringUtil::null;

	if (mOrientation == HORIZONTAL)
	{
		mDragStartThumbRect = LLRect(0, getRect().getHeight(), p.thumb_width, 0);
	}
	else
	{
		mDragStartThumbRect = LLRect(0, p.thumb_width, getRect().getWidth(), 0);
	}

	if (p.mouse_down_callback.isProvided())
	{
		setMouseDownCallback(initCommitCallback(p.mouse_down_callback));
	}
	if (p.mouse_up_callback.isProvided())
	{
		setMouseUpCallback(initCommitCallback(p.mouse_up_callback));
	}

	if (p.overlap_threshold.isProvided() && p.overlap_threshold > mIncrement)
    {
        mOverlapThreshold = p.overlap_threshold - mIncrement;
    }
    else
    {
        mOverlapThreshold = 0;
    }

	for (LLInitParam::ParamIterator<SliderParams>::const_iterator it = p.sliders.begin();
		it != p.sliders.end();
		++it)
	{
		if (it->name.isProvided())
		{
			addSlider(it->value, it->name);
		}
		else
		{
			addSlider(it->value);
		}
	}

	if (p.thumb_image.isProvided())
	{
		mThumbImagep = LLUI::getUIImage(p.thumb_image());
	}
	mThumbHighlightColor = p.thumb_highlight_color.isProvided() ? p.thumb_highlight_color() : static_cast<LLUIColor>(gFocusMgr.getFocusColor());
}

LLMultiSlider::~LLMultiSlider()
{
	delete mMouseDownSignal;
	delete mMouseUpSignal;
}

F32 LLMultiSlider::getNearestIncrement(F32 value) const
{
    value = llclamp(value, mMinValue, mMaxValue);

    // Round to nearest increment (bias towards rounding down)
    value -= mMinValue;
    value += mIncrement / 2.0001f;
    value -= fmod(value, mIncrement);
    return mMinValue + value;
}

void LLMultiSlider::setSliderValue(const std::string& name, F32 value, BOOL from_event)
{
	// exit if not there
	if(!mValue.has(name)) {
		return;
	}

    F32 newValue = getNearestIncrement(value);

	// now, make sure no overlap
	// if we want that
	if(!mAllowOverlap) {
		bool hit = false;

		// look at the current spot
		// and see if anything is there
		LLSD::map_iterator mIt = mValue.beginMap();

		// increment is our distance between points, use to eliminate round error
		F32 threshold = mOverlapThreshold + (mIncrement / 4);
		// If loop overlap is enabled, check if we overlap with points 'after' max value (project to lower)
		F32 loop_up_check = (mLoopOverlap && (value + threshold) > mMaxValue) ? (value + threshold - mMaxValue + mMinValue) : mMinValue - 1.0f;
		// If loop overlap is enabled, check if we overlap with points 'before' min value (project to upper)
		F32 loop_down_check = (mLoopOverlap && (value - threshold) < mMinValue) ? (value - threshold - mMinValue + mMaxValue) : mMaxValue + 1.0f;

		for(;mIt != mValue.endMap(); mIt++)
		{
			F32 locationVal = (F32)mIt->second.asReal();
			// Check nearby values
			F32 testVal = locationVal - newValue;
			if (testVal > -threshold
				&& testVal < threshold
				&& mIt->first != name)
			{
				hit = true;
				break;
			}
			if (mLoopOverlap)
			{
				// Check edge overlap values
				if (locationVal < loop_up_check)
				{
					hit = true;
					break;
				}
				if (locationVal > loop_down_check)
				{
					hit = true;
					break;
				}
			}
		}

		// if none found, stop
		if(hit) {
			return;
		}
	}
	

	// now set it in the map
	mValue[name] = newValue;

	// set the control if it's the current slider and not from an event
	if (!from_event && name == mCurSlider)
	{
		setControlValue(mValue);
	}
	
	F32 t = (newValue - mMinValue) / (mMaxValue - mMinValue);
	if (mOrientation == HORIZONTAL)
	{
		S32 left_edge = mThumbWidth/2;
		S32 right_edge = getRect().getWidth() - (mThumbWidth/2);

		S32 x = left_edge + S32( t * (right_edge - left_edge) );

		mThumbRects[name].mLeft = x - (mThumbWidth / 2);
		mThumbRects[name].mRight = x + (mThumbWidth / 2);
	}
	else
	{
		S32 bottom_edge = mThumbWidth/2;
		S32 top_edge = getRect().getHeight() - (mThumbWidth/2);

		S32 x = bottom_edge + S32( t * (top_edge - bottom_edge) );

		mThumbRects[name].mTop = x + (mThumbWidth / 2);
		mThumbRects[name].mBottom = x - (mThumbWidth / 2);
	}
}

void LLMultiSlider::setValue(const LLSD& value)
{
	// only do if it's a map
	if(value.isMap()) {
		
		// add each value... the first in the map becomes the current
		LLSD::map_const_iterator mIt = value.beginMap();
		mCurSlider = mIt->first;

		for(; mIt != value.endMap(); mIt++) {
			setSliderValue(mIt->first, (F32)mIt->second.asReal(), TRUE);
		}
	}
}

F32 LLMultiSlider::getSliderValue(const std::string& name) const
{
	if (mValue.has(name))
	{
		return (F32)mValue[name].asReal();
	}
	return 0;
}

void LLMultiSlider::setCurSlider(const std::string& name)
{
	if(mValue.has(name)) {
		mCurSlider = name;
	}
}

F32 LLMultiSlider::getSliderValueFromPos(S32 xpos, S32 ypos) const
{
    F32 t = 0;
    if (mOrientation == HORIZONTAL)
    {
        S32 left_edge = mThumbWidth / 2;
        S32 right_edge = getRect().getWidth() - (mThumbWidth / 2);

        xpos += mMouseOffset;
        xpos = llclamp(xpos, left_edge, right_edge);

        t = F32(xpos - left_edge) / (right_edge - left_edge);
    }
    else
    {
        S32 bottom_edge = mThumbWidth / 2;
        S32 top_edge = getRect().getHeight() - (mThumbWidth / 2);

        ypos += mMouseOffset;
        ypos = llclamp(ypos, bottom_edge, top_edge);

        t = F32(ypos - bottom_edge) / (top_edge - bottom_edge);
    }

    return((t * (mMaxValue - mMinValue)) + mMinValue);
}


LLRect LLMultiSlider::getSliderThumbRect(const std::string& name) const
{
    auto it = mThumbRects.find(name);
    if (it != mThumbRects.end())
        return (*it).second;
    return LLRect();
}

void LLMultiSlider::setSliderThumbImage(const std::string &name)
{
    if (!name.empty())
    {
        mThumbImagep = LLUI::getUIImage(name);
    }
    else
        clearSliderThumbImage();
}

void LLMultiSlider::clearSliderThumbImage()
{
    mThumbImagep = NULL;
}

void LLMultiSlider::resetCurSlider()
{
	mCurSlider = LLStringUtil::null;
}

const std::string& LLMultiSlider::addSlider()
{
	return addSlider(mInitialValue);
}

const std::string& LLMultiSlider::addSlider(F32 val)
{
	std::stringstream newName;
	F32 initVal = val;

	if(mValue.size() >= mMaxNumSliders) {
		return LLStringUtil::null;
	}

	// create a new name
	newName << "sldr" << mNameCounter;
	mNameCounter++;

	bool foundOne = findUnusedValue(initVal);
	if(!foundOne) {
		return LLStringUtil::null;
	}

	// add a new thumb rect
	if (mOrientation == HORIZONTAL)
	{
		mThumbRects[newName.str()] = LLRect(0, getRect().getHeight(), mThumbWidth, 0);
	}
	else
	{
		mThumbRects[newName.str()] = LLRect(0, mThumbWidth, getRect().getWidth(), 0);
	}

	// add the value and set the current slider to this one
	mValue.insert(newName.str(), initVal);
	mCurSlider = newName.str();

	// move the slider
	setSliderValue(mCurSlider, initVal, TRUE);

	return mCurSlider;
}

bool LLMultiSlider::addSlider(F32 val, const std::string& name)
{
	F32 initVal = val;

	if(mValue.size() >= mMaxNumSliders) {
		return false;
	}

	bool foundOne = findUnusedValue(initVal);
	if(!foundOne) {
		return false;
	}

	// add a new thumb rect
	if (mOrientation == HORIZONTAL)
	{
		mThumbRects[name] = LLRect(0, getRect().getHeight(), mThumbWidth, 0);
	}
	else
	{
		mThumbRects[name] = LLRect(0, mThumbWidth, getRect().getWidth(), 0);
	}

	// add the value and set the current slider to this one
	mValue.insert(name, initVal);
	mCurSlider = name;

	// move the slider
	setSliderValue(mCurSlider, initVal, TRUE);

	return true;
}

bool LLMultiSlider::findUnusedValue(F32& initVal)
{
	bool firstTry = true;

	// find the first open slot starting with
	// the initial value
	while(true) {
		
		bool hit = false;

		// look at the current spot
		// and see if anything is there
		F32 threshold = mAllowOverlap ? FLOAT_THRESHOLD : mOverlapThreshold + (mIncrement / 4);
		LLSD::map_iterator mIt = mValue.beginMap();
		for(;mIt != mValue.endMap(); mIt++) {
			
			F32 testVal = (F32)mIt->second.asReal() - initVal;
			if(testVal > -threshold && testVal < threshold)
			{
				hit = true;
				break;
			}
		}

		// if we found one
		if(!hit) {
			break;
		}

		// increment and wrap if need be
		initVal += mIncrement;
		if(initVal > mMaxValue) {
			initVal = mMinValue;
		}

		// stop if it's filled
		if(initVal == mInitialValue && !firstTry) {
			LL_WARNS() << "Whoa! Too many multi slider elements to add one to" << LL_ENDL;
			return false;
		}

		firstTry = false;
		continue;
	}

	return true;
}


void LLMultiSlider::deleteSlider(const std::string& name)
{
	// can't delete last slider
	if(mValue.size() <= 0) {
		return;
	}

	// get rid of value from mValue and its thumb rect
	mValue.erase(name);
	mThumbRects.erase(name);

	// set to the last created
	if(mValue.size() > 0) {
		std::map<std::string, LLRect>::iterator mIt = mThumbRects.end();
		mIt--;
		mCurSlider = mIt->first;
	}
}

void LLMultiSlider::clear()
{
	while(mThumbRects.size() > 0 && mValue.size() > 0) {
		deleteCurSlider();
	}

	if (mThumbRects.size() > 0 || mValue.size() > 0)
	{
		LL_WARNS() << "Failed to fully clear Multi slider" << LL_ENDL;
	}

	LLF32UICtrl::clear();
}

BOOL LLMultiSlider::handleHover(S32 x, S32 y, MASK mask)
{
	if( gFocusMgr.getMouseCapture() == this )
	{
		setCurSliderValue(getSliderValueFromPos(x, y));
		onCommit();

		getWindow()->setCursor(UI_CURSOR_ARROW);
		// _LL_DEBUGS("UserInput") << "hover handled by " << getName() << " (active)" << LL_ENDL;		
	}
	else
	{
        if (getEnabled())
        {
            mHoverSlider.clear();
            std::map<std::string, LLRect>::iterator  mIt = mThumbRects.begin();
            for (; mIt != mThumbRects.end(); mIt++)
            {
                if (mIt->second.pointInRect(x, y))
                {
                    mHoverSlider = mIt->first;
                    break;
                }
            }
        }
        else
        {
            mHoverSlider.clear();
        }

		getWindow()->setCursor(UI_CURSOR_ARROW);
		// _LL_DEBUGS("UserInput") << "hover handled by " << getName() << " (inactive)" << LL_ENDL;		
	}
	return TRUE;
}

BOOL LLMultiSlider::handleMouseUp(S32 x, S32 y, MASK mask)
{
	BOOL handled = FALSE;

	if( gFocusMgr.getMouseCapture() == this )
	{
		gFocusMgr.setMouseCapture( NULL );

		if (mMouseUpSignal)
			(*mMouseUpSignal)( this, LLSD() );

		handled = TRUE;
		make_ui_sound("UISndClickRelease");
	}
	else
	{
		handled = TRUE;
	}

	return handled;
}

BOOL LLMultiSlider::handleMouseDown(S32 x, S32 y, MASK mask)
{
	// only do sticky-focus on non-chrome widgets
	if (!getIsChrome())
	{
		setFocus(TRUE);
	}
	if (mMouseDownSignal)
		(*mMouseDownSignal)( this, LLSD() );

	if (MASK_CONTROL & mask) // if CTRL is modifying
	{
		setCurSliderValue(mInitialValue);
		onCommit();
	}
	else
	{
		// scroll through thumbs to see if we have a new one selected and select that one
		std::map<std::string, LLRect>::iterator mIt = mThumbRects.begin();
		for(; mIt != mThumbRects.end(); mIt++) {
			
			// check if inside.  If so, set current slider and continue
			if(mIt->second.pointInRect(x,y)) {
				mCurSlider = mIt->first;
				break;
			}
		}

		if (!mCurSlider.empty())
		{
			// Find the offset of the actual mouse location from the center of the thumb.
			if (mThumbRects[mCurSlider].pointInRect(x,y))
			{
				if (mOrientation == HORIZONTAL)
				{
					mMouseOffset = (mThumbRects[mCurSlider].mLeft + mThumbWidth / 2) - x;
				}
				else
				{
					mMouseOffset = (mThumbRects[mCurSlider].mBottom + mThumbWidth / 2) - y;
				}
			}
			else
			{
				mMouseOffset = 0;
			}

			// Start dragging the thumb
			// No handler needed for focus lost since this class has no state that depends on it.
			gFocusMgr.setMouseCapture( this );
			mDragStartThumbRect = mThumbRects[mCurSlider];
		}
	}
	make_ui_sound("UISndClick");

	return TRUE;
}

BOOL	LLMultiSlider::handleKeyHere(KEY key, MASK mask)
{
	BOOL handled = FALSE;
	switch(key)
	{
	case KEY_UP:
	case KEY_DOWN:
		// eat up and down keys to be consistent
		handled = TRUE;
		break;
	case KEY_LEFT:
		setCurSliderValue(getCurSliderValue() - getIncrement());
		onCommit();
		handled = TRUE;
		break;
	case KEY_RIGHT:
		setCurSliderValue(getCurSliderValue() + getIncrement());
		onCommit();
		handled = TRUE;
		break;
	default:
		break;
	}
	return handled;
}

/*virtual*/
void LLMultiSlider::onMouseLeave(S32 x, S32 y, MASK mask)
{
    mHoverSlider.clear();
    LLF32UICtrl::onMouseLeave(x, y, mask);
}

void LLMultiSlider::draw()
{
	static LLUICachedControl<S32> extra_triangle_height ("UIExtraTriangleHeight", 0);
	static LLUICachedControl<S32> extra_triangle_width ("UIExtraTriangleWidth", 0);
	LLColor4 curThumbColor;

	std::map<std::string, LLRect>::iterator mIt;
	std::map<std::string, LLRect>::iterator curSldrIt;
	std::map<std::string, LLRect>::iterator hoverSldrIt;

	// Draw background and thumb.

	// drawing solids requires texturing be disabled
	gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);

	LLRect rect(mDragStartThumbRect);

	F32 opacity = getEnabled() ? 1.f : 0.3f;

	// Track
	LLUIImagePtr thumb_imagep = LLUI::getUIImage("Rounded_Square");

	static LLUICachedControl<S32> multi_track_height_width ("UIMultiTrackHeight", 0);
	S32 height_offset = 0;
	S32 width_offset = 0;
	if (mOrientation == HORIZONTAL)
	{
		height_offset = (getRect().getHeight() - multi_track_height_width) / 2;
	}
	else
	{
		width_offset = (getRect().getWidth() - multi_track_height_width) / 2;
	}
	LLRect track_rect(width_offset, getRect().getHeight() - height_offset, getRect().getWidth() - width_offset, height_offset);


	if(mDrawTrack)
	{
		track_rect.stretch(-1);
		thumb_imagep->draw(track_rect, mTrackColor.get() % opacity);
	}

	// if we're supposed to use a drawn triangle
	// simple gl call for the triangle
	if(mUseTriangle) {

		for(mIt = mThumbRects.begin(); mIt != mThumbRects.end(); mIt++) {

			gl_triangle_2d(
				mIt->second.mLeft - extra_triangle_width, 
				mIt->second.mTop + extra_triangle_height,
				mIt->second.mRight + extra_triangle_width, 
				mIt->second.mTop + extra_triangle_height,
				mIt->second.mLeft + mIt->second.getWidth() / 2, 
				mIt->second.mBottom - extra_triangle_height,
				mTriangleColor.get() % opacity, TRUE);
		}
	}
	else if (!thumb_imagep && !mThumbImagep)
	{
		// draw all the thumbs
		curSldrIt = mThumbRects.end();
		hoverSldrIt = mThumbRects.end();
		for(mIt = mThumbRects.begin(); mIt != mThumbRects.end(); mIt++) {
			
			// choose the color
			curThumbColor = mThumbCenterColor.get();
			if(mIt->first == mCurSlider) {
				
				curSldrIt = mIt;
				continue;
			}
			if (mIt->first == mHoverSlider && getEnabled() && gFocusMgr.getMouseCapture() != this)
			{
				// draw last, after current one
				hoverSldrIt = mIt;
				continue;
			}

			// the draw command
			gl_rect_2d(mIt->second, curThumbColor, TRUE);
		}

		// now draw the current and hover sliders
		if(curSldrIt != mThumbRects.end())
		{
			gl_rect_2d(curSldrIt->second, mThumbCenterSelectedColor.get(), TRUE);
		}

		// and draw the drag start
		if (gFocusMgr.getMouseCapture() == this)
		{
			gl_rect_2d(mDragStartThumbRect, mThumbCenterColor.get() % opacity, FALSE);
		}
		else if (hoverSldrIt != mThumbRects.end())
		{
			gl_rect_2d(hoverSldrIt->second, mThumbCenterSelectedColor.get(), TRUE);
		}
	}
	else
	{
		LLMouseHandler* capture = gFocusMgr.getMouseCapture();
		if (capture == this)
		{
			// draw drag start (ghost)
			if (mThumbImagep)
			{
				mThumbImagep->draw(mDragStartThumbRect, mThumbCenterColor.get() % 0.3f);
			}
			else
			{
				thumb_imagep->drawSolid(mDragStartThumbRect, mThumbCenterColor.get() % 0.3f);
			}
		}

		// draw the highlight
		if (hasFocus())
		{
			if (!mCurSlider.empty())
			{
				if (mThumbImagep)
				{
					mThumbImagep->drawBorder(mThumbRects[mCurSlider], mThumbHighlightColor, gFocusMgr.getFocusFlashWidth());
				}
				else
				{
					thumb_imagep->drawBorder(mThumbRects[mCurSlider], gFocusMgr.getFocusColor(), gFocusMgr.getFocusFlashWidth());
				}
			}
		}
        if (!mHoverSlider.empty())
        {
            if (mThumbImagep)
            {
                mThumbImagep->drawBorder(mThumbRects[mHoverSlider], mThumbHighlightColor, gFocusMgr.getFocusFlashWidth());
            }
            else
            {
                thumb_imagep->drawBorder(mThumbRects[mHoverSlider], gFocusMgr.getFocusColor(), gFocusMgr.getFocusFlashWidth());
            }
        }

		// draw the thumbs
		curSldrIt = mThumbRects.end();
		hoverSldrIt = mThumbRects.end();
		for(mIt = mThumbRects.begin(); mIt != mThumbRects.end(); mIt++) 
		{
			// choose the color
			curThumbColor = mThumbCenterColor.get();
			if(mIt->first == mCurSlider) 
			{
				// don't draw now, draw last
				curSldrIt = mIt;
				continue;				
			}
			if (mIt->first == mHoverSlider && getEnabled() && gFocusMgr.getMouseCapture() != this) 
			{
				// don't draw now, draw last, after current one
				hoverSldrIt = mIt;
				continue;
			}
			
			// the draw command
			if (mThumbImagep)
			{
				if (getEnabled())
				{
					mThumbImagep->draw(mIt->second);
				}
				else
				{
					mThumbImagep->draw(mIt->second, LLColor4::grey % 0.8f);
				}
			}
			else if (capture == this)
			{
				thumb_imagep->drawSolid(mIt->second, curThumbColor);
			}
			else
			{
				thumb_imagep->drawSolid(mIt->second, curThumbColor % opacity);
			}
		}
		
		// draw cur and hover slider last
		if(curSldrIt != mThumbRects.end()) 
		{
			if (mThumbImagep)
			{
				if (getEnabled())
				{
					mThumbImagep->draw(curSldrIt->second);
				}
				else
				{
					mThumbImagep->draw(curSldrIt->second, LLColor4::grey % 0.8f);
				}
			}
			else if (capture == this)
			{
				thumb_imagep->drawSolid(curSldrIt->second, mThumbCenterSelectedColor.get());
			}
			else
			{
				thumb_imagep->drawSolid(curSldrIt->second, mThumbCenterSelectedColor.get() % opacity);
			}
		}
		if(hoverSldrIt != mThumbRects.end()) 
		{
			if (mThumbImagep)
			{
				mThumbImagep->draw(hoverSldrIt->second);
			}
			else
			{
				thumb_imagep->drawSolid(hoverSldrIt->second, mThumbCenterSelectedColor.get());
			}
		}
	}

	LLF32UICtrl::draw();
}
boost::signals2::connection LLMultiSlider::setMouseDownCallback( const commit_signal_t::slot_type& cb ) 
{ 
	if (!mMouseDownSignal) mMouseDownSignal = new commit_signal_t();
	return mMouseDownSignal->connect(cb); 
}

boost::signals2::connection LLMultiSlider::setMouseUpCallback(	const commit_signal_t::slot_type& cb )   
{ 
	if (!mMouseUpSignal) mMouseUpSignal = new commit_signal_t();
	return mMouseUpSignal->connect(cb); 
}
