PageRenderTime 30ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/indra/llui/llradiogroup.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 482 lines | 371 code | 57 blank | 54 comment | 64 complexity | 9578eed182187c28bf6dbb0f64e9ac82 MD5 | raw file
Possible License(s): LGPL-2.1
  1. /**
  2. * @file llradiogroup.cpp
  3. * @brief LLRadioGroup base class
  4. *
  5. * $LicenseInfo:firstyear=2001&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 "llboost.h"
  28. #include "llradiogroup.h"
  29. #include "indra_constants.h"
  30. #include "llviewborder.h"
  31. #include "llcontrol.h"
  32. #include "llui.h"
  33. #include "llfocusmgr.h"
  34. #include "lluictrlfactory.h"
  35. #include "llsdutil.h"
  36. static LLDefaultChildRegistry::Register<LLRadioGroup> r1("radio_group");
  37. /*
  38. * An invisible view containing multiple mutually exclusive toggling
  39. * buttons (usually radio buttons). Automatically handles the mutex
  40. * condition by highlighting only one button at a time.
  41. */
  42. class LLRadioCtrl : public LLCheckBoxCtrl
  43. {
  44. public:
  45. typedef LLRadioGroup::ItemParams Params;
  46. /*virtual*/ ~LLRadioCtrl();
  47. /*virtual*/ void setValue(const LLSD& value);
  48. /*virtual*/ BOOL postBuild();
  49. LLSD getPayload() { return mPayload; }
  50. // Ensure label is in an attribute, not the contents
  51. static void setupParamsForExport(Params& p, LLView* parent);
  52. protected:
  53. LLRadioCtrl(const LLRadioGroup::ItemParams& p);
  54. friend class LLUICtrlFactory;
  55. LLSD mPayload; // stores data that this item represents in the radio group
  56. };
  57. static LLWidgetNameRegistry::StaticRegistrar register_radio_item(&typeid(LLRadioGroup::ItemParams), "radio_item");
  58. LLRadioGroup::Params::Params()
  59. : allow_deselect("allow_deselect"),
  60. items("item")
  61. {
  62. addSynonym(items, "radio_item");
  63. // radio items are not tabbable until they are selected
  64. tab_stop = false;
  65. }
  66. LLRadioGroup::LLRadioGroup(const LLRadioGroup::Params& p)
  67. : LLUICtrl(p),
  68. mFont(p.font.isProvided() ? p.font() : LLFontGL::getFontSansSerifSmall()),
  69. mSelectedIndex(-1),
  70. mAllowDeselect(p.allow_deselect)
  71. {}
  72. void LLRadioGroup::initFromParams(const Params& p)
  73. {
  74. for (LLInitParam::ParamIterator<ItemParams>::const_iterator it = p.items.begin();
  75. it != p.items.end();
  76. ++it)
  77. {
  78. LLRadioGroup::ItemParams item_params(*it);
  79. if (!item_params.font.isProvided())
  80. {
  81. item_params.font = mFont; // apply radio group font by default
  82. }
  83. item_params.commit_callback.function = boost::bind(&LLRadioGroup::onClickButton, this, _1);
  84. item_params.from_xui = p.from_xui;
  85. if (p.from_xui)
  86. {
  87. applyXUILayout(item_params, this);
  88. }
  89. LLRadioCtrl* item = LLUICtrlFactory::create<LLRadioCtrl>(item_params, this);
  90. mRadioButtons.push_back(item);
  91. }
  92. // call this *after* setting up mRadioButtons so we can handle setValue() calls
  93. LLUICtrl::initFromParams(p);
  94. }
  95. LLRadioGroup::~LLRadioGroup()
  96. {
  97. }
  98. // virtual
  99. BOOL LLRadioGroup::postBuild()
  100. {
  101. if (!mRadioButtons.empty())
  102. {
  103. mRadioButtons[0]->setTabStop(true);
  104. }
  105. return TRUE;
  106. }
  107. void LLRadioGroup::setIndexEnabled(S32 index, BOOL enabled)
  108. {
  109. S32 count = 0;
  110. for (button_list_t::iterator iter = mRadioButtons.begin();
  111. iter != mRadioButtons.end(); ++iter)
  112. {
  113. LLRadioCtrl* child = *iter;
  114. if (count == index)
  115. {
  116. child->setEnabled(enabled);
  117. if (index == mSelectedIndex && enabled == FALSE)
  118. {
  119. setSelectedIndex(-1);
  120. }
  121. break;
  122. }
  123. count++;
  124. }
  125. count = 0;
  126. if (mSelectedIndex < 0)
  127. {
  128. // Set to highest enabled value < index,
  129. // or lowest value above index if none lower are enabled
  130. // or 0 if none are enabled
  131. for (button_list_t::iterator iter = mRadioButtons.begin();
  132. iter != mRadioButtons.end(); ++iter)
  133. {
  134. LLRadioCtrl* child = *iter;
  135. if (count >= index && mSelectedIndex >= 0)
  136. {
  137. break;
  138. }
  139. if (child->getEnabled())
  140. {
  141. setSelectedIndex(count);
  142. }
  143. count++;
  144. }
  145. if (mSelectedIndex < 0)
  146. {
  147. setSelectedIndex(0);
  148. }
  149. }
  150. }
  151. BOOL LLRadioGroup::setSelectedIndex(S32 index, BOOL from_event)
  152. {
  153. if ((S32)mRadioButtons.size() <= index )
  154. {
  155. return FALSE;
  156. }
  157. if (mSelectedIndex >= 0)
  158. {
  159. LLRadioCtrl* old_radio_item = mRadioButtons[mSelectedIndex];
  160. old_radio_item->setTabStop(false);
  161. old_radio_item->setValue( FALSE );
  162. }
  163. else
  164. {
  165. mRadioButtons[0]->setTabStop(false);
  166. }
  167. mSelectedIndex = index;
  168. if (mSelectedIndex >= 0)
  169. {
  170. LLRadioCtrl* radio_item = mRadioButtons[mSelectedIndex];
  171. radio_item->setTabStop(true);
  172. radio_item->setValue( TRUE );
  173. if (hasFocus())
  174. {
  175. radio_item->focusFirstItem(FALSE, FALSE);
  176. }
  177. }
  178. if (!from_event)
  179. {
  180. setControlValue(getValue());
  181. }
  182. return TRUE;
  183. }
  184. BOOL LLRadioGroup::handleKeyHere(KEY key, MASK mask)
  185. {
  186. BOOL handled = FALSE;
  187. // do any of the tab buttons have keyboard focus?
  188. if (mask == MASK_NONE)
  189. {
  190. switch(key)
  191. {
  192. case KEY_DOWN:
  193. if (!setSelectedIndex((getSelectedIndex() + 1)))
  194. {
  195. make_ui_sound("UISndInvalidOp");
  196. }
  197. else
  198. {
  199. onCommit();
  200. }
  201. handled = TRUE;
  202. break;
  203. case KEY_UP:
  204. if (!setSelectedIndex((getSelectedIndex() - 1)))
  205. {
  206. make_ui_sound("UISndInvalidOp");
  207. }
  208. else
  209. {
  210. onCommit();
  211. }
  212. handled = TRUE;
  213. break;
  214. case KEY_LEFT:
  215. if (!setSelectedIndex((getSelectedIndex() - 1)))
  216. {
  217. make_ui_sound("UISndInvalidOp");
  218. }
  219. else
  220. {
  221. onCommit();
  222. }
  223. handled = TRUE;
  224. break;
  225. case KEY_RIGHT:
  226. if (!setSelectedIndex((getSelectedIndex() + 1)))
  227. {
  228. make_ui_sound("UISndInvalidOp");
  229. }
  230. else
  231. {
  232. onCommit();
  233. }
  234. handled = TRUE;
  235. break;
  236. default:
  237. break;
  238. }
  239. }
  240. return handled;
  241. }
  242. BOOL LLRadioGroup::handleMouseDown(S32 x, S32 y, MASK mask)
  243. {
  244. // grab focus preemptively, before child button takes mousecapture
  245. //
  246. if (hasTabStop())
  247. {
  248. focusFirstItem(FALSE, FALSE);
  249. }
  250. return LLUICtrl::handleMouseDown(x, y, mask);
  251. }
  252. // Handle one button being clicked. All child buttons must have this
  253. // function as their callback function.
  254. void LLRadioGroup::onClickButton(LLUICtrl* ctrl)
  255. {
  256. // llinfos << "LLRadioGroup::onClickButton" << llendl;
  257. LLRadioCtrl* clicked_radio = dynamic_cast<LLRadioCtrl*>(ctrl);
  258. if (!clicked_radio)
  259. return;
  260. S32 index = 0;
  261. for (button_list_t::iterator iter = mRadioButtons.begin();
  262. iter != mRadioButtons.end(); ++iter)
  263. {
  264. LLRadioCtrl* radio = *iter;
  265. if (radio == clicked_radio)
  266. {
  267. if (index == mSelectedIndex && mAllowDeselect)
  268. {
  269. // don't select anything
  270. setSelectedIndex(-1);
  271. }
  272. else
  273. {
  274. setSelectedIndex(index);
  275. }
  276. // BUG: Calls click callback even if button didn't actually change
  277. onCommit();
  278. return;
  279. }
  280. index++;
  281. }
  282. llwarns << "LLRadioGroup::onClickButton - clicked button that isn't a child" << llendl;
  283. }
  284. void LLRadioGroup::setValue( const LLSD& value )
  285. {
  286. int idx = 0;
  287. for (button_list_t::const_iterator iter = mRadioButtons.begin();
  288. iter != mRadioButtons.end(); ++iter)
  289. {
  290. LLRadioCtrl* radio = *iter;
  291. if (radio->getPayload().asString() == value.asString())
  292. {
  293. setSelectedIndex(idx);
  294. idx = -1;
  295. break;
  296. }
  297. ++idx;
  298. }
  299. if (idx != -1)
  300. {
  301. // string not found, try integer
  302. if (value.isInteger())
  303. {
  304. setSelectedIndex((S32) value.asInteger(), TRUE);
  305. }
  306. else
  307. {
  308. setSelectedIndex(-1, TRUE);
  309. }
  310. }
  311. }
  312. LLSD LLRadioGroup::getValue() const
  313. {
  314. int index = getSelectedIndex();
  315. int idx = 0;
  316. for (button_list_t::const_iterator iter = mRadioButtons.begin();
  317. iter != mRadioButtons.end(); ++iter)
  318. {
  319. if (idx == index) return LLSD((*iter)->getPayload());
  320. ++idx;
  321. }
  322. return LLSD();
  323. }
  324. // LLCtrlSelectionInterface functions
  325. BOOL LLRadioGroup::setCurrentByID( const LLUUID& id )
  326. {
  327. return FALSE;
  328. }
  329. LLUUID LLRadioGroup::getCurrentID() const
  330. {
  331. return LLUUID::null;
  332. }
  333. BOOL LLRadioGroup::setSelectedByValue(const LLSD& value, BOOL selected)
  334. {
  335. S32 idx = 0;
  336. for (button_list_t::const_iterator iter = mRadioButtons.begin();
  337. iter != mRadioButtons.end(); ++iter)
  338. {
  339. if((*iter)->getPayload().asString() == value.asString())
  340. {
  341. setSelectedIndex(idx);
  342. return TRUE;
  343. }
  344. idx++;
  345. }
  346. return FALSE;
  347. }
  348. LLSD LLRadioGroup::getSelectedValue()
  349. {
  350. return getValue();
  351. }
  352. BOOL LLRadioGroup::isSelected(const LLSD& value) const
  353. {
  354. S32 idx = 0;
  355. for (button_list_t::const_iterator iter = mRadioButtons.begin();
  356. iter != mRadioButtons.end(); ++iter)
  357. {
  358. if((*iter)->getPayload().asString() == value.asString())
  359. {
  360. if (idx == mSelectedIndex)
  361. {
  362. return TRUE;
  363. }
  364. }
  365. idx++;
  366. }
  367. return FALSE;
  368. }
  369. BOOL LLRadioGroup::operateOnSelection(EOperation op)
  370. {
  371. return FALSE;
  372. }
  373. BOOL LLRadioGroup::operateOnAll(EOperation op)
  374. {
  375. return FALSE;
  376. }
  377. LLRadioGroup::ItemParams::ItemParams()
  378. : value("value")
  379. {
  380. addSynonym(value, "initial_value");
  381. }
  382. LLRadioCtrl::LLRadioCtrl(const LLRadioGroup::ItemParams& p)
  383. : LLCheckBoxCtrl(p),
  384. mPayload(p.value)
  385. {
  386. // use name as default "Value" for backwards compatibility
  387. if (!p.value.isProvided())
  388. {
  389. mPayload = p.name();
  390. }
  391. }
  392. BOOL LLRadioCtrl::postBuild()
  393. {
  394. // Old-style radio_item used the text contents to indicate the label,
  395. // but new-style radio_item uses label attribute.
  396. std::string value = getValue().asString();
  397. if (!value.empty())
  398. {
  399. setLabel(value);
  400. }
  401. return TRUE;
  402. }
  403. LLRadioCtrl::~LLRadioCtrl()
  404. {
  405. }
  406. void LLRadioCtrl::setValue(const LLSD& value)
  407. {
  408. LLCheckBoxCtrl::setValue(value);
  409. mButton->setTabStop(value.asBoolean());
  410. }
  411. // *TODO: Remove this function after the initial XUI XML re-export pass.
  412. // static
  413. void LLRadioCtrl::setupParamsForExport(Params& p, LLView* parent)
  414. {
  415. std::string label = p.label;
  416. if (label.empty())
  417. {
  418. // We don't have a label attribute, so move the text contents
  419. // stored in "value" into the label
  420. std::string initial_value = p.LLUICtrl::Params::initial_value();
  421. p.label = initial_value;
  422. p.LLUICtrl::Params::initial_value = LLSD();
  423. }
  424. LLCheckBoxCtrl::setupParamsForExport(p, parent);
  425. }