Files
SingularityViewer/indra/llui/llmultislider.cpp
Shyotl e756140e1d Innitial commit of experimental v2 texture system port work. Compiles and runs on windows, at least. Fixing bugs as they come.
Need to test:
localassetbrowser
preview related floaters
hgfloatertexteditor
maps
media textures! Currently very hacky
web browser
alpha masks on avatars
bumpmaps
Are all sky components appearing?
LLViewerDynamicTexture (texture baking, browser, animated textures, anim previews, etc)
Snapshot related features
Customize avatar
vfs floater
UI textures in general
Texture priority issues
2011-03-31 03:22:01 -05:00

635 lines
15 KiB
C++

/**
* @file llmultisldr.cpp
* @brief LLMultiSlider base class
*
* $LicenseInfo:firstyear=2007&license=viewergpl$
*
* Copyright (c) 2007-2009, Linden Research, Inc.
*
* Second Life Viewer Source Code
* The source code in this file ("Source Code") is provided by Linden Lab
* to you under the terms of the GNU General Public License, version 2.0
* ("GPL"), unless you have obtained a separate licensing agreement
* ("Other License"), formally executed by you and Linden Lab. Terms of
* the GPL can be found in doc/GPL-license.txt in this distribution, or
* online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
*
* There are special exceptions to the terms and conditions of the GPL as
* it is applied to this Source Code. View the full text of the exception
* in the file doc/FLOSS-exception.txt in this software distribution, or
* online at
* http://secondlifegrid.net/programs/open_source/licensing/flossexception
*
* By copying, modifying or distributing this software, you acknowledge
* that you have read and understood your obligations described above,
* and agree to abide by those obligations.
*
* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
* COMPLETENESS OR PERFORMANCE.
* $/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 "llimagegl.h"
#include <sstream>
static LLRegisterWidget<LLMultiSlider> r("multi_slider_bar");
const S32 MULTI_THUMB_WIDTH = 8;
const S32 MULTI_TRACK_HEIGHT = 6;
const F32 FLOAT_THRESHOLD = 0.00001f;
const S32 EXTRA_TRIANGLE_WIDTH = 2;
const S32 EXTRA_TRIANGLE_HEIGHT = -2;
S32 LLMultiSlider::mNameCounter = 0;
LLMultiSlider::LLMultiSlider(
const std::string& name,
const LLRect& rect,
void (*on_commit_callback)(LLUICtrl* ctrl, void* userdata),
void* callback_userdata,
F32 initial_value,
F32 min_value,
F32 max_value,
F32 increment,
S32 max_sliders,
BOOL allow_overlap,
BOOL draw_track,
BOOL use_triangle,
const std::string& control_name)
:
LLUICtrl( name, rect, TRUE, on_commit_callback, callback_userdata,
FOLLOWS_LEFT | FOLLOWS_TOP),
mInitialValue( initial_value ),
mMinValue( min_value ),
mMaxValue( max_value ),
mIncrement( increment ),
mMaxNumSliders(max_sliders),
mAllowOverlap(allow_overlap),
mDrawTrack(draw_track),
mUseTriangle(use_triangle),
mMouseOffset( 0 ),
mDragStartThumbRect( 0, getRect().getHeight(), MULTI_THUMB_WIDTH, 0 ),
mTrackColor( LLUI::sColorsGroup->getColor( "MultiSliderTrackColor" ) ),
mThumbOutlineColor( LLUI::sColorsGroup->getColor( "MultiSliderThumbOutlineColor" ) ),
mThumbCenterColor( LLUI::sColorsGroup->getColor( "MultiSliderThumbCenterColor" ) ),
mThumbCenterSelectedColor( LLUI::sColorsGroup->getColor( "MultiSliderThumbCenterSelectedColor" ) ),
mDisabledThumbColor(LLUI::sColorsGroup->getColor( "MultiSliderDisabledThumbColor" ) ),
mTriangleColor(LLUI::sColorsGroup->getColor( "MultiSliderTriangleColor" ) ),
mMouseDownCallback( NULL ),
mMouseUpCallback( NULL )
{
mValue.emptyMap();
mCurSlider = LLStringUtil::null;
// properly handle setting the starting thumb rect
// do it this way to handle both the operating-on-settings
// and standalone ways of using this
setControlName(control_name, NULL);
setValue(getValue());
}
void LLMultiSlider::setSliderValue(const std::string& name, F32 value, BOOL from_event)
{
// exit if not there
if(!mValue.has(name)) {
return;
}
value = llclamp( value, mMinValue, mMaxValue );
// Round to nearest increment (bias towards rounding down)
value -= mMinValue;
value += mIncrement/2.0001f;
value -= fmod(value, mIncrement);
F32 newValue = mMinValue + 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();
for(;mIt != mValue.endMap(); mIt++) {
F32 testVal = (F32)mIt->second.asReal() - newValue;
if(testVal > -FLOAT_THRESHOLD && testVal < FLOAT_THRESHOLD &&
mIt->first != name) {
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);
S32 left_edge = MULTI_THUMB_WIDTH/2;
S32 right_edge = getRect().getWidth() - (MULTI_THUMB_WIDTH/2);
S32 x = left_edge + S32( t * (right_edge - left_edge) );
mThumbRects[name].mLeft = x - (MULTI_THUMB_WIDTH/2);
mThumbRects[name].mRight = x + (MULTI_THUMB_WIDTH/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
{
return (F32)mValue[name].asReal();
}
void LLMultiSlider::setCurSlider(const std::string& name)
{
if(mValue.has(name)) {
mCurSlider = name;
}
}
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
mThumbRects[newName.str()] = LLRect( 0, getRect().getHeight(), MULTI_THUMB_WIDTH, 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::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
LLSD::map_iterator mIt = mValue.beginMap();
for(;mIt != mValue.endMap(); mIt++) {
F32 testVal = (F32)mIt->second.asReal() - initVal;
if(testVal > -FLOAT_THRESHOLD && testVal < FLOAT_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) {
llwarns << "Whoa! Too many multi slider elements to add one to" << llendl;
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) {
deleteCurSlider();
}
LLUICtrl::clear();
}
BOOL LLMultiSlider::handleHover(S32 x, S32 y, MASK mask)
{
if( gFocusMgr.getMouseCapture() == this )
{
S32 left_edge = MULTI_THUMB_WIDTH/2;
S32 right_edge = getRect().getWidth() - (MULTI_THUMB_WIDTH/2);
x += mMouseOffset;
x = llclamp( x, left_edge, right_edge );
F32 t = F32(x - left_edge) / (right_edge - left_edge);
setCurSliderValue(t * (mMaxValue - mMinValue) + mMinValue );
onCommit();
getWindow()->setCursor(UI_CURSOR_ARROW);
lldebugst(LLERR_USER_INPUT) << "hover handled by " << getName() << " (active)" << llendl;
}
else
{
getWindow()->setCursor(UI_CURSOR_ARROW);
lldebugst(LLERR_USER_INPUT) << "hover handled by " << getName() << " (inactive)" << llendl;
}
return TRUE;
}
BOOL LLMultiSlider::handleMouseUp(S32 x, S32 y, MASK mask)
{
BOOL handled = FALSE;
if( gFocusMgr.getMouseCapture() == this )
{
gFocusMgr.setMouseCapture( NULL );
if( mMouseUpCallback )
{
mMouseUpCallback( this, mCallbackUserData );
}
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( mMouseDownCallback )
{
mMouseDownCallback( this, mCallbackUserData );
}
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;
}
}
// Find the offset of the actual mouse location from the center of the thumb.
if (mThumbRects[mCurSlider].pointInRect(x,y))
{
mMouseOffset = (mThumbRects[mCurSlider].mLeft + MULTI_THUMB_WIDTH/2) - x;
}
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;
}
void LLMultiSlider::draw()
{
LLColor4 curThumbColor;
std::map<std::string, LLRect>::iterator mIt;
std::map<std::string, LLRect>::iterator curSldrIt;
// 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.tga");
S32 height_offset = (getRect().getHeight() - MULTI_TRACK_HEIGHT) / 2;
LLRect track_rect(0, getRect().getHeight() - height_offset, getRect().getWidth(), height_offset );
if(mDrawTrack)
{
track_rect.stretch(-1);
thumb_imagep->draw(track_rect, mTrackColor % 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, TRUE);
}
}
else if (!thumb_imagep)
{
// draw all the thumbs
curSldrIt = mThumbRects.end();
for(mIt = mThumbRects.begin(); mIt != mThumbRects.end(); mIt++) {
// choose the color
curThumbColor = mThumbCenterColor;
if(mIt->first == mCurSlider) {
curSldrIt = mIt;
continue;
//curThumbColor = mThumbCenterSelectedColor;
}
// the draw command
gl_rect_2d(mIt->second, curThumbColor, TRUE);
}
// now draw the current slider
if(curSldrIt != mThumbRects.end()) {
gl_rect_2d(curSldrIt->second, mThumbCenterSelectedColor, TRUE);
}
// and draw the drag start
if (gFocusMgr.getMouseCapture() == this)
{
gl_rect_2d(mDragStartThumbRect, mThumbCenterColor % opacity, FALSE);
}
}
else if( gFocusMgr.getMouseCapture() == this )
{
// draw drag start
thumb_imagep->drawSolid(mDragStartThumbRect, mThumbCenterColor % 0.3f);
// draw the highlight
if (hasFocus())
{
thumb_imagep->drawBorder(mThumbRects[mCurSlider], gFocusMgr.getFocusColor(), gFocusMgr.getFocusFlashWidth());
}
// draw the thumbs
curSldrIt = mThumbRects.end();
for(mIt = mThumbRects.begin(); mIt != mThumbRects.end(); mIt++)
{
// choose the color
curThumbColor = mThumbCenterColor;
if(mIt->first == mCurSlider)
{
// don't draw now, draw last
curSldrIt = mIt;
continue;
}
// the draw command
thumb_imagep->drawSolid(mIt->second, curThumbColor);
}
// draw cur slider last
if(curSldrIt != mThumbRects.end())
{
thumb_imagep->drawSolid(curSldrIt->second, mThumbCenterSelectedColor);
}
}
else
{
// draw highlight
if (hasFocus())
{
thumb_imagep->drawBorder(mThumbRects[mCurSlider], gFocusMgr.getFocusColor(), gFocusMgr.getFocusFlashWidth());
}
// draw thumbs
curSldrIt = mThumbRects.end();
for(mIt = mThumbRects.begin(); mIt != mThumbRects.end(); mIt++)
{
// choose the color
curThumbColor = mThumbCenterColor;
if(mIt->first == mCurSlider)
{
curSldrIt = mIt;
continue;
//curThumbColor = mThumbCenterSelectedColor;
}
thumb_imagep->drawSolid(mIt->second, curThumbColor % opacity);
}
if(curSldrIt != mThumbRects.end())
{
thumb_imagep->drawSolid(curSldrIt->second, mThumbCenterSelectedColor % opacity);
}
}
LLUICtrl::draw();
}
// virtual
LLXMLNodePtr LLMultiSlider::getXML(bool save_children) const
{
LLXMLNodePtr node = LLUICtrl::getXML();
node->setName(LL_MULTI_SLIDER_TAG);
node->createChild("initial_val", TRUE)->setFloatValue(getInitialValue());
node->createChild("min_val", TRUE)->setFloatValue(getMinValue());
node->createChild("max_val", TRUE)->setFloatValue(getMaxValue());
node->createChild("increment", TRUE)->setFloatValue(getIncrement());
return node;
}
//static
LLView* LLMultiSlider::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory)
{
std::string name("multi_slider_bar");
node->getAttributeString("name", name);
LLRect rect;
createRect(node, rect, parent, LLRect());
F32 initial_value = 0.f;
node->getAttributeF32("initial_val", initial_value);
F32 min_value = 0.f;
node->getAttributeF32("min_val", min_value);
F32 max_value = 1.f;
node->getAttributeF32("max_val", max_value);
F32 increment = 0.1f;
node->getAttributeF32("increment", increment);
S32 max_sliders = 1;
node->getAttributeS32("max_sliders", max_sliders);
BOOL allow_overlap = FALSE;
node->getAttributeBOOL("allow_overlap", allow_overlap);
BOOL draw_track = TRUE;
node->getAttributeBOOL("draw_track", draw_track);
BOOL use_triangle = FALSE;
node->getAttributeBOOL("use_triangle", use_triangle);
LLMultiSlider* multiSlider = new LLMultiSlider(name,
rect,
NULL,
NULL,
initial_value,
min_value,
max_value,
increment,
max_sliders,
allow_overlap,
draw_track,
use_triangle);
multiSlider->initFromXML(node, parent);
return multiSlider;
}