aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/linden/indra/llui/llradiogroup.cpp
diff options
context:
space:
mode:
authorJacek Antonelli2008-08-15 23:44:46 -0500
committerJacek Antonelli2008-08-15 23:44:46 -0500
commit38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4 (patch)
treeadca584755d22ca041a2dbfc35d4eca01f70b32c /linden/indra/llui/llradiogroup.cpp
parentREADME.txt (diff)
downloadmeta-impy-38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4.zip
meta-impy-38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4.tar.gz
meta-impy-38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4.tar.bz2
meta-impy-38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4.tar.xz
Second Life viewer sources 1.13.2.12
Diffstat (limited to 'linden/indra/llui/llradiogroup.cpp')
-rw-r--r--linden/indra/llui/llradiogroup.cpp459
1 files changed, 459 insertions, 0 deletions
diff --git a/linden/indra/llui/llradiogroup.cpp b/linden/indra/llui/llradiogroup.cpp
new file mode 100644
index 0000000..f52faf9
--- /dev/null
+++ b/linden/indra/llui/llradiogroup.cpp
@@ -0,0 +1,459 @@
1/**
2 * @file llradiogroup.cpp
3 * @brief LLRadioGroup base class
4 *
5 * Copyright (c) 2001-2007, Linden Research, Inc.
6 *
7 * The source code in this file ("Source Code") is provided by Linden Lab
8 * to you under the terms of the GNU General Public License, version 2.0
9 * ("GPL"), unless you have obtained a separate licensing agreement
10 * ("Other License"), formally executed by you and Linden Lab. Terms of
11 * the GPL can be found in doc/GPL-license.txt in this distribution, or
12 * online at http://secondlife.com/developers/opensource/gplv2
13 *
14 * There are special exceptions to the terms and conditions of the GPL as
15 * it is applied to this Source Code. View the full text of the exception
16 * in the file doc/FLOSS-exception.txt in this software distribution, or
17 * online at http://secondlife.com/developers/opensource/flossexception
18 *
19 * By copying, modifying or distributing this software, you acknowledge
20 * that you have read and understood your obligations described above,
21 * and agree to abide by those obligations.
22 *
23 * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
24 * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
25 * COMPLETENESS OR PERFORMANCE.
26 */
27
28// An invisible view containing multiple mutually exclusive toggling
29// buttons (usually radio buttons). Automatically handles the mutex
30// condition by highlighting only one button at a time.
31
32#include "linden_common.h"
33
34#include "llboost.h"
35
36#include "llradiogroup.h"
37#include "indra_constants.h"
38
39#include "llviewborder.h"
40#include "llcontrol.h"
41#include "llui.h"
42#include "llfocusmgr.h"
43
44LLRadioGroup::LLRadioGroup(const LLString& name, const LLRect& rect,
45 const LLString& control_name,
46 LLUICtrlCallback callback,
47 void* userdata,
48 BOOL border)
49: LLUICtrl(name, rect, TRUE, callback, userdata, FOLLOWS_LEFT | FOLLOWS_TOP),
50 mSelectedIndex(0)
51{
52 setControlName(control_name, NULL);
53 init(border);
54}
55
56LLRadioGroup::LLRadioGroup(const LLString& name, const LLRect& rect,
57 S32 initial_index,
58 LLUICtrlCallback callback,
59 void* userdata,
60 BOOL border) :
61 LLUICtrl(name, rect, TRUE, callback, userdata, FOLLOWS_LEFT | FOLLOWS_TOP),
62 mSelectedIndex(initial_index)
63{
64 init(border);
65}
66
67void LLRadioGroup::init(BOOL border)
68{
69 if (border)
70 {
71 addChild( new LLViewBorder( "radio group border",
72 LLRect(0, mRect.getHeight(), mRect.getWidth(), 0),
73 LLViewBorder::BEVEL_NONE,
74 LLViewBorder::STYLE_LINE,
75 1 ) );
76 }
77 mHasBorder = border;
78}
79
80
81
82
83LLRadioGroup::~LLRadioGroup()
84{
85}
86
87
88// virtual
89void LLRadioGroup::setEnabled(BOOL enabled)
90{
91 for (child_list_const_iter_t child_iter = getChildList()->begin();
92 child_iter != getChildList()->end(); ++child_iter)
93 {
94 LLView *child = *child_iter;
95 child->setEnabled(enabled);
96 }
97 LLView::setEnabled(enabled);
98}
99
100void LLRadioGroup::setIndexEnabled(S32 index, BOOL enabled)
101{
102 S32 count = 0;
103 for (button_list_t::iterator iter = mRadioButtons.begin();
104 iter != mRadioButtons.end(); ++iter)
105 {
106 LLRadioCtrl* child = *iter;
107 if (count == index)
108 {
109 child->setEnabled(enabled);
110 if (index == mSelectedIndex && enabled == FALSE)
111 {
112 setSelectedIndex(-1);
113 }
114 break;
115 }
116 count++;
117 }
118 count = 0;
119 if (mSelectedIndex < 0)
120 {
121 // Set to highest enabled value < index,
122 // or lowest value above index if none lower are enabled
123 // or 0 if none are enabled
124 for (button_list_t::iterator iter = mRadioButtons.begin();
125 iter != mRadioButtons.end(); ++iter)
126 {
127 LLRadioCtrl* child = *iter;
128 if (count >= index && mSelectedIndex >= 0)
129 {
130 break;
131 }
132 if (child->getEnabled())
133 {
134 setSelectedIndex(count);
135 }
136 count++;
137 }
138 if (mSelectedIndex < 0)
139 {
140 setSelectedIndex(0);
141 }
142 }
143}
144
145S32 LLRadioGroup::getSelectedIndex() const
146{
147 return mSelectedIndex;
148}
149
150BOOL LLRadioGroup::setSelectedIndex(S32 index, BOOL from_event)
151{
152 if (index < 0 || index >= (S32)mRadioButtons.size())
153 {
154 return FALSE;
155 }
156
157 mSelectedIndex = index;
158
159 if (!from_event)
160 {
161 setControlValue(getSelectedIndex());
162 }
163
164 return TRUE;
165}
166
167BOOL LLRadioGroup::handleKeyHere(KEY key, MASK mask, BOOL called_from_parent)
168{
169 BOOL handled = FALSE;
170 // do any of the tab buttons have keyboard focus?
171 if (getEnabled() && !called_from_parent)
172 {
173 switch(key)
174 {
175 case KEY_DOWN:
176 if (!setSelectedIndex((getSelectedIndex() + 1)))
177 {
178 make_ui_sound("UISndInvalidOp");
179 }
180 else
181 {
182 onCommit();
183 }
184 handled = TRUE;
185 break;
186 case KEY_UP:
187 if (!setSelectedIndex((getSelectedIndex() - 1)))
188 {
189 make_ui_sound("UISndInvalidOp");
190 }
191 else
192 {
193 onCommit();
194 }
195 handled = TRUE;
196 break;
197 case KEY_LEFT:
198 if (!setSelectedIndex((getSelectedIndex() - 1)))
199 {
200 make_ui_sound("UISndInvalidOp");
201 }
202 else
203 {
204 onCommit();
205 }
206 handled = TRUE;
207 break;
208 case KEY_RIGHT:
209 if (!setSelectedIndex((getSelectedIndex() + 1)))
210 {
211 make_ui_sound("UISndInvalidOp");
212 }
213 else
214 {
215 onCommit();
216 }
217 handled = TRUE;
218 break;
219 default:
220 break;
221 }
222 }
223 return handled;
224}
225
226void LLRadioGroup::draw()
227{
228 S32 current_button = 0;
229
230 BOOL take_focus = FALSE;
231 if (gFocusMgr.childHasKeyboardFocus(this))
232 {
233 take_focus = TRUE;
234 }
235
236 for (button_list_t::iterator iter = mRadioButtons.begin();
237 iter != mRadioButtons.end(); ++iter)
238 {
239 LLRadioCtrl* radio = *iter;
240 BOOL selected = (current_button == mSelectedIndex);
241 radio->setValue( selected );
242 if (take_focus && selected && !gFocusMgr.childHasKeyboardFocus(radio))
243 {
244 radio->focusFirstItem();
245 }
246 current_button++;
247 }
248
249 LLView::draw();
250}
251
252
253// When adding a button, we need to ensure that the radio
254// group gets a message when the button is clicked.
255LLRadioCtrl* LLRadioGroup::addRadioButton(const LLString& name, const LLString& label, const LLRect& rect, const LLFontGL* font )
256{
257 // Highlight will get fixed in draw method above
258 LLRadioCtrl* radio = new LLRadioCtrl(name, rect, label, font,
259 onClickButton, this);
260 addChild(radio);
261 mRadioButtons.push_back(radio);
262 return radio;
263}
264
265// Handle one button being clicked. All child buttons must have this
266// function as their callback function.
267
268// static
269void LLRadioGroup::onClickButton(LLUICtrl* ui_ctrl, void* userdata)
270{
271 // llinfos << "LLRadioGroup::onClickButton" << llendl;
272
273 LLRadioCtrl* clickedRadio = (LLRadioCtrl*) ui_ctrl;
274 LLRadioGroup* self = (LLRadioGroup*) userdata;
275
276 S32 counter = 0;
277 for (button_list_t::iterator iter = self->mRadioButtons.begin();
278 iter != self->mRadioButtons.end(); ++iter)
279 {
280 LLRadioCtrl* radio = *iter;
281 if (radio == clickedRadio)
282 {
283 // llinfos << "clicked button " << counter << llendl;
284 self->setSelectedIndex(counter);
285 self->setControlValue(counter);
286
287 // BUG: Calls click callback even if button didn't actually change
288 self->onCommit();
289
290 return;
291 }
292
293 counter++;
294 }
295
296 llwarns << "LLRadioGroup::onClickButton - clicked button that isn't a child" << llendl;
297}
298
299void LLRadioGroup::setValue( const LLSD& value )
300{
301 LLString value_name = value.asString();
302 int idx = 0;
303 for (button_list_t::const_iterator iter = mRadioButtons.begin();
304 iter != mRadioButtons.end(); ++iter)
305 {
306 LLRadioCtrl* radio = *iter;
307 if (radio->getName() == value_name)
308 {
309 setSelectedIndex(idx);
310 idx = -1;
311 break;
312 }
313 ++idx;
314 }
315 if (idx != -1)
316 {
317 // string not found, try integer
318 if (value.isInteger())
319 {
320 setSelectedIndex((S32) value.asInteger(), TRUE);
321 }
322 else
323 {
324 llwarns << "LLRadioGroup::setValue: value not found: " << value_name << llendl;
325 }
326 }
327}
328
329LLSD LLRadioGroup::getValue() const
330{
331 int index = getSelectedIndex();
332 int idx = 0;
333 for (button_list_t::const_iterator iter = mRadioButtons.begin();
334 iter != mRadioButtons.end(); ++iter)
335 {
336 if (idx == index) return LLSD((*iter)->getName());
337 ++idx;
338 }
339 return LLSD();
340}
341
342// virtual
343LLXMLNodePtr LLRadioGroup::getXML(bool save_children) const
344{
345 LLXMLNodePtr node = LLUICtrl::getXML();
346
347 // Attributes
348
349 node->createChild("draw_border", TRUE)->setBoolValue(mHasBorder);
350
351 // Contents
352
353 for (button_list_t::const_iterator iter = mRadioButtons.begin();
354 iter != mRadioButtons.end(); ++iter)
355 {
356 LLRadioCtrl* radio = *iter;
357
358 LLXMLNodePtr child_node = radio->LLView::getXML();
359 child_node->setStringValue(radio->getLabel());
360 child_node->setName("radio_item");
361
362 node->addChild(child_node);
363 }
364
365 return node;
366}
367
368// static
369LLView* LLRadioGroup::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory)
370{
371 LLString name("radio_group");
372 node->getAttributeString("name", name);
373
374 U32 initial_value = 0;
375 node->getAttributeU32("initial_value", initial_value);
376
377 BOOL draw_border = TRUE;
378 node->getAttributeBOOL("draw_border", draw_border);
379
380 LLRect rect;
381 createRect(node, rect, parent, LLRect());
382
383 LLRadioGroup* radio_group = new LLRadioGroup(name,
384 rect,
385 initial_value,
386 NULL,
387 NULL,
388 draw_border);
389
390 const LLString& contents = node->getValue();
391
392 LLRect group_rect = radio_group->getRect();
393
394 LLFontGL *font = LLView::selectFont(node);
395
396 if (contents.find_first_not_of(" \n\t") != contents.npos)
397 {
398 // ...old school default vertical layout
399 typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
400 boost::char_separator<char> sep("\t\n");
401 tokenizer tokens(contents, sep);
402 tokenizer::iterator token_iter = tokens.begin();
403
404 const S32 HPAD = 4, VPAD = 4;
405 S32 cur_y = group_rect.getHeight() - VPAD;
406
407 while(token_iter != tokens.end())
408 {
409 const char* line = token_iter->c_str();
410 LLRect rect(HPAD, cur_y, group_rect.getWidth() - (2 * HPAD), cur_y - 15);
411 cur_y -= VPAD + 15;
412 radio_group->addRadioButton("radio", line, rect, font);
413 ++token_iter;
414 }
415 llwarns << "Legacy radio group format used! Please convert to use <radio_item> tags!" << llendl;
416 }
417 else
418 {
419 // ...per pixel layout
420 LLXMLNodePtr child;
421 for (child = node->getFirstChild(); child.notNull(); child = child->getNextSibling())
422 {
423 if (child->hasName("radio_item"))
424 {
425 LLRect item_rect;
426 createRect(child, item_rect, radio_group, rect);
427
428 LLString radioname("radio");
429 child->getAttributeString("name", radioname);
430 LLString item_label = child->getTextContents();
431 LLRadioCtrl* radio = radio_group->addRadioButton(radioname, item_label.c_str(), item_rect, font);
432
433 radio->initFromXML(child, radio_group);
434 }
435 }
436 }
437
438 radio_group->initFromXML(node, parent);
439
440 return radio_group;
441}
442
443
444LLRadioCtrl::LLRadioCtrl(const LLString& name, const LLRect& rect, const LLString& label,
445 const LLFontGL* font, void (*commit_callback)(LLUICtrl*, void*), void* callback_userdata) :
446 LLCheckBoxCtrl(name, rect, label, font, commit_callback, callback_userdata, FALSE, RADIO_STYLE)
447{
448 setTabStop(FALSE);
449}
450
451LLRadioCtrl::~LLRadioCtrl()
452{
453}
454
455void LLRadioCtrl::setValue(const LLSD& value)
456{
457 LLCheckBoxCtrl::setValue(value);
458 mButton->setTabStop(value.asBoolean());
459}