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
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
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
00134
00135
00136
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
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
00163 void LLSpinCtrl::onUpBtn( void *userdata )
00164 {
00165 LLSpinCtrl* self = (LLSpinCtrl*) userdata;
00166 if( self->getEnabled() )
00167 {
00168
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
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
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
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
00279 F32 displayed_value = clamp_precision((F32)getValue().asReal(), mPrecision);
00280
00281
00282
00283
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
00448
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
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