llradiogroup.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479
  1. /**
  2. * @file llradiogroup.cpp
  3. * @brief LLRadioGroup base class
  4. *
  5. * $LicenseInfo:firstyear=2001&license=viewergpl$
  6. *
  7. * Copyright (c) 2001-2009, Linden Research, Inc.
  8. *
  9. * Second Life Viewer Source Code
  10. * The source code in this file ("Source Code") is provided by Linden Lab
  11. * to you under the terms of the GNU General Public License, version 2.0
  12. * ("GPL"), unless you have obtained a separate licensing agreement
  13. * ("Other License"), formally executed by you and Linden Lab. Terms of
  14. * the GPL can be found in doc/GPL-license.txt in this distribution, or
  15. * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
  16. *
  17. * There are special exceptions to the terms and conditions of the GPL as
  18. * it is applied to this Source Code. View the full text of the exception
  19. * in the file doc/FLOSS-exception.txt in this software distribution, or
  20. * online at
  21. * http://secondlifegrid.net/programs/open_source/licensing/flossexception
  22. *
  23. * By copying, modifying or distributing this software, you acknowledge
  24. * that you have read and understood your obligations described above,
  25. * and agree to abide by those obligations.
  26. *
  27. * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
  28. * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
  29. * COMPLETENESS OR PERFORMANCE.
  30. * $/LicenseInfo$
  31. */
  32. #include "linden_common.h"
  33. #include "llradiogroup.h"
  34. #include "indra_constants.h"
  35. #include "llcontrol.h"
  36. #include "llviewborder.h"
  37. static const std::string LL_RADIO_GROUP_TAG = "radio_group";
  38. static const std::string LL_RADIO_ITEM_TAG = "radio_item";
  39. static LLRegisterWidget<LLRadioGroup> r17(LL_RADIO_GROUP_TAG);
  40. ///////////////////////////////////////////////////////////////////////////////
  41. // LLRadioCtrl class
  42. ///////////////////////////////////////////////////////////////////////////////
  43. //virtual
  44. const std::string& LLRadioCtrl::getTag() const
  45. {
  46. return LL_RADIO_ITEM_TAG;
  47. }
  48. void LLRadioCtrl::setValue(const LLSD& value)
  49. {
  50. LLCheckBoxCtrl::setValue(value);
  51. mButton->setTabStop(value.asBoolean());
  52. }
  53. //virtual
  54. LLXMLNodePtr LLRadioCtrl::getXML(bool save_children) const
  55. {
  56. LLXMLNodePtr nodep = LLCheckBoxCtrl::getXML();
  57. nodep->setName(LL_RADIO_ITEM_TAG);
  58. nodep->setStringValue(getLabel());
  59. return nodep;
  60. }
  61. ///////////////////////////////////////////////////////////////////////////////
  62. // LLRadioGroup class
  63. ///////////////////////////////////////////////////////////////////////////////
  64. LLRadioGroup::LLRadioGroup(const std::string& name, const LLRect& rect,
  65. const char* control_name,
  66. LLUICtrlCallback callback, void* userdata,
  67. bool border)
  68. : LLUICtrl(name, rect, true, callback, userdata, FOLLOWS_LEFT | FOLLOWS_TOP),
  69. mSelectedIndex(0)
  70. {
  71. setControlName(control_name, NULL);
  72. init(border);
  73. }
  74. LLRadioGroup::LLRadioGroup(const std::string& name, const LLRect& rect,
  75. S32 initial_index, LLUICtrlCallback callback,
  76. void* userdata, bool border)
  77. : LLUICtrl(name, rect, true, callback, userdata, FOLLOWS_LEFT | FOLLOWS_TOP),
  78. mSelectedIndex(initial_index)
  79. {
  80. init(border);
  81. }
  82. void LLRadioGroup::init(bool border)
  83. {
  84. if (border)
  85. {
  86. addChild(new LLViewBorder("radio group border",
  87. LLRect(0, getRect().getHeight(),
  88. getRect().getWidth(), 0),
  89. LLViewBorder::BEVEL_NONE,
  90. LLViewBorder::STYLE_LINE, 1));
  91. }
  92. mHasBorder = border;
  93. }
  94. //virtual
  95. void LLRadioGroup::setEnabled(bool enabled)
  96. {
  97. for (child_list_const_iter_t it = getChildList()->begin(),
  98. end = getChildList()->end();
  99. it != end; ++it)
  100. {
  101. (*it)->setEnabled(enabled);
  102. }
  103. LLView::setEnabled(enabled);
  104. }
  105. void LLRadioGroup::setIndexEnabled(S32 index, bool enabled)
  106. {
  107. button_list_t::iterator begin = mRadioButtons.begin();
  108. button_list_t::iterator end = mRadioButtons.end();
  109. S32 count = 0;
  110. for (button_list_t::iterator iter = begin; iter != end; ++iter)
  111. {
  112. LLRadioCtrl* childp = *iter;
  113. if (count == index)
  114. {
  115. childp->setEnabled(enabled);
  116. if (index == mSelectedIndex && !enabled)
  117. {
  118. setSelectedIndex(-1);
  119. }
  120. break;
  121. }
  122. ++count;
  123. }
  124. count = 0;
  125. if (mSelectedIndex < 0)
  126. {
  127. // Set to highest enabled value < index, or lowest value above index if
  128. // none lower are enabled, or 0 if none are enabled
  129. for (button_list_t::iterator iter = begin; iter != end; ++iter)
  130. {
  131. if (count >= index && mSelectedIndex >= 0)
  132. {
  133. break;
  134. }
  135. LLRadioCtrl* childp = *iter;
  136. if (childp->getEnabled())
  137. {
  138. setSelectedIndex(count);
  139. }
  140. ++count;
  141. }
  142. if (mSelectedIndex < 0)
  143. {
  144. setSelectedIndex(0);
  145. }
  146. }
  147. }
  148. bool LLRadioGroup::setSelectedIndex(S32 index, bool from_event)
  149. {
  150. if (index < 0 || index >= (S32)mRadioButtons.size())
  151. {
  152. return false;
  153. }
  154. mSelectedIndex = index;
  155. if (!from_event)
  156. {
  157. setControlValue(getSelectedIndex());
  158. }
  159. return true;
  160. }
  161. bool LLRadioGroup::handleKeyHere(KEY key, MASK mask)
  162. {
  163. bool handled = false;
  164. // do any of the tab buttons have keyboard focus ?
  165. if (mask == MASK_NONE)
  166. {
  167. handled = true;
  168. switch (key)
  169. {
  170. case KEY_DOWN:
  171. if (!setSelectedIndex(getSelectedIndex() + 1))
  172. {
  173. make_ui_sound("UISndInvalidOp");
  174. }
  175. else
  176. {
  177. onCommit();
  178. }
  179. break;
  180. case KEY_UP:
  181. if (!setSelectedIndex(getSelectedIndex() - 1))
  182. {
  183. make_ui_sound("UISndInvalidOp");
  184. }
  185. else
  186. {
  187. onCommit();
  188. }
  189. break;
  190. case KEY_LEFT:
  191. if (!setSelectedIndex(getSelectedIndex() - 1))
  192. {
  193. make_ui_sound("UISndInvalidOp");
  194. }
  195. else
  196. {
  197. onCommit();
  198. }
  199. break;
  200. case KEY_RIGHT:
  201. if (!setSelectedIndex(getSelectedIndex() + 1))
  202. {
  203. make_ui_sound("UISndInvalidOp");
  204. }
  205. else
  206. {
  207. onCommit();
  208. }
  209. break;
  210. default:
  211. handled = false;
  212. }
  213. }
  214. return handled;
  215. }
  216. void LLRadioGroup::draw()
  217. {
  218. S32 current_button = 0;
  219. bool take_focus = false;
  220. if (gFocusMgr.childHasKeyboardFocus(this))
  221. {
  222. take_focus = true;
  223. }
  224. for (button_list_t::iterator iter = mRadioButtons.begin(),
  225. end = mRadioButtons.end();
  226. iter != end; ++iter)
  227. {
  228. LLRadioCtrl* radiop = *iter;
  229. bool selected = current_button == mSelectedIndex;
  230. radiop->setValue(selected);
  231. if (take_focus && selected && !gFocusMgr.childHasKeyboardFocus(radiop))
  232. {
  233. // Do not flash keyboard focus when navigating via keyboard
  234. bool dont_flash = false;
  235. radiop->focusFirstItem(false, dont_flash);
  236. }
  237. ++current_button;
  238. }
  239. LLView::draw();
  240. }
  241. // When adding a button, we need to ensure that the radio group gets a message
  242. // when the button is clicked.
  243. LLRadioCtrl* LLRadioGroup::addRadioButton(const std::string& name,
  244. const std::string& label,
  245. const LLRect& rect,
  246. const LLFontGL* fontp)
  247. {
  248. // Highlight will get fixed in draw method above
  249. LLRadioCtrl* radiop = new LLRadioCtrl(name, rect, label, fontp,
  250. onClickButton, this);
  251. addChild(radiop);
  252. mRadioButtons.push_back(radiop);
  253. return radiop;
  254. }
  255. // Handle one button being clicked. All child buttons must have this method as
  256. // their callback.
  257. //static
  258. void LLRadioGroup::onClickButton(LLUICtrl* ctrlp, void* userdata)
  259. {
  260. LLRadioCtrl* clicked_radiop = (LLRadioCtrl*)ctrlp;
  261. LLRadioGroup* self = (LLRadioGroup*)userdata;
  262. S32 counter = 0;
  263. for (button_list_t::iterator iter = self->mRadioButtons.begin(),
  264. end = self->mRadioButtons.end();
  265. iter != end; ++iter)
  266. {
  267. LLRadioCtrl* radiop = *iter;
  268. if (radiop == clicked_radiop)
  269. {
  270. #if 1 // Note: original code did not have this check and unconditionnaly
  271. // called onCommit(), which was commented as a bug; this fix should
  272. // not have any bad consequence... HB
  273. if (self->mSelectedIndex != counter)
  274. #endif
  275. {
  276. self->setSelectedIndex(counter);
  277. self->setControlValue(counter);
  278. self->onCommit();
  279. }
  280. return;
  281. }
  282. ++counter;
  283. }
  284. // This should never happen.
  285. llerrs << "Passed LLUICtrl pointer is not a child button of radio group: "
  286. << self->getName() << llendl;
  287. }
  288. void LLRadioGroup::setValue(const LLSD& value)
  289. {
  290. std::string value_name = value.asString();
  291. S32 idx = 0;
  292. for (button_list_t::const_iterator iter = mRadioButtons.begin(),
  293. end = mRadioButtons.end();
  294. iter != end; ++iter)
  295. {
  296. LLRadioCtrl* radiop = *iter;
  297. if (radiop->getName() == value_name)
  298. {
  299. setSelectedIndex(idx);
  300. idx = -1;
  301. break;
  302. }
  303. ++idx;
  304. }
  305. if (idx != -1)
  306. {
  307. // String not found, try integer
  308. if (value.isInteger())
  309. {
  310. setSelectedIndex(value.asInteger(), true);
  311. }
  312. else
  313. {
  314. llwarns << "Value not found: " << value_name << llendl;
  315. }
  316. }
  317. }
  318. LLSD LLRadioGroup::getValue() const
  319. {
  320. S32 index = getSelectedIndex();
  321. S32 idx = 0;
  322. for (button_list_t::const_iterator iter = mRadioButtons.begin(),
  323. end = mRadioButtons.end();
  324. iter != end; ++iter)
  325. {
  326. if (idx == index)
  327. {
  328. return LLSD((*iter)->getName());
  329. }
  330. ++idx;
  331. }
  332. return LLSD();
  333. }
  334. //virtual
  335. const std::string& LLRadioGroup::getTag() const
  336. {
  337. return LL_RADIO_GROUP_TAG;
  338. }
  339. //virtual
  340. LLXMLNodePtr LLRadioGroup::getXML(bool save_children) const
  341. {
  342. LLXMLNodePtr nodep = LLUICtrl::getXML();
  343. nodep->setName(LL_RADIO_GROUP_TAG);
  344. // Attributes
  345. nodep->createChild("draw_border", true)->setBoolValue(mHasBorder);
  346. // Contents
  347. for (button_list_t::const_iterator iter = mRadioButtons.begin(),
  348. end = mRadioButtons.end();
  349. iter != end; ++iter)
  350. {
  351. LLXMLNodePtr child_nodep = (*iter)->getXML();
  352. nodep->addChild(child_nodep);
  353. }
  354. return nodep;
  355. }
  356. // static
  357. LLView* LLRadioGroup::fromXML(LLXMLNodePtr nodep, LLView* parentp,
  358. LLUICtrlFactory*)
  359. {
  360. std::string name = LL_RADIO_GROUP_TAG;
  361. nodep->getAttributeString("name", name);
  362. U32 initial_value = 0;
  363. nodep->getAttributeU32("initial_value", initial_value);
  364. bool draw_border = true;
  365. nodep->getAttributeBool("draw_border", draw_border);
  366. LLRect rect;
  367. createRect(nodep, rect, parentp, LLRect());
  368. LLRadioGroup* groupp = new LLRadioGroup(name, rect, initial_value, NULL,
  369. NULL, draw_border);
  370. LLFontGL* fontp = LLView::selectFont(nodep);
  371. LLXMLNodePtr childp;
  372. for (LLXMLNodePtr childp = nodep->getFirstChild(); childp.notNull();
  373. childp = childp->getNextSibling())
  374. {
  375. if (childp->hasName("radio_item"))
  376. {
  377. LLRect item_rect;
  378. createRect(childp, item_rect, groupp, rect);
  379. std::string radioname("radio");
  380. childp->getAttributeString("name", radioname);
  381. std::string item_label = childp->getTextContents();
  382. LLRadioCtrl* radiop = groupp->addRadioButton(radioname, item_label,
  383. item_rect, fontp);
  384. radiop->initFromXML(childp, groupp);
  385. }
  386. }
  387. groupp->initFromXML(nodep, parentp);
  388. return groupp;
  389. }
  390. bool LLRadioGroup::setSelectedByValue(const LLSD& value, bool selected)
  391. {
  392. S32 idx = 0;
  393. std::string value_string = value.asString();
  394. for (button_list_t::const_iterator iter = mRadioButtons.begin(),
  395. end = mRadioButtons.end();
  396. iter != end; ++iter)
  397. {
  398. if ((*iter)->getName() == value_string)
  399. {
  400. setSelectedIndex(idx);
  401. return true;
  402. }
  403. ++idx;
  404. }
  405. return false;
  406. }
  407. bool LLRadioGroup::isSelected(const LLSD& value) const
  408. {
  409. S32 idx = 0;
  410. std::string value_string = value.asString();
  411. for (button_list_t::const_iterator iter = mRadioButtons.begin(),
  412. end = mRadioButtons.end();
  413. iter != end; ++iter)
  414. {
  415. if ((*iter)->getName() == value_string)
  416. {
  417. if (idx == mSelectedIndex)
  418. {
  419. return true;
  420. }
  421. }
  422. ++idx;
  423. }
  424. return false;
  425. }