llmultislider.cpp

Go to the documentation of this file.
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"                 // for the MASK constants
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         // properly handle setting the starting thumb rect
00097         // do it this way to handle both the operating-on-settings
00098         // and standalone ways of using this
00099         setControlName(control_name, NULL);
00100         setValue(getValue());
00101 }
00102 
00103 void LLMultiSlider::setSliderValue(const LLString& name, F32 value, BOOL from_event)
00104 {
00105         // exit if not there
00106         if(!mValue.has(name)) {
00107                 return;
00108         }
00109 
00110         value = llclamp( value, mMinValue, mMaxValue );
00111 
00112         // Round to nearest increment (bias towards rounding down)
00113         value -= mMinValue;
00114         value += mIncrement/2.0001f;
00115         value -= fmod(value, mIncrement);
00116         F32 newValue = mMinValue + value;
00117 
00118         // now, make sure no overlap
00119         // if we want that
00120         if(!mAllowOverlap) {
00121                 bool hit = false;
00122 
00123                 // look at the current spot
00124                 // and see if anything is there
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                 // if none found, stop
00137                 if(hit) {
00138                         return;
00139                 }
00140         }
00141         
00142 
00143         // now set it in the map
00144         mValue[name] = newValue;
00145 
00146         // set the control if it's the current slider and not from an event
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         // only do if it's a map
00165         if(value.isMap()) {
00166                 
00167                 // add each value... the first in the map becomes the current
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         // create a new name
00204         newName << "sldr" << mNameCounter;
00205         mNameCounter++;
00206 
00207         bool foundOne = findUnusedValue(initVal);
00208         if(!foundOne) {
00209                 return LLString::null;
00210         }
00211 
00212         // add a new thumb rect
00213         mThumbRects[newName.str()] = LLRect( 0, getRect().getHeight(), MULTI_THUMB_WIDTH, 0 );
00214 
00215         // add the value and set the current slider to this one
00216         mValue.insert(newName.str(), initVal);
00217         mCurSlider = newName.str();
00218 
00219         // move the slider
00220         setSliderValue(mCurSlider, initVal, TRUE);
00221 
00222         return mCurSlider;
00223 }
00224 
00225 bool LLMultiSlider::findUnusedValue(F32& initVal)
00226 {
00227         bool firstTry = true;
00228 
00229         // find the first open slot starting with
00230         // the initial value
00231         while(true) {
00232                 
00233                 bool hit = false;
00234 
00235                 // look at the current spot
00236                 // and see if anything is there
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                 // if we found one
00248                 if(!hit) {
00249                         break;
00250                 }
00251 
00252                 // increment and wrap if need be
00253                 initVal += mIncrement;
00254                 if(initVal > mMaxValue) {
00255                         initVal = mMinValue;
00256                 }
00257 
00258                 // stop if it's filled
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         // can't delete last slider
00275         if(mValue.size() <= 0) {
00276                 return;
00277         }
00278 
00279         // get rid of value from mValue and its thumb rect
00280         mValue.erase(name);
00281         mThumbRects.erase(name);
00282 
00283         // set to the last created
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         // only do sticky-focus on non-chrome widgets
00351         if (!getIsChrome())
00352         {
00353                 setFocus(TRUE);
00354         }
00355         if( mMouseDownCallback )
00356         {
00357                 mMouseDownCallback( this, mCallbackUserData );
00358         }
00359 
00360         if (MASK_CONTROL & mask) // if CTRL is modifying
00361         {
00362                 setCurSliderValue(mInitialValue);
00363                 onCommit();
00364         }
00365         else
00366         {
00367                 // scroll through thumbs to see if we have a new one selected and select that one
00368                 std::map<LLString, LLRect>::iterator mIt = mThumbRects.begin();
00369                 for(; mIt != mThumbRects.end(); mIt++) {
00370                         
00371                         // check if inside.  If so, set current slider and continue
00372                         if(mIt->second.pointInRect(x,y)) {
00373                                 mCurSlider = mIt->first;
00374                                 break;
00375                         }
00376                 }
00377 
00378                 // Find the offset of the actual mouse location from the center of the thumb.
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                 // Start dragging the thumb
00389                 // No handler needed for focus lost since this class has no state that depends on it.
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                 // eat up and down keys to be consistent
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         // Draw background and thumb.
00432 
00433         // drawing solids requires texturing be disabled
00434         LLGLSNoTexture no_texture;
00435 
00436         LLRect rect(mDragStartThumbRect);
00437 
00438         F32 opacity = getEnabled() ? 1.f : 0.3f;
00439 
00440         // Track
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         // if we're supposed to use a drawn triangle
00454         // simple gl call for the triangle
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                 // draw all the thumbs
00472                 curSldrIt = mThumbRects.end();
00473                 for(mIt = mThumbRects.begin(); mIt != mThumbRects.end(); mIt++) {
00474                         
00475                         // choose the color
00476                         curThumbColor = mThumbCenterColor;
00477                         if(mIt->first == mCurSlider) {
00478                                 
00479                                 curSldrIt = mIt;
00480                                 continue;
00481                                 //curThumbColor = mThumbCenterSelectedColor;
00482                         }
00483 
00484                         // the draw command
00485                         gl_rect_2d(mIt->second, curThumbColor, TRUE);
00486                 }
00487 
00488                 // now draw the current slider
00489                 if(curSldrIt != mThumbRects.end()) {
00490                         gl_rect_2d(curSldrIt->second, mThumbCenterSelectedColor, TRUE);
00491                 }
00492 
00493                 // and draw the drag start
00494                 if (gFocusMgr.getMouseCapture() == this)
00495                 {
00496                         gl_rect_2d(mDragStartThumbRect, mThumbCenterColor % opacity, FALSE);
00497                 }
00498         }
00499         else if( gFocusMgr.getMouseCapture() == this )
00500         {
00501                 // draw drag start
00502                 thumb_imagep->drawSolid(mDragStartThumbRect, mThumbCenterColor % 0.3f);
00503 
00504                 // draw the highlight
00505                 if (hasFocus())
00506                 {
00507                         thumb_imagep->drawBorder(mThumbRects[mCurSlider], gFocusMgr.getFocusColor(), gFocusMgr.getFocusFlashWidth());
00508                 }
00509 
00510                 // draw the thumbs
00511                 curSldrIt = mThumbRects.end();
00512                 for(mIt = mThumbRects.begin(); mIt != mThumbRects.end(); mIt++) 
00513                 {
00514                         // choose the color
00515                         curThumbColor = mThumbCenterColor;
00516                         if(mIt->first == mCurSlider) 
00517                         {
00518                                 // don't draw now, draw last
00519                                 curSldrIt = mIt;
00520                                 continue;                               
00521                         }
00522                         
00523                         // the draw command
00524                         thumb_imagep->drawSolid(mIt->second, curThumbColor);
00525                 }
00526                 
00527                 // draw cur slider last
00528                 if(curSldrIt != mThumbRects.end()) 
00529                 {
00530                         thumb_imagep->drawSolid(curSldrIt->second, mThumbCenterSelectedColor);
00531                 }
00532                 
00533         }
00534         else
00535         { 
00536                 // draw highlight
00537                 if (hasFocus())
00538                 {
00539                         thumb_imagep->drawBorder(mThumbRects[mCurSlider], gFocusMgr.getFocusColor(), gFocusMgr.getFocusFlashWidth());
00540                 }
00541 
00542                 // draw thumbs
00543                 curSldrIt = mThumbRects.end();
00544                 for(mIt = mThumbRects.begin(); mIt != mThumbRects.end(); mIt++) 
00545                 {
00546                         
00547                         // choose the color
00548                         curThumbColor = mThumbCenterColor;
00549                         if(mIt->first == mCurSlider) 
00550                         {
00551                                 curSldrIt = mIt;
00552                                 continue;
00553                                 //curThumbColor = mThumbCenterSelectedColor;
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 // virtual
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 //static
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 }

Generated on Fri May 16 08:32:54 2008 for SecondLife by  doxygen 1.5.5