PageRenderTime 94ms CodeModel.GetById 6ms RepoModel.GetById 0ms app.codeStats 1ms

/indra/llui/llmultislider.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 621 lines | 431 code | 108 blank | 82 comment | 80 complexity | 7dabc0087a57ff7f6d22835ca12a35e8 MD5 | raw file
Possible License(s): LGPL-2.1
  1. /**
  2. * @file llmultisldr.cpp
  3. * @brief LLMultiSlider base class
  4. *
  5. * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  6. * Second Life Viewer Source Code
  7. * Copyright (C) 2010, Linden Research, Inc.
  8. *
  9. * This library is free software; you can redistribute it and/or
  10. * modify it under the terms of the GNU Lesser General Public
  11. * License as published by the Free Software Foundation;
  12. * version 2.1 of the License only.
  13. *
  14. * This library is distributed in the hope that it will be useful,
  15. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  17. * Lesser General Public License for more details.
  18. *
  19. * You should have received a copy of the GNU Lesser General Public
  20. * License along with this library; if not, write to the Free Software
  21. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  22. *
  23. * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
  24. * $/LicenseInfo$
  25. */
  26. #include "linden_common.h"
  27. #include "llmultislider.h"
  28. #include "llui.h"
  29. #include "llgl.h"
  30. #include "llwindow.h"
  31. #include "llfocusmgr.h"
  32. #include "llkeyboard.h" // for the MASK constants
  33. #include "llcontrol.h"
  34. #include "lluictrlfactory.h"
  35. #include "lluiimage.h"
  36. #include <sstream>
  37. static LLDefaultChildRegistry::Register<LLMultiSlider> r("multi_slider_bar");
  38. const F32 FLOAT_THRESHOLD = 0.00001f;
  39. S32 LLMultiSlider::mNameCounter = 0;
  40. LLMultiSlider::SliderParams::SliderParams()
  41. : name("name"),
  42. value("value", 0.f)
  43. {
  44. }
  45. LLMultiSlider::Params::Params()
  46. : max_sliders("max_sliders", 1),
  47. allow_overlap("allow_overlap", false),
  48. draw_track("draw_track", true),
  49. use_triangle("use_triangle", false),
  50. track_color("track_color"),
  51. thumb_disabled_color("thumb_disabled_color"),
  52. thumb_outline_color("thumb_outline_color"),
  53. thumb_center_color("thumb_center_color"),
  54. thumb_center_selected_color("thumb_center_selected_color"),
  55. triangle_color("triangle_color"),
  56. mouse_down_callback("mouse_down_callback"),
  57. mouse_up_callback("mouse_up_callback"),
  58. thumb_width("thumb_width"),
  59. sliders("slider")
  60. {}
  61. LLMultiSlider::LLMultiSlider(const LLMultiSlider::Params& p)
  62. : LLF32UICtrl(p),
  63. mMouseOffset( 0 ),
  64. mDragStartThumbRect( 0, getRect().getHeight(), p.thumb_width, 0 ),
  65. mMaxNumSliders(p.max_sliders),
  66. mAllowOverlap(p.allow_overlap),
  67. mDrawTrack(p.draw_track),
  68. mUseTriangle(p.use_triangle),
  69. mTrackColor(p.track_color()),
  70. mThumbOutlineColor(p.thumb_outline_color()),
  71. mThumbCenterColor(p.thumb_center_color()),
  72. mThumbCenterSelectedColor(p.thumb_center_selected_color()),
  73. mDisabledThumbColor(p.thumb_disabled_color()),
  74. mTriangleColor(p.triangle_color()),
  75. mThumbWidth(p.thumb_width),
  76. mMouseDownSignal(NULL),
  77. mMouseUpSignal(NULL)
  78. {
  79. mValue.emptyMap();
  80. mCurSlider = LLStringUtil::null;
  81. if (p.mouse_down_callback.isProvided())
  82. {
  83. setMouseDownCallback(initCommitCallback(p.mouse_down_callback));
  84. }
  85. if (p.mouse_up_callback.isProvided())
  86. {
  87. setMouseUpCallback(initCommitCallback(p.mouse_up_callback));
  88. }
  89. for (LLInitParam::ParamIterator<SliderParams>::const_iterator it = p.sliders.begin();
  90. it != p.sliders.end();
  91. ++it)
  92. {
  93. if (it->name.isProvided())
  94. {
  95. addSlider(it->value, it->name);
  96. }
  97. else
  98. {
  99. addSlider(it->value);
  100. }
  101. }
  102. }
  103. LLMultiSlider::~LLMultiSlider()
  104. {
  105. delete mMouseDownSignal;
  106. delete mMouseUpSignal;
  107. }
  108. void LLMultiSlider::setSliderValue(const std::string& name, F32 value, BOOL from_event)
  109. {
  110. // exit if not there
  111. if(!mValue.has(name)) {
  112. return;
  113. }
  114. value = llclamp( value, mMinValue, mMaxValue );
  115. // Round to nearest increment (bias towards rounding down)
  116. value -= mMinValue;
  117. value += mIncrement/2.0001f;
  118. value -= fmod(value, mIncrement);
  119. F32 newValue = mMinValue + value;
  120. // now, make sure no overlap
  121. // if we want that
  122. if(!mAllowOverlap) {
  123. bool hit = false;
  124. // look at the current spot
  125. // and see if anything is there
  126. LLSD::map_iterator mIt = mValue.beginMap();
  127. for(;mIt != mValue.endMap(); mIt++) {
  128. F32 testVal = (F32)mIt->second.asReal() - newValue;
  129. if(testVal > -FLOAT_THRESHOLD && testVal < FLOAT_THRESHOLD &&
  130. mIt->first != name) {
  131. hit = true;
  132. break;
  133. }
  134. }
  135. // if none found, stop
  136. if(hit) {
  137. return;
  138. }
  139. }
  140. // now set it in the map
  141. mValue[name] = newValue;
  142. // set the control if it's the current slider and not from an event
  143. if (!from_event && name == mCurSlider)
  144. {
  145. setControlValue(mValue);
  146. }
  147. F32 t = (newValue - mMinValue) / (mMaxValue - mMinValue);
  148. S32 left_edge = mThumbWidth/2;
  149. S32 right_edge = getRect().getWidth() - (mThumbWidth/2);
  150. S32 x = left_edge + S32( t * (right_edge - left_edge) );
  151. mThumbRects[name].mLeft = x - (mThumbWidth/2);
  152. mThumbRects[name].mRight = x + (mThumbWidth/2);
  153. }
  154. void LLMultiSlider::setValue(const LLSD& value)
  155. {
  156. // only do if it's a map
  157. if(value.isMap()) {
  158. // add each value... the first in the map becomes the current
  159. LLSD::map_const_iterator mIt = value.beginMap();
  160. mCurSlider = mIt->first;
  161. for(; mIt != value.endMap(); mIt++) {
  162. setSliderValue(mIt->first, (F32)mIt->second.asReal(), TRUE);
  163. }
  164. }
  165. }
  166. F32 LLMultiSlider::getSliderValue(const std::string& name) const
  167. {
  168. return (F32)mValue[name].asReal();
  169. }
  170. void LLMultiSlider::setCurSlider(const std::string& name)
  171. {
  172. if(mValue.has(name)) {
  173. mCurSlider = name;
  174. }
  175. }
  176. const std::string& LLMultiSlider::addSlider()
  177. {
  178. return addSlider(mInitialValue);
  179. }
  180. const std::string& LLMultiSlider::addSlider(F32 val)
  181. {
  182. std::stringstream newName;
  183. F32 initVal = val;
  184. if(mValue.size() >= mMaxNumSliders) {
  185. return LLStringUtil::null;
  186. }
  187. // create a new name
  188. newName << "sldr" << mNameCounter;
  189. mNameCounter++;
  190. bool foundOne = findUnusedValue(initVal);
  191. if(!foundOne) {
  192. return LLStringUtil::null;
  193. }
  194. // add a new thumb rect
  195. mThumbRects[newName.str()] = LLRect( 0, getRect().getHeight(), mThumbWidth, 0 );
  196. // add the value and set the current slider to this one
  197. mValue.insert(newName.str(), initVal);
  198. mCurSlider = newName.str();
  199. // move the slider
  200. setSliderValue(mCurSlider, initVal, TRUE);
  201. return mCurSlider;
  202. }
  203. void LLMultiSlider::addSlider(F32 val, const std::string& name)
  204. {
  205. F32 initVal = val;
  206. if(mValue.size() >= mMaxNumSliders) {
  207. return;
  208. }
  209. bool foundOne = findUnusedValue(initVal);
  210. if(!foundOne) {
  211. return;
  212. }
  213. // add a new thumb rect
  214. mThumbRects[name] = LLRect( 0, getRect().getHeight(), mThumbWidth, 0 );
  215. // add the value and set the current slider to this one
  216. mValue.insert(name, initVal);
  217. mCurSlider = name;
  218. // move the slider
  219. setSliderValue(mCurSlider, initVal, TRUE);
  220. }
  221. bool LLMultiSlider::findUnusedValue(F32& initVal)
  222. {
  223. bool firstTry = true;
  224. // find the first open slot starting with
  225. // the initial value
  226. while(true) {
  227. bool hit = false;
  228. // look at the current spot
  229. // and see if anything is there
  230. LLSD::map_iterator mIt = mValue.beginMap();
  231. for(;mIt != mValue.endMap(); mIt++) {
  232. F32 testVal = (F32)mIt->second.asReal() - initVal;
  233. if(testVal > -FLOAT_THRESHOLD && testVal < FLOAT_THRESHOLD) {
  234. hit = true;
  235. break;
  236. }
  237. }
  238. // if we found one
  239. if(!hit) {
  240. break;
  241. }
  242. // increment and wrap if need be
  243. initVal += mIncrement;
  244. if(initVal > mMaxValue) {
  245. initVal = mMinValue;
  246. }
  247. // stop if it's filled
  248. if(initVal == mInitialValue && !firstTry) {
  249. llwarns << "Whoa! Too many multi slider elements to add one to" << llendl;
  250. return false;
  251. }
  252. firstTry = false;
  253. continue;
  254. }
  255. return true;
  256. }
  257. void LLMultiSlider::deleteSlider(const std::string& name)
  258. {
  259. // can't delete last slider
  260. if(mValue.size() <= 0) {
  261. return;
  262. }
  263. // get rid of value from mValue and its thumb rect
  264. mValue.erase(name);
  265. mThumbRects.erase(name);
  266. // set to the last created
  267. if(mValue.size() > 0) {
  268. std::map<std::string, LLRect>::iterator mIt = mThumbRects.end();
  269. mIt--;
  270. mCurSlider = mIt->first;
  271. }
  272. }
  273. void LLMultiSlider::clear()
  274. {
  275. while(mThumbRects.size() > 0) {
  276. deleteCurSlider();
  277. }
  278. LLF32UICtrl::clear();
  279. }
  280. BOOL LLMultiSlider::handleHover(S32 x, S32 y, MASK mask)
  281. {
  282. if( gFocusMgr.getMouseCapture() == this )
  283. {
  284. S32 left_edge = mThumbWidth/2;
  285. S32 right_edge = getRect().getWidth() - (mThumbWidth/2);
  286. x += mMouseOffset;
  287. x = llclamp( x, left_edge, right_edge );
  288. F32 t = F32(x - left_edge) / (right_edge - left_edge);
  289. setCurSliderValue(t * (mMaxValue - mMinValue) + mMinValue );
  290. onCommit();
  291. getWindow()->setCursor(UI_CURSOR_ARROW);
  292. lldebugst(LLERR_USER_INPUT) << "hover handled by " << getName() << " (active)" << llendl;
  293. }
  294. else
  295. {
  296. getWindow()->setCursor(UI_CURSOR_ARROW);
  297. lldebugst(LLERR_USER_INPUT) << "hover handled by " << getName() << " (inactive)" << llendl;
  298. }
  299. return TRUE;
  300. }
  301. BOOL LLMultiSlider::handleMouseUp(S32 x, S32 y, MASK mask)
  302. {
  303. BOOL handled = FALSE;
  304. if( gFocusMgr.getMouseCapture() == this )
  305. {
  306. gFocusMgr.setMouseCapture( NULL );
  307. if (mMouseUpSignal)
  308. (*mMouseUpSignal)( this, LLSD() );
  309. handled = TRUE;
  310. make_ui_sound("UISndClickRelease");
  311. }
  312. else
  313. {
  314. handled = TRUE;
  315. }
  316. return handled;
  317. }
  318. BOOL LLMultiSlider::handleMouseDown(S32 x, S32 y, MASK mask)
  319. {
  320. // only do sticky-focus on non-chrome widgets
  321. if (!getIsChrome())
  322. {
  323. setFocus(TRUE);
  324. }
  325. if (mMouseDownSignal)
  326. (*mMouseDownSignal)( this, LLSD() );
  327. if (MASK_CONTROL & mask) // if CTRL is modifying
  328. {
  329. setCurSliderValue(mInitialValue);
  330. onCommit();
  331. }
  332. else
  333. {
  334. // scroll through thumbs to see if we have a new one selected and select that one
  335. std::map<std::string, LLRect>::iterator mIt = mThumbRects.begin();
  336. for(; mIt != mThumbRects.end(); mIt++) {
  337. // check if inside. If so, set current slider and continue
  338. if(mIt->second.pointInRect(x,y)) {
  339. mCurSlider = mIt->first;
  340. break;
  341. }
  342. }
  343. // Find the offset of the actual mouse location from the center of the thumb.
  344. if (mThumbRects[mCurSlider].pointInRect(x,y))
  345. {
  346. mMouseOffset = (mThumbRects[mCurSlider].mLeft + mThumbWidth/2) - x;
  347. }
  348. else
  349. {
  350. mMouseOffset = 0;
  351. }
  352. // Start dragging the thumb
  353. // No handler needed for focus lost since this class has no state that depends on it.
  354. gFocusMgr.setMouseCapture( this );
  355. mDragStartThumbRect = mThumbRects[mCurSlider];
  356. }
  357. make_ui_sound("UISndClick");
  358. return TRUE;
  359. }
  360. BOOL LLMultiSlider::handleKeyHere(KEY key, MASK mask)
  361. {
  362. BOOL handled = FALSE;
  363. switch(key)
  364. {
  365. case KEY_UP:
  366. case KEY_DOWN:
  367. // eat up and down keys to be consistent
  368. handled = TRUE;
  369. break;
  370. case KEY_LEFT:
  371. setCurSliderValue(getCurSliderValue() - getIncrement());
  372. onCommit();
  373. handled = TRUE;
  374. break;
  375. case KEY_RIGHT:
  376. setCurSliderValue(getCurSliderValue() + getIncrement());
  377. onCommit();
  378. handled = TRUE;
  379. break;
  380. default:
  381. break;
  382. }
  383. return handled;
  384. }
  385. void LLMultiSlider::draw()
  386. {
  387. static LLUICachedControl<S32> extra_triangle_height ("UIExtraTriangleHeight", 0);
  388. static LLUICachedControl<S32> extra_triangle_width ("UIExtraTriangleWidth", 0);
  389. LLColor4 curThumbColor;
  390. std::map<std::string, LLRect>::iterator mIt;
  391. std::map<std::string, LLRect>::iterator curSldrIt;
  392. // Draw background and thumb.
  393. // drawing solids requires texturing be disabled
  394. gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
  395. LLRect rect(mDragStartThumbRect);
  396. F32 opacity = getEnabled() ? 1.f : 0.3f;
  397. // Track
  398. LLUIImagePtr thumb_imagep = LLUI::getUIImage("Rounded_Square");
  399. static LLUICachedControl<S32> multi_track_height ("UIMultiTrackHeight", 0);
  400. S32 height_offset = (getRect().getHeight() - multi_track_height) / 2;
  401. LLRect track_rect(0, getRect().getHeight() - height_offset, getRect().getWidth(), height_offset );
  402. if(mDrawTrack)
  403. {
  404. track_rect.stretch(-1);
  405. thumb_imagep->draw(track_rect, mTrackColor.get() % opacity);
  406. }
  407. // if we're supposed to use a drawn triangle
  408. // simple gl call for the triangle
  409. if(mUseTriangle) {
  410. for(mIt = mThumbRects.begin(); mIt != mThumbRects.end(); mIt++) {
  411. gl_triangle_2d(
  412. mIt->second.mLeft - extra_triangle_width,
  413. mIt->second.mTop + extra_triangle_height,
  414. mIt->second.mRight + extra_triangle_width,
  415. mIt->second.mTop + extra_triangle_height,
  416. mIt->second.mLeft + mIt->second.getWidth() / 2,
  417. mIt->second.mBottom - extra_triangle_height,
  418. mTriangleColor.get() % opacity, TRUE);
  419. }
  420. }
  421. else if (!thumb_imagep)
  422. {
  423. // draw all the thumbs
  424. curSldrIt = mThumbRects.end();
  425. for(mIt = mThumbRects.begin(); mIt != mThumbRects.end(); mIt++) {
  426. // choose the color
  427. curThumbColor = mThumbCenterColor.get();
  428. if(mIt->first == mCurSlider) {
  429. curSldrIt = mIt;
  430. continue;
  431. //curThumbColor = mThumbCenterSelectedColor;
  432. }
  433. // the draw command
  434. gl_rect_2d(mIt->second, curThumbColor, TRUE);
  435. }
  436. // now draw the current slider
  437. if(curSldrIt != mThumbRects.end()) {
  438. gl_rect_2d(curSldrIt->second, mThumbCenterSelectedColor.get(), TRUE);
  439. }
  440. // and draw the drag start
  441. if (gFocusMgr.getMouseCapture() == this)
  442. {
  443. gl_rect_2d(mDragStartThumbRect, mThumbCenterColor.get() % opacity, FALSE);
  444. }
  445. }
  446. else if( gFocusMgr.getMouseCapture() == this )
  447. {
  448. // draw drag start
  449. thumb_imagep->drawSolid(mDragStartThumbRect, mThumbCenterColor.get() % 0.3f);
  450. // draw the highlight
  451. if (hasFocus())
  452. {
  453. thumb_imagep->drawBorder(mThumbRects[mCurSlider], gFocusMgr.getFocusColor(), gFocusMgr.getFocusFlashWidth());
  454. }
  455. // draw the thumbs
  456. curSldrIt = mThumbRects.end();
  457. for(mIt = mThumbRects.begin(); mIt != mThumbRects.end(); mIt++)
  458. {
  459. // choose the color
  460. curThumbColor = mThumbCenterColor.get();
  461. if(mIt->first == mCurSlider)
  462. {
  463. // don't draw now, draw last
  464. curSldrIt = mIt;
  465. continue;
  466. }
  467. // the draw command
  468. thumb_imagep->drawSolid(mIt->second, curThumbColor);
  469. }
  470. // draw cur slider last
  471. if(curSldrIt != mThumbRects.end())
  472. {
  473. thumb_imagep->drawSolid(curSldrIt->second, mThumbCenterSelectedColor.get());
  474. }
  475. }
  476. else
  477. {
  478. // draw highlight
  479. if (hasFocus())
  480. {
  481. thumb_imagep->drawBorder(mThumbRects[mCurSlider], gFocusMgr.getFocusColor(), gFocusMgr.getFocusFlashWidth());
  482. }
  483. // draw thumbs
  484. curSldrIt = mThumbRects.end();
  485. for(mIt = mThumbRects.begin(); mIt != mThumbRects.end(); mIt++)
  486. {
  487. // choose the color
  488. curThumbColor = mThumbCenterColor.get();
  489. if(mIt->first == mCurSlider)
  490. {
  491. curSldrIt = mIt;
  492. continue;
  493. //curThumbColor = mThumbCenterSelectedColor;
  494. }
  495. thumb_imagep->drawSolid(mIt->second, curThumbColor % opacity);
  496. }
  497. if(curSldrIt != mThumbRects.end())
  498. {
  499. thumb_imagep->drawSolid(curSldrIt->second, mThumbCenterSelectedColor.get() % opacity);
  500. }
  501. }
  502. LLF32UICtrl::draw();
  503. }
  504. boost::signals2::connection LLMultiSlider::setMouseDownCallback( const commit_signal_t::slot_type& cb )
  505. {
  506. if (!mMouseDownSignal) mMouseDownSignal = new commit_signal_t();
  507. return mMouseDownSignal->connect(cb);
  508. }
  509. boost::signals2::connection LLMultiSlider::setMouseUpCallback( const commit_signal_t::slot_type& cb )
  510. {
  511. if (!mMouseUpSignal) mMouseUpSignal = new commit_signal_t();
  512. return mMouseUpSignal->connect(cb);
  513. }