PageRenderTime 142ms CodeModel.GetById 97ms app.highlight 41ms RepoModel.GetById 1ms app.codeStats 1ms

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