diff options
author | Jacek Antonelli | 2008-08-15 23:44:46 -0500 |
---|---|---|
committer | Jacek Antonelli | 2008-08-15 23:44:46 -0500 |
commit | 38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4 (patch) | |
tree | adca584755d22ca041a2dbfc35d4eca01f70b32c /linden/indra/llui/llspinctrl.cpp | |
parent | README.txt (diff) | |
download | meta-impy-38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4.zip meta-impy-38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4.tar.gz meta-impy-38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4.tar.bz2 meta-impy-38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4.tar.xz |
Second Life viewer sources 1.13.2.12
Diffstat (limited to 'linden/indra/llui/llspinctrl.cpp')
-rw-r--r-- | linden/indra/llui/llspinctrl.cpp | 528 |
1 files changed, 528 insertions, 0 deletions
diff --git a/linden/indra/llui/llspinctrl.cpp b/linden/indra/llui/llspinctrl.cpp new file mode 100644 index 0000000..b93ed02 --- /dev/null +++ b/linden/indra/llui/llspinctrl.cpp | |||
@@ -0,0 +1,528 @@ | |||
1 | /** | ||
2 | * @file llspinctrl.cpp | ||
3 | * @brief LLSpinCtrl base class | ||
4 | * | ||
5 | * Copyright (c) 2001-2007, Linden Research, Inc. | ||
6 | * | ||
7 | * The source code in this file ("Source Code") is provided by Linden Lab | ||
8 | * to you under the terms of the GNU General Public License, version 2.0 | ||
9 | * ("GPL"), unless you have obtained a separate licensing agreement | ||
10 | * ("Other License"), formally executed by you and Linden Lab. Terms of | ||
11 | * the GPL can be found in doc/GPL-license.txt in this distribution, or | ||
12 | * online at http://secondlife.com/developers/opensource/gplv2 | ||
13 | * | ||
14 | * There are special exceptions to the terms and conditions of the GPL as | ||
15 | * it is applied to this Source Code. View the full text of the exception | ||
16 | * in the file doc/FLOSS-exception.txt in this software distribution, or | ||
17 | * online at http://secondlife.com/developers/opensource/flossexception | ||
18 | * | ||
19 | * By copying, modifying or distributing this software, you acknowledge | ||
20 | * that you have read and understood your obligations described above, | ||
21 | * and agree to abide by those obligations. | ||
22 | * | ||
23 | * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO | ||
24 | * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, | ||
25 | * COMPLETENESS OR PERFORMANCE. | ||
26 | */ | ||
27 | |||
28 | #include "linden_common.h" | ||
29 | |||
30 | #include "llspinctrl.h" | ||
31 | |||
32 | #include "llgl.h" | ||
33 | #include "llui.h" | ||
34 | #include "lluiconstants.h" | ||
35 | |||
36 | #include "llstring.h" | ||
37 | #include "llfontgl.h" | ||
38 | #include "lllineeditor.h" | ||
39 | #include "llbutton.h" | ||
40 | #include "lltextbox.h" | ||
41 | #include "llkeyboard.h" | ||
42 | #include "llmath.h" | ||
43 | #include "sound_ids.h" | ||
44 | #include "audioengine.h" | ||
45 | #include "llcontrol.h" | ||
46 | #include "llfocusmgr.h" | ||
47 | #include "llresmgr.h" | ||
48 | |||
49 | const U32 MAX_STRING_LENGTH = 32; | ||
50 | |||
51 | |||
52 | LLSpinCtrl::LLSpinCtrl( const LLString& name, const LLRect& rect, const LLString& label, const LLFontGL* font, | ||
53 | void (*commit_callback)(LLUICtrl*, void*), | ||
54 | void* callback_user_data, | ||
55 | F32 initial_value, F32 min_value, F32 max_value, F32 increment, | ||
56 | const LLString& control_name, | ||
57 | S32 label_width) | ||
58 | : | ||
59 | LLUICtrl(name, rect, TRUE, commit_callback, callback_user_data, FOLLOWS_LEFT | FOLLOWS_TOP ), | ||
60 | mValue( initial_value ), | ||
61 | mInitialValue( initial_value ), | ||
62 | mMaxValue( max_value ), | ||
63 | mMinValue( min_value ), | ||
64 | mIncrement( increment ), | ||
65 | mPrecision( 3 ), | ||
66 | mLabelBox( NULL ), | ||
67 | mTextEnabledColor( LLUI::sColorsGroup->getColor( "LabelTextColor" ) ), | ||
68 | mTextDisabledColor( LLUI::sColorsGroup->getColor( "LabelDisabledColor" ) ), | ||
69 | mbHasBeenSet( FALSE ) | ||
70 | { | ||
71 | S32 top = mRect.getHeight(); | ||
72 | S32 bottom = top - 2 * SPINCTRL_BTN_HEIGHT; | ||
73 | S32 centered_top = top; | ||
74 | S32 centered_bottom = bottom; | ||
75 | S32 btn_left = 0; | ||
76 | |||
77 | // Label | ||
78 | if( !label.empty() ) | ||
79 | { | ||
80 | LLRect label_rect( 0, centered_top, label_width, centered_bottom ); | ||
81 | mLabelBox = new LLTextBox( "SpinCtrl Label", label_rect, label.c_str(), font ); | ||
82 | addChild(mLabelBox); | ||
83 | |||
84 | btn_left += label_rect.mRight + SPINCTRL_SPACING; | ||
85 | } | ||
86 | |||
87 | S32 btn_right = btn_left + SPINCTRL_BTN_WIDTH; | ||
88 | |||
89 | // Spin buttons | ||
90 | LLRect up_rect( btn_left, top, btn_right, top - SPINCTRL_BTN_HEIGHT ); | ||
91 | LLString out_id = "UIImgBtnSpinUpOutUUID"; | ||
92 | LLString in_id = "UIImgBtnSpinUpInUUID"; | ||
93 | mUpBtn = new LLButton( | ||
94 | "SpinCtrl Up", up_rect, | ||
95 | out_id, | ||
96 | in_id, | ||
97 | "", | ||
98 | &LLSpinCtrl::onUpBtn, this, LLFontGL::sSansSerif ); | ||
99 | mUpBtn->setFollowsLeft(); | ||
100 | mUpBtn->setFollowsBottom(); | ||
101 | mUpBtn->setHeldDownCallback( &LLSpinCtrl::onUpBtn ); | ||
102 | mUpBtn->setTabStop(FALSE); | ||
103 | addChild(mUpBtn); | ||
104 | |||
105 | LLRect down_rect( btn_left, top - SPINCTRL_BTN_HEIGHT, btn_right, bottom ); | ||
106 | out_id = "UIImgBtnSpinDownOutUUID"; | ||
107 | in_id = "UIImgBtnSpinDownInUUID"; | ||
108 | mDownBtn = new LLButton( | ||
109 | "SpinCtrl Down", down_rect, | ||
110 | out_id, | ||
111 | in_id, | ||
112 | "", | ||
113 | &LLSpinCtrl::onDownBtn, this, LLFontGL::sSansSerif ); | ||
114 | mDownBtn->setFollowsLeft(); | ||
115 | mDownBtn->setFollowsBottom(); | ||
116 | mDownBtn->setHeldDownCallback( &LLSpinCtrl::onDownBtn ); | ||
117 | mDownBtn->setTabStop(FALSE); | ||
118 | addChild(mDownBtn); | ||
119 | |||
120 | LLRect editor_rect( btn_right + 1, centered_top, mRect.getWidth(), centered_bottom ); | ||
121 | mEditor = new LLLineEditor( "SpinCtrl Editor", editor_rect, "", font, | ||
122 | MAX_STRING_LENGTH, | ||
123 | &LLSpinCtrl::onEditorCommit, NULL, NULL, this, | ||
124 | &LLLineEditor::prevalidateFloat ); | ||
125 | mEditor->setFollowsLeft(); | ||
126 | mEditor->setFollowsBottom(); | ||
127 | mEditor->setFocusReceivedCallback( &LLSpinCtrl::onEditorGainFocus ); | ||
128 | //RN: this seems to be a BAD IDEA, as it makes the editor behavior different when it has focus | ||
129 | // than when it doesn't. Instead, if you always have to double click to select all the text, | ||
130 | // it's easier to understand | ||
131 | //mEditor->setSelectAllonFocusReceived(TRUE); | ||
132 | mEditor->setIgnoreTab(TRUE); | ||
133 | addChild(mEditor); | ||
134 | |||
135 | updateEditor(); | ||
136 | setSpanChildren( TRUE ); | ||
137 | } | ||
138 | |||
139 | LLSpinCtrl::~LLSpinCtrl() | ||
140 | { | ||
141 | // Children all cleaned up by default view destructor. | ||
142 | } | ||
143 | |||
144 | |||
145 | // static | ||
146 | void LLSpinCtrl::onUpBtn( void *userdata ) | ||
147 | { | ||
148 | LLSpinCtrl* self = (LLSpinCtrl*) userdata; | ||
149 | if( self->getEnabled() ) | ||
150 | { | ||
151 | // use getValue()/setValue() to force reload from/to control | ||
152 | F32 val = (F32)self->getValue().asReal() + self->mIncrement; | ||
153 | val = llmin( val, self->mMaxValue ); | ||
154 | |||
155 | if( self->mValidateCallback ) | ||
156 | { | ||
157 | F32 saved_val = (F32)self->getValue().asReal(); | ||
158 | self->setValue(val); | ||
159 | if( !self->mValidateCallback( self, self->mCallbackUserData ) ) | ||
160 | { | ||
161 | self->setValue( saved_val ); | ||
162 | self->reportInvalidData(); | ||
163 | self->updateEditor(); | ||
164 | return; | ||
165 | } | ||
166 | } | ||
167 | else | ||
168 | { | ||
169 | self->setValue(val); | ||
170 | } | ||
171 | |||
172 | self->updateEditor(); | ||
173 | self->onCommit(); | ||
174 | } | ||
175 | } | ||
176 | |||
177 | // static | ||
178 | void LLSpinCtrl::onDownBtn( void *userdata ) | ||
179 | { | ||
180 | LLSpinCtrl* self = (LLSpinCtrl*) userdata; | ||
181 | |||
182 | if( self->getEnabled() ) | ||
183 | { | ||
184 | F32 val = (F32)self->getValue().asReal() - self->mIncrement; | ||
185 | val = llmax( val, self->mMinValue ); | ||
186 | |||
187 | if( self->mValidateCallback ) | ||
188 | { | ||
189 | F32 saved_val = (F32)self->getValue().asReal(); | ||
190 | self->setValue(val); | ||
191 | if( !self->mValidateCallback( self, self->mCallbackUserData ) ) | ||
192 | { | ||
193 | self->setValue( saved_val ); | ||
194 | self->reportInvalidData(); | ||
195 | self->updateEditor(); | ||
196 | return; | ||
197 | } | ||
198 | } | ||
199 | else | ||
200 | { | ||
201 | self->setValue(val); | ||
202 | } | ||
203 | |||
204 | self->updateEditor(); | ||
205 | self->onCommit(); | ||
206 | } | ||
207 | } | ||
208 | |||
209 | // static | ||
210 | void LLSpinCtrl::onEditorGainFocus( LLUICtrl* caller, void *userdata ) | ||
211 | { | ||
212 | LLSpinCtrl* self = (LLSpinCtrl*) userdata; | ||
213 | llassert( caller == self->mEditor ); | ||
214 | |||
215 | self->onFocusReceived(); | ||
216 | } | ||
217 | |||
218 | void LLSpinCtrl::setValue(const LLSD& value ) | ||
219 | { | ||
220 | F32 v = (F32)value.asReal(); | ||
221 | if (mValue != v || !mbHasBeenSet) | ||
222 | { | ||
223 | mbHasBeenSet = TRUE; | ||
224 | mValue = v; | ||
225 | |||
226 | if (!mEditor->hasFocus()) | ||
227 | { | ||
228 | updateEditor(); | ||
229 | } | ||
230 | } | ||
231 | } | ||
232 | |||
233 | LLSD LLSpinCtrl::getValue() const | ||
234 | { | ||
235 | return mValue; | ||
236 | } | ||
237 | |||
238 | void LLSpinCtrl::clear() | ||
239 | { | ||
240 | setValue(mMinValue); | ||
241 | mEditor->clear(); | ||
242 | mbHasBeenSet = FALSE; | ||
243 | } | ||
244 | |||
245 | |||
246 | void LLSpinCtrl::updateEditor() | ||
247 | { | ||
248 | LLLocale locale(LLLocale::USER_LOCALE); | ||
249 | |||
250 | // Don't display very small negative values as -0.000 | ||
251 | F32 displayed_value = (F32)floor(getValue().asReal() * pow(10, mPrecision) + 0.5) / (F32)pow(10, mPrecision); | ||
252 | |||
253 | // if( S32( displayed_value * pow( 10, mPrecision ) ) == 0 ) | ||
254 | // { | ||
255 | // displayed_value = 0.f; | ||
256 | // } | ||
257 | |||
258 | LLString format = llformat("%%.%df", mPrecision); | ||
259 | LLString text = llformat(format.c_str(), displayed_value); | ||
260 | mEditor->setText( text ); | ||
261 | } | ||
262 | |||
263 | void LLSpinCtrl::onEditorCommit( LLUICtrl* caller, void *userdata ) | ||
264 | { | ||
265 | BOOL success = FALSE; | ||
266 | |||
267 | LLSpinCtrl* self = (LLSpinCtrl*) userdata; | ||
268 | llassert( caller == self->mEditor ); | ||
269 | |||
270 | LLString text = self->mEditor->getText(); | ||
271 | if( LLLineEditor::postvalidateFloat( text ) ) | ||
272 | { | ||
273 | LLLocale locale(LLLocale::USER_LOCALE); | ||
274 | F32 val = (F32) atof(text.c_str()); | ||
275 | |||
276 | if (val < self->mMinValue) val = self->mMinValue; | ||
277 | if (val > self->mMaxValue) val = self->mMaxValue; | ||
278 | |||
279 | if( self->mValidateCallback ) | ||
280 | { | ||
281 | F32 saved_val = self->mValue; | ||
282 | self->mValue = val; | ||
283 | if( self->mValidateCallback( self, self->mCallbackUserData ) ) | ||
284 | { | ||
285 | success = TRUE; | ||
286 | self->onCommit(); | ||
287 | } | ||
288 | else | ||
289 | { | ||
290 | self->mValue = saved_val; | ||
291 | } | ||
292 | } | ||
293 | else | ||
294 | { | ||
295 | self->mValue = val; | ||
296 | self->onCommit(); | ||
297 | success = TRUE; | ||
298 | } | ||
299 | } | ||
300 | self->updateEditor(); | ||
301 | |||
302 | if( !success ) | ||
303 | { | ||
304 | self->reportInvalidData(); | ||
305 | } | ||
306 | } | ||
307 | |||
308 | |||
309 | void LLSpinCtrl::forceEditorCommit() | ||
310 | { | ||
311 | onEditorCommit(mEditor, this); | ||
312 | } | ||
313 | |||
314 | |||
315 | void LLSpinCtrl::setFocus(BOOL b) | ||
316 | { | ||
317 | LLUICtrl::setFocus( b ); | ||
318 | mEditor->setFocus( b ); | ||
319 | } | ||
320 | |||
321 | void LLSpinCtrl::setEnabled(BOOL b) | ||
322 | { | ||
323 | LLUICtrl::setEnabled( b ); | ||
324 | mEditor->setEnabled( b ); | ||
325 | } | ||
326 | |||
327 | |||
328 | void LLSpinCtrl::setTentative(BOOL b) | ||
329 | { | ||
330 | mEditor->setTentative(b); | ||
331 | LLUICtrl::setTentative(b); | ||
332 | } | ||
333 | |||
334 | |||
335 | BOOL LLSpinCtrl::isMouseHeldDown() | ||
336 | { | ||
337 | return | ||
338 | gFocusMgr.getMouseCapture() == mDownBtn || | ||
339 | gFocusMgr.getMouseCapture() == mUpBtn; | ||
340 | } | ||
341 | |||
342 | void LLSpinCtrl::onCommit() | ||
343 | { | ||
344 | setTentative(FALSE); | ||
345 | |||
346 | setControlValue(mValue); | ||
347 | |||
348 | LLUICtrl::onCommit(); | ||
349 | } | ||
350 | |||
351 | |||
352 | void LLSpinCtrl::setPrecision(S32 precision) | ||
353 | { | ||
354 | if (precision < 0 || precision > 10) | ||
355 | { | ||
356 | llerrs << "LLSpinCtrl::setPrecision - precision out of range" << llendl; | ||
357 | return; | ||
358 | } | ||
359 | |||
360 | mPrecision = precision; | ||
361 | updateEditor(); | ||
362 | } | ||
363 | |||
364 | void LLSpinCtrl::setLabel(const LLString& label) | ||
365 | { | ||
366 | if (mLabelBox) | ||
367 | { | ||
368 | mLabelBox->setText(label); | ||
369 | } | ||
370 | else | ||
371 | { | ||
372 | llwarns << "Attempting to set label on LLSpinCtrl constructed without one " << getName() << llendl; | ||
373 | } | ||
374 | } | ||
375 | |||
376 | void LLSpinCtrl::onTabInto() | ||
377 | { | ||
378 | mEditor->onTabInto(); | ||
379 | } | ||
380 | |||
381 | |||
382 | void LLSpinCtrl::reportInvalidData() | ||
383 | { | ||
384 | make_ui_sound("UISndBadKeystroke"); | ||
385 | } | ||
386 | |||
387 | void LLSpinCtrl::draw() | ||
388 | { | ||
389 | if( mLabelBox ) | ||
390 | { | ||
391 | mLabelBox->setColor( mEnabled ? mTextEnabledColor : mTextDisabledColor ); | ||
392 | } | ||
393 | LLUICtrl::draw(); | ||
394 | } | ||
395 | |||
396 | |||
397 | BOOL LLSpinCtrl::handleScrollWheel(S32 x, S32 y, S32 clicks) | ||
398 | { | ||
399 | if( mEnabled ) | ||
400 | { | ||
401 | if( clicks > 0 ) | ||
402 | { | ||
403 | while( clicks-- ) | ||
404 | { | ||
405 | LLSpinCtrl::onDownBtn(this); | ||
406 | } | ||
407 | } | ||
408 | else | ||
409 | while( clicks++ ) | ||
410 | { | ||
411 | LLSpinCtrl::onUpBtn(this); | ||
412 | } | ||
413 | } | ||
414 | |||
415 | return TRUE; | ||
416 | } | ||
417 | |||
418 | BOOL LLSpinCtrl::handleKeyHere(KEY key, MASK mask, BOOL called_from_parent) | ||
419 | { | ||
420 | if (mEditor->hasFocus()) | ||
421 | { | ||
422 | if(key == KEY_ESCAPE) | ||
423 | { | ||
424 | // text editors don't support revert normally (due to user confusion) | ||
425 | // but not allowing revert on a spinner seems dangerous | ||
426 | updateEditor(); | ||
427 | mEditor->setFocus(FALSE); | ||
428 | return TRUE; | ||
429 | } | ||
430 | if(key == KEY_UP) | ||
431 | { | ||
432 | LLSpinCtrl::onUpBtn(this); | ||
433 | return TRUE; | ||
434 | } | ||
435 | if(key == KEY_DOWN) | ||
436 | { | ||
437 | LLSpinCtrl::onDownBtn(this); | ||
438 | return TRUE; | ||
439 | } | ||
440 | } | ||
441 | return FALSE; | ||
442 | } | ||
443 | |||
444 | // virtual | ||
445 | LLXMLNodePtr LLSpinCtrl::getXML(bool save_children) const | ||
446 | { | ||
447 | LLXMLNodePtr node = LLUICtrl::getXML(); | ||
448 | |||
449 | node->createChild("decimal_digits", TRUE)->setIntValue(mPrecision); | ||
450 | |||
451 | if (mLabelBox) | ||
452 | { | ||
453 | node->createChild("label", TRUE)->setStringValue(mLabelBox->getText()); | ||
454 | |||
455 | node->createChild("label_width", TRUE)->setIntValue(mLabelBox->getRect().getWidth()); | ||
456 | } | ||
457 | |||
458 | node->createChild("initial_val", TRUE)->setFloatValue(mInitialValue); | ||
459 | |||
460 | node->createChild("min_val", TRUE)->setFloatValue(mMinValue); | ||
461 | |||
462 | node->createChild("max_val", TRUE)->setFloatValue(mMaxValue); | ||
463 | |||
464 | node->createChild("increment", TRUE)->setFloatValue(mIncrement); | ||
465 | |||
466 | addColorXML(node, mTextEnabledColor, "text_enabled_color", "LabelTextColor"); | ||
467 | addColorXML(node, mTextDisabledColor, "text_disabled_color", "LabelDisabledColor"); | ||
468 | |||
469 | return node; | ||
470 | } | ||
471 | |||
472 | LLView* LLSpinCtrl::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory) | ||
473 | { | ||
474 | LLString name("spinner"); | ||
475 | node->getAttributeString("name", name); | ||
476 | |||
477 | LLString label; | ||
478 | node->getAttributeString("label", label); | ||
479 | |||
480 | LLRect rect; | ||
481 | createRect(node, rect, parent, LLRect()); | ||
482 | |||
483 | LLFontGL* font = LLView::selectFont(node); | ||
484 | |||
485 | F32 initial_value = 0.f; | ||
486 | node->getAttributeF32("initial_val", initial_value); | ||
487 | |||
488 | F32 min_value = 0.f; | ||
489 | node->getAttributeF32("min_val", min_value); | ||
490 | |||
491 | F32 max_value = 1.f; | ||
492 | node->getAttributeF32("max_val", max_value); | ||
493 | |||
494 | F32 increment = 0.1f; | ||
495 | node->getAttributeF32("increment", increment); | ||
496 | |||
497 | U32 precision = 3; | ||
498 | node->getAttributeU32("decimal_digits", precision); | ||
499 | |||
500 | S32 label_width = llmin(40, rect.getWidth() - 40); | ||
501 | node->getAttributeS32("label_width", label_width); | ||
502 | |||
503 | LLUICtrlCallback callback = NULL; | ||
504 | |||
505 | if(label.empty()) | ||
506 | { | ||
507 | label.assign( node->getValue() ); | ||
508 | } | ||
509 | |||
510 | LLSpinCtrl* spinner = new LLSpinCtrl(name, | ||
511 | rect, | ||
512 | label, | ||
513 | font, | ||
514 | callback, | ||
515 | NULL, | ||
516 | initial_value, | ||
517 | min_value, | ||
518 | max_value, | ||
519 | increment, | ||
520 | "", | ||
521 | label_width); | ||
522 | |||
523 | spinner->setPrecision(precision); | ||
524 | |||
525 | spinner->initFromXML(node, parent); | ||
526 | |||
527 | return spinner; | ||
528 | } | ||