diff options
author | Steven Bennetts <steve@lindenlab.com> | 2009-05-08 07:43:08 +0000 |
---|---|---|
committer | Steven Bennetts <steve@lindenlab.com> | 2009-05-08 07:43:08 +0000 |
commit | a4000c3744e42fcbb638e742f3b63fa31a0dee15 (patch) | |
tree | 7f472c30e65bbfa04ee9bc06631a1af305cc31fb /indra/newview/lllocationinputctrl.cpp | |
parent | 6c4cadbb04d633ad7b762058bdeba6e1f650dafd (diff) |
merge trunk@116587 skinning-7@119389 -> viewer-2.0.0-skinning-7
Diffstat (limited to 'indra/newview/lllocationinputctrl.cpp')
-rw-r--r-- | indra/newview/lllocationinputctrl.cpp | 1126 |
1 files changed, 1126 insertions, 0 deletions
diff --git a/indra/newview/lllocationinputctrl.cpp b/indra/newview/lllocationinputctrl.cpp new file mode 100644 index 0000000000..67bf2d7265 --- /dev/null +++ b/indra/newview/lllocationinputctrl.cpp @@ -0,0 +1,1126 @@ +/** + * @file lllocationinputmonitorctrl.cpp + * @brief Combobox-like location input control + * + * $LicenseInfo:firstyear=2009&license=viewergpl$ + * + * Copyright (c) 2009, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +// file includes +#include "lllocationinputctrl.h" + +// common includes +#include <llstring.h> +#include <llcombobox.h> + +// newview includes +#include "llbutton.h" +#include "llkeyboard.h" +#include "llscrolllistctrl.h" +#include "llwindow.h" +#include "llfloater.h" +#include "llscrollbar.h" +#include "llscrolllistcell.h" +#include "llscrolllistitem.h" +#include "llcontrol.h" +#include "llfocusmgr.h" +#include "lllineeditor.h" +#include "v2math.h" +#include "lluictrlfactory.h" + +// Globals +static S32 MAX_COMBO_WIDTH = 500; + +static LLRegisterWidget<LLLocationInputCtrl> r("location_input"); + +LLLocationInputCtrl::LLLocationInputCtrl(const LLLocationInputCtrl::Params& p) +: LLUICtrl(p), + mTextEntry(NULL), + mTextEntryTentative(TRUE), + mListPosition(BELOW), + mAllowTextEntry(p.allow_text_entry), + mSelectOnFocus(p.select_on_focus), + mHasAutocompletedText(false), + mMaxChars(p.max_chars), + mPrearrangeCallback(p.prearrange_callback()), + mTextEntryCallback(p.text_entry_callback()), + mSelectionCallback(p.selection_callback()), + mArrowImage(p.arrow_image) +{ + // Text label button + + LLButton::Params button_params; + button_params.name(p.label); + button_params.image_unselected.name("square_btn_32x128.tga"); + button_params.image_selected.name("square_btn_selected_32x128.tga"); + button_params.image_disabled.name("square_btn_32x128.tga"); + button_params.image_disabled_selected.name("square_btn_selected_32x128.tga"); + button_params.image_overlay.name("combobox_arrow.tga"); + button_params.image_overlay_alignment("right"); + button_params.scale_image(true); + button_params.mouse_down_callback.function(boost::bind(&LLLocationInputCtrl::onButtonDown, this)); + button_params.font(LLFontGL::getFontSansSerifSmall()); + button_params.follows.flags(FOLLOWS_LEFT|FOLLOWS_BOTTOM|FOLLOWS_RIGHT); + button_params.font_halign(LLFontGL::LEFT); + button_params.rect(p.rect); + button_params.pad_right(2); + + mButton = LLUICtrlFactory::create<LLButton>(button_params); + mButton->setRightHPad(2); //redo to compensate for button hack that leaves space for a character + addChild(mButton); + + LLScrollListCtrl::Params params; + params.name ("LocationInput"); + params.commit_callback.function(boost::bind(&LLLocationInputCtrl::onItemSelected, this, _2)); + params.visible(false); + params.bg_writeable_color(LLColor4::white); + params.commit_on_keyboard_movement(false); + + mList = LLUICtrlFactory::create<LLScrollListCtrl>(params); + addChild(mList); + + for (LLInitParam::ParamIterator<LLScrollListItem::Params>::const_iterator it = p.items().begin(); + it != p.items().end(); + ++it) + { + mList->addRow(*it); + } + + setTopLostCallback(boost::bind(&LLLocationInputCtrl::hideList, this)); +} + +LLLocationInputCtrl::~LLLocationInputCtrl() +{ + // children automatically deleted, including mMenu, mButton +} + +void LLLocationInputCtrl::setEnabled(BOOL enabled) +{ + LLView::setEnabled(enabled); + mButton->setEnabled(enabled); +} + +void LLLocationInputCtrl::clear() +{ + if (mTextEntry) + { + mTextEntry->setText(LLStringUtil::null); + } + mButton->setLabelSelected(LLStringUtil::null); + mButton->setLabelUnselected(LLStringUtil::null); + mButton->setDisabledLabel(LLStringUtil::null); + mButton->setDisabledSelectedLabel(LLStringUtil::null); + mList->deselectAllItems(); +} + +void LLLocationInputCtrl::onCommit() +{ + if (mAllowTextEntry && getCurrentIndex() != -1) + { + // we have selected an existing item, blitz the manual text entry with + // the properly capitalized item + mTextEntry->setValue(getSimple()); + mTextEntry->setTentative(FALSE); + } + LLUICtrl::onCommit(); +} + +// virtual +BOOL LLLocationInputCtrl::isDirty() const +{ + BOOL grubby = FALSE; + if ( mList ) + { + grubby = mList->isDirty(); + } + return grubby; +} + +// virtual Clear dirty state +void LLLocationInputCtrl::resetDirty() +{ + if ( mList ) + { + mList->resetDirty(); + } +} + + +// add item "name" to menu +LLScrollListItem* LLLocationInputCtrl::add(const std::string& name, EAddPosition pos, BOOL enabled) +{ + LLScrollListItem* item = mList->addSimpleElement(name, pos); + item->setEnabled(enabled); + if (!mAllowTextEntry && mLabel.empty()) + { + selectFirstItem(); + } + return item; +} + +// add item "name" with a unique id to menu +LLScrollListItem* LLLocationInputCtrl::add(const std::string& name, const LLUUID& id, EAddPosition pos, BOOL enabled ) +{ + LLScrollListItem* item = mList->addSimpleElement(name, pos, id); + item->setEnabled(enabled); + if (!mAllowTextEntry && mLabel.empty()) + { + selectFirstItem(); + } + return item; +} + +// add item "name" with attached userdata +LLScrollListItem* LLLocationInputCtrl::add(const std::string& name, void* userdata, EAddPosition pos, BOOL enabled ) +{ + LLScrollListItem* item = mList->addSimpleElement(name, pos); + item->setEnabled(enabled); + item->setUserdata( userdata ); + if (!mAllowTextEntry && mLabel.empty()) + { + selectFirstItem(); + } + return item; +} + +// add item "name" with attached generic data +LLScrollListItem* LLLocationInputCtrl::add(const std::string& name, LLSD value, EAddPosition pos, BOOL enabled ) +{ + LLScrollListItem* item = mList->addSimpleElement(name, pos, value); + item->setEnabled(enabled); + if (!mAllowTextEntry && mLabel.empty()) + { + selectFirstItem(); + } + return item; +} + +LLScrollListItem* LLLocationInputCtrl::addSeparator(EAddPosition pos) +{ + return mList->addSeparator(pos); +} + +void LLLocationInputCtrl::sortByName(BOOL ascending) +{ + mList->sortOnce(0, ascending); +} + + +// Choose an item with a given name in the menu. +// Returns TRUE if the item was found. +BOOL LLLocationInputCtrl::setSimple(const LLStringExplicit& name) +{ + BOOL found = mList->selectItemByLabel(name, FALSE); + + if (found) + { + setLabel(name); + } + + return found; +} + +// virtual +void LLLocationInputCtrl::setValue(const LLSD& value) +{ + BOOL found = mList->selectByValue(value); + if (found) + { + LLScrollListItem* item = mList->getFirstSelected(); + if (item) + { + setLabel( mList->getSelectedItemLabel() ); + } + } +} + +const std::string LLLocationInputCtrl::getSimple() const +{ + const std::string res = mList->getSelectedItemLabel(); + if (res.empty() && mAllowTextEntry) + { + return mTextEntry->getText(); + } + else + { + return res; + } +} + +const std::string LLLocationInputCtrl::getSelectedItemLabel(S32 column) const +{ + return mList->getSelectedItemLabel(column); +} + +// virtual +LLSD LLLocationInputCtrl::getValue() const +{ + LLScrollListItem* item = mList->getFirstSelected(); + if( item ) + { + return item->getValue(); + } + else if (mAllowTextEntry) + { + return mTextEntry->getValue(); + } + else + { + return LLSD(); + } +} + +void LLLocationInputCtrl::setLabel(const LLStringExplicit& name) +{ + if ( mTextEntry ) + { + mTextEntry->setText(name); + if (mList->selectItemByLabel(name, FALSE)) + { + mTextEntry->setTentative(FALSE); + } + else + { + mTextEntry->setTentative(mTextEntryTentative); + } + } + + if (!mAllowTextEntry) + { + mButton->setLabelUnselected(name); + mButton->setLabelSelected(name); + mButton->setDisabledLabel(name); + mButton->setDisabledSelectedLabel(name); + } +} + + +BOOL LLLocationInputCtrl::remove(const std::string& name) +{ + BOOL found = mList->selectItemByLabel(name); + + if (found) + { + LLScrollListItem* item = mList->getFirstSelected(); + if (item) + { + mList->deleteSingleItem(mList->getItemIndex(item)); + } + } + + return found; +} + +BOOL LLLocationInputCtrl::remove(S32 index) +{ + if (index < mList->getItemCount()) + { + mList->deleteSingleItem(index); + return TRUE; + } + return FALSE; +} + +// Keyboard focus lost. +void LLLocationInputCtrl::onFocusLost() +{ + hideList(); + // if valid selection + if (mAllowTextEntry && getCurrentIndex() != -1) + { + mTextEntry->selectAll(); + } + LLUICtrl::onFocusLost(); +} + +void LLLocationInputCtrl::setButtonVisible(BOOL visible) +{ + static LLUICachedControl<S32> drop_shadow_button ("DropShadowButton", 0); + + mButton->setVisible(visible); + if (mTextEntry) + { + LLRect text_entry_rect(0, getRect().getHeight(), getRect().getWidth(), 0); + if (visible) + { + text_entry_rect.mRight -= llmax(8,mArrowImage->getWidth()) + 2 * drop_shadow_button; + } + //mTextEntry->setRect(text_entry_rect); + mTextEntry->reshape(text_entry_rect.getWidth(), text_entry_rect.getHeight(), TRUE); + } +} + +/*virtual*/ +BOOL LLLocationInputCtrl::postBuild() +{ + // If providing user text entry or descriptive label don't select an item under the hood + if (!acceptsTextInput() && mLabel.empty()) + { + selectFirstItem(); + } + updateLayout(); + return TRUE; +} + +void LLLocationInputCtrl::draw() +{ + mButton->setEnabled(getEnabled() /*&& !mList->isEmpty()*/); + + // Draw children normally + LLUICtrl::draw(); +} + +BOOL LLLocationInputCtrl::setCurrentByIndex( S32 index ) +{ + BOOL found = mList->selectNthItem( index ); + if (found) + { + setLabel(mList->getSelectedItemLabel()); + } + return found; +} + +S32 LLLocationInputCtrl::getCurrentIndex() const +{ + LLScrollListItem* item = mList->getFirstSelected(); + if( item ) + { + return mList->getItemIndex( item ); + } + return -1; +} + + +void LLLocationInputCtrl::updateLayout() +{ + static LLUICachedControl<S32> drop_shadow_button ("DropShadowButton", 0); + LLRect rect = getLocalRect(); + if (mAllowTextEntry) + { + S32 shadow_size = drop_shadow_button; + mButton->setRect(LLRect( getRect().getWidth() - llmax(8,mArrowImage->getWidth()) - 2 * shadow_size, + rect.mTop, rect.mRight, rect.mBottom)); + mButton->setTabStop(FALSE); + mButton->setHAlign(LLFontGL::HCENTER); + + if (!mTextEntry) + { + LLRect text_entry_rect(0, getRect().getHeight(), getRect().getWidth(), 0); + text_entry_rect.mRight -= llmax(8,mArrowImage->getWidth()) + 2 * drop_shadow_button; + // clear label on button + std::string cur_label = mButton->getLabelSelected(); + LLLineEditor::Params params; + params.name ("combo_text_entry"); + params.rect (text_entry_rect); + params.default_text (LLStringUtil::null); + params.font (LLFontGL::getFontSansSerifSmall()); + params.max_length_bytes (mMaxChars); + params.commit_callback.function(boost::bind(&LLLocationInputCtrl::onTextCommit, this, _2)); + params.keystroke_callback (boost::bind(&LLLocationInputCtrl::onTextEntry, this, _1)); + params.focus_lost_callback (NULL); + params.select_on_focus (mSelectOnFocus); + params.handle_edit_keys_directly (true); + params.commit_on_focus_lost (false); + params.follows.flags (FOLLOWS_ALL); + mTextEntry = LLUICtrlFactory::create<LLLineEditor> (params); + mTextEntry->setText(cur_label); + mTextEntry->setIgnoreTab(TRUE); + mTextEntry->setRevertOnEsc(FALSE); + //mTextEntry->setFocusReceivedCallback(boost::bind(&LLLocationInputCtrl::hideList, this)); + addChild(mTextEntry); + } + else + { + mTextEntry->setVisible(TRUE); + mTextEntry->setMaxTextLength(mMaxChars); + } + + // clear label on button + setLabel(LLStringUtil::null); + + mButton->setFollows(FOLLOWS_BOTTOM | FOLLOWS_TOP | FOLLOWS_RIGHT); + } + else if (!mAllowTextEntry) + { + mButton->setRect(rect); + mButton->setTabStop(TRUE); + mButton->setHAlign(LLFontGL::LEFT); + + if (mTextEntry) + { + mTextEntry->setVisible(FALSE); + } + mButton->setFollowsAll(); + } +} + +void* LLLocationInputCtrl::getCurrentUserdata() +{ + LLScrollListItem* item = mList->getFirstSelected(); + if( item ) + { + return item->getUserdata(); + } + return NULL; +} + + +void LLLocationInputCtrl::showList() +{ + // Make sure we don't go off top of screen. + LLCoordWindow window_size; + getWindow()->getSize(&window_size); + //HACK: shouldn't have to know about scale here + mList->fitContents( 192, llfloor((F32)window_size.mY / LLUI::sGLScaleFactor.mV[VY]) - 50 ); + + // Make sure that we can see the whole list + LLRect root_view_local; + LLView* root_view = getRootView(); + root_view->localRectToOtherView(root_view->getLocalRect(), &root_view_local, this); + + LLRect rect = mList->getRect(); + + S32 min_width = getRect().getWidth(); + S32 max_width = llmax(min_width, MAX_COMBO_WIDTH); + // make sure we have up to date content width metrics + mList->calcColumnWidths(); + S32 list_width = llclamp(mList->getMaxContentWidth(), min_width, max_width); + + if (mListPosition == BELOW) + { + if (rect.getHeight() <= -root_view_local.mBottom) + { + // Move rect so it hangs off the bottom of this view + rect.setLeftTopAndSize(0, 0, list_width, rect.getHeight() ); + } + else + { + // stack on top or bottom, depending on which has more room + if (-root_view_local.mBottom > root_view_local.mTop - getRect().getHeight()) + { + // Move rect so it hangs off the bottom of this view + rect.setLeftTopAndSize(0, 0, list_width, llmin(-root_view_local.mBottom, rect.getHeight())); + } + else + { + // move rect so it stacks on top of this view (clipped to size of screen) + rect.setOriginAndSize(0, getRect().getHeight(), list_width, llmin(root_view_local.mTop - getRect().getHeight(), rect.getHeight())); + } + } + } + else // ABOVE + { + if (rect.getHeight() <= root_view_local.mTop - getRect().getHeight()) + { + // move rect so it stacks on top of this view (clipped to size of screen) + rect.setOriginAndSize(0, getRect().getHeight(), list_width, llmin(root_view_local.mTop - getRect().getHeight(), rect.getHeight())); + } + else + { + // stack on top or bottom, depending on which has more room + if (-root_view_local.mBottom > root_view_local.mTop - getRect().getHeight()) + { + // Move rect so it hangs off the bottom of this view + rect.setLeftTopAndSize(0, 0, list_width, llmin(-root_view_local.mBottom, rect.getHeight())); + } + else + { + // move rect so it stacks on top of this view (clipped to size of screen) + rect.setOriginAndSize(0, getRect().getHeight(), list_width, llmin(root_view_local.mTop - getRect().getHeight(), rect.getHeight())); + } + } + + } + mList->setOrigin(rect.mLeft, rect.mBottom); + mList->reshape(rect.getWidth(), rect.getHeight()); + mList->translateIntoRect(root_view_local, FALSE); + + // Make sure we didn't go off bottom of screen + S32 x, y; + mList->localPointToScreen(0, 0, &x, &y); + + if (y < 0) + { + mList->translate(0, -y); + } + + // NB: this call will trigger the focuslost callback which will hide the list, so do it first + // before finally showing the list + + mList->setFocus(TRUE); + + // register ourselves as a "top" control + // effectively putting us into a special draw layer + // and not affecting the bounding rectangle calculation + gFocusMgr.setTopCtrl(this); + + // Show the list and push the button down + mButton->setToggleState(TRUE); + mList->setVisible(TRUE); + + setUseBoundingRect(TRUE); +} + +void LLLocationInputCtrl::hideList() +{ + //*HACK: store the original value explicitly somewhere, not just in label + std::string orig_selection = mAllowTextEntry ? mTextEntry->getText() : mButton->getLabelSelected(); + + // assert selection in list + mList->selectItemByLabel(orig_selection, FALSE); + + mButton->setToggleState(FALSE); + mList->setVisible(FALSE); + mList->mouseOverHighlightNthItem(-1); + + setUseBoundingRect(FALSE); + + if( gFocusMgr.getTopCtrl() == this ) + { + gFocusMgr.setTopCtrl(NULL); + } +} + +void LLLocationInputCtrl::onButtonDown() +{ + if (!mList->getVisible()) + { +#if 0 // XXX VS + LLScrollListItem* last_selected_item = mList->getLastSelectedItem(); + if (last_selected_item) + { + // highlight the original selection before potentially selecting a new item + mList->mouseOverHighlightNthItem(mList->getItemIndex(last_selected_item)); + } +#endif + + prearrangeList(); + + if (mList->getItemCount() != 0) + { + showList(); + } + + setFocus( TRUE ); + + // pass mouse capture on to list if button is depressed + if (mButton->hasMouseCapture()) + { + gFocusMgr.setMouseCapture(mList); + } + } + else + { + hideList(); + // XXX VS + mTextEntry->setFocus(TRUE); + } + +} + + +//------------------------------------------------------------------ +// static functions +//------------------------------------------------------------------ + +void LLLocationInputCtrl::onItemSelected(const LLSD& data) +{ + const std::string name = mList->getSelectedItemLabel(); + + S32 cur_id = getCurrentIndex(); + if (cur_id != -1) + { + setLabel(name); + + if (mAllowTextEntry) + { + gFocusMgr.setKeyboardFocus(mTextEntry); + mTextEntry->selectAll(); + } + } + + // hiding the list reasserts the old value stored in the text editor/dropdown button + hideList(); + + // commit does the reverse, asserting the value in the list + onCommit(); + + // call the callback if it exists + if(mSelectionCallback) + { + mSelectionCallback(this, data); + } +} + +BOOL LLLocationInputCtrl::handleToolTip(S32 x, S32 y, std::string& msg, LLRect* sticky_rect_screen) +{ + std::string tool_tip; + + if(LLUICtrl::handleToolTip(x, y, msg, sticky_rect_screen)) + { + return TRUE; + } + + if (LLUI::sShowXUINames) + { + tool_tip = getShowNamesToolTip(); + } + else + { + tool_tip = getToolTip(); + if (tool_tip.empty()) + { + tool_tip = getSelectedItemLabel(); + } + } + + if( !tool_tip.empty() ) + { + msg = tool_tip; + + // Convert rect local to screen coordinates + localPointToScreen( + 0, 0, + &(sticky_rect_screen->mLeft), &(sticky_rect_screen->mBottom) ); + localPointToScreen( + getRect().getWidth(), getRect().getHeight(), + &(sticky_rect_screen->mRight), &(sticky_rect_screen->mTop) ); + } + return TRUE; +} + +BOOL LLLocationInputCtrl::handleKeyHere(KEY key, MASK mask) +{ + BOOL result = FALSE; + if (hasFocus()) + { + if (mList->getVisible() + && key == KEY_ESCAPE && mask == MASK_NONE) + { + hideList(); + // XXX VS + mTextEntry->setFocus(TRUE); + return TRUE; + } + //give list a chance to pop up and handle key + LLScrollListItem* last_selected_item = mList->getLastSelectedItem(); + if (last_selected_item) + { + // highlight the original selection before potentially selecting a new item + mList->mouseOverHighlightNthItem(mList->getItemIndex(last_selected_item)); + } + result = mList->handleKeyHere(key, mask); + + // will only see return key if it is originating from line editor + // since the dropdown button eats the key + if (key == KEY_RETURN) + { + // don't show list and don't eat key input when committing + // free-form text entry with RETURN since user already knows + // what they are trying to select + return FALSE; + } + // if selection has changed, pop open list + // XXX VS +#if 1 + else if(key == KEY_DOWN && mList->getItemCount() != 0) +#else + else if (mList->getLastSelectedItem() != last_selected_item) +#endif + { + showList(); + } + + } + return result; +} + +BOOL LLLocationInputCtrl::handleUnicodeCharHere(llwchar uni_char) +{ + BOOL result = FALSE; + if (gFocusMgr.childHasKeyboardFocus(this)) + { + // space bar just shows the list + if (' ' != uni_char ) + { + LLScrollListItem* last_selected_item = mList->getLastSelectedItem(); + if (last_selected_item) + { + // highlight the original selection before potentially selecting a new item + mList->mouseOverHighlightNthItem(mList->getItemIndex(last_selected_item)); + } + result = mList->handleUnicodeCharHere(uni_char); + if (mList->getLastSelectedItem() != last_selected_item) + { + showList(); + } + } + } + return result; +} + +void LLLocationInputCtrl::setTextEntry(const LLStringExplicit& text) +{ + if (mTextEntry) + { + setText(text); + updateSelection(); + } +} + +/** + * Useful if we want to just set the text entry value, no matter what the list contains. + * + * This is faster than setTextEntry(). + */ +void LLLocationInputCtrl::setText(const LLStringExplicit& text) +{ + if (mTextEntry) + mTextEntry->setText(text); +} + +void LLLocationInputCtrl::onTextEntry(LLLineEditor* line_editor) +{ + if (mTextEntryCallback != NULL) + { + (mTextEntryCallback)(line_editor, LLSD()); + } + + KEY key = gKeyboard->currentKey(); + + // XXX VS + { + if (line_editor->getText().empty()) + { + prearrangeList(); // resets filter + hideList(); + } + // Moving cursor should not affect showing the list. + else if (key != KEY_LEFT && key != KEY_RIGHT && key != KEY_HOME && key != KEY_END) + { + prearrangeList(line_editor->getText()); + if (mList->getItemCount() != 0) + { + showList(); + } + else + { + // Hide the list if it's empty. + hideList(); + } + + mTextEntry->setFocus(TRUE); + } + } + + if (key == KEY_BACKSPACE || + key == KEY_DELETE) + { + if (mList->selectItemByLabel(line_editor->getText(), FALSE)) + { + line_editor->setTentative(FALSE); + } + else + { + line_editor->setTentative(mTextEntryTentative); + mList->deselectAllItems(); + } + return; + } + + if (key == KEY_LEFT || + key == KEY_RIGHT) + { + return; + } + + if (key == KEY_DOWN) + { + setCurrentByIndex(llmin(getItemCount() - 1, getCurrentIndex() + 1)); + if (!mList->getVisible()) + { + prearrangeList(); + + if (mList->getItemCount() != 0) + { + showList(); + } + } + line_editor->selectAll(); + line_editor->setTentative(FALSE); + } + else + { + // RN: presumably text entry + updateSelection(); + } +} + +void LLLocationInputCtrl::updateSelection() +{ + LLWString left_wstring = mTextEntry->getWText().substr(0, mTextEntry->getCursor()); + // user-entered portion of string, based on assumption that any selected + // text was a result of auto-completion + LLWString user_wstring = mHasAutocompletedText ? left_wstring : mTextEntry->getWText(); + std::string full_string = mTextEntry->getText(); + + // go ahead and arrange drop down list on first typed character, even + // though we aren't showing it... some code relies on prearrange + // callback to populate content + if( mTextEntry->getWText().size() == 1 ) + { + prearrangeList(mTextEntry->getText()); + } + + if (mList->selectItemByLabel(full_string, FALSE)) + { + mTextEntry->setTentative(FALSE); + } + else if (mList->selectItemByPrefix(left_wstring, FALSE)) + { + LLWString selected_item = utf8str_to_wstring(mList->getSelectedItemLabel()); + LLWString wtext = left_wstring + selected_item.substr(left_wstring.size(), selected_item.size()); + mTextEntry->setText(wstring_to_utf8str(wtext)); + mTextEntry->setSelection(left_wstring.size(), mTextEntry->getWText().size()); + mTextEntry->endSelection(); + mTextEntry->setTentative(FALSE); + mHasAutocompletedText = TRUE; + } + else // no matching items found + { + mList->deselectAllItems(); + mTextEntry->setText(wstring_to_utf8str(user_wstring)); // removes text added by autocompletion + mTextEntry->setTentative(mTextEntryTentative); + mHasAutocompletedText = FALSE; + } +} + +void LLLocationInputCtrl::onTextCommit(const LLSD& data) +{ + std::string text = mTextEntry->getText(); + setSimple(text); + onCommit(); + mTextEntry->selectAll(); +} + +void LLLocationInputCtrl::setFocus(BOOL b) +{ + LLUICtrl::setFocus(b); + + if (b) + { + mList->clearSearchString(); + if (mList->getVisible()) + { + mList->setFocus(TRUE); + } + else + { + mTextEntry->setFocus(TRUE); + } + } +} + +//============================================================================ +// LLCtrlListInterface functions + +S32 LLLocationInputCtrl::getItemCount() const +{ + return mList->getItemCount(); +} + +void LLLocationInputCtrl::addColumn(const LLSD& column, EAddPosition pos) +{ + mList->clearColumns(); + mList->addColumn(column, pos); +} + +void LLLocationInputCtrl::clearColumns() +{ + mList->clearColumns(); +} + +void LLLocationInputCtrl::setColumnLabel(const std::string& column, const std::string& label) +{ + mList->setColumnLabel(column, label); +} + +LLScrollListItem* LLLocationInputCtrl::addElement(const LLSD& value, EAddPosition pos, void* userdata) +{ + return mList->addElement(value, pos, userdata); +} + +LLScrollListItem* LLLocationInputCtrl::addSimpleElement(const std::string& value, EAddPosition pos, const LLSD& id) +{ + return mList->addSimpleElement(value, pos, id); +} + +void LLLocationInputCtrl::clearRows() +{ + mList->clearRows(); +} + +void LLLocationInputCtrl::sortByColumn(const std::string& name, BOOL ascending) +{ + mList->sortByColumn(name, ascending); +} + +//============================================================================ +//LLCtrlSelectionInterface functions + +BOOL LLLocationInputCtrl::setCurrentByID(const LLUUID& id) +{ + BOOL found = mList->selectByID( id ); + + if (found) + { + setLabel(mList->getSelectedItemLabel()); + } + + return found; +} + +LLUUID LLLocationInputCtrl::getCurrentID() const +{ + return mList->getStringUUIDSelectedItem(); +} +BOOL LLLocationInputCtrl::setSelectedByValue(const LLSD& value, BOOL selected) +{ + BOOL found = mList->setSelectedByValue(value, selected); + if (found) + { + setLabel(mList->getSelectedItemLabel()); + } + return found; +} + +LLSD LLLocationInputCtrl::getSelectedValue() +{ + return mList->getSelectedValue(); +} + +BOOL LLLocationInputCtrl::isSelected(const LLSD& value) const +{ + return mList->isSelected(value); +} + +BOOL LLLocationInputCtrl::operateOnSelection(EOperation op) +{ + if (op == OP_DELETE) + { + mList->deleteSelectedItems(); + return TRUE; + } + return FALSE; +} + +BOOL LLLocationInputCtrl::operateOnAll(EOperation op) +{ + if (op == OP_DELETE) + { + clearRows(); + return TRUE; + } + return FALSE; +} + +BOOL LLLocationInputCtrl::selectItemRange( S32 first, S32 last ) +{ + return mList->selectItemRange(first, last); +} + +void LLLocationInputCtrl::prearrangeList(std::string filter) +{ + if (mPrearrangeCallback) + { + mPrearrangeCallback(this, LLSD(filter)); + } +} + +//=========================================================================== + +BOOL LLLocationInputCtrl::childHasFocus() const +{ + return LLUICtrl::hasFocus() || mButton->hasFocus() || mList->hasFocus() || mTextEntry->hasFocus(); +} + +BOOL LLLocationInputCtrl::canCut() const +{ + return mTextEntry ? mTextEntry->canCut() : false; +} + +BOOL LLLocationInputCtrl::canCopy() const +{ + return mTextEntry ? mTextEntry->canCopy() : false; +} + +BOOL LLLocationInputCtrl::canPaste() const +{ + return mTextEntry ? mTextEntry->canPaste() : false; +} + +BOOL LLLocationInputCtrl::canDeselect() const +{ + return mTextEntry ? mTextEntry->canDeselect() : false; +} + +BOOL LLLocationInputCtrl::canSelectAll() const +{ + return mTextEntry ? mTextEntry->canSelectAll() : false; +} + +void LLLocationInputCtrl::cut() +{ + if (mTextEntry) + mTextEntry->cut(); +} + +void LLLocationInputCtrl::copy() +{ + if (mTextEntry) + mTextEntry->copy(); +} + +void LLLocationInputCtrl::paste() +{ + if (mTextEntry) + mTextEntry->paste(); +} + +void LLLocationInputCtrl::deleteSelection() +{ + if (mTextEntry) + mTextEntry->deleteSelection(); +} + +void LLLocationInputCtrl::selectAll() +{ + if (mTextEntry) + mTextEntry->selectAll(); +} |