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
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
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
00133
00134
00135
00136 mEditor->setIgnoreTab(TRUE);
00137 addChild(mEditor);
00138
00139 updateEditor();
00140 setSpanChildren( TRUE );
00141 }
00142
00143 LLSpinCtrl::~LLSpinCtrl()
00144 {
00145
00146 }
00147
00148
00149 F32 clamp_precision(F32 value, S32 decimal_precision)
00150 {
00151
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
00167 void LLSpinCtrl::onUpBtn( void *userdata )
00168 {
00169 LLSpinCtrl* self = (LLSpinCtrl*) userdata;
00170 if( self->getEnabled() )
00171 {
00172
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
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
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
00275 F32 displayed_value = clamp_precision((F32)getValue().asReal(), mPrecision);
00276
00277
00278
00279
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
00449
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
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 }