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/llcombobox.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 '')
-rw-r--r-- | linden/indra/llui/llcombobox.cpp | 1153 |
1 files changed, 1153 insertions, 0 deletions
diff --git a/linden/indra/llui/llcombobox.cpp b/linden/indra/llui/llcombobox.cpp new file mode 100644 index 0000000..b19be9a --- /dev/null +++ b/linden/indra/llui/llcombobox.cpp | |||
@@ -0,0 +1,1153 @@ | |||
1 | /** | ||
2 | * @file llcombobox.cpp | ||
3 | * @brief LLComboBox 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 | // A control that displays the name of the chosen item, which when | ||
29 | // clicked shows a scrolling box of options. | ||
30 | |||
31 | #include "linden_common.h" | ||
32 | |||
33 | // file includes | ||
34 | #include "llcombobox.h" | ||
35 | |||
36 | // common includes | ||
37 | #include "llstring.h" | ||
38 | |||
39 | // newview includes | ||
40 | #include "llbutton.h" | ||
41 | #include "llkeyboard.h" | ||
42 | #include "llscrolllistctrl.h" | ||
43 | #include "llwindow.h" | ||
44 | #include "llfloater.h" | ||
45 | #include "llscrollbar.h" | ||
46 | #include "llcontrol.h" | ||
47 | #include "llfocusmgr.h" | ||
48 | #include "lllineeditor.h" | ||
49 | #include "v2math.h" | ||
50 | |||
51 | // Globals | ||
52 | S32 LLCOMBOBOX_HEIGHT = 0; | ||
53 | S32 LLCOMBOBOX_WIDTH = 0; | ||
54 | |||
55 | LLComboBox::LLComboBox( const LLString& name, const LLRect &rect, const LLString& label, | ||
56 | void (*commit_callback)(LLUICtrl*,void*), | ||
57 | void *callback_userdata, | ||
58 | S32 list_width | ||
59 | ) | ||
60 | : LLUICtrl(name, rect, TRUE, commit_callback, callback_userdata, | ||
61 | FOLLOWS_LEFT | FOLLOWS_TOP), | ||
62 | mDrawButton(TRUE), | ||
63 | mTextEntry(NULL), | ||
64 | mArrowImage(NULL), | ||
65 | mAllowTextEntry(FALSE), | ||
66 | mMaxChars(20), | ||
67 | mTextEntryTentative(TRUE), | ||
68 | mPrearrangeCallback( NULL ), | ||
69 | mTextEntryCallback( NULL ), | ||
70 | mListWidth(list_width) | ||
71 | { | ||
72 | // For now, all comboboxes don't take keyboard focus when clicked. | ||
73 | // This might change if it is part of a modal dialog. | ||
74 | // mKeyboardFocusOnClick = FALSE; | ||
75 | |||
76 | // Revert to standard behavior. When this control's parent is hidden, it needs to | ||
77 | // hide this ctrl--which won't just happen automatically since when LLComboBox is | ||
78 | // showing its list, it's also set to TopView. When keyboard focus is cleared all | ||
79 | // controls (including this one) know that they are no longer editing. | ||
80 | mKeyboardFocusOnClick = TRUE; | ||
81 | |||
82 | LLRect r; | ||
83 | r.setOriginAndSize(0, 0, rect.getWidth(), rect.getHeight()); | ||
84 | |||
85 | // Always use text box | ||
86 | // Text label button | ||
87 | mButton = new LLSquareButton("comboxbox button", | ||
88 | r, label, NULL, LLString::null, | ||
89 | &LLComboBox::onButtonClick, this); | ||
90 | mButton->setFont(LLFontGL::sSansSerifSmall); | ||
91 | mButton->setFollows(FOLLOWS_LEFT | FOLLOWS_BOTTOM | FOLLOWS_RIGHT); | ||
92 | mButton->setHAlign( LLFontGL::LEFT ); | ||
93 | |||
94 | const S32 ARROW_WIDTH = 16; | ||
95 | mButton->setRightHPad( ARROW_WIDTH ); | ||
96 | addChild(mButton); | ||
97 | |||
98 | // Default size, will be set by arrange() call in button callback. | ||
99 | if (list_width == 0) | ||
100 | { | ||
101 | list_width = mRect.getWidth() + SCROLLBAR_SIZE; | ||
102 | } | ||
103 | r.setOriginAndSize(0, 16, list_width, 220); | ||
104 | |||
105 | // disallow multiple selection | ||
106 | mList = new LLScrollListCtrl( | ||
107 | "ComboBox", r, | ||
108 | &LLComboBox::onItemSelected, this, FALSE); | ||
109 | mList->setVisible(FALSE); | ||
110 | mList->setBgWriteableColor( LLColor4(1,1,1,1) ); | ||
111 | mList->setCommitOnKeyboardMovement(FALSE); | ||
112 | addChild(mList); | ||
113 | |||
114 | LLRect border_rect(0, mRect.getHeight(), mRect.getWidth(), 0); | ||
115 | mBorder = new LLViewBorder( "combo border", border_rect ); | ||
116 | addChild( mBorder ); | ||
117 | mBorder->setFollows(FOLLOWS_LEFT|FOLLOWS_RIGHT|FOLLOWS_TOP|FOLLOWS_BOTTOM); | ||
118 | |||
119 | LLUUID arrow_image_id( LLUI::sAssetsGroup->getString("combobox_arrow.tga") ); | ||
120 | mArrowImage = LLUI::sImageProvider->getUIImageByID(arrow_image_id); | ||
121 | } | ||
122 | |||
123 | |||
124 | LLComboBox::~LLComboBox() | ||
125 | { | ||
126 | // children automatically deleted, including mMenu, mButton | ||
127 | } | ||
128 | |||
129 | // virtual | ||
130 | LLXMLNodePtr LLComboBox::getXML(bool save_children) const | ||
131 | { | ||
132 | LLXMLNodePtr node = LLUICtrl::getXML(); | ||
133 | |||
134 | // Attributes | ||
135 | |||
136 | node->createChild("allow_text_entry", TRUE)->setBoolValue(mAllowTextEntry); | ||
137 | |||
138 | node->createChild("max_chars", TRUE)->setIntValue(mMaxChars); | ||
139 | |||
140 | // Contents | ||
141 | |||
142 | std::vector<LLScrollListItem*> data_list = mList->getAllData(); | ||
143 | std::vector<LLScrollListItem*>::iterator data_itor; | ||
144 | for (data_itor = data_list.begin(); data_itor != data_list.end(); ++data_itor) | ||
145 | { | ||
146 | LLScrollListItem* item = *data_itor; | ||
147 | LLScrollListCell* cell = item->getColumn(0); | ||
148 | if (cell) | ||
149 | { | ||
150 | LLXMLNodePtr item_node = node->createChild("combo_item", FALSE); | ||
151 | LLSD value = item->getValue(); | ||
152 | item_node->createChild("value", TRUE)->setStringValue(value.asString()); | ||
153 | item_node->createChild("enabled", TRUE)->setBoolValue(item->getEnabled()); | ||
154 | item_node->setStringValue(cell->getText()); | ||
155 | } | ||
156 | } | ||
157 | |||
158 | return node; | ||
159 | } | ||
160 | |||
161 | // static | ||
162 | LLView* LLComboBox::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory) | ||
163 | { | ||
164 | LLString name("combo_box"); | ||
165 | node->getAttributeString("name", name); | ||
166 | |||
167 | LLString label(""); | ||
168 | node->getAttributeString("label", label); | ||
169 | |||
170 | LLRect rect; | ||
171 | createRect(node, rect, parent, LLRect()); | ||
172 | |||
173 | BOOL allow_text_entry = FALSE; | ||
174 | node->getAttributeBOOL("allow_text_entry", allow_text_entry); | ||
175 | |||
176 | S32 max_chars = 20; | ||
177 | node->getAttributeS32("max_chars", max_chars); | ||
178 | |||
179 | LLUICtrlCallback callback = NULL; | ||
180 | |||
181 | LLComboBox* combo_box = new LLComboBox(name, | ||
182 | rect, | ||
183 | label, | ||
184 | callback, | ||
185 | NULL); | ||
186 | combo_box->setAllowTextEntry(allow_text_entry, max_chars); | ||
187 | |||
188 | combo_box->initFromXML(node, parent); | ||
189 | |||
190 | const LLString& contents = node->getValue(); | ||
191 | |||
192 | if (contents.find_first_not_of(" \n\t") != contents.npos) | ||
193 | { | ||
194 | llerrs << "Legacy combo box item format used! Please convert to <combo_item> tags!" << llendl; | ||
195 | } | ||
196 | else | ||
197 | { | ||
198 | LLXMLNodePtr child; | ||
199 | for (child = node->getFirstChild(); child.notNull(); child = child->getNextSibling()) | ||
200 | { | ||
201 | if (child->hasName("combo_item")) | ||
202 | { | ||
203 | LLString label = child->getTextContents(); | ||
204 | |||
205 | LLString value = label; | ||
206 | child->getAttributeString("value", value); | ||
207 | |||
208 | combo_box->add(label, LLSD(value) ); | ||
209 | } | ||
210 | } | ||
211 | } | ||
212 | |||
213 | combo_box->selectFirstItem(); | ||
214 | |||
215 | return combo_box; | ||
216 | } | ||
217 | |||
218 | void LLComboBox::setEnabled(BOOL enabled) | ||
219 | { | ||
220 | LLUICtrl::setEnabled(enabled); | ||
221 | mButton->setEnabled(enabled); | ||
222 | } | ||
223 | |||
224 | // *HACK: these are all hacks to support the fact that the combobox | ||
225 | // has mouse capture so we can hide the list when we don't handle the | ||
226 | // mouse up event | ||
227 | BOOL LLComboBox::handleHover(S32 x, S32 y, MASK mask) | ||
228 | { | ||
229 | if (mList->getVisible()) | ||
230 | { | ||
231 | S32 local_x, local_y; | ||
232 | LLView::localPointToOtherView(x, y, &local_x, &local_y, mList); | ||
233 | if (mList->pointInView(local_x, local_y)) | ||
234 | { | ||
235 | return mList->handleHover(local_x, local_y, mask); | ||
236 | } | ||
237 | } | ||
238 | return LLUICtrl::handleHover(x, y, mask); | ||
239 | } | ||
240 | |||
241 | BOOL LLComboBox::handleMouseDown(S32 x, S32 y, MASK mask) | ||
242 | { | ||
243 | if (mList->getVisible()) | ||
244 | { | ||
245 | S32 local_x, local_y; | ||
246 | LLView::localPointToOtherView(x, y, &local_x, &local_y, mList); | ||
247 | if (mList->pointInView(local_x, local_y)) | ||
248 | { | ||
249 | return mList->handleMouseDown(local_x, local_y, mask); | ||
250 | } | ||
251 | } | ||
252 | BOOL has_focus_now = hasFocus(); | ||
253 | BOOL handled = LLUICtrl::handleMouseDown(x, y, mask); | ||
254 | if (handled && !has_focus_now) | ||
255 | { | ||
256 | onFocusReceived(); | ||
257 | } | ||
258 | |||
259 | return handled; | ||
260 | } | ||
261 | |||
262 | BOOL LLComboBox::handleRightMouseDown(S32 x, S32 y, MASK mask) | ||
263 | { | ||
264 | if (mList->getVisible()) | ||
265 | { | ||
266 | S32 local_x, local_y; | ||
267 | LLView::localPointToOtherView(x, y, &local_x, &local_y, mList); | ||
268 | if (mList->pointInView(local_x, local_y)) | ||
269 | { | ||
270 | return mList->handleRightMouseDown(local_x, local_y, mask); | ||
271 | } | ||
272 | } | ||
273 | return LLUICtrl::handleRightMouseDown(x, y, mask); | ||
274 | } | ||
275 | |||
276 | BOOL LLComboBox::handleRightMouseUp(S32 x, S32 y, MASK mask) | ||
277 | { | ||
278 | if (mList->getVisible()) | ||
279 | { | ||
280 | S32 local_x, local_y; | ||
281 | LLView::localPointToOtherView(x, y, &local_x, &local_y, mList); | ||
282 | if (mList->pointInView(local_x, local_y)) | ||
283 | { | ||
284 | return mList->handleRightMouseUp(local_x, local_y, mask); | ||
285 | } | ||
286 | } | ||
287 | return LLUICtrl::handleRightMouseUp(x, y, mask); | ||
288 | } | ||
289 | |||
290 | BOOL LLComboBox::handleDoubleClick(S32 x, S32 y, MASK mask) | ||
291 | { | ||
292 | if (mList->getVisible()) | ||
293 | { | ||
294 | S32 local_x, local_y; | ||
295 | LLView::localPointToOtherView(x, y, &local_x, &local_y, mList); | ||
296 | if (mList->pointInView(local_x, local_y)) | ||
297 | { | ||
298 | return mList->handleDoubleClick(local_x, local_y, mask); | ||
299 | } | ||
300 | } | ||
301 | return LLUICtrl::handleDoubleClick(x, y, mask); | ||
302 | } | ||
303 | |||
304 | BOOL LLComboBox::handleMouseUp(S32 x, S32 y, MASK mask) | ||
305 | { | ||
306 | BOOL handled = childrenHandleMouseUp(x, y, mask) != NULL; | ||
307 | |||
308 | if (!handled && mList->getVisible()) | ||
309 | { | ||
310 | S32 local_x, local_y; | ||
311 | LLView::localPointToOtherView(x, y, &local_x, &local_y, mList); | ||
312 | if (mList->pointInView(local_x, local_y)) | ||
313 | { | ||
314 | handled = mList->handleMouseUp(local_x, local_y, mask); | ||
315 | } | ||
316 | } | ||
317 | |||
318 | if( !handled && gFocusMgr.getMouseCapture() == this ) | ||
319 | { | ||
320 | // Mouse events that we didn't handle cause the list to be hidden. | ||
321 | // Eat mouse event, regardless of where on the screen it happens. | ||
322 | hideList(); | ||
323 | handled = TRUE; | ||
324 | } | ||
325 | |||
326 | return handled; | ||
327 | } | ||
328 | |||
329 | void LLComboBox::clear() | ||
330 | { | ||
331 | if (mTextEntry) | ||
332 | { | ||
333 | mTextEntry->setText(""); | ||
334 | } | ||
335 | mButton->setLabelSelected(""); | ||
336 | mButton->setLabelUnselected(""); | ||
337 | mButton->setDisabledLabel(""); | ||
338 | mButton->setDisabledSelectedLabel(""); | ||
339 | mList->deselectAllItems(); | ||
340 | } | ||
341 | |||
342 | void LLComboBox::onCommit() | ||
343 | { | ||
344 | if (mAllowTextEntry && getCurrentIndex() != -1) | ||
345 | { | ||
346 | // we have selected an existing item, blitz the manual text entry with | ||
347 | // the properly capitalized item | ||
348 | mTextEntry->setValue(getSimple()); | ||
349 | mTextEntry->setTentative(FALSE); | ||
350 | } | ||
351 | LLUICtrl::onCommit(); | ||
352 | } | ||
353 | |||
354 | // add item "name" to menu | ||
355 | void LLComboBox::add(const LLString& name, EAddPosition pos, BOOL enabled) | ||
356 | { | ||
357 | mList->addSimpleItem(name, pos, enabled); | ||
358 | mList->selectFirstItem(); | ||
359 | } | ||
360 | |||
361 | // add item "name" with a unique id to menu | ||
362 | void LLComboBox::add(const LLString& name, const LLUUID& id, EAddPosition pos, BOOL enabled ) | ||
363 | { | ||
364 | mList->addSimpleItem(name, LLSD(id), pos, enabled); | ||
365 | mList->selectFirstItem(); | ||
366 | } | ||
367 | |||
368 | // add item "name" with attached userdata | ||
369 | void LLComboBox::add(const LLString& name, void* userdata, EAddPosition pos, BOOL enabled ) | ||
370 | { | ||
371 | LLScrollListItem* item = mList->addSimpleItem(name, pos, enabled); | ||
372 | item->setUserdata( userdata ); | ||
373 | mList->selectFirstItem(); | ||
374 | } | ||
375 | |||
376 | // add item "name" with attached generic data | ||
377 | void LLComboBox::add(const LLString& name, LLSD value, EAddPosition pos, BOOL enabled ) | ||
378 | { | ||
379 | mList->addSimpleItem(name, value, pos, enabled); | ||
380 | mList->selectFirstItem(); | ||
381 | } | ||
382 | |||
383 | |||
384 | void LLComboBox::sortByName() | ||
385 | { | ||
386 | mList->sortByColumn(0, TRUE); | ||
387 | } | ||
388 | |||
389 | |||
390 | // Choose an item with a given name in the menu. | ||
391 | // Returns TRUE if the item was found. | ||
392 | BOOL LLComboBox::setSimple(const LLString& name) | ||
393 | { | ||
394 | BOOL found = mList->selectSimpleItem(name, FALSE); | ||
395 | |||
396 | if (found) | ||
397 | { | ||
398 | setLabel(name); | ||
399 | } | ||
400 | |||
401 | return found; | ||
402 | } | ||
403 | |||
404 | // virtual | ||
405 | void LLComboBox::setValue(const LLSD& value) | ||
406 | { | ||
407 | BOOL found = mList->selectByValue(value); | ||
408 | if (found) | ||
409 | { | ||
410 | LLScrollListItem* item = mList->getFirstSelected(); | ||
411 | if (item) | ||
412 | { | ||
413 | setLabel( mList->getSimpleSelectedItem() ); | ||
414 | } | ||
415 | } | ||
416 | } | ||
417 | |||
418 | const LLString& LLComboBox::getSimple() const | ||
419 | { | ||
420 | const LLString& res = mList->getSimpleSelectedItem(); | ||
421 | if (res.empty() && mAllowTextEntry) | ||
422 | { | ||
423 | return mTextEntry->getText(); | ||
424 | } | ||
425 | else | ||
426 | { | ||
427 | return res; | ||
428 | } | ||
429 | } | ||
430 | |||
431 | const LLString& LLComboBox::getSimpleSelectedItem(S32 column) const | ||
432 | { | ||
433 | return mList->getSimpleSelectedItem(column); | ||
434 | } | ||
435 | |||
436 | // virtual | ||
437 | LLSD LLComboBox::getValue() const | ||
438 | { | ||
439 | LLScrollListItem* item = mList->getFirstSelected(); | ||
440 | if( item ) | ||
441 | { | ||
442 | return item->getValue(); | ||
443 | } | ||
444 | else if (mAllowTextEntry) | ||
445 | { | ||
446 | return mTextEntry->getValue(); | ||
447 | } | ||
448 | else | ||
449 | { | ||
450 | return LLSD(); | ||
451 | } | ||
452 | } | ||
453 | |||
454 | void LLComboBox::setLabel(const LLString& name) | ||
455 | { | ||
456 | if ( mAllowTextEntry ) | ||
457 | { | ||
458 | mTextEntry->setText(name); | ||
459 | if (mList->selectSimpleItem(name, FALSE)) | ||
460 | { | ||
461 | mTextEntry->setTentative(FALSE); | ||
462 | } | ||
463 | else | ||
464 | { | ||
465 | mTextEntry->setTentative(mTextEntryTentative); | ||
466 | } | ||
467 | } | ||
468 | else | ||
469 | { | ||
470 | mButton->setLabelUnselected(name); | ||
471 | mButton->setLabelSelected(name); | ||
472 | mButton->setDisabledLabel(name); | ||
473 | mButton->setDisabledSelectedLabel(name); | ||
474 | } | ||
475 | } | ||
476 | |||
477 | |||
478 | BOOL LLComboBox::remove(const LLString& name) | ||
479 | { | ||
480 | BOOL found = mList->selectSimpleItem(name); | ||
481 | |||
482 | if (found) | ||
483 | { | ||
484 | LLScrollListItem* item = mList->getFirstSelected(); | ||
485 | if (item) | ||
486 | { | ||
487 | mList->deleteSingleItem(mList->getItemIndex(item)); | ||
488 | } | ||
489 | } | ||
490 | |||
491 | return found; | ||
492 | } | ||
493 | |||
494 | BOOL LLComboBox::remove(S32 index) | ||
495 | { | ||
496 | if (index < mList->getItemCount()) | ||
497 | { | ||
498 | mList->deleteSingleItem(index); | ||
499 | return TRUE; | ||
500 | } | ||
501 | return FALSE; | ||
502 | } | ||
503 | |||
504 | // Keyboard focus lost. | ||
505 | void LLComboBox::onFocusLost() | ||
506 | { | ||
507 | hideList(); | ||
508 | // if valid selection | ||
509 | if (mAllowTextEntry && getCurrentIndex() != -1) | ||
510 | { | ||
511 | mTextEntry->selectAll(); | ||
512 | } | ||
513 | } | ||
514 | |||
515 | void LLComboBox::setButtonVisible(BOOL visible) | ||
516 | { | ||
517 | mButton->setVisible(visible); | ||
518 | mDrawButton = visible; | ||
519 | if (mTextEntry) | ||
520 | { | ||
521 | LLRect text_entry_rect(0, mRect.getHeight(), mRect.getWidth(), 0); | ||
522 | if (visible) | ||
523 | { | ||
524 | text_entry_rect.mRight -= mArrowImage->getWidth() + 2 * LLUI::sConfigGroup->getS32("DropShadowButton"); | ||
525 | } | ||
526 | //mTextEntry->setRect(text_entry_rect); | ||
527 | mTextEntry->reshape(text_entry_rect.getWidth(), text_entry_rect.getHeight(), TRUE); | ||
528 | } | ||
529 | } | ||
530 | |||
531 | void LLComboBox::draw() | ||
532 | { | ||
533 | if( getVisible() ) | ||
534 | { | ||
535 | mBorder->setKeyboardFocusHighlight(hasFocus()); | ||
536 | |||
537 | mButton->setEnabled(mEnabled /*&& !mList->isEmpty()*/); | ||
538 | |||
539 | // Draw children | ||
540 | LLUICtrl::draw(); | ||
541 | |||
542 | if (mDrawButton) | ||
543 | { | ||
544 | // Paste the graphic on the right edge | ||
545 | if (!mArrowImage.isNull()) | ||
546 | { | ||
547 | S32 left = mRect.getWidth() - mArrowImage->getWidth() - LLUI::sConfigGroup->getS32("DropShadowButton"); | ||
548 | |||
549 | gl_draw_image( left, 0, mArrowImage, | ||
550 | LLColor4::white); | ||
551 | } | ||
552 | } | ||
553 | } | ||
554 | } | ||
555 | |||
556 | BOOL LLComboBox::setCurrentByIndex( S32 index ) | ||
557 | { | ||
558 | BOOL found = mList->selectNthItem( index ); | ||
559 | if (found) | ||
560 | { | ||
561 | setLabel(mList->getSimpleSelectedItem()); | ||
562 | } | ||
563 | return found; | ||
564 | } | ||
565 | |||
566 | S32 LLComboBox::getCurrentIndex() const | ||
567 | { | ||
568 | LLScrollListItem* item = mList->getFirstSelected(); | ||
569 | if( item ) | ||
570 | { | ||
571 | return mList->getItemIndex( item ); | ||
572 | } | ||
573 | return -1; | ||
574 | } | ||
575 | |||
576 | |||
577 | void* LLComboBox::getCurrentUserdata() | ||
578 | { | ||
579 | LLScrollListItem* item = mList->getFirstSelected(); | ||
580 | if( item ) | ||
581 | { | ||
582 | return item->getUserdata(); | ||
583 | } | ||
584 | return NULL; | ||
585 | } | ||
586 | |||
587 | |||
588 | void LLComboBox::showList() | ||
589 | { | ||
590 | // Make sure we don't go off top of screen. | ||
591 | LLCoordWindow window_size; | ||
592 | getWindow()->getSize(&window_size); | ||
593 | //HACK: shouldn't have to know about scale here | ||
594 | mList->arrange( 192, llfloor((F32)window_size.mY / LLUI::sGLScaleFactor.mV[VY]) - 50 ); | ||
595 | |||
596 | // Move rect so it hangs off the bottom of this view | ||
597 | LLRect rect = mList->getRect(); | ||
598 | |||
599 | rect.setLeftTopAndSize(0, 0, rect.getWidth(), rect.getHeight() ); | ||
600 | mList->setRect(rect); | ||
601 | |||
602 | // Make sure that we can see the whole list | ||
603 | LLRect floater_area_screen; | ||
604 | LLRect floater_area_local; | ||
605 | gFloaterView->getParent()->localRectToScreen( gFloaterView->getRect(), &floater_area_screen ); | ||
606 | screenRectToLocal( floater_area_screen, &floater_area_local ); | ||
607 | mList->translateIntoRect( floater_area_local, FALSE ); | ||
608 | |||
609 | // Make sure we didn't go off bottom of screen | ||
610 | S32 x, y; | ||
611 | mList->localPointToScreen(0, 0, &x, &y); | ||
612 | |||
613 | if (y < 0) | ||
614 | { | ||
615 | mList->translate(0, -y); | ||
616 | } | ||
617 | |||
618 | gFocusMgr.setMouseCapture( this, LLComboBox::onMouseCaptureLost ); | ||
619 | // NB: this call will trigger the focuslost callback which will hide the list, so do it first | ||
620 | // before finally showing the list | ||
621 | |||
622 | if (!mList->getFirstSelected()) | ||
623 | { | ||
624 | // if nothing is selected, select the first item | ||
625 | // so that the callback is not immediately triggered on setFocus() | ||
626 | mList->selectFirstItem(); | ||
627 | } | ||
628 | gFocusMgr.setKeyboardFocus(mList, onListFocusLost); | ||
629 | |||
630 | // Show the list and push the button down | ||
631 | mButton->setToggleState(TRUE); | ||
632 | mList->setVisible(TRUE); | ||
633 | |||
634 | gFocusMgr.setTopView(mList, LLComboBox::onTopViewLost ); | ||
635 | |||
636 | } | ||
637 | |||
638 | void LLComboBox::hideList() | ||
639 | { | ||
640 | mButton->setToggleState(FALSE); | ||
641 | mList->setVisible(FALSE); | ||
642 | mList->highlightNthItem(-1); | ||
643 | |||
644 | if( gFocusMgr.getTopView() == mList ) | ||
645 | { | ||
646 | gFocusMgr.setTopView(NULL, NULL); | ||
647 | } | ||
648 | |||
649 | if( gFocusMgr.getMouseCapture() == this ) | ||
650 | { | ||
651 | gFocusMgr.setMouseCapture( NULL, NULL ); | ||
652 | } | ||
653 | |||
654 | if( gFocusMgr.getKeyboardFocus() == mList ) | ||
655 | { | ||
656 | if (mAllowTextEntry) | ||
657 | { | ||
658 | mTextEntry->setFocus(TRUE); | ||
659 | } | ||
660 | else | ||
661 | { | ||
662 | setFocus(TRUE); | ||
663 | } | ||
664 | } | ||
665 | } | ||
666 | |||
667 | |||
668 | |||
669 | //------------------------------------------------------------------ | ||
670 | // static functions | ||
671 | //------------------------------------------------------------------ | ||
672 | |||
673 | // static | ||
674 | void LLComboBox::onButtonClick(void *userdata) | ||
675 | { | ||
676 | LLComboBox *self = (LLComboBox *)userdata; | ||
677 | |||
678 | if (!self->mList->getVisible()) | ||
679 | { | ||
680 | LLScrollListItem* last_selected_item = self->mList->getLastSelectedItem(); | ||
681 | if (last_selected_item) | ||
682 | { | ||
683 | // highlight the original selection before potentially selecting a new item | ||
684 | self->mList->highlightNthItem(self->mList->getItemIndex(last_selected_item)); | ||
685 | } | ||
686 | |||
687 | if( self->mPrearrangeCallback ) | ||
688 | { | ||
689 | self->mPrearrangeCallback( self, self->mCallbackUserData ); | ||
690 | } | ||
691 | |||
692 | if (self->mList->getItemCount() != 0) | ||
693 | { | ||
694 | self->showList(); | ||
695 | } | ||
696 | |||
697 | if (self->mKeyboardFocusOnClick && !self->hasFocus()) | ||
698 | { | ||
699 | self->setFocus( TRUE ); | ||
700 | } | ||
701 | } | ||
702 | else | ||
703 | { | ||
704 | // hide and release keyboard focus | ||
705 | self->hideList(); | ||
706 | |||
707 | self->onCommit(); | ||
708 | } | ||
709 | } | ||
710 | |||
711 | |||
712 | |||
713 | // static | ||
714 | void LLComboBox::onItemSelected(LLUICtrl* item, void *userdata) | ||
715 | { | ||
716 | // Note: item is the LLScrollListCtrl | ||
717 | LLComboBox *self = (LLComboBox *) userdata; | ||
718 | |||
719 | const LLString& name = self->mList->getSimpleSelectedItem(); | ||
720 | |||
721 | self->hideList(); | ||
722 | |||
723 | S32 cur_id = self->getCurrentIndex(); | ||
724 | if (cur_id != -1) | ||
725 | { | ||
726 | self->setLabel(self->mList->getSimpleSelectedItem()); | ||
727 | |||
728 | if (self->mAllowTextEntry) | ||
729 | { | ||
730 | self->mTextEntry->setText(name); | ||
731 | self->mTextEntry->setTentative(FALSE); | ||
732 | gFocusMgr.setKeyboardFocus(self->mTextEntry, NULL); | ||
733 | self->mTextEntry->selectAll(); | ||
734 | } | ||
735 | else | ||
736 | { | ||
737 | self->mButton->setLabelUnselected( name ); | ||
738 | self->mButton->setLabelSelected( name ); | ||
739 | self->mButton->setDisabledLabel( name ); | ||
740 | self->mButton->setDisabledSelectedLabel( name ); | ||
741 | } | ||
742 | } | ||
743 | self->onCommit(); | ||
744 | } | ||
745 | |||
746 | // static | ||
747 | void LLComboBox::onTopViewLost(LLView* old_focus) | ||
748 | { | ||
749 | LLComboBox *self = (LLComboBox *) old_focus->getParent(); | ||
750 | self->hideList(); | ||
751 | } | ||
752 | |||
753 | |||
754 | // static | ||
755 | void LLComboBox::onMouseCaptureLost(LLMouseHandler*) | ||
756 | { | ||
757 | // Can't hide the list here. If the list scrolls off the screen, | ||
758 | // and you click in the arrow buttons of the scroll bar, they must capture | ||
759 | // the mouse to handle scrolling-while-mouse-down. | ||
760 | } | ||
761 | |||
762 | BOOL LLComboBox::handleToolTip(S32 x, S32 y, LLString& msg, LLRect* sticky_rect_screen) | ||
763 | { | ||
764 | |||
765 | LLString tool_tip; | ||
766 | |||
767 | if (LLUI::sShowXUINames) | ||
768 | { | ||
769 | tool_tip = getShowNamesToolTip(); | ||
770 | } | ||
771 | else | ||
772 | { | ||
773 | tool_tip = mToolTipMsg; | ||
774 | } | ||
775 | |||
776 | if( getVisible() && pointInView( x, y ) ) | ||
777 | { | ||
778 | if( !tool_tip.empty() ) | ||
779 | { | ||
780 | msg = tool_tip; | ||
781 | |||
782 | // Convert rect local to screen coordinates | ||
783 | localPointToScreen( | ||
784 | 0, 0, | ||
785 | &(sticky_rect_screen->mLeft), &(sticky_rect_screen->mBottom) ); | ||
786 | localPointToScreen( | ||
787 | mRect.getWidth(), mRect.getHeight(), | ||
788 | &(sticky_rect_screen->mRight), &(sticky_rect_screen->mTop) ); | ||
789 | } | ||
790 | return TRUE; | ||
791 | } | ||
792 | return FALSE; | ||
793 | } | ||
794 | |||
795 | BOOL LLComboBox::handleKeyHere(KEY key, MASK mask, BOOL called_from_parent) | ||
796 | { | ||
797 | BOOL result = FALSE; | ||
798 | if (gFocusMgr.childHasKeyboardFocus(this)) | ||
799 | { | ||
800 | //give list a chance to pop up and handle key | ||
801 | LLScrollListItem* last_selected_item = mList->getLastSelectedItem(); | ||
802 | if (last_selected_item) | ||
803 | { | ||
804 | // highlight the original selection before potentially selecting a new item | ||
805 | mList->highlightNthItem(mList->getItemIndex(last_selected_item)); | ||
806 | } | ||
807 | result = mList->handleKeyHere(key, mask, FALSE); | ||
808 | // if selection has changed, pop open list | ||
809 | if (mList->getLastSelectedItem() != last_selected_item) | ||
810 | { | ||
811 | showList(); | ||
812 | } | ||
813 | } | ||
814 | return result; | ||
815 | } | ||
816 | |||
817 | BOOL LLComboBox::handleUnicodeCharHere(llwchar uni_char, BOOL called_from_parent) | ||
818 | { | ||
819 | BOOL result = FALSE; | ||
820 | if (gFocusMgr.childHasKeyboardFocus(this)) | ||
821 | { | ||
822 | // space bar just shows the list | ||
823 | if (' ' != uni_char ) | ||
824 | { | ||
825 | LLScrollListItem* last_selected_item = mList->getLastSelectedItem(); | ||
826 | if (last_selected_item) | ||
827 | { | ||
828 | // highlight the original selection before potentially selecting a new item | ||
829 | mList->highlightNthItem(mList->getItemIndex(last_selected_item)); | ||
830 | } | ||
831 | result = mList->handleUnicodeCharHere(uni_char, called_from_parent); | ||
832 | if (mList->getLastSelectedItem() != last_selected_item) | ||
833 | { | ||
834 | showList(); | ||
835 | } | ||
836 | } | ||
837 | } | ||
838 | return result; | ||
839 | } | ||
840 | |||
841 | void LLComboBox::setAllowTextEntry(BOOL allow, S32 max_chars, BOOL set_tentative) | ||
842 | { | ||
843 | LLRect rect( 0, mRect.getHeight(), mRect.getWidth(), 0); | ||
844 | if (allow && !mAllowTextEntry) | ||
845 | { | ||
846 | S32 shadow_size = LLUI::sConfigGroup->getS32("DropShadowButton"); | ||
847 | mButton->setRect(LLRect( mRect.getWidth() - mArrowImage->getWidth() - 2 * shadow_size, | ||
848 | rect.mTop, rect.mRight, rect.mBottom)); | ||
849 | mButton->setTabStop(FALSE); | ||
850 | |||
851 | // clear label on button | ||
852 | LLString cur_label = mButton->getLabelSelected(); | ||
853 | setLabel(""); | ||
854 | if (!mTextEntry) | ||
855 | { | ||
856 | LLRect text_entry_rect(0, mRect.getHeight(), mRect.getWidth(), 0); | ||
857 | text_entry_rect.mRight -= mArrowImage->getWidth() + 2 * LLUI::sConfigGroup->getS32("DropShadowButton"); | ||
858 | mTextEntry = new LLLineEditor("combo_text_entry", | ||
859 | text_entry_rect, | ||
860 | "", | ||
861 | LLFontGL::sSansSerifSmall, | ||
862 | max_chars, | ||
863 | onTextCommit, | ||
864 | onTextEntry, | ||
865 | NULL, | ||
866 | this, | ||
867 | NULL, // prevalidate func | ||
868 | LLViewBorder::BEVEL_NONE, | ||
869 | LLViewBorder::STYLE_LINE, | ||
870 | 0); // no border | ||
871 | mTextEntry->setSelectAllonFocusReceived(TRUE); | ||
872 | mTextEntry->setHandleEditKeysDirectly(TRUE); | ||
873 | mTextEntry->setCommitOnFocusLost(FALSE); | ||
874 | mTextEntry->setText(cur_label); | ||
875 | mTextEntry->setIgnoreTab(TRUE); | ||
876 | addChild(mTextEntry); | ||
877 | mMaxChars = max_chars; | ||
878 | } | ||
879 | else | ||
880 | { | ||
881 | mTextEntry->setVisible(TRUE); | ||
882 | } | ||
883 | } | ||
884 | else if (!allow && mAllowTextEntry) | ||
885 | { | ||
886 | mButton->setRect(rect); | ||
887 | mButton->setTabStop(TRUE); | ||
888 | |||
889 | if (mTextEntry) | ||
890 | { | ||
891 | mTextEntry->setVisible(FALSE); | ||
892 | } | ||
893 | } | ||
894 | mAllowTextEntry = allow; | ||
895 | mTextEntryTentative = set_tentative; | ||
896 | } | ||
897 | |||
898 | void LLComboBox::setTextEntry(const LLString& text) | ||
899 | { | ||
900 | if (mTextEntry) | ||
901 | { | ||
902 | mTextEntry->setText(text); | ||
903 | updateSelection(); | ||
904 | } | ||
905 | } | ||
906 | |||
907 | //static | ||
908 | void LLComboBox::onTextEntry(LLLineEditor* line_editor, void* user_data) | ||
909 | { | ||
910 | LLComboBox* self = (LLComboBox*)user_data; | ||
911 | |||
912 | if (self->mTextEntryCallback) | ||
913 | { | ||
914 | (*self->mTextEntryCallback)(line_editor, self->mCallbackUserData); | ||
915 | } | ||
916 | |||
917 | KEY key = gKeyboard->currentKey(); | ||
918 | if (key == KEY_BACKSPACE || | ||
919 | key == KEY_DELETE) | ||
920 | { | ||
921 | if (self->mList->selectSimpleItem(line_editor->getText(), FALSE)) | ||
922 | { | ||
923 | line_editor->setTentative(FALSE); | ||
924 | } | ||
925 | else | ||
926 | { | ||
927 | line_editor->setTentative(self->mTextEntryTentative); | ||
928 | } | ||
929 | return; | ||
930 | } | ||
931 | |||
932 | if (key == KEY_LEFT || | ||
933 | key == KEY_RIGHT) | ||
934 | { | ||
935 | return; | ||
936 | } | ||
937 | |||
938 | if (key == KEY_DOWN) | ||
939 | { | ||
940 | self->setCurrentByIndex(llmin(self->getItemCount() - 1, self->getCurrentIndex() + 1)); | ||
941 | if (!self->mList->getVisible()) | ||
942 | { | ||
943 | if( self->mPrearrangeCallback ) | ||
944 | { | ||
945 | self->mPrearrangeCallback( self, self->mCallbackUserData ); | ||
946 | } | ||
947 | |||
948 | if (self->mList->getItemCount() != 0) | ||
949 | { | ||
950 | self->showList(); | ||
951 | } | ||
952 | } | ||
953 | line_editor->selectAll(); | ||
954 | line_editor->setTentative(FALSE); | ||
955 | } | ||
956 | else if (key == KEY_UP) | ||
957 | { | ||
958 | self->setCurrentByIndex(llmax(0, self->getCurrentIndex() - 1)); | ||
959 | if (!self->mList->getVisible()) | ||
960 | { | ||
961 | if( self->mPrearrangeCallback ) | ||
962 | { | ||
963 | self->mPrearrangeCallback( self, self->mCallbackUserData ); | ||
964 | } | ||
965 | |||
966 | if (self->mList->getItemCount() != 0) | ||
967 | { | ||
968 | self->showList(); | ||
969 | } | ||
970 | } | ||
971 | line_editor->selectAll(); | ||
972 | line_editor->setTentative(FALSE); | ||
973 | } | ||
974 | else | ||
975 | { | ||
976 | // RN: presumably text entry | ||
977 | self->updateSelection(); | ||
978 | } | ||
979 | } | ||
980 | |||
981 | void LLComboBox::updateSelection() | ||
982 | { | ||
983 | LLWString left_wstring = mTextEntry->getWText().substr(0, mTextEntry->getCursor()); | ||
984 | // user-entered portion of string, based on assumption that any selected | ||
985 | // text was a result of auto-completion | ||
986 | LLWString user_wstring = mTextEntry->hasSelection() ? left_wstring : mTextEntry->getWText(); | ||
987 | LLString full_string = mTextEntry->getText(); | ||
988 | |||
989 | // go ahead and arrange drop down list on first typed character, even | ||
990 | // though we aren't showing it... some code relies on prearrange | ||
991 | // callback to populate content | ||
992 | if( mTextEntry->getWText().size() == 1 ) | ||
993 | { | ||
994 | if (mPrearrangeCallback) | ||
995 | { | ||
996 | mPrearrangeCallback( this, mCallbackUserData ); | ||
997 | } | ||
998 | } | ||
999 | |||
1000 | if (mList->selectSimpleItem(full_string, FALSE)) | ||
1001 | { | ||
1002 | mTextEntry->setTentative(FALSE); | ||
1003 | } | ||
1004 | else if (!mList->selectSimpleItemByPrefix(left_wstring, FALSE)) | ||
1005 | { | ||
1006 | mList->deselectAllItems(); | ||
1007 | mTextEntry->setText(wstring_to_utf8str(user_wstring)); | ||
1008 | mTextEntry->setTentative(mTextEntryTentative); | ||
1009 | } | ||
1010 | else | ||
1011 | { | ||
1012 | LLWString selected_item = utf8str_to_wstring(mList->getSimpleSelectedItem()); | ||
1013 | LLWString wtext = left_wstring + selected_item.substr(left_wstring.size(), selected_item.size()); | ||
1014 | mTextEntry->setText(wstring_to_utf8str(wtext)); | ||
1015 | mTextEntry->setSelection(left_wstring.size(), mTextEntry->getWText().size()); | ||
1016 | mTextEntry->endSelection(); | ||
1017 | mTextEntry->setTentative(FALSE); | ||
1018 | } | ||
1019 | } | ||
1020 | |||
1021 | //static | ||
1022 | void LLComboBox::onTextCommit(LLUICtrl* caller, void* user_data) | ||
1023 | { | ||
1024 | LLComboBox* self = (LLComboBox*)user_data; | ||
1025 | LLString text = self->mTextEntry->getText(); | ||
1026 | self->setSimple(text); | ||
1027 | self->onCommit(); | ||
1028 | self->mTextEntry->selectAll(); | ||
1029 | } | ||
1030 | |||
1031 | void LLComboBox::setFocus(BOOL b) | ||
1032 | { | ||
1033 | LLUICtrl::setFocus(b); | ||
1034 | |||
1035 | if (b) | ||
1036 | { | ||
1037 | mList->clearSearchString(); | ||
1038 | } | ||
1039 | } | ||
1040 | |||
1041 | //============================================================================ | ||
1042 | // LLCtrlListInterface functions | ||
1043 | |||
1044 | S32 LLComboBox::getItemCount() const | ||
1045 | { | ||
1046 | return mList->getItemCount(); | ||
1047 | } | ||
1048 | |||
1049 | void LLComboBox::addColumn(const LLSD& column, EAddPosition pos) | ||
1050 | { | ||
1051 | mList->clearColumns(); | ||
1052 | mList->addColumn(column, pos); | ||
1053 | } | ||
1054 | |||
1055 | void LLComboBox::clearColumns() | ||
1056 | { | ||
1057 | mList->clearColumns(); | ||
1058 | } | ||
1059 | |||
1060 | void LLComboBox::setColumnLabel(const LLString& column, const LLString& label) | ||
1061 | { | ||
1062 | mList->setColumnLabel(column, label); | ||
1063 | } | ||
1064 | |||
1065 | LLScrollListItem* LLComboBox::addElement(const LLSD& value, EAddPosition pos, void* userdata) | ||
1066 | { | ||
1067 | return mList->addElement(value, pos, userdata); | ||
1068 | } | ||
1069 | |||
1070 | LLScrollListItem* LLComboBox::addSimpleElement(const LLString& value, EAddPosition pos, const LLSD& id) | ||
1071 | { | ||
1072 | return mList->addSimpleElement(value, pos, id); | ||
1073 | } | ||
1074 | |||
1075 | void LLComboBox::clearRows() | ||
1076 | { | ||
1077 | mList->clearRows(); | ||
1078 | } | ||
1079 | |||
1080 | void LLComboBox::sortByColumn(LLString name, BOOL ascending) | ||
1081 | { | ||
1082 | } | ||
1083 | |||
1084 | //============================================================================ | ||
1085 | //LLCtrlSelectionInterface functions | ||
1086 | |||
1087 | BOOL LLComboBox::setCurrentByID(const LLUUID& id) | ||
1088 | { | ||
1089 | BOOL found = mList->selectByID( id ); | ||
1090 | |||
1091 | if (found) | ||
1092 | { | ||
1093 | setLabel(mList->getSimpleSelectedItem()); | ||
1094 | } | ||
1095 | |||
1096 | return found; | ||
1097 | } | ||
1098 | |||
1099 | LLUUID LLComboBox::getCurrentID() | ||
1100 | { | ||
1101 | return mList->getStringUUIDSelectedItem(); | ||
1102 | } | ||
1103 | BOOL LLComboBox::setSelectedByValue(LLSD value, BOOL selected) | ||
1104 | { | ||
1105 | BOOL found = mList->setSelectedByValue(value, selected); | ||
1106 | if (found) | ||
1107 | { | ||
1108 | setLabel(mList->getSimpleSelectedItem()); | ||
1109 | } | ||
1110 | return found; | ||
1111 | } | ||
1112 | |||
1113 | LLSD LLComboBox::getSimpleSelectedValue() | ||
1114 | { | ||
1115 | return mList->getSimpleSelectedValue(); | ||
1116 | } | ||
1117 | |||
1118 | BOOL LLComboBox::isSelected(LLSD value) | ||
1119 | { | ||
1120 | return mList->isSelected(value); | ||
1121 | } | ||
1122 | |||
1123 | BOOL LLComboBox::operateOnSelection(EOperation op) | ||
1124 | { | ||
1125 | if (op == OP_DELETE) | ||
1126 | { | ||
1127 | mList->deleteSelectedItems(); | ||
1128 | return TRUE; | ||
1129 | } | ||
1130 | return FALSE; | ||
1131 | } | ||
1132 | |||
1133 | BOOL LLComboBox::operateOnAll(EOperation op) | ||
1134 | { | ||
1135 | if (op == OP_DELETE) | ||
1136 | { | ||
1137 | clearRows(); | ||
1138 | return TRUE; | ||
1139 | } | ||
1140 | return FALSE; | ||
1141 | } | ||
1142 | |||
1143 | //static | ||
1144 | void LLComboBox::onListFocusLost(LLUICtrl* old_focus) | ||
1145 | { | ||
1146 | // if focus is going to nothing (user hit ESC), take it back | ||
1147 | LLComboBox* combo = (LLComboBox*)old_focus->getParent(); | ||
1148 | combo->hideList(); | ||
1149 | if (gFocusMgr.getKeyboardFocus() == NULL) | ||
1150 | { | ||
1151 | combo->focusFirstItem(); | ||
1152 | } | ||
1153 | } | ||