diff options
Diffstat (limited to 'indra/llui/llmultislider.cpp')
-rw-r--r-- | indra/llui/llmultislider.cpp | 1760 |
1 files changed, 880 insertions, 880 deletions
diff --git a/indra/llui/llmultislider.cpp b/indra/llui/llmultislider.cpp index 685b1c8b98..a29ccab737 100644 --- a/indra/llui/llmultislider.cpp +++ b/indra/llui/llmultislider.cpp @@ -1,880 +1,880 @@ -/** - * @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 = LLSD::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); - } - } - - mRoundedSquareImgp = LLUI::getUIImage("Rounded_Square"); - 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 - 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); - mRoundedSquareImgp->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 (!mRoundedSquareImgp && !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 - { - mRoundedSquareImgp->drawSolid(mDragStartThumbRect, mThumbCenterColor.get() % 0.3f); - } - } - - // draw the highlight - if (hasFocus()) - { - if (!mCurSlider.empty()) - { - if (mThumbImagep) - { - mThumbImagep->drawBorder(mThumbRects[mCurSlider], mThumbHighlightColor, gFocusMgr.getFocusFlashWidth()); - } - else - { - mRoundedSquareImgp->drawBorder(mThumbRects[mCurSlider], gFocusMgr.getFocusColor(), gFocusMgr.getFocusFlashWidth()); - } - } - } - if (!mHoverSlider.empty()) - { - if (mThumbImagep) - { - mThumbImagep->drawBorder(mThumbRects[mHoverSlider], mThumbHighlightColor, gFocusMgr.getFocusFlashWidth()); - } - else - { - mRoundedSquareImgp->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) - { - mRoundedSquareImgp->drawSolid(mIt->second, curThumbColor); - } - else - { - mRoundedSquareImgp->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) - { - mRoundedSquareImgp->drawSolid(curSldrIt->second, mThumbCenterSelectedColor.get()); - } - else - { - mRoundedSquareImgp->drawSolid(curSldrIt->second, mThumbCenterSelectedColor.get() % opacity); - } - } - if(hoverSldrIt != mThumbRects.end()) - { - if (mThumbImagep) - { - mThumbImagep->draw(hoverSldrIt->second); - } - else - { - mRoundedSquareImgp->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); -} +/**
+ * @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 = LLSD::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);
+ }
+ }
+
+ mRoundedSquareImgp = LLUI::getUIImage("Rounded_Square");
+ 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
+ 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);
+ mRoundedSquareImgp->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 (!mRoundedSquareImgp && !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
+ {
+ mRoundedSquareImgp->drawSolid(mDragStartThumbRect, mThumbCenterColor.get() % 0.3f);
+ }
+ }
+
+ // draw the highlight
+ if (hasFocus())
+ {
+ if (!mCurSlider.empty())
+ {
+ if (mThumbImagep)
+ {
+ mThumbImagep->drawBorder(mThumbRects[mCurSlider], mThumbHighlightColor, gFocusMgr.getFocusFlashWidth());
+ }
+ else
+ {
+ mRoundedSquareImgp->drawBorder(mThumbRects[mCurSlider], gFocusMgr.getFocusColor(), gFocusMgr.getFocusFlashWidth());
+ }
+ }
+ }
+ if (!mHoverSlider.empty())
+ {
+ if (mThumbImagep)
+ {
+ mThumbImagep->drawBorder(mThumbRects[mHoverSlider], mThumbHighlightColor, gFocusMgr.getFocusFlashWidth());
+ }
+ else
+ {
+ mRoundedSquareImgp->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)
+ {
+ mRoundedSquareImgp->drawSolid(mIt->second, curThumbColor);
+ }
+ else
+ {
+ mRoundedSquareImgp->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)
+ {
+ mRoundedSquareImgp->drawSolid(curSldrIt->second, mThumbCenterSelectedColor.get());
+ }
+ else
+ {
+ mRoundedSquareImgp->drawSolid(curSldrIt->second, mThumbCenterSelectedColor.get() % opacity);
+ }
+ }
+ if(hoverSldrIt != mThumbRects.end())
+ {
+ if (mThumbImagep)
+ {
+ mThumbImagep->draw(hoverSldrIt->second);
+ }
+ else
+ {
+ mRoundedSquareImgp->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);
+}
|