diff --git a/indra/llui/llcombobox.cpp b/indra/llui/llcombobox.cpp index 30a10c39a..6fd7084df 100644 --- a/indra/llui/llcombobox.cpp +++ b/indra/llui/llcombobox.cpp @@ -64,17 +64,20 @@ LLComboBox::LLComboBox( const std::string& name, const LLRect &rect, const std:: commit_callback_t commit_callback) : LLUICtrl(name, rect, TRUE, commit_callback, FOLLOWS_LEFT | FOLLOWS_TOP), mTextEntry(NULL), - mArrowImage(NULL), - mAllowTextEntry(FALSE), - mMaxChars(20), mTextEntryTentative(TRUE), - mListPosition(BELOW), + mArrowImage(NULL), + mHasAutocompletedText(false), + mAllowTextEntry(false), + mAllowNewValues(false), + mMaxChars(20), mPrearrangeCallback( NULL ), mTextEntryCallback( NULL ), + mListPosition(BELOW), mSuppressTentative( false ), mSuppressAutoComplete( false ), - mLabel(label), - mListColor(LLUI::sColorsGroup->getColor("ComboBoxBg")) + mListColor(LLUI::sColorsGroup->getColor("ComboBoxBg")), + mLastSelectedIndex(-1), + mLabel(label) { // Always use text box // Text label button @@ -91,17 +94,25 @@ LLComboBox::LLComboBox( const std::string& name, const LLRect &rect, const std:: mButton->setFont(LLFontGL::getFontSansSerifSmall()); mButton->setFollows(FOLLOWS_LEFT | FOLLOWS_BOTTOM | FOLLOWS_RIGHT); mButton->setHAlign( LLFontGL::LEFT ); - mButton->setRightHPad(2); + if(mAllowTextEntry) + { + mButton->setRightHPad(2); + } + addChild(mButton); // disallow multiple selection mList = new LLScrollListCtrl(std::string("ComboBox"), LLRect(), - boost::bind(&LLComboBox::onItemSelected, this), FALSE); + boost::bind(&LLComboBox::onItemSelected, this, _2), FALSE); mList->setVisible(FALSE); mList->setBgWriteableColor(mListColor); mList->setCommitOnKeyboardMovement(FALSE); addChild(mList); + // Mouse-down on button will transfer mouse focus to the list + // Grab the mouse-up event and make sure the button state is correct + mList->setMouseUpCallback(boost::bind(&LLComboBox::onListMouseUp, this)); + mArrowImage = LLUI::getUIImage("combobox_arrow.tga"); mButton->setImageOverlay("combobox_arrow.tga", LLFontGL::RIGHT); @@ -234,6 +245,7 @@ void LLComboBox::clear() mButton->setLabelSelected(LLStringUtil::null); mButton->setLabelUnselected(LLStringUtil::null); mList->deselectAllItems(); + mLastSelectedIndex = -1; } void LLComboBox::onCommit() @@ -280,6 +292,11 @@ void LLComboBox::resetDirty() } } +bool LLComboBox::itemExists(const std::string& name) +{ + return mList->getItemByLabel(name); +} + void LLComboBox::resetTextDirty() { if ( mTextEntry ) @@ -358,6 +375,7 @@ BOOL LLComboBox::setSimple(const LLStringExplicit& name) if (found) { setLabel(name); + mLastSelectedIndex = mList->getFirstSelectedIndex(); } return found; @@ -372,14 +390,19 @@ void LLComboBox::setValue(const LLSD& value) LLScrollListItem* item = mList->getFirstSelected(); if (item) { - setLabel( mList->getSelectedItemLabel() ); + updateLabel(); } + mLastSelectedIndex = mList->getFirstSelectedIndex(); + } + else + { + mLastSelectedIndex = -1; } } const std::string LLComboBox::getSimple() const { - const std::string res = mList->getSelectedItemLabel(); + const std::string res = getSelectedItemLabel(); if (res.empty() && mAllowTextEntry) { return mTextEntry->getText(); @@ -421,6 +444,7 @@ void LLComboBox::setLabel(const LLStringExplicit& name) if (mList->selectItemByLabel(name, FALSE)) { mTextEntry->setTentative(FALSE); + mLastSelectedIndex = mList->getFirstSelectedIndex(); } else { @@ -435,6 +459,25 @@ void LLComboBox::setLabel(const LLStringExplicit& name) } } +void LLComboBox::updateLabel() +{ + // Update the combo editor with the selected + // item label. + if (mTextEntry) + { + mTextEntry->setText(getSelectedItemLabel()); + mTextEntry->setTentative(FALSE); + } + + // If combo box doesn't allow text entry update + // the combo button label. + if (!mAllowTextEntry) + { + std::string label = getSelectedItemLabel(); + mButton->setLabelUnselected(label); + mButton->setLabelSelected(label); + } +} BOOL LLComboBox::remove(const std::string& name) { @@ -447,6 +490,7 @@ BOOL LLComboBox::remove(const std::string& name) { mList->deleteSingleItem(mList->getItemIndex(item)); } + mLastSelectedIndex = mList->getFirstSelectedIndex(); } return found; @@ -457,6 +501,7 @@ BOOL LLComboBox::remove(S32 index) if (index < mList->getItemCount()) { mList->deleteSingleItem(index); + setLabel(getSelectedItemLabel()); return TRUE; } return FALSE; @@ -502,7 +547,8 @@ BOOL LLComboBox::setCurrentByIndex( S32 index ) BOOL found = mList->selectNthItem( index ); if (found) { - setLabel(mList->getSelectedItemLabel()); + setLabel(getSelectedItemLabel()); + mLastSelectedIndex = index; } return found; } @@ -560,10 +606,12 @@ void LLComboBox::updateLayout() mButton->setFollows(FOLLOWS_BOTTOM | FOLLOWS_TOP | FOLLOWS_RIGHT); } - else if (!mAllowTextEntry) + else { mButton->setRect(rect); mButton->setTabStop(TRUE); + mButton->setLabelUnselected(mLabel); + mButton->setLabelSelected(mLabel); if (mTextEntry) { @@ -682,24 +730,28 @@ void LLComboBox::showList() void LLComboBox::hideList() { -#if 0 // Don't do this! mTextEntry->getText() can be truncated, in which case selectItemByLabel - // fails and this only resets the selection :/ - - // *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); -#endif - - mButton->setToggleState(FALSE); - mList->setVisible(FALSE); - mList->mouseOverHighlightNthItem(-1); - - setUseBoundingRect(FALSE); - if( gFocusMgr.getTopCtrl() == this ) + if (mList->getVisible()) { - gFocusMgr.setTopCtrl(NULL); + // assert selection in list + if(mAllowNewValues) + { + // mLastSelectedIndex = -1 means that we entered a new value, don't select + // any of existing items in this case. + if(mLastSelectedIndex >= 0) + mList->selectNthItem(mLastSelectedIndex); + } + else if(mLastSelectedIndex >= 0) + mList->selectNthItem(mLastSelectedIndex); + + mButton->setToggleState(FALSE); + mList->setVisible(FALSE); + mList->mouseOverHighlightNthItem(-1); + + setUseBoundingRect(FALSE); + if( gFocusMgr.getTopCtrl() == this ) + { + gFocusMgr.setTopCtrl(NULL); + } } } @@ -730,10 +782,14 @@ void LLComboBox::onButtonMouseDown() setFocus( TRUE ); // pass mouse capture on to list if button is depressed - /*if (self->mButton->hasMouseCapture()) + if (mButton->hasMouseCapture()) { - gFocusMgr.setMouseCapture(self->mList); - }*/ + gFocusMgr.setMouseCapture(mList); + + // But keep the "pressed" look, which buttons normally lose when they + // lose focus + mButton->setForcePressedState(true); + } } else { @@ -742,15 +798,23 @@ void LLComboBox::onButtonMouseDown() } -void LLComboBox::onItemSelected() +void LLComboBox::onListMouseUp() { - // Note: item is the LLScrollListCtrl - const std::string name = mList->getSelectedItemLabel(); + // In some cases this is the termination of a mouse click that started on + // the button, so clear its pressed state + mButton->setForcePressedState(false); +} - S32 cur_id = getCurrentIndex(); - if (cur_id != -1) +//------------------------------------------------------------------ +// static functions +//------------------------------------------------------------------ + +void LLComboBox::onItemSelected(const LLSD& data) +{ + mLastSelectedIndex = getCurrentIndex(); + if (mLastSelectedIndex != -1) { - setLabel(name); + updateLabel(); if (mAllowTextEntry) { @@ -758,7 +822,6 @@ void LLComboBox::onItemSelected() mTextEntry->selectAll(); } } - // hiding the list reasserts the old value stored in the text editor/dropdown button hideList(); @@ -879,6 +942,7 @@ void LLComboBox::setTextEntry(const LLStringExplicit& text) if (mTextEntry) { mTextEntry->setText(text); + mHasAutocompletedText = FALSE; updateSelection(); } } @@ -902,12 +966,14 @@ void LLComboBox::onTextEntry(LLLineEditor* line_editor) if (mList->selectItemByLabel(line_editor->getText(), FALSE)) { line_editor->setTentative(FALSE); + mLastSelectedIndex = mList->getFirstSelectedIndex(); } else { if (!mSuppressTentative) line_editor->setTentative(mTextEntryTentative); mList->deselectAllItems(); + mLastSelectedIndex = -1; } return; } @@ -964,7 +1030,7 @@ void LLComboBox::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 = mTextEntry->hasSelection() ? left_wstring : mTextEntry->getWText(); + 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 @@ -978,21 +1044,26 @@ void LLComboBox::updateSelection() if (mList->selectItemByLabel(full_string, FALSE)) { mTextEntry->setTentative(FALSE); + mLastSelectedIndex = mList->getFirstSelectedIndex(); } - else if (!mList->selectItemByPrefix(left_wstring, FALSE)) + else if (mList->selectItemByPrefix(left_wstring, FALSE)) { - mList->deselectAllItems(); - mTextEntry->setText(wstring_to_utf8str(user_wstring)); - if (!mSuppressTentative) mTextEntry->setTentative(mTextEntryTentative); - } - else - { - LLWString selected_item = utf8str_to_wstring(mList->getSelectedItemLabel()); + LLWString selected_item = utf8str_to_wstring(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; + mLastSelectedIndex = mList->getFirstSelectedIndex(); + } + else // no matching items found + { + mList->deselectAllItems(); + mTextEntry->setText(wstring_to_utf8str(user_wstring)); // removes text added by autocompletion + mTextEntry->setTentative(mTextEntryTentative); + mHasAutocompletedText = FALSE; + mLastSelectedIndex = -1; } } @@ -1109,7 +1180,8 @@ BOOL LLComboBox::setCurrentByID(const LLUUID& id) if (found) { - setLabel(mList->getSelectedItemLabel()); + setLabel(getSelectedItemLabel()); + mLastSelectedIndex = mList->getFirstSelectedIndex(); } return found; @@ -1124,7 +1196,7 @@ BOOL LLComboBox::setSelectedByValue(const LLSD& value, BOOL selected) BOOL found = mList->setSelectedByValue(value, selected); if (found) { - setLabel(mList->getSelectedItemLabel()); + setLabel(getSelectedItemLabel()); } return found; } diff --git a/indra/llui/llcombobox.h b/indra/llui/llcombobox.h index f061865b8..b6165ae0f 100644 --- a/indra/llui/llcombobox.h +++ b/indra/llui/llcombobox.h @@ -123,6 +123,7 @@ public: LLScrollListItem* addSeparator(EAddPosition pos = ADD_BOTTOM); BOOL remove( S32 index ); // remove item by index, return TRUE if found and removed void removeall() { clearRows(); } + bool itemExists(const std::string& name); void sortByName(BOOL ascending = TRUE); // Sort the entries in the combobox by name @@ -131,12 +132,15 @@ public: // Get name of current item. Returns an empty string if not found. const std::string getSimple() const; // Get contents of column x of selected row - const std::string getSelectedItemLabel(S32 column = 0) const; + virtual const std::string getSelectedItemLabel(S32 column = 0) const; // Sets the label, which doesn't have to exist in the label. // This is probably a UI abuse. void setLabel(const LLStringExplicit& name); + // Updates the combobox label to match the selected list item. + void updateLabel(); + BOOL remove(const std::string& name); // remove item "name", return TRUE if found and removed BOOL setCurrentByIndex( S32 index ); @@ -185,7 +189,8 @@ public: void setButtonVisible(BOOL visible); void onButtonMouseDown(); - void onItemSelected(); + void onListMouseUp(); + void onItemSelected(const LLSD& data); void onTextCommit(const LLSD& data); @@ -205,10 +210,12 @@ protected: EPreferredPosition mListPosition; LLPointer mArrowImage; std::string mLabel; + BOOL mHasAutocompletedText; LLColor4 mListColor; private: BOOL mAllowTextEntry; + BOOL mAllowNewValues; S32 mMaxChars; BOOL mTextEntryTentative; bool mSuppressAutoComplete; @@ -216,6 +223,7 @@ private: commit_callback_t mPrearrangeCallback; commit_callback_t mTextEntryCallback; boost::signals2::connection mTopLostSignalConnection; + S32 mLastSelectedIndex; }; class LLFlyoutButton : public LLComboBox diff --git a/indra/newview/hippogridmanager.cpp b/indra/newview/hippogridmanager.cpp index d7c02be7f..5fb8195f7 100644 --- a/indra/newview/hippogridmanager.cpp +++ b/indra/newview/hippogridmanager.cpp @@ -646,6 +646,8 @@ HippoGridManager::HippoGridManager() : HippoGridManager::~HippoGridManager() { cleanup(); + if(mCurrentGridChangeSignal) + delete mCurrentGridChangeSignal; } @@ -805,6 +807,7 @@ void HippoGridManager::setDefaultGrid(const std::string& grid) void HippoGridManager::setCurrentGrid(const std::string& grid) { + HippoGridInfo* prevGrid = getGrid(mCurrentGrid); GridIterator it = mGridInfo.find(grid); if (it != mGridInfo.end()) { @@ -815,6 +818,8 @@ void HippoGridManager::setCurrentGrid(const std::string& grid) llwarns << "Unknown grid '" << grid << "'. Setting to default grid." << llendl; mCurrentGrid = mDefaultGrid; } + if(mCurrentGridChangeSignal) + (*mCurrentGridChangeSignal)(getGrid(mCurrentGrid),prevGrid); } diff --git a/indra/newview/hippogridmanager.h b/indra/newview/hippogridmanager.h index e5600dfa9..605a02e0d 100644 --- a/indra/newview/hippogridmanager.h +++ b/indra/newview/hippogridmanager.h @@ -15,6 +15,8 @@ #include "expat/expat.h" #endif +#include + class LLSD; @@ -152,6 +154,8 @@ private: class HippoGridManager { public: + typedef boost::signals2::signal current_grid_change_signal_t; + HippoGridManager(); ~HippoGridManager(); @@ -179,6 +183,13 @@ public: GridIterator beginGrid() { return mGridInfo.begin(); } GridIterator endGrid() { return mGridInfo.end(); } + boost::signals2::connection setCurrentGridChangeCallback( const current_grid_change_signal_t::slot_type& cb ) + { + if(!mCurrentGridChangeSignal) + mCurrentGridChangeSignal = new current_grid_change_signal_t; + return mCurrentGridChangeSignal->connect(cb); + } + private: friend class HippoGridInfo; std::map mGridInfo; @@ -187,6 +198,8 @@ private: HippoGridInfo* mConnectedGrid; int mDefaultGridsVersion; + current_grid_change_signal_t* mCurrentGridChangeSignal; + void cleanup(); void loadFromFile(); void parseFile(const std::string& fileName, bool mergeIfNewer); diff --git a/indra/newview/hippopanelgrids.cpp b/indra/newview/hippopanelgrids.cpp index c5fe98de2..b0239fead 100644 --- a/indra/newview/hippopanelgrids.cpp +++ b/indra/newview/hippopanelgrids.cpp @@ -227,7 +227,6 @@ void HippoPanelGridsImpl::apply() // adding new grid did not fail gHippoGridManager->setCurrentGrid(mCurGrid); } - LLPanelLogin::refreshLoginPage(); gHippoGridManager->saveFile(); refresh(); // update render compatibility diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 29a339eca..efbd2b5fe 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -2332,7 +2332,6 @@ bool LLAppViewer::initConfiguration() if(LLStartUp::getStartSLURL().getType() == LLSLURL::LOCATION) { gHippoGridManager->setCurrentGrid(LLStartUp::getStartSLURL().getGrid()); - } } else if(clp.hasOption("slurl")) diff --git a/indra/newview/llpanellogin.cpp b/indra/newview/llpanellogin.cpp index 79d4c56d3..5e818d880 100644 --- a/indra/newview/llpanellogin.cpp +++ b/indra/newview/llpanellogin.cpp @@ -278,9 +278,8 @@ LLPanelLogin::LLPanelLogin(const LLRect &rect, { LL_INFOS("AppInit")<<"no valid LoginLocation, using home"<setCurrentGridChangeCallback(boost::bind(&LLPanelLogin::onCurGridChange,this,_1,_2)); } void LLPanelLogin::setSiteIsAlive( bool alive ) @@ -631,7 +630,6 @@ void LLPanelLogin::setFields(const LLSavedLoginEntry& entry, bool takeFocus) if(!grid.empty() && gHippoGridManager->getGrid(grid) && grid != gHippoGridManager->getCurrentGridName()) { gHippoGridManager->setCurrentGrid(grid); - LLPanelLogin::refreshLoginPage(); } if (entry.getPassword().empty()) @@ -702,13 +700,10 @@ void LLPanelLogin::updateLocationSelectorsVisibility() #endif // RLV_EXTENSION_STARTLOCATION // [/RLVa:KB] - sInstance->getChild("start_location_combo")->setVisible(show_start); // maintain ShowStartLocation if legacy - sInstance->getChild("start_location_text")->setVisible(show_start); + sInstance->getChildView("location_panel")->setVisible(show_start); - bool show_server = true; - sInstance->getChild("grids_combo")->setVisible(show_server); - sInstance->getChild("grids_text")->setVisible(show_server); - sInstance->getChild("grids_btn")->setVisible(show_server); + bool show_server = true; + sInstance->getChildView("grids_panel")->setVisible(show_server); } } @@ -1030,6 +1025,17 @@ void LLPanelLogin::onPassKey(LLLineEditor* caller) } } +void LLPanelLogin::onCurGridChange(HippoGridInfo* new_grid, HippoGridInfo* old_grid) +{ + refreshLoginPage(); + if(old_grid != new_grid) //Changed grid? Reset the location combobox + { + std::string defaultStartLocation = gSavedSettings.getString("LoginLocation"); + LLSLURL defaultStart(defaultStartLocation); + LLStartUp::setStartSLURL(defaultStart.isSpatial() ? defaultStart : LLSLURL(LLSLURL::SIM_LOCATION_HOME)); // calls onUpdateStartSLURL + } +} + // static //void LLPanelLogin::updateServer() void LLPanelLogin::refreshLoginPage() @@ -1074,7 +1080,6 @@ void LLPanelLogin::refreshLoginPage() void LLPanelLogin::onSelectGrid(LLUICtrl *ctrl) { gHippoGridManager->setCurrentGrid(ctrl->getValue()); - LLPanelLogin::refreshLoginPage(); } void LLPanelLogin::onLocationSLURL() diff --git a/indra/newview/llpanellogin.h b/indra/newview/llpanellogin.h index 5041871ed..2158105e0 100644 --- a/indra/newview/llpanellogin.h +++ b/indra/newview/llpanellogin.h @@ -41,6 +41,7 @@ class LLUIImage; class LLComboBox; +class HippoGridInfo; class LLPanelLogin: public LLPanel, @@ -92,6 +93,7 @@ public: void updateGridCombo(); + void onCurGridChange(HippoGridInfo* new_grid, HippoGridInfo* old_grid); static void loadLoginPage(); static void refreshLoginPage(); static void giveFocus(); diff --git a/indra/newview/skins/default/xui/en-us/panel_login.xml b/indra/newview/skins/default/xui/en-us/panel_login.xml index 917653980..d8879f6d2 100644 --- a/indra/newview/skins/default/xui/en-us/panel_login.xml +++ b/indra/newview/skins/default/xui/en-us/panel_login.xml @@ -13,72 +13,81 @@ http://secondlife.com/account/request.php - + + - Name or Username: - - - - Password: - - - + Name or Username: + + + - - - + + + - Grid: - - - -