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

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