llspinctrl.cpp

Go to the documentation of this file.
00001 
00032 #include "linden_common.h"
00033  
00034 #include "llspinctrl.h"
00035 
00036 #include "llgl.h"
00037 #include "llui.h"
00038 #include "lluiconstants.h"
00039 
00040 #include "llstring.h"
00041 #include "llfontgl.h"
00042 #include "lllineeditor.h"
00043 #include "llbutton.h"
00044 #include "lltextbox.h"
00045 #include "llkeyboard.h"
00046 #include "llmath.h"
00047 #include "sound_ids.h"
00048 #include "audioengine.h"
00049 #include "llcontrol.h"
00050 #include "llfocusmgr.h"
00051 #include "llresmgr.h"
00052 
00053 const U32 MAX_STRING_LENGTH = 32;
00054 
00055  
00056 LLSpinCtrl::LLSpinCtrl( const LLString& name, const LLRect& rect, const LLString& label, const LLFontGL* font,
00057         void (*commit_callback)(LLUICtrl*, void*),
00058         void* callback_user_data,
00059         F32 initial_value, F32 min_value, F32 max_value, F32 increment,
00060         const LLString& control_name,
00061         S32 label_width)
00062         :
00063         LLUICtrl(name, rect, TRUE, commit_callback, callback_user_data, FOLLOWS_LEFT | FOLLOWS_TOP ),
00064         mValue( initial_value ),
00065         mInitialValue( initial_value ),
00066         mMaxValue( max_value ),
00067         mMinValue( min_value ),
00068         mIncrement( increment ),
00069         mPrecision( 3 ),
00070         mLabelBox( NULL ),
00071         mTextEnabledColor( LLUI::sColorsGroup->getColor( "LabelTextColor" ) ),
00072         mTextDisabledColor( LLUI::sColorsGroup->getColor( "LabelDisabledColor" ) ),
00073         mbHasBeenSet( FALSE )
00074 {
00075         S32 top = mRect.getHeight();
00076         S32 bottom = top - 2 * SPINCTRL_BTN_HEIGHT;
00077         S32 centered_top = top;
00078         S32 centered_bottom = bottom;
00079         S32 btn_left = 0;
00080 
00081         // Label
00082         if( !label.empty() )
00083         {
00084                 LLRect label_rect( 0, centered_top, label_width, centered_bottom );
00085                 mLabelBox = new LLTextBox( "SpinCtrl Label", label_rect, label.c_str(), font );
00086                 addChild(mLabelBox);
00087 
00088                 btn_left += label_rect.mRight + SPINCTRL_SPACING;
00089         }
00090 
00091         S32 btn_right = btn_left + SPINCTRL_BTN_WIDTH;
00092         
00093         // Spin buttons
00094         LLRect up_rect( btn_left, top, btn_right, top - SPINCTRL_BTN_HEIGHT );
00095         LLString out_id = "UIImgBtnSpinUpOutUUID";
00096         LLString in_id = "UIImgBtnSpinUpInUUID";
00097         mUpBtn = new LLButton(
00098                 "SpinCtrl Up", up_rect,
00099                 out_id,
00100                 in_id,
00101                 "",
00102                 &LLSpinCtrl::onUpBtn, this, LLFontGL::sSansSerif );
00103         mUpBtn->setFollowsLeft();
00104         mUpBtn->setFollowsBottom();
00105         mUpBtn->setHeldDownCallback( &LLSpinCtrl::onUpBtn );
00106         mUpBtn->setTabStop(FALSE);
00107         addChild(mUpBtn);
00108 
00109         LLRect down_rect( btn_left, top - SPINCTRL_BTN_HEIGHT, btn_right, bottom );
00110         out_id = "UIImgBtnSpinDownOutUUID";
00111         in_id = "UIImgBtnSpinDownInUUID";
00112         mDownBtn = new LLButton(
00113                 "SpinCtrl Down", down_rect,
00114                 out_id,
00115                 in_id,
00116                 "",
00117                 &LLSpinCtrl::onDownBtn, this, LLFontGL::sSansSerif );
00118         mDownBtn->setFollowsLeft();
00119         mDownBtn->setFollowsBottom();
00120         mDownBtn->setHeldDownCallback( &LLSpinCtrl::onDownBtn );
00121         mDownBtn->setTabStop(FALSE);
00122         addChild(mDownBtn);
00123 
00124         LLRect editor_rect( btn_right + 1, centered_top, mRect.getWidth(), centered_bottom );
00125         mEditor = new LLLineEditor( "SpinCtrl Editor", editor_rect, "", font,
00126                 MAX_STRING_LENGTH,
00127                 &LLSpinCtrl::onEditorCommit, NULL, NULL, this,
00128                 &LLLineEditor::prevalidateFloat );
00129         mEditor->setFollowsLeft();
00130         mEditor->setFollowsBottom();
00131         mEditor->setFocusReceivedCallback( &LLSpinCtrl::onEditorGainFocus );
00132         //RN: this seems to be a BAD IDEA, as it makes the editor behavior different when it has focus
00133         // than when it doesn't.  Instead, if you always have to double click to select all the text, 
00134         // it's easier to understand
00135         //mEditor->setSelectAllonFocusReceived(TRUE);
00136         mEditor->setIgnoreTab(TRUE);
00137         addChild(mEditor);
00138 
00139         updateEditor();
00140         setSpanChildren( TRUE );
00141 }
00142 
00143 LLSpinCtrl::~LLSpinCtrl()
00144 {
00145         // Children all cleaned up by default view destructor.
00146 }
00147 
00148 
00149 F32 clamp_precision(F32 value, S32 decimal_precision)
00150 {
00151         // pow() isn't perfect
00152         
00153         F64 clamped_value = value;
00154         for (S32 i = 0; i < decimal_precision; i++)
00155                 clamped_value *= 10.0;
00156 
00157         clamped_value = llround((F32)clamped_value);
00158 
00159         for (S32 i = 0; i < decimal_precision; i++)
00160                 clamped_value /= 10.0;
00161         
00162         return (F32)clamped_value;
00163 }
00164 
00165 
00166 // static
00167 void LLSpinCtrl::onUpBtn( void *userdata )
00168 {
00169         LLSpinCtrl* self = (LLSpinCtrl*) userdata;
00170         if( self->getEnabled() )
00171         {
00172                 // use getValue()/setValue() to force reload from/to control
00173                 F32 val = (F32)self->getValue().asReal() + self->mIncrement;
00174                 val = clamp_precision(val, self->mPrecision);
00175                 val = llmin( val, self->mMaxValue );
00176                 
00177                 if( self->mValidateCallback )
00178                 {
00179                         F32 saved_val = (F32)self->getValue().asReal();
00180                         self->setValue(val);
00181                         if( !self->mValidateCallback( self, self->mCallbackUserData ) )
00182                         {
00183                                 self->setValue( saved_val );
00184                                 self->reportInvalidData();
00185                                 self->updateEditor();
00186                                 return;
00187                         }
00188                 }
00189                 else
00190                 {
00191                         self->setValue(val);
00192                 }
00193 
00194                 self->updateEditor();
00195                 self->onCommit();
00196         }
00197 }
00198 
00199 // static
00200 void LLSpinCtrl::onDownBtn( void *userdata )
00201 {
00202         LLSpinCtrl* self = (LLSpinCtrl*) userdata;
00203 
00204         if( self->getEnabled() )
00205         {
00206                 F32 val = (F32)self->getValue().asReal() - self->mIncrement;
00207                 val = clamp_precision(val, self->mPrecision);
00208                 val = llmax( val, self->mMinValue );
00209 
00210                 if( self->mValidateCallback )
00211                 {
00212                         F32 saved_val = (F32)self->getValue().asReal();
00213                         self->setValue(val);
00214                         if( !self->mValidateCallback( self, self->mCallbackUserData ) )
00215                         {
00216                                 self->setValue( saved_val );
00217                                 self->reportInvalidData();
00218                                 self->updateEditor();
00219                                 return;
00220                         }
00221                 }
00222                 else
00223                 {
00224                         self->setValue(val);
00225                 }
00226                 
00227                 self->updateEditor();
00228                 self->onCommit();
00229         }
00230 }
00231 
00232 // static
00233 void LLSpinCtrl::onEditorGainFocus( LLUICtrl* caller, void *userdata )
00234 {
00235         LLSpinCtrl* self = (LLSpinCtrl*) userdata;
00236         llassert( caller == self->mEditor );
00237 
00238         self->onFocusReceived();
00239 }
00240 
00241 void LLSpinCtrl::setValue(const LLSD& value )
00242 {
00243         F32 v = (F32)value.asReal();
00244         if (mValue != v || !mbHasBeenSet)
00245         {
00246                 mbHasBeenSet = TRUE;
00247                 mValue = v;
00248                 
00249                 if (!mEditor->hasFocus())
00250                 {
00251                         updateEditor();
00252                 }
00253         }
00254 }
00255 
00256 LLSD LLSpinCtrl::getValue() const
00257 {
00258         return mValue;
00259 }
00260 
00261 void LLSpinCtrl::clear()
00262 {
00263         setValue(mMinValue);
00264         mEditor->clear();
00265         mbHasBeenSet = FALSE;
00266 }
00267 
00268 
00269 
00270 void LLSpinCtrl::updateEditor()
00271 {
00272         LLLocale locale(LLLocale::USER_LOCALE);
00273 
00274         // Don't display very small negative values as -0.000
00275         F32 displayed_value = clamp_precision((F32)getValue().asReal(), mPrecision);
00276 
00277 //      if( S32( displayed_value * pow( 10, mPrecision ) ) == 0 )
00278 //      {
00279 //              displayed_value = 0.f;
00280 //      }
00281 
00282         LLString format = llformat("%%.%df", mPrecision);
00283         LLString text = llformat(format.c_str(), displayed_value);
00284         mEditor->setText( text );
00285 }
00286 
00287 void LLSpinCtrl::onEditorCommit( LLUICtrl* caller, void *userdata )
00288 {
00289         BOOL success = FALSE;
00290         
00291         LLSpinCtrl* self = (LLSpinCtrl*) userdata;
00292         llassert( caller == self->mEditor );
00293 
00294         LLString text = self->mEditor->getText();
00295         if( LLLineEditor::postvalidateFloat( text ) )
00296         {
00297                 LLLocale locale(LLLocale::USER_LOCALE);
00298                 F32 val = (F32) atof(text.c_str());
00299 
00300                 if (val < self->mMinValue) val = self->mMinValue;
00301                 if (val > self->mMaxValue) val = self->mMaxValue;
00302 
00303                 if( self->mValidateCallback )
00304                 {
00305                         F32 saved_val = self->mValue;
00306                         self->mValue = val;
00307                         if( self->mValidateCallback( self, self->mCallbackUserData ) )
00308                         {
00309                                 success = TRUE;
00310                                 self->onCommit();
00311                         }
00312                         else
00313                         {
00314                                 self->mValue = saved_val;
00315                         }
00316                 }
00317                 else
00318                 {
00319                         self->mValue = val;
00320                         self->onCommit();
00321                         success = TRUE;
00322                 }
00323         }
00324         self->updateEditor();
00325 
00326         if( !success )
00327         {
00328                 self->reportInvalidData();              
00329         }
00330 }
00331 
00332 
00333 void LLSpinCtrl::forceEditorCommit()
00334 {
00335         onEditorCommit(mEditor, this);
00336 }
00337 
00338 
00339 void LLSpinCtrl::setFocus(BOOL b)
00340 {
00341         LLUICtrl::setFocus( b );
00342         mEditor->setFocus( b );
00343 }
00344 
00345 void LLSpinCtrl::setEnabled(BOOL b)
00346 {
00347         LLView::setEnabled( b );
00348         mEditor->setEnabled( b );
00349 }
00350 
00351 
00352 void LLSpinCtrl::setTentative(BOOL b)
00353 {
00354         mEditor->setTentative(b);
00355         LLUICtrl::setTentative(b);
00356 }
00357 
00358 
00359 BOOL LLSpinCtrl::isMouseHeldDown()
00360 {
00361         return 
00362                 mDownBtn->hasMouseCapture()
00363                 || mUpBtn->hasMouseCapture();
00364 }
00365 
00366 void LLSpinCtrl::onCommit()
00367 {
00368         setTentative(FALSE);
00369 
00370         setControlValue(mValue);
00371 
00372         LLUICtrl::onCommit();
00373 }
00374 
00375 
00376 void LLSpinCtrl::setPrecision(S32 precision)
00377 {
00378         if (precision < 0 || precision > 10)
00379         {
00380                 llerrs << "LLSpinCtrl::setPrecision - precision out of range" << llendl;
00381                 return;
00382         }
00383 
00384         mPrecision = precision;
00385         updateEditor();
00386 }
00387 
00388 void LLSpinCtrl::setLabel(const LLStringExplicit& label)
00389 {
00390         if (mLabelBox)
00391         {
00392                 mLabelBox->setText(label);
00393         }
00394         else
00395         {
00396                 llwarns << "Attempting to set label on LLSpinCtrl constructed without one " << getName() << llendl;
00397         }
00398 }
00399 
00400 void LLSpinCtrl::onTabInto()
00401 {
00402         mEditor->onTabInto(); 
00403 }
00404 
00405 
00406 void LLSpinCtrl::reportInvalidData()
00407 {
00408         make_ui_sound("UISndBadKeystroke");
00409 }
00410 
00411 void LLSpinCtrl::draw()
00412 {
00413         if( mLabelBox )
00414         {
00415                 mLabelBox->setColor( mEnabled ? mTextEnabledColor : mTextDisabledColor );
00416         }
00417         LLUICtrl::draw();
00418 }
00419 
00420 
00421 BOOL LLSpinCtrl::handleScrollWheel(S32 x, S32 y, S32 clicks)
00422 {
00423         if( mEnabled )
00424         {
00425                 if( clicks > 0 )
00426                 {
00427                         while( clicks-- )
00428                         {
00429                                 LLSpinCtrl::onDownBtn(this);
00430                         }
00431                 }
00432                 else
00433                 while( clicks++ )
00434                 {
00435                         LLSpinCtrl::onUpBtn(this);
00436                 }
00437         }
00438 
00439         return TRUE;
00440 }
00441 
00442 BOOL LLSpinCtrl::handleKeyHere(KEY key, MASK mask, BOOL called_from_parent)
00443 {
00444         if (mEditor->hasFocus())
00445         {
00446                 if(key == KEY_ESCAPE)
00447                 {
00448                         // text editors don't support revert normally (due to user confusion)
00449                         // but not allowing revert on a spinner seems dangerous
00450                         updateEditor();
00451                         mEditor->setFocus(FALSE);
00452                         return TRUE;
00453                 }
00454                 if(key == KEY_UP)
00455                 {
00456                         LLSpinCtrl::onUpBtn(this);
00457                         return TRUE;
00458                 }
00459                 if(key == KEY_DOWN)
00460                 {
00461                         LLSpinCtrl::onDownBtn(this);
00462                         return TRUE;
00463                 }
00464         }
00465         return FALSE;
00466 }
00467 
00468 // virtual
00469 LLXMLNodePtr LLSpinCtrl::getXML(bool save_children) const
00470 {
00471         LLXMLNodePtr node = LLUICtrl::getXML();
00472 
00473         node->createChild("decimal_digits", TRUE)->setIntValue(mPrecision);
00474 
00475         if (mLabelBox)
00476         {
00477                 node->createChild("label", TRUE)->setStringValue(mLabelBox->getText());
00478 
00479                 node->createChild("label_width", TRUE)->setIntValue(mLabelBox->getRect().getWidth());
00480         }
00481 
00482         node->createChild("initial_val", TRUE)->setFloatValue(mInitialValue);
00483 
00484         node->createChild("min_val", TRUE)->setFloatValue(mMinValue);
00485 
00486         node->createChild("max_val", TRUE)->setFloatValue(mMaxValue);
00487 
00488         node->createChild("increment", TRUE)->setFloatValue(mIncrement);
00489         
00490         addColorXML(node, mTextEnabledColor, "text_enabled_color", "LabelTextColor");
00491         addColorXML(node, mTextDisabledColor, "text_disabled_color", "LabelDisabledColor");
00492 
00493         return node;
00494 }
00495 
00496 LLView* LLSpinCtrl::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory)
00497 {
00498         LLString name("spinner");
00499         node->getAttributeString("name", name);
00500 
00501         LLString label;
00502         node->getAttributeString("label", label);
00503 
00504         LLRect rect;
00505         createRect(node, rect, parent, LLRect());
00506 
00507         LLFontGL* font = LLView::selectFont(node);
00508 
00509         F32 initial_value = 0.f;
00510         node->getAttributeF32("initial_val", initial_value);
00511 
00512         F32 min_value = 0.f;
00513         node->getAttributeF32("min_val", min_value);
00514 
00515         F32 max_value = 1.f; 
00516         node->getAttributeF32("max_val", max_value);
00517 
00518         F32 increment = 0.1f;
00519         node->getAttributeF32("increment", increment);
00520 
00521         U32 precision = 3;
00522         node->getAttributeU32("decimal_digits", precision);
00523         
00524         S32 label_width = llmin(40, rect.getWidth() - 40);
00525         node->getAttributeS32("label_width", label_width);
00526 
00527         LLUICtrlCallback callback = NULL;
00528 
00529         if(label.empty())
00530         {
00531                 label.assign( node->getValue() );
00532         }
00533 
00534         LLSpinCtrl* spinner = new LLSpinCtrl(name,
00535                                                         rect,
00536                                                         label,
00537                                                         font,
00538                                                         callback,
00539                                                         NULL,
00540                                                         initial_value, 
00541                                                         min_value, 
00542                                                         max_value, 
00543                                                         increment,
00544                                                         "",
00545                                                         label_width);
00546 
00547         spinner->setPrecision(precision);
00548 
00549         spinner->initFromXML(node, parent);
00550 
00551         return spinner;
00552 }
00553 
00554 BOOL LLSpinCtrl::isDirty() const
00555 {
00556         return( mValue != mInitialValue );
00557 }

Generated on Thu Jul 1 06:09:12 2010 for Second Life Viewer by  doxygen 1.4.7