llradiogroup.cpp

Go to the documentation of this file.
00001 
00032 // An invisible view containing multiple mutually exclusive toggling 
00033 // buttons (usually radio buttons).  Automatically handles the mutex
00034 // condition by highlighting only one button at a time.
00035 
00036 #include "linden_common.h"
00037 
00038 #include "llboost.h"
00039 
00040 #include "llradiogroup.h"
00041 #include "indra_constants.h"
00042 
00043 #include "llviewborder.h"
00044 #include "llcontrol.h"
00045 #include "llui.h"
00046 #include "llfocusmgr.h"
00047 
00048 LLRadioGroup::LLRadioGroup(const LLString& name, const LLRect& rect,
00049                                                    const LLString& control_name,
00050                                                    LLUICtrlCallback callback,
00051                                                    void* userdata,
00052                                                    BOOL border)
00053 :       LLUICtrl(name, rect, TRUE, callback, userdata, FOLLOWS_LEFT | FOLLOWS_TOP),
00054         mSelectedIndex(0)
00055 {
00056         setControlName(control_name, NULL);
00057         init(border);
00058 }
00059 
00060 LLRadioGroup::LLRadioGroup(const LLString& name, const LLRect& rect,
00061                                                    S32 initial_index,
00062                                                    LLUICtrlCallback callback,
00063                                                    void* userdata,
00064                                                    BOOL border) :
00065         LLUICtrl(name, rect, TRUE, callback, userdata, FOLLOWS_LEFT | FOLLOWS_TOP),
00066         mSelectedIndex(initial_index)
00067 {
00068         init(border);
00069 }
00070 
00071 void LLRadioGroup::init(BOOL border)
00072 {
00073         if (border)
00074         {
00075                 addChild( new LLViewBorder( "radio group border", 
00076                                                                         LLRect(0, mRect.getHeight(), mRect.getWidth(), 0), 
00077                                                                         LLViewBorder::BEVEL_NONE, 
00078                                                                         LLViewBorder::STYLE_LINE, 
00079                                                                         1 ) );
00080         }
00081         mHasBorder = border;
00082 }
00083 
00084 
00085 
00086 
00087 LLRadioGroup::~LLRadioGroup()
00088 {
00089 }
00090 
00091 
00092 // virtual
00093 void LLRadioGroup::setEnabled(BOOL enabled)
00094 {
00095         for (child_list_const_iter_t child_iter = getChildList()->begin();
00096                  child_iter != getChildList()->end(); ++child_iter)
00097         {
00098                 LLView *child = *child_iter;
00099                 child->setEnabled(enabled);
00100         }
00101         LLView::setEnabled(enabled);
00102 }
00103 
00104 void LLRadioGroup::setIndexEnabled(S32 index, BOOL enabled)
00105 {
00106         S32 count = 0;
00107         for (button_list_t::iterator iter = mRadioButtons.begin();
00108                  iter != mRadioButtons.end(); ++iter)
00109         {
00110                 LLRadioCtrl* child = *iter;
00111                 if (count == index)
00112                 {
00113                         child->setEnabled(enabled);
00114                         if (index == mSelectedIndex && enabled == FALSE)
00115                         {
00116                                 setSelectedIndex(-1);
00117                         }
00118                         break;
00119                 }
00120                 count++;
00121         }
00122         count = 0;
00123         if (mSelectedIndex < 0)
00124         {
00125                 // Set to highest enabled value < index,
00126                 // or lowest value above index if none lower are enabled
00127                 // or 0 if none are enabled
00128                 for (button_list_t::iterator iter = mRadioButtons.begin();
00129                          iter != mRadioButtons.end(); ++iter)
00130                 {
00131                         LLRadioCtrl* child = *iter;
00132                         if (count >= index && mSelectedIndex >= 0)
00133                         {
00134                                 break;
00135                         }
00136                         if (child->getEnabled())
00137                         {
00138                                 setSelectedIndex(count);
00139                         }
00140                         count++;
00141                 }
00142                 if (mSelectedIndex < 0)
00143                 {
00144                         setSelectedIndex(0);
00145                 }
00146         }
00147 }
00148 
00149 S32 LLRadioGroup::getSelectedIndex() const
00150 {
00151         return mSelectedIndex;
00152 }
00153 
00154 BOOL LLRadioGroup::setSelectedIndex(S32 index, BOOL from_event)
00155 {
00156         if (index < 0 || index >= (S32)mRadioButtons.size())
00157         {
00158                 return FALSE;
00159         }
00160 
00161         mSelectedIndex = index;
00162 
00163         if (!from_event)
00164         {
00165                 setControlValue(getSelectedIndex());
00166         }
00167 
00168         return TRUE;
00169 }
00170 
00171 BOOL LLRadioGroup::handleKeyHere(KEY key, MASK mask, BOOL called_from_parent)
00172 {
00173         BOOL handled = FALSE;
00174         // do any of the tab buttons have keyboard focus?
00175         if (getEnabled() && !called_from_parent && mask == MASK_NONE)
00176         {
00177                 switch(key)
00178                 {
00179                 case KEY_DOWN:
00180                         if (!setSelectedIndex((getSelectedIndex() + 1)))
00181                         {
00182                                 make_ui_sound("UISndInvalidOp");
00183                         }
00184                         else
00185                         {
00186                                 onCommit();
00187                         }
00188                         handled = TRUE;
00189                         break;
00190                 case KEY_UP:
00191                         if (!setSelectedIndex((getSelectedIndex() - 1)))
00192                         {
00193                                 make_ui_sound("UISndInvalidOp");
00194                         }
00195                         else
00196                         {
00197                                 onCommit();
00198                         }
00199                         handled = TRUE;
00200                         break;
00201                 case KEY_LEFT:
00202                         if (!setSelectedIndex((getSelectedIndex() - 1)))
00203                         {
00204                                 make_ui_sound("UISndInvalidOp");
00205                         }
00206                         else
00207                         {
00208                                 onCommit();
00209                         }
00210                         handled = TRUE;
00211                         break;
00212                 case KEY_RIGHT:
00213                         if (!setSelectedIndex((getSelectedIndex() + 1)))
00214                         {
00215                                 make_ui_sound("UISndInvalidOp");
00216                         }
00217                         else
00218                         {
00219                                 onCommit();
00220                         }
00221                         handled = TRUE;
00222                         break;
00223                 default:
00224                         break;
00225                 }
00226         }
00227         return handled;
00228 }
00229 
00230 void LLRadioGroup::draw()
00231 {
00232         S32 current_button = 0;
00233 
00234         BOOL take_focus = FALSE;
00235         if (gFocusMgr.childHasKeyboardFocus(this))
00236         {
00237                 take_focus = TRUE;
00238         }
00239 
00240         for (button_list_t::iterator iter = mRadioButtons.begin();
00241                  iter != mRadioButtons.end(); ++iter)
00242         {
00243                 LLRadioCtrl* radio = *iter;
00244                 BOOL selected = (current_button == mSelectedIndex);
00245                 radio->setValue( selected );
00246                 if (take_focus && selected && !gFocusMgr.childHasKeyboardFocus(radio))
00247                 {
00248                         radio->focusFirstItem();
00249                 }
00250                 current_button++;
00251         }
00252 
00253         LLView::draw();
00254 }
00255 
00256 
00257 // When adding a button, we need to ensure that the radio
00258 // group gets a message when the button is clicked.
00259 LLRadioCtrl* LLRadioGroup::addRadioButton(const LLString& name, const LLString& label, const LLRect& rect, const LLFontGL* font )
00260 {
00261         // Highlight will get fixed in draw method above
00262         LLRadioCtrl* radio = new LLRadioCtrl(name, rect, label, font,
00263                 onClickButton, this);
00264         addChild(radio);
00265         mRadioButtons.push_back(radio);
00266         return radio;
00267 }
00268 
00269 // Handle one button being clicked.  All child buttons must have this
00270 // function as their callback function.
00271 
00272 // static
00273 void LLRadioGroup::onClickButton(LLUICtrl* ui_ctrl, void* userdata)
00274 {
00275         // llinfos << "LLRadioGroup::onClickButton" << llendl;
00276 
00277         LLRadioCtrl* clickedRadio = (LLRadioCtrl*) ui_ctrl;
00278         LLRadioGroup* self = (LLRadioGroup*) userdata;
00279 
00280         S32 counter = 0;
00281         for (button_list_t::iterator iter = self->mRadioButtons.begin();
00282                  iter != self->mRadioButtons.end(); ++iter)
00283         {
00284                 LLRadioCtrl* radio = *iter;
00285                 if (radio == clickedRadio)
00286                 {
00287                         // llinfos << "clicked button " << counter << llendl;
00288                         self->setSelectedIndex(counter);
00289                         self->setControlValue(counter);
00290                         
00291                         // BUG: Calls click callback even if button didn't actually change
00292                         self->onCommit();
00293 
00294                         return;
00295                 }
00296 
00297                 counter++;
00298         }
00299 
00300         llwarns << "LLRadioGroup::onClickButton - clicked button that isn't a child" << llendl;
00301 }
00302 
00303 void LLRadioGroup::setValue( const LLSD& value )
00304 {
00305         LLString value_name = value.asString();
00306         int idx = 0;
00307         for (button_list_t::const_iterator iter = mRadioButtons.begin();
00308                  iter != mRadioButtons.end(); ++iter)
00309         {
00310                 LLRadioCtrl* radio = *iter;
00311                 if (radio->getName() == value_name)
00312                 {
00313                         setSelectedIndex(idx);
00314                         idx = -1;
00315                         break;
00316                 }
00317                 ++idx;
00318         }
00319         if (idx != -1)
00320         {
00321                 // string not found, try integer
00322                 if (value.isInteger())
00323                 {
00324                         setSelectedIndex((S32) value.asInteger(), TRUE);
00325                 }
00326                 else
00327                 {
00328                         llwarns << "LLRadioGroup::setValue: value not found: " << value_name << llendl;
00329                 }
00330         }
00331 }
00332 
00333 LLSD LLRadioGroup::getValue() const
00334 {
00335         int index = getSelectedIndex();
00336         int idx = 0;
00337         for (button_list_t::const_iterator iter = mRadioButtons.begin();
00338                  iter != mRadioButtons.end(); ++iter)
00339         {
00340                 if (idx == index) return LLSD((*iter)->getName());
00341                 ++idx;
00342         }
00343         return LLSD();
00344 }
00345 
00346 // virtual
00347 LLXMLNodePtr LLRadioGroup::getXML(bool save_children) const
00348 {
00349         LLXMLNodePtr node = LLUICtrl::getXML();
00350 
00351         // Attributes
00352 
00353         node->createChild("draw_border", TRUE)->setBoolValue(mHasBorder);
00354 
00355         // Contents
00356 
00357         for (button_list_t::const_iterator iter = mRadioButtons.begin();
00358                  iter != mRadioButtons.end(); ++iter)
00359         {
00360                 LLRadioCtrl* radio = *iter;
00361 
00362                 LLXMLNodePtr child_node = radio->LLView::getXML();
00363                 child_node->setStringValue(radio->getLabel());
00364                 child_node->setName("radio_item");
00365 
00366                 node->addChild(child_node);
00367         }
00368 
00369         return node;
00370 }
00371 
00372 // static
00373 LLView* LLRadioGroup::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory)
00374 {
00375         LLString name("radio_group");
00376         node->getAttributeString("name", name);
00377 
00378         U32 initial_value = 0;
00379         node->getAttributeU32("initial_value", initial_value);
00380 
00381         BOOL draw_border = TRUE;
00382         node->getAttributeBOOL("draw_border", draw_border);
00383 
00384         LLRect rect;
00385         createRect(node, rect, parent, LLRect());
00386 
00387         LLRadioGroup* radio_group = new LLRadioGroup(name, 
00388                 rect,
00389                 initial_value,
00390                 NULL,
00391                 NULL,
00392                 draw_border);
00393 
00394         const LLString& contents = node->getValue();
00395 
00396         LLRect group_rect = radio_group->getRect();
00397 
00398         LLFontGL *font = LLView::selectFont(node);
00399 
00400         if (contents.find_first_not_of(" \n\t") != contents.npos)
00401         {
00402                 // ...old school default vertical layout
00403                 typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
00404                 boost::char_separator<char> sep("\t\n");
00405                 tokenizer tokens(contents, sep);
00406                 tokenizer::iterator token_iter = tokens.begin();
00407         
00408                 const S32 HPAD = 4, VPAD = 4;
00409                 S32 cur_y = group_rect.getHeight() - VPAD;
00410         
00411                 while(token_iter != tokens.end())
00412                 {
00413                         const char* line = token_iter->c_str();
00414                         LLRect rect(HPAD, cur_y, group_rect.getWidth() - (2 * HPAD), cur_y - 15);
00415                         cur_y -= VPAD + 15;
00416                         radio_group->addRadioButton("radio", line, rect, font);
00417                         ++token_iter;
00418                 }
00419                 llwarns << "Legacy radio group format used! Please convert to use <radio_item> tags!" << llendl;
00420         }
00421         else
00422         {
00423                 // ...per pixel layout
00424                 LLXMLNodePtr child;
00425                 for (child = node->getFirstChild(); child.notNull(); child = child->getNextSibling())
00426                 {
00427                         if (child->hasName("radio_item"))
00428                         {
00429                                 LLRect item_rect;
00430                                 createRect(child, item_rect, radio_group, rect);
00431 
00432                                 LLString radioname("radio");
00433                                 child->getAttributeString("name", radioname);
00434                                 LLString item_label = child->getTextContents();
00435 
00436                                 // HACK by Dale Glass: Like in LLTextBox
00437                                 // This allows having labelless radio buttons in the TrustNet ratings dialog
00438                                 if ( item_label == "" )
00439                                 {
00440                                         item_label = " ";
00441                                 }
00442 
00443                                 LLRadioCtrl* radio = radio_group->addRadioButton(radioname, item_label.c_str(), item_rect, font);
00444 
00445                                 radio->initFromXML(child, radio_group);
00446                         }
00447                 }
00448         }
00449 
00450         radio_group->initFromXML(node, parent);
00451 
00452         return radio_group;
00453 }
00454 
00455 // LLCtrlSelectionInterface functions
00456 BOOL    LLRadioGroup::setCurrentByID( const LLUUID& id )
00457 {
00458         return FALSE;
00459 }
00460 
00461 LLUUID  LLRadioGroup::getCurrentID()
00462 {
00463         return LLUUID::null;
00464 }
00465 
00466 BOOL    LLRadioGroup::setSelectedByValue(LLSD value, BOOL selected)
00467 {
00468         S32 idx = 0;
00469         std::string value_string = value.asString();
00470         for (button_list_t::const_iterator iter = mRadioButtons.begin();
00471                  iter != mRadioButtons.end(); ++iter)
00472         {
00473                 if((*iter)->getName() == value_string)
00474                 {
00475                         setSelectedIndex(idx);
00476                         return TRUE;
00477                 }
00478                 idx++;
00479         }
00480 
00481         return FALSE;
00482 }
00483 
00484 LLSD    LLRadioGroup::getSimpleSelectedValue()
00485 {
00486         return getValue();      
00487 }
00488 
00489 BOOL    LLRadioGroup::isSelected(LLSD value)
00490 {
00491         S32 idx = 0;
00492         std::string value_string = value.asString();
00493         for (button_list_t::const_iterator iter = mRadioButtons.begin();
00494                  iter != mRadioButtons.end(); ++iter)
00495         {
00496                 if((*iter)->getName() == value_string)
00497                 {
00498                         if (idx == mSelectedIndex) 
00499                         {
00500                                 return TRUE;
00501                         }
00502                 }
00503                 idx++;
00504         }
00505         return FALSE;
00506 }
00507 
00508 BOOL    LLRadioGroup::operateOnSelection(EOperation op)
00509 {
00510         return FALSE;
00511 }
00512 
00513 BOOL    LLRadioGroup::operateOnAll(EOperation op)
00514 {
00515         return FALSE;
00516 }
00517 
00518 
00519 LLRadioCtrl::LLRadioCtrl(const LLString& name, const LLRect& rect, const LLString& label,       
00520                                                  const LLFontGL* font, void (*commit_callback)(LLUICtrl*, void*), void* callback_userdata) :
00521                                 LLCheckBoxCtrl(name, rect, label, font, commit_callback, callback_userdata, FALSE, RADIO_STYLE)
00522 {
00523         setTabStop(FALSE);
00524 }
00525 
00526 LLRadioCtrl::~LLRadioCtrl()
00527 {
00528 }
00529 
00530 void LLRadioCtrl::setValue(const LLSD& value)
00531 {
00532         LLCheckBoxCtrl::setValue(value);
00533         mButton->setTabStop(value.asBoolean());
00534 }
00535 

Generated on Thu Jul 1 06:09:04 2010 for Second Life Viewer by  doxygen 1.4.7