123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353 |
- /**
- * @file llmultisliderctrl.cpp
- * @brief LLMultiSliderCtrl 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 "llmultisliderctrl.h"
- #include "llcontrol.h"
- #include "llgl.h"
- #include "llimagegl.h"
- #include "llkeyboard.h" // For the MASK constants
- #include "lllineeditor.h"
- #include "lllocale.h"
- #include "lltextbox.h"
- #include "llwindow.h"
- static const std::string LL_MULTI_SLIDER_CTRL_TAG = "multi_slider";
- static LLRegisterWidget<LLMultiSliderCtrl> r11(LL_MULTI_SLIDER_CTRL_TAG);
- constexpr U32 MAX_STRING_LENGTH = 10;
- // Space between label, slider, and text
- constexpr S32 MULTI_SLIDERCTRL_SPACING = 4;
- constexpr S32 MULTI_THUMB_WIDTH = 8;
- constexpr S32 MULTI_TRACK_HEIGHT = 6;
- constexpr F32 FLOAT_THRESHOLD = 0.00001f;
- constexpr S32 EXTRA_TRIANGLE_WIDTH = 2;
- constexpr S32 EXTRA_TRIANGLE_HEIGHT = -2;
- ///////////////////////////////////////////////////////////////////////////////
- // LLMultiSlider class (slider UI element for the milti-slider)
- ///////////////////////////////////////////////////////////////////////////////
- S32 LLMultiSlider::mNameCounter = 0;
- LLMultiSlider::LLMultiSlider(const std::string& name, const LLRect& rect,
- void (*on_commit_callback)(LLUICtrl*, void*),
- void* callback_userdata, F32 initial_value,
- F32 min_value, F32 max_value, F32 increment,
- S32 max_sliders, F32 overlap_threshold,
- bool allow_overlap, bool loop_overlap,
- bool draw_track, bool use_triangle, bool vertical,
- const char* 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),
- mOverlapThreshold(overlap_threshold),
- mAllowOverlap(allow_overlap),
- mLoopOverlap(loop_overlap),
- mDrawTrack(draw_track),
- mUseTriangle(use_triangle),
- mVertical(vertical),
- mMouseOffset(0),
- mMouseDownCallback(NULL),
- mMouseUpCallback(NULL)
- {
- if (mVertical)
- {
- mDragStartThumbRect = LLRect(0, MULTI_THUMB_WIDTH,
- getRect().getWidth(), 0);
- }
- else
- {
- mDragStartThumbRect = LLRect(0, getRect().getHeight(),
- MULTI_THUMB_WIDTH, 0);
- }
- if (mOverlapThreshold && mOverlapThreshold > mIncrement)
- {
- mOverlapThreshold -= mIncrement;
- }
- else
- {
- mOverlapThreshold = 0.f;
- }
- // 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;
- }
- // Round to nearest increment (bias towards rounding down)
- value = llclamp(value, mMinValue, mMaxValue) - mMinValue +
- mIncrement / 2.0001f;
- value -= fmodf(value, mIncrement);
- F32 new_value = mMinValue + value;
- // Now, make sure no overlap if we want that
- if (!mAllowOverlap)
- {
- // Increment is our distance between points; use it to eliminate
- // rounding error.
- F32 threshold = mOverlapThreshold + mIncrement / 4.f;
- // If loop overlap is enabled, check if we overlap with points 'after'
- // max value (project to lower).
- F32 loop_up_check;
- if (mLoopOverlap && value + threshold > mMaxValue)
- {
- loop_up_check = value + threshold - mMaxValue + mMinValue;
- }
- else
- {
- loop_up_check = mMinValue - 1.f;
- }
- // If loop overlap is enabled, check if we overlap with points 'before'
- // min value (project to upper).
- F32 loop_down_check;
- if (mLoopOverlap && value - threshold < mMinValue)
- {
- loop_down_check = value - threshold - mMinValue + mMaxValue;
- }
- else
- {
- loop_down_check = mMaxValue + 1.f;
- }
- // Look at the current spot and see if anything is there
- for (LLSD::map_iterator it = mValue.beginMap(), end = mValue.endMap();
- it != end; ++it)
- {
- F32 loc_val = (F32)it->second.asReal();
- F32 test_val = loc_val - new_value;
- if (test_val > -threshold && test_val < threshold &&
- it->first != name)
- {
- // Already occupied !
- return;
- }
- if (mLoopOverlap &&
- (loc_val < loop_up_check || loc_val > loop_down_check))
- {
- return;
- }
- }
- }
- // Now set it in the map
- mValue[name] = new_value;
- // Set the control if it's the current slider and not from an event
- if (!from_event && name == mCurSlider)
- {
- setControlValue(mValue);
- }
- F32 t = (new_value - mMinValue) / (mMaxValue - mMinValue);
- if (mVertical)
- {
- S32 bottom_edge = MULTI_THUMB_WIDTH / 2;
- S32 top_edge = getRect().getHeight() - MULTI_THUMB_WIDTH / 2;
- S32 x = bottom_edge + S32(t * (top_edge - bottom_edge));
- mThumbRects[name].mTop = x + MULTI_THUMB_WIDTH / 2;
- mThumbRects[name].mBottom = x - MULTI_THUMB_WIDTH / 2;
- }
- else
- {
- 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;
- }
- }
- //virtual
- void LLMultiSlider::setValue(const LLSD& value)
- {
- // Only do if it is a map
- if (value.isMap())
- {
- // Add each value... the first in the map becomes the current
- LLSD::map_const_iterator it = value.beginMap();
- LLSD::map_const_iterator end = value.endMap();
- mCurSlider = it->first;
- for ( ; it != end; ++it)
- {
- setSliderValue(it->first, (F32)it->second.asReal(), true);
- }
- }
- }
- F32 LLMultiSlider::getSliderValue(const std::string& name) const
- {
- return mValue.has(name) ? (F32)mValue[name].asReal() : 0.f;
- }
- void LLMultiSlider::setCurSlider(const std::string& name)
- {
- if (mValue.has(name))
- {
- mCurSlider = name;
- }
- }
- F32 LLMultiSlider::getSliderValueFromPos(S32 xpos, S32 ypos) const
- {
- F32 t;
- if (mVertical)
- {
- S32 bottom_edge = MULTI_THUMB_WIDTH / 2;
- S32 top_edge = getRect().getHeight() - MULTI_THUMB_WIDTH / 2;
- ypos += mMouseOffset;
- ypos = llclamp(ypos, bottom_edge, top_edge);
- t = F32(ypos - bottom_edge) / F32(top_edge - bottom_edge);
- }
- else
- {
- S32 left_edge = MULTI_THUMB_WIDTH / 2;
- S32 right_edge = getRect().getWidth() - MULTI_THUMB_WIDTH / 2;
- xpos += mMouseOffset;
- xpos = llclamp(xpos, left_edge, right_edge);
- t = F32(xpos - left_edge) / F32(right_edge - left_edge);
- }
- return t * (mMaxValue - mMinValue) + mMinValue;
- }
- const std::string& LLMultiSlider::addSlider(F32 val)
- {
- F32 init_val = val;
- if (mValue.size() >= mMaxNumSliders)
- {
- return LLStringUtil::null;
- }
- // Create a new name
- std::string new_name = llformat("sldr%d", mNameCounter++);
- if (!findUnusedValue(init_val))
- {
- return LLStringUtil::null;
- }
- // Add a new thumb rect
- if (mVertical)
- {
- mThumbRects[new_name] = LLRect(0, MULTI_THUMB_WIDTH,
- getRect().getWidth(), 0);
- }
- else
- {
- mThumbRects[new_name] = LLRect(0, getRect().getHeight(),
- MULTI_THUMB_WIDTH, 0);
- }
- // Add the value and set the current slider to this one
- mValue.insert(new_name, init_val);
- mCurSlider = new_name;
- // Move the slider
- setSliderValue(mCurSlider, init_val, true);
- return mCurSlider;
- }
- bool LLMultiSlider::addSlider(F32 val, const std::string& name)
- {
- F32 init_val = val;
- if (mValue.size() >= mMaxNumSliders)
- {
- return false;
- }
- if (!findUnusedValue(init_val))
- {
- return false;
- }
- // Add a new thumb rect
- if (mVertical)
- {
- mThumbRects[name] = LLRect(0, MULTI_THUMB_WIDTH,
- getRect().getWidth(), 0);
- }
- else
- {
- mThumbRects[name] = LLRect(0, getRect().getHeight(),
- MULTI_THUMB_WIDTH, 0);
- }
- // Add the value and set the current slider to this one
- mValue.insert(name, init_val);
- mCurSlider = name;
- // Move the slider
- setSliderValue(mCurSlider, init_val, true);
- return true;
- }
- bool LLMultiSlider::findUnusedValue(F32& init_val)
- {
- bool first_try = 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.f;
- for (LLSD::map_iterator it = mValue.beginMap(), end = mValue.endMap();
- it != end; ++it)
- {
- F32 test_val = (F32)it->second.asReal() - init_val;
- if (test_val > -threshold && test_val < threshold)
- {
- hit = true;
- break;
- }
- }
- // If we found one
- if (!hit)
- {
- break;
- }
- // Increment and wrap if need be
- init_val += mIncrement;
- if (init_val > mMaxValue)
- {
- init_val = mMinValue;
- }
- // Stop if it is filled
- if (init_val == mInitialValue && !first_try)
- {
- llwarns << "Too many multi slider elements !" << llendl;
- return false;
- }
- first_try = false;
- continue;
- }
- return true;
- }
- void LLMultiSlider::deleteSlider(const std::string& name)
- {
- // Cannot delete the 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)
- {
- rect_map_t::iterator it = mThumbRects.end();
- mCurSlider = (--it)->first;
- }
- }
- //virtual
- void LLMultiSlider::clear()
- {
- while (mThumbRects.size() > 0 && mValue.size() > 0)
- {
- deleteCurSlider();
- }
- LLUICtrl::clear();
- }
- //virtual
- bool LLMultiSlider::handleHover(S32 x, S32 y, MASK mask)
- {
- if (gFocusMgr.getMouseCapture() == this)
- {
- setCurSliderValue(getSliderValueFromPos(x, y));
- onCommit();
- gWindowp->setCursor(UI_CURSOR_ARROW);
- LL_DEBUGS("UserInput") << "hover handled by " << getName()
- << " (active)" << LL_ENDL;
- }
- else
- {
- gWindowp->setCursor(UI_CURSOR_ARROW);
- LL_DEBUGS("UserInput") << "hover handled by " << getName()
- << " (inactive)" << LL_ENDL;
- }
- return true;
- }
- //virtual
- bool LLMultiSlider::handleMouseUp(S32 x, S32 y, MASK mask)
- {
- bool handled = false;
- if (gFocusMgr.getMouseCapture() == this)
- {
- gFocusMgr.setMouseCapture(NULL);
- if (mMouseUpCallback)
- {
- mMouseUpCallback(x, y, mCallbackUserData);
- }
- handled = true;
- make_ui_sound("UISndClickRelease");
- }
- else
- {
- handled = true;
- }
- return handled;
- }
- //virtual
- bool LLMultiSlider::handleMouseDown(S32 x, S32 y, MASK mask)
- {
- // Only do sticky-focus on non-chrome widgets
- if (!getIsChrome())
- {
- setFocus(true);
- }
- if (mMouseDownCallback)
- {
- mMouseDownCallback(x, y, mCallbackUserData);
- }
- if (mask & MASK_CONTROL) // if CTRL is modifying
- {
- setCurSliderValue(mInitialValue);
- onCommit();
- }
- else
- {
- // Scroll through thumbs to see if we have a new one selected and
- // select that one
- for (rect_map_t::iterator it = mThumbRects.begin(),
- end = mThumbRects.end();
- it != end; ++it)
- {
- // Check if inside. If so, set current slider and continue.
- if (it->second.pointInRect(x, y))
- {
- mCurSlider = it->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))
- {
- 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;
- }
- //virtual
- bool LLMultiSlider::handleKeyHere(KEY key, MASK mask)
- {
- switch (key)
- {
- case KEY_UP:
- if (mVertical)
- {
- setCurSliderValue(getCurSliderValue() + getIncrement());
- onCommit();
- }
- return true;
- case KEY_DOWN:
- if (mVertical)
- {
- setCurSliderValue(getCurSliderValue() - getIncrement());
- onCommit();
- }
- return true;
- case KEY_LEFT:
- if (!mVertical)
- {
- setCurSliderValue(getCurSliderValue() - getIncrement());
- onCommit();
- }
- return true;
- case KEY_RIGHT:
- if (!mVertical)
- {
- setCurSliderValue(getCurSliderValue() + getIncrement());
- onCommit();
- }
- return true;
- default:
- break;
- }
- return false;
- }
- //virtual
- void LLMultiSlider::draw()
- {
- rect_map_t::iterator it;
- rect_map_t::iterator begin = mThumbRects.begin();
- rect_map_t::iterator end = mThumbRects.end();
- rect_map_t::iterator cur_sldr_it;
- // Draw background and thumb.
- // Drawing solids requires texturing to be disabled
- gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
- LLRect rect(mDragStartThumbRect);
- F32 opacity = getEnabled() ? 1.f : 0.3f;
- // Track
- S32 height_offset = 0;
- S32 width_offset = 0;
- if (mVertical)
- {
- width_offset = (getRect().getWidth() - MULTI_TRACK_HEIGHT) / 2;
- }
- else
- {
- height_offset = (getRect().getHeight() - MULTI_TRACK_HEIGHT) / 2;
- }
- LLRect track_rect(width_offset, getRect().getHeight() - height_offset,
- getRect().getWidth() - width_offset, height_offset);
- if (mDrawTrack)
- {
- track_rect.stretch(-1);
- LLUIImage::sRoundedSquare->draw(track_rect,
- LLUI::sMultiSliderTrackColor %
- opacity);
- }
- // If we are supposed to use a drawn triangle simple GL call for the
- // triangle
- if (mUseTriangle)
- {
- for (it = begin; it != end; ++it)
- {
- gl_triangle_2d(it->second.mLeft - EXTRA_TRIANGLE_WIDTH,
- it->second.mTop + EXTRA_TRIANGLE_HEIGHT,
- it->second.mRight + EXTRA_TRIANGLE_WIDTH,
- it->second.mTop + EXTRA_TRIANGLE_HEIGHT,
- it->second.mLeft + it->second.getWidth() / 2,
- it->second.mBottom - EXTRA_TRIANGLE_HEIGHT,
- LLUI::sMultiSliderTriangleColor, true);
- }
- }
- else if (gFocusMgr.getMouseCapture() == this)
- {
- // Draw drag start
- LLUIImage::sRoundedSquare->drawSolid(mDragStartThumbRect,
- LLUI::sMultiSliderThumbCenterColor %
- 0.3f);
- // Draw the highlight
- if (hasFocus())
- {
- LLUIImage::sRoundedSquare->drawBorder(mThumbRects[mCurSlider],
- gFocusMgr.getFocusColor(),
- gFocusMgr.getFocusFlashWidth());
- }
- // Draw the thumbs
- cur_sldr_it = end;
- // Choose the color
- LLColor4 cur_thumb_col = LLUI::sMultiSliderThumbCenterColor;
- for (it = begin; it != end; ++it)
- {
- if (it->first == mCurSlider)
- {
- // Do not draw now, draw last
- cur_sldr_it = it;
- continue;
- }
- // The draw command
- LLUIImage::sRoundedSquare->drawSolid(it->second, cur_thumb_col);
- }
- // Draw current slider last
- if (cur_sldr_it != end)
- {
- LLUIImage::sRoundedSquare->drawSolid(cur_sldr_it->second,
- LLUI::sMultiSliderThumbCenterSelectedColor);
- }
- }
- else
- {
- // Draw highlight
- if (hasFocus())
- {
- LLUIImage::sRoundedSquare->drawBorder(mThumbRects[mCurSlider],
- gFocusMgr.getFocusColor(),
- gFocusMgr.getFocusFlashWidth());
- }
- // Draw thumbs
- cur_sldr_it = end;
- // Choose the color
- LLColor4 cur_thumb_col = LLUI::sMultiSliderThumbCenterColor % opacity;
- for (it = begin; it != end; ++it)
- {
- if (it->first == mCurSlider)
- {
- cur_sldr_it = it;
- continue;
- }
- LLUIImage::sRoundedSquare->drawSolid(it->second, cur_thumb_col);
- }
- // Draw current slider last
- if (cur_sldr_it != end)
- {
- LLUIImage::sRoundedSquare->drawSolid(cur_sldr_it->second,
- LLUI::sMultiSliderThumbCenterSelectedColor %
- opacity);
- }
- }
- LLUICtrl::draw();
- }
- ///////////////////////////////////////////////////////////////////////////////
- // LLMultiSliderCtrl class proper
- ///////////////////////////////////////////////////////////////////////////////
- LLMultiSliderCtrl::LLMultiSliderCtrl(const std::string& name,
- const LLRect& rect,
- const std::string& label,
- const LLFontGL* fontp,
- S32 label_width, S32 text_left,
- bool show_text, bool can_edit_text,
- void (*commit_callback)(LLUICtrl*, void*),
- void* callback_user_data,
- F32 initial_value, F32 min_value,
- F32 max_value, F32 increment,
- S32 max_sliders, F32 overlap_threshold,
- bool allow_overlap, bool loop_overlap,
- bool draw_track, bool use_triangle,
- bool vertical, const char* ctrl_name)
- : LLUICtrl(name, rect, true, commit_callback, callback_user_data),
- mFont(fontp),
- mShowText(show_text),
- mCanEditText(can_edit_text),
- mPrecision(3),
- mLabelBox(NULL),
- mLabelWidth(label_width),
- mEditor(NULL),
- mTextBox(NULL),
- mTextEnabledColor(LLUI::sColorsGroup->getColor("LabelTextColor")),
- mTextDisabledColor(LLUI::sColorsGroup->getColor("LabelDisabledColor")),
- mSliderMouseUpCallback(NULL),
- mSliderMouseDownCallback(NULL)
- {
- S32 top = getRect().getHeight();
- S32 bottom = 0;
- S32 left = 0;
- // Label
- if (!label.empty())
- {
- if (label_width == 0)
- {
- label_width = fontp->getWidth(label);
- }
- LLRect label_rect(left, top, label_width, bottom);
- mLabelBox = new LLTextBox("MultiSliderCtrl Label", label_rect, label,
- fontp);
- addChild(mLabelBox);
- }
- S32 slider_right = getRect().getWidth();
- if (show_text)
- {
- slider_right = text_left - MULTI_SLIDERCTRL_SPACING;
- }
- S32 slider_left = label_width ? label_width + MULTI_SLIDERCTRL_SPACING : 0;
- LLRect slider_rect(slider_left, top, slider_right, bottom);
- mMultiSlider = new LLMultiSlider(LL_MULTI_SLIDER_CTRL_TAG, slider_rect,
- LLMultiSliderCtrl::onSliderCommit, this,
- initial_value, min_value, max_value,
- increment, max_sliders, overlap_threshold,
- allow_overlap, loop_overlap, draw_track,
- use_triangle, vertical, ctrl_name);
- addChild(mMultiSlider);
- mCurValue = mMultiSlider->getCurSliderValue();
- if (show_text)
- {
- LLRect text_rect(text_left, top, getRect().getWidth(), bottom);
- if (can_edit_text)
- {
- mEditor = new LLLineEditor("MultiSliderCtrl Editor", text_rect,
- LLStringUtil::null, fontp,
- MAX_STRING_LENGTH,
- &LLMultiSliderCtrl::onEditorCommit,
- NULL, NULL, this,
- &LLLineEditor::prevalidateFloat);
- mEditor->setFollowsLeft();
- mEditor->setFollowsBottom();
- mEditor->setFocusReceivedCallback(&LLMultiSliderCtrl::onEditorGainFocus,
- this);
- mEditor->setIgnoreTab(true);
- #if 0 // Do not do this, as selecting the entire text is single clicking
- // in some cases and double clicking in others:
- mEditor->setSelectAllonFocusReceived(true);
- #endif
- addChild(mEditor);
- }
- else
- {
- mTextBox = new LLTextBox("MultiSliderCtrl Text", text_rect,
- LLStringUtil::null, fontp);
- mTextBox->setFollowsLeft();
- mTextBox->setFollowsBottom();
- addChild(mTextBox);
- }
- }
- updateText();
- }
- //virtual
- LLMultiSliderCtrl::~LLMultiSliderCtrl()
- {
- // Children all cleaned up by default view destructor.
- }
- //static
- void LLMultiSliderCtrl::onEditorGainFocus(LLFocusableElement* caller,
- void* userdata)
- {
- LLMultiSliderCtrl* self = (LLMultiSliderCtrl*) userdata;
- llassert(caller == self->mEditor);
- self->onFocusReceived();
- }
- //virtual
- void LLMultiSliderCtrl::setValue(const LLSD& value)
- {
- mMultiSlider->setValue(value);
- mCurValue = mMultiSlider->getCurSliderValue();
- updateText();
- }
- void LLMultiSliderCtrl::setSliderValue(const std::string& name, F32 v,
- bool from_event)
- {
- mMultiSlider->setSliderValue(name, v, from_event);
- mCurValue = mMultiSlider->getCurSliderValue();
- updateText();
- }
- void LLMultiSliderCtrl::setCurSlider(const std::string& name)
- {
- mMultiSlider->setCurSlider(name);
- mCurValue = mMultiSlider->getCurSliderValue();
- }
- void LLMultiSliderCtrl::setLabel(const std::string& label)
- {
- if (mLabelBox)
- {
- mLabelBox->setText(label);
- }
- }
- //virtual
- bool LLMultiSliderCtrl::setLabelArg(const std::string& key,
- const std::string& text)
- {
- bool res = false;
- if (mLabelBox)
- {
- res = mLabelBox->setTextArg(key, text);
- if (res && mLabelWidth == 0)
- {
- S32 label_width = mFont->getWidth(mLabelBox->getText());
- LLRect rect = mLabelBox->getRect();
- S32 prev_right = rect.mRight;
- rect.mRight = rect.mLeft + label_width;
- mLabelBox->setRect(rect);
- S32 delta = rect.mRight - prev_right;
- rect = mMultiSlider->getRect();
- S32 left = rect.mLeft + delta;
- left = llclamp(left, 0, rect.mRight - MULTI_SLIDERCTRL_SPACING);
- rect.mLeft = left;
- mMultiSlider->setRect(rect);
- }
- }
- return res;
- }
- const std::string& LLMultiSliderCtrl::addSlider()
- {
- const std::string& name = mMultiSlider->addSlider();
- // If it returns null, pass it on
- if (name == LLStringUtil::null)
- {
- return LLStringUtil::null;
- }
- // Otherwise, update stuff
- mCurValue = mMultiSlider->getCurSliderValue();
- updateText();
- return name;
- }
- const std::string& LLMultiSliderCtrl::addSlider(F32 val)
- {
- const std::string& name = mMultiSlider->addSlider(val);
- // If it returns empty, pass it on
- if (name.empty())
- {
- return LLStringUtil::null;
- }
- // Otherwise, update stuff
- mCurValue = mMultiSlider->getCurSliderValue();
- updateText();
- return name;
- }
- bool LLMultiSliderCtrl::addSlider(F32 val, const std::string& name)
- {
- bool res = mMultiSlider->addSlider(val, name);
- if (res)
- {
- mCurValue = mMultiSlider->getCurSliderValue();
- updateText();
- }
- return res;
- }
- void LLMultiSliderCtrl::deleteSlider(const std::string& name)
- {
- mMultiSlider->deleteSlider(name);
- mCurValue = mMultiSlider->getCurSliderValue();
- updateText();
- }
- //virtual
- void LLMultiSliderCtrl::clear()
- {
- setCurSliderValue(0.f);
- if (mEditor)
- {
- mEditor->setText("");
- }
- if (mTextBox)
- {
- mTextBox->setText("");
- }
- // Get rid of sliders
- mMultiSlider->clear();
- }
- bool LLMultiSliderCtrl::isMouseHeldDown()
- {
- return gFocusMgr.getMouseCapture() == mMultiSlider;
- }
- void LLMultiSliderCtrl::updateText()
- {
- if (mEditor || mTextBox)
- {
- LLLocale locale(LLLocale::USER_LOCALE);
- // Do not display very small negative values as -0.000
- F32 displayed_value = floorf(getCurSliderValue() *
- powf(10.f, mPrecision) + 0.5f) /
- powf(10.f, mPrecision);
- std::string format = llformat("%%.%df", mPrecision);
- std::string text = llformat(format.c_str(), displayed_value);
- if (mEditor)
- {
- mEditor->setText(text);
- }
- else
- {
- mTextBox->setText(text);
- }
- }
- }
- //static
- void LLMultiSliderCtrl::onEditorCommit(LLUICtrl* caller, void* userdata)
- {
- LLMultiSliderCtrl* self = (LLMultiSliderCtrl*) userdata;
- llassert(caller == self->mEditor);
- F32 val = self->mCurValue;
- F32 saved_val = self->mCurValue;
- bool success = false;
- std::string text = self->mEditor->getText();
- if (LLLineEditor::postvalidateFloat(text))
- {
- LLLocale locale(LLLocale::USER_LOCALE);
- val = (F32) atof(text.c_str());
- if (self->mMultiSlider->getMinValue() <= val &&
- val <= self->mMultiSlider->getMaxValue())
- {
- if (self->mValidateCallback)
- {
- // Set the value temporarily so that the callback can retrieve
- // it:
- self->setCurSliderValue(val);
- if (self->mValidateCallback(self, self->mCallbackUserData))
- {
- success = true;
- }
- }
- else
- {
- self->setCurSliderValue(val);
- success = true;
- }
- }
- }
- if (success)
- {
- self->onCommit();
- }
- else
- {
- if (self->getCurSliderValue() != saved_val)
- {
- self->setCurSliderValue(saved_val);
- }
- self->reportInvalidData();
- }
- self->updateText();
- }
- //static
- void LLMultiSliderCtrl::onSliderCommit(LLUICtrl* caller, void* userdata)
- {
- LLMultiSliderCtrl* self = (LLMultiSliderCtrl*)userdata;
- F32 saved_val = self->mCurValue;
- F32 new_val = self->mMultiSlider->getCurSliderValue();
- bool success = false;
- if (self->mValidateCallback)
- {
- // Set the value temporarily so that the callback can retrieve it:
- self->mCurValue = new_val;
- if (self->mValidateCallback(self, self->mCallbackUserData))
- {
- success = true;
- }
- }
- else
- {
- self->mCurValue = new_val;
- success = true;
- }
- if (success)
- {
- self->onCommit();
- }
- else
- {
- if (self->mCurValue != saved_val)
- {
- self->setCurSliderValue(saved_val);
- }
- self->reportInvalidData();
- }
- self->updateText();
- }
- //virtual
- void LLMultiSliderCtrl::setEnabled(bool b)
- {
- LLUICtrl::setEnabled(b);
- if (mLabelBox)
- {
- mLabelBox->setColor(b ? mTextEnabledColor : mTextDisabledColor);
- }
- mMultiSlider->setEnabled(b);
- if (mEditor)
- {
- mEditor->setEnabled(b);
- }
- if (mTextBox)
- {
- mTextBox->setColor(b ? mTextEnabledColor : mTextDisabledColor);
- }
- }
- //virtual
- void LLMultiSliderCtrl::setTentative(bool b)
- {
- if (mEditor)
- {
- mEditor->setTentative(b);
- }
- LLUICtrl::setTentative(b);
- }
- //virtual
- void LLMultiSliderCtrl::onCommit()
- {
- setTentative(false);
- if (mEditor)
- {
- mEditor->setTentative(false);
- }
- LLUICtrl::onCommit();
- }
- void LLMultiSliderCtrl::setPrecision(S32 precision)
- {
- if (precision < 0 || precision > 10)
- {
- S32 p = precision;
- precision = llclamp(precision, 0, 10);
- llwarns << "Precision out of range: " << p << " - clamped to: "
- << precision << llendl;
- }
- mPrecision = precision;
- updateText();
- }
- void LLMultiSliderCtrl::setSliderMouseDownCallback(void (*cb)(S32, S32,
- void* userdata))
- {
- mSliderMouseDownCallback = cb;
- mMultiSlider->setMouseDownCallback(LLMultiSliderCtrl::onSliderMouseDown);
- }
- //static
- void LLMultiSliderCtrl::onSliderMouseDown(S32 x, S32 y, void* userdata)
- {
- LLMultiSliderCtrl* self = (LLMultiSliderCtrl*)userdata;
- if (self->mSliderMouseDownCallback)
- {
- self->mSliderMouseDownCallback(x, y, self->mCallbackUserData);
- }
- }
- void LLMultiSliderCtrl::setSliderMouseUpCallback(void (*cb)(S32, S32,
- void* userdata))
- {
- mSliderMouseUpCallback = cb;
- mMultiSlider->setMouseUpCallback(LLMultiSliderCtrl::onSliderMouseUp);
- }
- //static
- void LLMultiSliderCtrl::onSliderMouseUp(S32 x, S32 y, void* userdata)
- {
- LLMultiSliderCtrl* self = (LLMultiSliderCtrl*)userdata;
- if (self->mSliderMouseUpCallback)
- {
- self->mSliderMouseUpCallback(x, y, self->mCallbackUserData);
- }
- }
- //virtual
- void LLMultiSliderCtrl::onTabInto()
- {
- if (mEditor)
- {
- mEditor->onTabInto();
- }
- }
- void LLMultiSliderCtrl::reportInvalidData()
- {
- make_ui_sound("UISndBadKeystroke");
- }
- //virtual
- const std::string& LLMultiSliderCtrl::getControlName() const
- {
- return mMultiSlider->getControlName();
- }
- //virtual
- void LLMultiSliderCtrl::setControlName(const char* ctr_name, LLView* context)
- {
- mMultiSlider->setControlName(ctr_name, context);
- }
- //virtual
- const std::string& LLMultiSliderCtrl::getTag() const
- {
- return LL_MULTI_SLIDER_CTRL_TAG;
- }
- //virtual
- LLXMLNodePtr LLMultiSliderCtrl::getXML(bool save_children) const
- {
- LLXMLNodePtr node = LLUICtrl::getXML();
- node->setName(LL_MULTI_SLIDER_CTRL_TAG);
- node->createChild("show_text", true)->setBoolValue(mShowText);
- node->createChild("can_edit_text", true)->setBoolValue(mCanEditText);
- node->createChild("decimal_digits", true)->setIntValue(mPrecision);
- if (mLabelBox)
- {
- node->createChild("label", true)->setStringValue(mLabelBox->getText());
- }
- // TomY TODO: Do we really want to export the transient state of the
- // slider ?
- node->createChild("value", true)->setFloatValue(mCurValue);
- node->createChild("initial_val",
- true)->setFloatValue(mMultiSlider->getInitialValue());
- node->createChild("min_val",
- true)->setFloatValue(mMultiSlider->getMinValue());
- node->createChild("max_val",
- true)->setFloatValue(mMultiSlider->getMaxValue());
- node->createChild("increment",
- true)->setFloatValue(mMultiSlider->getIncrement());
- node->createChild("max_sliders",
- true)->setFloatValue(mMultiSlider->mMaxNumSliders);
- if (mMultiSlider->mOverlapThreshold)
- {
- F32 actual = mMultiSlider->mOverlapThreshold +
- mMultiSlider->getIncrement();
- node->createChild("overlap_threshold", true)->setFloatValue(actual);
- }
- if (mMultiSlider->mAllowOverlap)
- {
- node->createChild("allow_overlap", true)->setBoolValue(true);
- }
- if (mMultiSlider->mLoopOverlap)
- {
- node->createChild("loop_overlap", true)->setBoolValue(true);
- }
- if (!mMultiSlider->mDrawTrack)
- {
- node->createChild("draw_track", true)->setBoolValue(false);
- }
- if (mMultiSlider->mUseTriangle)
- {
- node->createChild("use_triangle", true)->setBoolValue(true);
- }
- if (mMultiSlider->mVertical)
- {
- node->createChild("orientation", true)->setStringValue("vertical");
- }
- addColorXML(node, mTextEnabledColor, "text_enabled_color",
- "LabelTextColor");
- addColorXML(node, mTextDisabledColor, "text_disabled_color",
- "LabelDisabledColor");
- return node;
- }
- //virtual
- LLView* LLMultiSliderCtrl::fromXML(LLXMLNodePtr node, LLView* parent,
- LLUICtrlFactory* factory)
- {
- std::string name = LL_MULTI_SLIDER_CTRL_TAG;
- node->getAttributeString("name", name);
- std::string label;
- node->getAttributeString("label", label);
- LLRect rect;
- createRect(node, rect, parent, LLRect());
- LLFontGL* fontp = LLView::selectFont(node);
- // *HACK: font might not be specified.
- if (!fontp)
- {
- fontp = LLFontGL::getFontSansSerifSmall();
- }
- S32 label_width = 0;
- node->getAttributeS32("label_width", label_width);
- bool show_text = true;
- node->getAttributeBool("show_text", show_text);
- bool can_edit_text = false;
- node->getAttributeBool("can_edit_text", can_edit_text);
- F32 overlap_threshold = 0.f;
- node->getAttributeF32("overlap_threshold", overlap_threshold);
- bool allow_overlap = false;
- node->getAttributeBool("allow_overlap", allow_overlap);
- bool loop_overlap = false;
- node->getAttributeBool("loop_overlap", loop_overlap);
- bool draw_track = true;
- node->getAttributeBool("draw_track", draw_track);
- bool use_triangle = false;
- node->getAttributeBool("use_triangle", use_triangle);
- 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);
- U32 precision = 3;
- node->getAttributeU32("decimal_digits", precision);
- S32 max_sliders = 1;
- node->getAttributeS32("max_sliders", max_sliders);
- std::string orientation;
- node->getAttributeString("orientation", orientation);
- S32 text_left = 0;
- if (show_text)
- {
- // Calculate the size of the text box (log max_value is number of
- // digits - 1 so plus 1)
- if (max_value)
- {
- text_left = fontp->getWidth("0") *
- (static_cast<S32>(log10f(max_value)) + precision + 1);
- }
- if (increment < 1.f)
- {
- // (mostly) take account of decimal point in value
- text_left += fontp->getWidth(".");
- }
- if (min_value < 0.f || max_value < 0.f)
- {
- // (mostly) take account of minus sign
- text_left += fontp->getWidth("-");
- }
- // Padding to make things look nicer
- text_left += 8;
- }
- LLUICtrlCallback callback = NULL;
- if (label.empty())
- {
- label.assign(node->getTextContents());
- }
- LLMultiSliderCtrl* slider =
- new LLMultiSliderCtrl(name, rect, label, fontp, label_width,
- rect.getWidth() - text_left, show_text,
- can_edit_text, callback, NULL, initial_value,
- min_value, max_value, increment, max_sliders,
- overlap_threshold, allow_overlap, loop_overlap,
- draw_track, use_triangle,
- orientation == "vertical");
- slider->setPrecision(precision);
- slider->initFromXML(node, parent);
- slider->updateText();
- return slider;
- }
|