diff options
Diffstat (limited to 'indra/llui/llradiogroup.cpp')
-rw-r--r-- | indra/llui/llradiogroup.cpp | 440 |
1 files changed, 440 insertions, 0 deletions
diff --git a/indra/llui/llradiogroup.cpp b/indra/llui/llradiogroup.cpp new file mode 100644 index 0000000000..69c0da6933 --- /dev/null +++ b/indra/llui/llradiogroup.cpp @@ -0,0 +1,440 @@ +/** + * @file llradiogroup.cpp + * @brief LLRadioGroup base class + * + * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc. + * $License$ + */ + +// An invisible view containing multiple mutually exclusive toggling +// buttons (usually radio buttons). Automatically handles the mutex +// condition by highlighting only one button at a time. + +#include "linden_common.h" + +#include "llboost.h" + +#include "llradiogroup.h" +#include "indra_constants.h" + +#include "llviewborder.h" +#include "llcontrol.h" +#include "llui.h" +#include "llfocusmgr.h" + +LLRadioGroup::LLRadioGroup(const LLString& name, const LLRect& rect, + const LLString& control_name, + LLUICtrlCallback callback, + void* userdata, + BOOL border) +: LLUICtrl(name, rect, TRUE, callback, userdata, FOLLOWS_LEFT | FOLLOWS_TOP), + mSelectedIndex(0) +{ + setControlName(control_name, NULL); + init(border); +} + +LLRadioGroup::LLRadioGroup(const LLString& name, const LLRect& rect, + S32 initial_index, + LLUICtrlCallback callback, + void* userdata, + BOOL border) : + LLUICtrl(name, rect, TRUE, callback, userdata, FOLLOWS_LEFT | FOLLOWS_TOP), + mSelectedIndex(initial_index) +{ + init(border); +} + +void LLRadioGroup::init(BOOL border) +{ + if (border) + { + addChild( new LLViewBorder( "radio group border", + LLRect(0, mRect.getHeight(), mRect.getWidth(), 0), + LLViewBorder::BEVEL_NONE, + LLViewBorder::STYLE_LINE, + 1 ) ); + } + mHasBorder = border; +} + + + + +LLRadioGroup::~LLRadioGroup() +{ +} + + +// virtual +void LLRadioGroup::setEnabled(BOOL enabled) +{ + for (child_list_const_iter_t child_iter = getChildList()->begin(); + child_iter != getChildList()->end(); ++child_iter) + { + LLView *child = *child_iter; + child->setEnabled(enabled); + } + LLView::setEnabled(enabled); +} + +void LLRadioGroup::setIndexEnabled(S32 index, BOOL enabled) +{ + S32 count = 0; + for (button_list_t::iterator iter = mRadioButtons.begin(); + iter != mRadioButtons.end(); ++iter) + { + LLRadioCtrl* child = *iter; + if (count == index) + { + child->setEnabled(enabled); + if (index == mSelectedIndex && enabled == FALSE) + { + setSelectedIndex(-1); + } + break; + } + count++; + } + count = 0; + if (mSelectedIndex < 0) + { + // Set to highest enabled value < index, + // or lowest value above index if none lower are enabled + // or 0 if none are enabled + for (button_list_t::iterator iter = mRadioButtons.begin(); + iter != mRadioButtons.end(); ++iter) + { + LLRadioCtrl* child = *iter; + if (count >= index && mSelectedIndex >= 0) + { + break; + } + if (child->getEnabled()) + { + setSelectedIndex(count); + } + count++; + } + if (mSelectedIndex < 0) + { + setSelectedIndex(0); + } + } +} + +S32 LLRadioGroup::getSelectedIndex() const +{ + return mSelectedIndex; +} + +BOOL LLRadioGroup::setSelectedIndex(S32 index, BOOL from_event) +{ + if (index < 0 || index >= (S32)mRadioButtons.size()) + { + return FALSE; + } + + mSelectedIndex = index; + + if (!from_event) + { + setControlValue(getSelectedIndex()); + } + + return TRUE; +} + +BOOL LLRadioGroup::handleKeyHere(KEY key, MASK mask, BOOL called_from_parent) +{ + BOOL handled = FALSE; + // do any of the tab buttons have keyboard focus? + if (getEnabled() && !called_from_parent) + { + switch(key) + { + case KEY_DOWN: + if (!setSelectedIndex((getSelectedIndex() + 1))) + { + make_ui_sound("UISndInvalidOp"); + } + else + { + onCommit(); + } + handled = TRUE; + break; + case KEY_UP: + if (!setSelectedIndex((getSelectedIndex() - 1))) + { + make_ui_sound("UISndInvalidOp"); + } + else + { + onCommit(); + } + handled = TRUE; + break; + case KEY_LEFT: + if (!setSelectedIndex((getSelectedIndex() - 1))) + { + make_ui_sound("UISndInvalidOp"); + } + else + { + onCommit(); + } + handled = TRUE; + break; + case KEY_RIGHT: + if (!setSelectedIndex((getSelectedIndex() + 1))) + { + make_ui_sound("UISndInvalidOp"); + } + else + { + onCommit(); + } + handled = TRUE; + break; + default: + break; + } + } + return handled; +} + +void LLRadioGroup::draw() +{ + S32 current_button = 0; + + BOOL take_focus = FALSE; + if (gFocusMgr.childHasKeyboardFocus(this)) + { + take_focus = TRUE; + } + + for (button_list_t::iterator iter = mRadioButtons.begin(); + iter != mRadioButtons.end(); ++iter) + { + LLRadioCtrl* radio = *iter; + BOOL selected = (current_button == mSelectedIndex); + radio->setValue( selected ); + if (take_focus && selected && !gFocusMgr.childHasKeyboardFocus(radio)) + { + radio->focusFirstItem(); + } + current_button++; + } + + LLView::draw(); +} + + +// When adding a button, we need to ensure that the radio +// group gets a message when the button is clicked. +LLRadioCtrl* LLRadioGroup::addRadioButton(const LLString& name, const LLString& label, const LLRect& rect, const LLFontGL* font ) +{ + // Highlight will get fixed in draw method above + LLRadioCtrl* radio = new LLRadioCtrl(name, rect, label, font, + onClickButton, this); + addChild(radio); + mRadioButtons.push_back(radio); + return radio; +} + +// Handle one button being clicked. All child buttons must have this +// function as their callback function. + +// static +void LLRadioGroup::onClickButton(LLUICtrl* ui_ctrl, void* userdata) +{ + // llinfos << "LLRadioGroup::onClickButton" << llendl; + + LLRadioCtrl* clickedRadio = (LLRadioCtrl*) ui_ctrl; + LLRadioGroup* self = (LLRadioGroup*) userdata; + + S32 counter = 0; + for (button_list_t::iterator iter = self->mRadioButtons.begin(); + iter != self->mRadioButtons.end(); ++iter) + { + LLRadioCtrl* radio = *iter; + if (radio == clickedRadio) + { + // llinfos << "clicked button " << counter << llendl; + self->setSelectedIndex(counter); + self->setControlValue(counter); + + // BUG: Calls click callback even if button didn't actually change + self->onCommit(); + + return; + } + + counter++; + } + + llwarns << "LLRadioGroup::onClickButton - clicked button that isn't a child" << llendl; +} + +void LLRadioGroup::setValue( const LLSD& value ) +{ + LLString value_name = value.asString(); + int idx = 0; + for (button_list_t::const_iterator iter = mRadioButtons.begin(); + iter != mRadioButtons.end(); ++iter) + { + LLRadioCtrl* radio = *iter; + if (radio->getName() == value_name) + { + setSelectedIndex(idx); + idx = -1; + break; + } + ++idx; + } + if (idx != -1) + { + // string not found, try integer + if (value.isInteger()) + { + setSelectedIndex((S32) value.asInteger(), TRUE); + } + else + { + llwarns << "LLRadioGroup::setValue: value not found: " << value_name << llendl; + } + } +} + +LLSD LLRadioGroup::getValue() const +{ + int index = getSelectedIndex(); + int idx = 0; + for (button_list_t::const_iterator iter = mRadioButtons.begin(); + iter != mRadioButtons.end(); ++iter) + { + if (idx == index) return LLSD((*iter)->getName()); + ++idx; + } + return LLSD(); +} + +// virtual +LLXMLNodePtr LLRadioGroup::getXML(bool save_children) const +{ + LLXMLNodePtr node = LLUICtrl::getXML(); + + // Attributes + + node->createChild("draw_border", TRUE)->setBoolValue(mHasBorder); + + // Contents + + for (button_list_t::const_iterator iter = mRadioButtons.begin(); + iter != mRadioButtons.end(); ++iter) + { + LLRadioCtrl* radio = *iter; + + LLXMLNodePtr child_node = radio->LLView::getXML(); + child_node->setStringValue(radio->getLabel()); + child_node->setName("radio_item"); + + node->addChild(child_node); + } + + return node; +} + +// static +LLView* LLRadioGroup::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory) +{ + LLString name("radio_group"); + node->getAttributeString("name", name); + + U32 initial_value = 0; + node->getAttributeU32("initial_value", initial_value); + + BOOL draw_border = TRUE; + node->getAttributeBOOL("draw_border", draw_border); + + LLRect rect; + createRect(node, rect, parent, LLRect()); + + LLRadioGroup* radio_group = new LLRadioGroup(name, + rect, + initial_value, + NULL, + NULL, + draw_border); + + const LLString& contents = node->getValue(); + + LLRect group_rect = radio_group->getRect(); + + LLFontGL *font = LLView::selectFont(node); + + if (contents.find_first_not_of(" \n\t") != contents.npos) + { + // ...old school default vertical layout + typedef boost::tokenizer<boost::char_separator<char> > tokenizer; + boost::char_separator<char> sep("\t\n"); + tokenizer tokens(contents, sep); + tokenizer::iterator token_iter = tokens.begin(); + + const S32 HPAD = 4, VPAD = 4; + S32 cur_y = group_rect.getHeight() - VPAD; + + while(token_iter != tokens.end()) + { + const char* line = token_iter->c_str(); + LLRect rect(HPAD, cur_y, group_rect.getWidth() - (2 * HPAD), cur_y - 15); + cur_y -= VPAD + 15; + radio_group->addRadioButton("radio", line, rect, font); + ++token_iter; + } + llwarns << "Legacy radio group format used! Please convert to use <radio_item> tags!" << llendl; + } + else + { + // ...per pixel layout + LLXMLNodePtr child; + for (child = node->getFirstChild(); child.notNull(); child = child->getNextSibling()) + { + if (child->hasName("radio_item")) + { + LLRect item_rect; + createRect(child, item_rect, radio_group, rect); + + LLString radioname("radio"); + child->getAttributeString("name", radioname); + LLString item_label = child->getTextContents(); + LLRadioCtrl* radio = radio_group->addRadioButton(radioname, item_label.c_str(), item_rect, font); + + radio->initFromXML(child, radio_group); + } + } + } + + radio_group->initFromXML(node, parent); + + return radio_group; +} + + +LLRadioCtrl::LLRadioCtrl(const LLString& name, const LLRect& rect, const LLString& label, + const LLFontGL* font, void (*commit_callback)(LLUICtrl*, void*), void* callback_userdata) : + LLCheckBoxCtrl(name, rect, label, font, commit_callback, callback_userdata, FALSE, RADIO_STYLE) +{ + setTabStop(FALSE); +} + +LLRadioCtrl::~LLRadioCtrl() +{ +} + +void LLRadioCtrl::setValue(const LLSD& value) +{ + LLCheckBoxCtrl::setValue(value); + mButton->setTabStop(value.asBoolean()); +} |