00001
00032 #include "linden_common.h"
00033
00034 #include "llmultislider.h"
00035 #include "llui.h"
00036
00037 #include "llgl.h"
00038 #include "llwindow.h"
00039 #include "llfocusmgr.h"
00040 #include "llkeyboard.h"
00041 #include "llcontrol.h"
00042 #include "llimagegl.h"
00043
00044 #include <sstream>
00045
00046 static LLRegisterWidget<LLMultiSlider> r("multi_slider_bar");
00047
00048 const S32 MULTI_THUMB_WIDTH = 8;
00049 const S32 MULTI_TRACK_HEIGHT = 6;
00050 const F32 FLOAT_THRESHOLD = 0.00001f;
00051 const S32 EXTRA_TRIANGLE_WIDTH = 2;
00052 const S32 EXTRA_TRIANGLE_HEIGHT = -2;
00053
00054 S32 LLMultiSlider::mNameCounter = 0;
00055
00056 LLMultiSlider::LLMultiSlider(
00057 const LLString& name,
00058 const LLRect& rect,
00059 void (*on_commit_callback)(LLUICtrl* ctrl, void* userdata),
00060 void* callback_userdata,
00061 F32 initial_value,
00062 F32 min_value,
00063 F32 max_value,
00064 F32 increment,
00065 S32 max_sliders,
00066 BOOL allow_overlap,
00067 BOOL draw_track,
00068 BOOL use_triangle,
00069 const LLString& control_name)
00070 :
00071 LLUICtrl( name, rect, TRUE, on_commit_callback, callback_userdata,
00072 FOLLOWS_LEFT | FOLLOWS_TOP),
00073
00074 mInitialValue( initial_value ),
00075 mMinValue( min_value ),
00076 mMaxValue( max_value ),
00077 mIncrement( increment ),
00078 mMaxNumSliders(max_sliders),
00079 mAllowOverlap(allow_overlap),
00080 mDrawTrack(draw_track),
00081 mUseTriangle(use_triangle),
00082 mMouseOffset( 0 ),
00083 mDragStartThumbRect( 0, getRect().getHeight(), MULTI_THUMB_WIDTH, 0 ),
00084 mTrackColor( LLUI::sColorsGroup->getColor( "MultiSliderTrackColor" ) ),
00085 mThumbOutlineColor( LLUI::sColorsGroup->getColor( "MultiSliderThumbOutlineColor" ) ),
00086 mThumbCenterColor( LLUI::sColorsGroup->getColor( "MultiSliderThumbCenterColor" ) ),
00087 mThumbCenterSelectedColor( LLUI::sColorsGroup->getColor( "MultiSliderThumbCenterSelectedColor" ) ),
00088 mDisabledThumbColor(LLUI::sColorsGroup->getColor( "MultiSliderDisabledThumbColor" ) ),
00089 mTriangleColor(LLUI::sColorsGroup->getColor( "MultiSliderTriangleColor" ) ),
00090 mMouseDownCallback( NULL ),
00091 mMouseUpCallback( NULL )
00092 {
00093 mValue.emptyMap();
00094 mCurSlider = LLString::null;
00095
00096
00097
00098
00099 setControlName(control_name, NULL);
00100 setValue(getValue());
00101 }
00102
00103 void LLMultiSlider::setSliderValue(const LLString& name, F32 value, BOOL from_event)
00104 {
00105
00106 if(!mValue.has(name)) {
00107 return;
00108 }
00109
00110 value = llclamp( value, mMinValue, mMaxValue );
00111
00112
00113 value -= mMinValue;
00114 value += mIncrement/2.0001f;
00115 value -= fmod(value, mIncrement);
00116 F32 newValue = mMinValue + value;
00117
00118
00119
00120 if(!mAllowOverlap) {
00121 bool hit = false;
00122
00123
00124
00125 LLSD::map_iterator mIt = mValue.beginMap();
00126 for(;mIt != mValue.endMap(); mIt++) {
00127
00128 F32 testVal = (F32)mIt->second.asReal() - newValue;
00129 if(testVal > -FLOAT_THRESHOLD && testVal < FLOAT_THRESHOLD &&
00130 mIt->first != name) {
00131 hit = true;
00132 break;
00133 }
00134 }
00135
00136
00137 if(hit) {
00138 return;
00139 }
00140 }
00141
00142
00143
00144 mValue[name] = newValue;
00145
00146
00147 if (!from_event && name == mCurSlider)
00148 {
00149 setControlValue(mValue);
00150 }
00151
00152 F32 t = (newValue - mMinValue) / (mMaxValue - mMinValue);
00153
00154 S32 left_edge = MULTI_THUMB_WIDTH/2;
00155 S32 right_edge = getRect().getWidth() - (MULTI_THUMB_WIDTH/2);
00156
00157 S32 x = left_edge + S32( t * (right_edge - left_edge) );
00158 mThumbRects[name].mLeft = x - (MULTI_THUMB_WIDTH/2);
00159 mThumbRects[name].mRight = x + (MULTI_THUMB_WIDTH/2);
00160 }
00161
00162 void LLMultiSlider::setValue(const LLSD& value)
00163 {
00164
00165 if(value.isMap()) {
00166
00167
00168 LLSD::map_const_iterator mIt = value.beginMap();
00169 mCurSlider = mIt->first;
00170
00171 for(; mIt != value.endMap(); mIt++) {
00172 setSliderValue(mIt->first, (F32)mIt->second.asReal(), TRUE);
00173 }
00174 }
00175 }
00176
00177 F32 LLMultiSlider::getSliderValue(const LLString& name) const
00178 {
00179 return (F32)mValue[name].asReal();
00180 }
00181
00182 void LLMultiSlider::setCurSlider(const LLString& name)
00183 {
00184 if(mValue.has(name)) {
00185 mCurSlider = name;
00186 }
00187 }
00188
00189 const LLString& LLMultiSlider::addSlider()
00190 {
00191 return addSlider(mInitialValue);
00192 }
00193
00194 const LLString& LLMultiSlider::addSlider(F32 val)
00195 {
00196 std::stringstream newName;
00197 F32 initVal = val;
00198
00199 if(mValue.size() >= mMaxNumSliders) {
00200 return LLString::null;
00201 }
00202
00203
00204 newName << "sldr" << mNameCounter;
00205 mNameCounter++;
00206
00207 bool foundOne = findUnusedValue(initVal);
00208 if(!foundOne) {
00209 return LLString::null;
00210 }
00211
00212
00213 mThumbRects[newName.str()] = LLRect( 0, getRect().getHeight(), MULTI_THUMB_WIDTH, 0 );
00214
00215
00216 mValue.insert(newName.str(), initVal);
00217 mCurSlider = newName.str();
00218
00219
00220 setSliderValue(mCurSlider, initVal, TRUE);
00221
00222 return mCurSlider;
00223 }
00224
00225 bool LLMultiSlider::findUnusedValue(F32& initVal)
00226 {
00227 bool firstTry = true;
00228
00229
00230
00231 while(true) {
00232
00233 bool hit = false;
00234
00235
00236
00237 LLSD::map_iterator mIt = mValue.beginMap();
00238 for(;mIt != mValue.endMap(); mIt++) {
00239
00240 F32 testVal = (F32)mIt->second.asReal() - initVal;
00241 if(testVal > -FLOAT_THRESHOLD && testVal < FLOAT_THRESHOLD) {
00242 hit = true;
00243 break;
00244 }
00245 }
00246
00247
00248 if(!hit) {
00249 break;
00250 }
00251
00252
00253 initVal += mIncrement;
00254 if(initVal > mMaxValue) {
00255 initVal = mMinValue;
00256 }
00257
00258
00259 if(initVal == mInitialValue && !firstTry) {
00260 llwarns << "Whoa! Too many multi slider elements to add one to" << llendl;
00261 return false;
00262 }
00263
00264 firstTry = false;
00265 continue;
00266 }
00267
00268 return true;
00269 }
00270
00271
00272 void LLMultiSlider::deleteSlider(const LLString& name)
00273 {
00274
00275 if(mValue.size() <= 0) {
00276 return;
00277 }
00278
00279
00280 mValue.erase(name);
00281 mThumbRects.erase(name);
00282
00283
00284 if(mValue.size() > 0) {
00285 std::map<LLString, LLRect>::iterator mIt = mThumbRects.end();
00286 mIt--;
00287 mCurSlider = mIt->first;
00288 }
00289 }
00290
00291 void LLMultiSlider::clear()
00292 {
00293 while(mThumbRects.size() > 0) {
00294 deleteCurSlider();
00295 }
00296
00297 LLUICtrl::clear();
00298 }
00299
00300 BOOL LLMultiSlider::handleHover(S32 x, S32 y, MASK mask)
00301 {
00302 if( gFocusMgr.getMouseCapture() == this )
00303 {
00304 S32 left_edge = MULTI_THUMB_WIDTH/2;
00305 S32 right_edge = getRect().getWidth() - (MULTI_THUMB_WIDTH/2);
00306
00307 x += mMouseOffset;
00308 x = llclamp( x, left_edge, right_edge );
00309
00310 F32 t = F32(x - left_edge) / (right_edge - left_edge);
00311 setCurSliderValue(t * (mMaxValue - mMinValue) + mMinValue );
00312 onCommit();
00313
00314 getWindow()->setCursor(UI_CURSOR_ARROW);
00315 lldebugst(LLERR_USER_INPUT) << "hover handled by " << getName() << " (active)" << llendl;
00316 }
00317 else
00318 {
00319 getWindow()->setCursor(UI_CURSOR_ARROW);
00320 lldebugst(LLERR_USER_INPUT) << "hover handled by " << getName() << " (inactive)" << llendl;
00321 }
00322 return TRUE;
00323 }
00324
00325 BOOL LLMultiSlider::handleMouseUp(S32 x, S32 y, MASK mask)
00326 {
00327 BOOL handled = FALSE;
00328
00329 if( gFocusMgr.getMouseCapture() == this )
00330 {
00331 gFocusMgr.setMouseCapture( NULL );
00332
00333 if( mMouseUpCallback )
00334 {
00335 mMouseUpCallback( this, mCallbackUserData );
00336 }
00337 handled = TRUE;
00338 make_ui_sound("UISndClickRelease");
00339 }
00340 else
00341 {
00342 handled = TRUE;
00343 }
00344
00345 return handled;
00346 }
00347
00348 BOOL LLMultiSlider::handleMouseDown(S32 x, S32 y, MASK mask)
00349 {
00350
00351 if (!getIsChrome())
00352 {
00353 setFocus(TRUE);
00354 }
00355 if( mMouseDownCallback )
00356 {
00357 mMouseDownCallback( this, mCallbackUserData );
00358 }
00359
00360 if (MASK_CONTROL & mask)
00361 {
00362 setCurSliderValue(mInitialValue);
00363 onCommit();
00364 }
00365 else
00366 {
00367
00368 std::map<LLString, LLRect>::iterator mIt = mThumbRects.begin();
00369 for(; mIt != mThumbRects.end(); mIt++) {
00370
00371
00372 if(mIt->second.pointInRect(x,y)) {
00373 mCurSlider = mIt->first;
00374 break;
00375 }
00376 }
00377
00378
00379 if (mThumbRects[mCurSlider].pointInRect(x,y))
00380 {
00381 mMouseOffset = (mThumbRects[mCurSlider].mLeft + MULTI_THUMB_WIDTH/2) - x;
00382 }
00383 else
00384 {
00385 mMouseOffset = 0;
00386 }
00387
00388
00389
00390 gFocusMgr.setMouseCapture( this );
00391 mDragStartThumbRect = mThumbRects[mCurSlider];
00392 }
00393 make_ui_sound("UISndClick");
00394
00395 return TRUE;
00396 }
00397
00398 BOOL LLMultiSlider::handleKeyHere(KEY key, MASK mask)
00399 {
00400 BOOL handled = FALSE;
00401 switch(key)
00402 {
00403 case KEY_UP:
00404 case KEY_DOWN:
00405
00406 handled = TRUE;
00407 break;
00408 case KEY_LEFT:
00409 setCurSliderValue(getCurSliderValue() - getIncrement());
00410 onCommit();
00411 handled = TRUE;
00412 break;
00413 case KEY_RIGHT:
00414 setCurSliderValue(getCurSliderValue() + getIncrement());
00415 onCommit();
00416 handled = TRUE;
00417 break;
00418 default:
00419 break;
00420 }
00421 return handled;
00422 }
00423
00424 void LLMultiSlider::draw()
00425 {
00426 LLColor4 curThumbColor;
00427
00428 std::map<LLString, LLRect>::iterator mIt;
00429 std::map<LLString, LLRect>::iterator curSldrIt;
00430
00431
00432
00433
00434 LLGLSNoTexture no_texture;
00435
00436 LLRect rect(mDragStartThumbRect);
00437
00438 F32 opacity = getEnabled() ? 1.f : 0.3f;
00439
00440
00441 LLUIImagePtr thumb_imagep = LLUI::sImageProvider->getUIImage("rounded_square.tga");
00442
00443 S32 height_offset = (getRect().getHeight() - MULTI_TRACK_HEIGHT) / 2;
00444 LLRect track_rect(0, getRect().getHeight() - height_offset, getRect().getWidth(), height_offset );
00445
00446
00447 if(mDrawTrack)
00448 {
00449 track_rect.stretch(-1);
00450 thumb_imagep->draw(track_rect, mTrackColor % opacity);
00451 }
00452
00453
00454
00455 if(mUseTriangle) {
00456
00457 for(mIt = mThumbRects.begin(); mIt != mThumbRects.end(); mIt++) {
00458
00459 gl_triangle_2d(
00460 mIt->second.mLeft - EXTRA_TRIANGLE_WIDTH,
00461 mIt->second.mTop + EXTRA_TRIANGLE_HEIGHT,
00462 mIt->second.mRight + EXTRA_TRIANGLE_WIDTH,
00463 mIt->second.mTop + EXTRA_TRIANGLE_HEIGHT,
00464 mIt->second.mLeft + mIt->second.getWidth() / 2,
00465 mIt->second.mBottom - EXTRA_TRIANGLE_HEIGHT,
00466 mTriangleColor, TRUE);
00467 }
00468 }
00469 else if (!thumb_imagep)
00470 {
00471
00472 curSldrIt = mThumbRects.end();
00473 for(mIt = mThumbRects.begin(); mIt != mThumbRects.end(); mIt++) {
00474
00475
00476 curThumbColor = mThumbCenterColor;
00477 if(mIt->first == mCurSlider) {
00478
00479 curSldrIt = mIt;
00480 continue;
00481
00482 }
00483
00484
00485 gl_rect_2d(mIt->second, curThumbColor, TRUE);
00486 }
00487
00488
00489 if(curSldrIt != mThumbRects.end()) {
00490 gl_rect_2d(curSldrIt->second, mThumbCenterSelectedColor, TRUE);
00491 }
00492
00493
00494 if (gFocusMgr.getMouseCapture() == this)
00495 {
00496 gl_rect_2d(mDragStartThumbRect, mThumbCenterColor % opacity, FALSE);
00497 }
00498 }
00499 else if( gFocusMgr.getMouseCapture() == this )
00500 {
00501
00502 thumb_imagep->drawSolid(mDragStartThumbRect, mThumbCenterColor % 0.3f);
00503
00504
00505 if (hasFocus())
00506 {
00507 thumb_imagep->drawBorder(mThumbRects[mCurSlider], gFocusMgr.getFocusColor(), gFocusMgr.getFocusFlashWidth());
00508 }
00509
00510
00511 curSldrIt = mThumbRects.end();
00512 for(mIt = mThumbRects.begin(); mIt != mThumbRects.end(); mIt++)
00513 {
00514
00515 curThumbColor = mThumbCenterColor;
00516 if(mIt->first == mCurSlider)
00517 {
00518
00519 curSldrIt = mIt;
00520 continue;
00521 }
00522
00523
00524 thumb_imagep->drawSolid(mIt->second, curThumbColor);
00525 }
00526
00527
00528 if(curSldrIt != mThumbRects.end())
00529 {
00530 thumb_imagep->drawSolid(curSldrIt->second, mThumbCenterSelectedColor);
00531 }
00532
00533 }
00534 else
00535 {
00536
00537 if (hasFocus())
00538 {
00539 thumb_imagep->drawBorder(mThumbRects[mCurSlider], gFocusMgr.getFocusColor(), gFocusMgr.getFocusFlashWidth());
00540 }
00541
00542
00543 curSldrIt = mThumbRects.end();
00544 for(mIt = mThumbRects.begin(); mIt != mThumbRects.end(); mIt++)
00545 {
00546
00547
00548 curThumbColor = mThumbCenterColor;
00549 if(mIt->first == mCurSlider)
00550 {
00551 curSldrIt = mIt;
00552 continue;
00553
00554 }
00555
00556 thumb_imagep->drawSolid(mIt->second, curThumbColor % opacity);
00557 }
00558
00559 if(curSldrIt != mThumbRects.end())
00560 {
00561 thumb_imagep->drawSolid(curSldrIt->second, mThumbCenterSelectedColor % opacity);
00562 }
00563 }
00564
00565 LLUICtrl::draw();
00566 }
00567
00568
00569 LLXMLNodePtr LLMultiSlider::getXML(bool save_children) const
00570 {
00571 LLXMLNodePtr node = LLUICtrl::getXML();
00572
00573 node->createChild("initial_val", TRUE)->setFloatValue(getInitialValue());
00574 node->createChild("min_val", TRUE)->setFloatValue(getMinValue());
00575 node->createChild("max_val", TRUE)->setFloatValue(getMaxValue());
00576 node->createChild("increment", TRUE)->setFloatValue(getIncrement());
00577
00578 return node;
00579 }
00580
00581
00582
00583 LLView* LLMultiSlider::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory)
00584 {
00585 LLString name("multi_slider_bar");
00586 node->getAttributeString("name", name);
00587
00588 LLRect rect;
00589 createRect(node, rect, parent, LLRect());
00590
00591 F32 initial_value = 0.f;
00592 node->getAttributeF32("initial_val", initial_value);
00593
00594 F32 min_value = 0.f;
00595 node->getAttributeF32("min_val", min_value);
00596
00597 F32 max_value = 1.f;
00598 node->getAttributeF32("max_val", max_value);
00599
00600 F32 increment = 0.1f;
00601 node->getAttributeF32("increment", increment);
00602
00603 S32 max_sliders = 1;
00604 node->getAttributeS32("max_sliders", max_sliders);
00605
00606 BOOL allow_overlap = FALSE;
00607 node->getAttributeBOOL("allow_overlap", allow_overlap);
00608
00609 BOOL draw_track = TRUE;
00610 node->getAttributeBOOL("draw_track", draw_track);
00611
00612 BOOL use_triangle = FALSE;
00613 node->getAttributeBOOL("use_triangle", use_triangle);
00614
00615 LLMultiSlider* multiSlider = new LLMultiSlider(name,
00616 rect,
00617 NULL,
00618 NULL,
00619 initial_value,
00620 min_value,
00621 max_value,
00622 increment,
00623 max_sliders,
00624 allow_overlap,
00625 draw_track,
00626 use_triangle);
00627
00628 multiSlider->initFromXML(node, parent);
00629
00630 return multiSlider;
00631 }