diff options
author | Jacek Antonelli | 2008-08-15 23:44:46 -0500 |
---|---|---|
committer | Jacek Antonelli | 2008-08-15 23:44:46 -0500 |
commit | 38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4 (patch) | |
tree | adca584755d22ca041a2dbfc35d4eca01f70b32c /linden/indra/llui/llradiogroup.cpp | |
parent | README.txt (diff) | |
download | meta-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.cpp | 459 |
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 | |||
44 | LLRadioGroup::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 | |||
56 | LLRadioGroup::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 | |||
67 | void 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 | |||
83 | LLRadioGroup::~LLRadioGroup() | ||
84 | { | ||
85 | } | ||
86 | |||
87 | |||
88 | // virtual | ||
89 | void 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 | |||
100 | void 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 | |||
145 | S32 LLRadioGroup::getSelectedIndex() const | ||
146 | { | ||
147 | return mSelectedIndex; | ||
148 | } | ||
149 | |||
150 | BOOL 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 | |||
167 | BOOL 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 | |||
226 | void 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. | ||
255 | LLRadioCtrl* 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 | ||
269 | void 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 | |||
299 | void 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 | |||
329 | LLSD 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 | ||
343 | LLXMLNodePtr 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 | ||
369 | LLView* 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 | |||
444 | LLRadioCtrl::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 | |||
451 | LLRadioCtrl::~LLRadioCtrl() | ||
452 | { | ||
453 | } | ||
454 | |||
455 | void LLRadioCtrl::setValue(const LLSD& value) | ||
456 | { | ||
457 | LLCheckBoxCtrl::setValue(value); | ||
458 | mButton->setTabStop(value.asBoolean()); | ||
459 | } | ||