diff options
author | Andrey Lihatskiy <alihatskiy@productengine.com> | 2024-05-22 20:51:58 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-05-22 20:51:58 +0300 |
commit | 6cc7dd09d5e69cf57e6de7fb568a0ad2693f9c9a (patch) | |
tree | fab23811a5cedc1ebf01479c852ee92ff62b636c /indra/newview/lllocationinputctrl.cpp | |
parent | ef8f4819822288e044ea719feb6af7a1f4df4c4e (diff) | |
parent | 7bb5afc11ee5a6af78302a8d76a9a619e2baaab2 (diff) |
Merge pull request #1545 from Ansariel/DRTVWR-600-maint-A
Merge main into DRTVWR-600-maint-a
Diffstat (limited to 'indra/newview/lllocationinputctrl.cpp')
-rw-r--r-- | indra/newview/lllocationinputctrl.cpp | 2570 |
1 files changed, 1290 insertions, 1280 deletions
diff --git a/indra/newview/lllocationinputctrl.cpp b/indra/newview/lllocationinputctrl.cpp index a64ee6857d..39e578eb80 100644 --- a/indra/newview/lllocationinputctrl.cpp +++ b/indra/newview/lllocationinputctrl.cpp @@ -1,1280 +1,1290 @@ -/** - * @file lllocationinputctrl.cpp - * @brief Combobox-like location input control - * - * $LicenseInfo:firstyear=2009&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#include "llviewerprecompiledheaders.h" - -// file includes -#include "lllocationinputctrl.h" - -// common includes -#include "llbutton.h" -#include "llfocusmgr.h" -#include "llhelp.h" -#include "llmenugl.h" -#include "llparcel.h" -#include "llstring.h" -#include "lltrans.h" -#include "lluictrlfactory.h" -#include "lltooltip.h" -#include "llnotificationsutil.h" -#include "llregionflags.h" - -// newview includes -#include "llagent.h" -#include "llfloaterreg.h" -#include "llfloatersidepanelcontainer.h" -#include "llinventoryobserver.h" -#include "lllandmarkactions.h" -#include "lllandmarklist.h" -#include "llpathfindingmanager.h" -#include "llpathfindingnavmesh.h" -#include "llpathfindingnavmeshstatus.h" -#include "llteleporthistory.h" -#include "llslurl.h" -#include "llstatusbar.h" // getHealth() -#include "lltrans.h" -#include "llviewerinventory.h" -#include "llviewerparcelmgr.h" -#include "llviewerregion.h" -#include "llviewercontrol.h" -#include "llviewermenu.h" -#include "llurllineeditorctrl.h" -#include "llagentui.h" - -#include "llmenuoptionpathfindingrebakenavmesh.h" -#include "llpathfindingmanager.h" - -//============================================================================ -/* - * "ADD LANDMARK" BUTTON UPDATING LOGIC - * - * If the current parcel has been landmarked, we should draw - * a special image on the button. - * - * To avoid determining the appropriate image on every draw() we do that - * only in the following cases: - * 1) Navbar is shown for the first time after login. - * 2) Agent moves to another parcel. - * 3) A landmark is created or removed. - * - * The first case is handled by the handleLoginComplete() method. - * - * The second case is handled by setting the "agent parcel changed" callback - * on LLViewerParcelMgr. - * - * The third case is the most complex one. We have two inventory observers for that: - * one is designated to handle adding landmarks, the other handles removal. - * Let's see how the former works. - * - * When we get notified about landmark addition, the landmark position is unknown yet. What we can - * do at that point is initiate loading the landmark data by LLLandmarkList and set the - * "loading finished" callback on it. Finally, when the callback is triggered, - * we can determine whether the landmark refers to a point within the current parcel - * and choose the appropriate image for the "Add landmark" button. - */ - -/** - * Initiates loading the landmarks that have been just added. - * - * Once the loading is complete we'll be notified - * with the callback we set for LLLandmarkList. - */ -class LLAddLandmarkObserver : public LLInventoryAddedObserver -{ -public: - LLAddLandmarkObserver(LLLocationInputCtrl* input) : mInput(input) {} - -private: - /*virtual*/ void done() - { - const uuid_set_t& added = gInventory.getAddedIDs(); - for (uuid_set_t::const_iterator it = added.begin(); it != added.end(); ++it) - { - LLInventoryItem* item = gInventory.getItem(*it); - if (!item || item->getType() != LLAssetType::AT_LANDMARK) - continue; - - // Start loading the landmark. - LLLandmark* lm = gLandmarkList.getAsset( - item->getAssetUUID(), - boost::bind(&LLLocationInputCtrl::onLandmarkLoaded, mInput, _1)); - if (lm) - { - // Already loaded? Great, handle it immediately (the callback won't be called). - mInput->onLandmarkLoaded(lm); - } - } - } - - LLLocationInputCtrl* mInput; -}; - -/** - * Updates the "Add landmark" button once a landmark gets removed. - */ -class LLRemoveLandmarkObserver : public LLInventoryObserver -{ -public: - LLRemoveLandmarkObserver(LLLocationInputCtrl* input) : mInput(input) {} - -private: - /*virtual*/ void changed(U32 mask) - { - if (mask & (~(LLInventoryObserver::LABEL| - LLInventoryObserver::INTERNAL| - LLInventoryObserver::ADD| - LLInventoryObserver::CREATE| - LLInventoryObserver::UPDATE_CREATE))) - { - mInput->updateAddLandmarkButton(); - } - } - - LLLocationInputCtrl* mInput; -}; - -class LLParcelChangeObserver : public LLParcelObserver -{ -public: - LLParcelChangeObserver(LLLocationInputCtrl* input) : mInput(input) {} - -private: - /*virtual*/ void changed() - { - if (mInput) - { - mInput->refreshParcelIcons(); - } - } - - LLLocationInputCtrl* mInput; -}; - -//============================================================================ - - -static LLDefaultChildRegistry::Register<LLLocationInputCtrl> r("location_input"); - -LLLocationInputCtrl::Params::Params() -: icon_maturity_general("icon_maturity_general"), - icon_maturity_adult("icon_maturity_adult"), - icon_maturity_moderate("icon_maturity_moderate"), - add_landmark_image_enabled("add_landmark_image_enabled"), - add_landmark_image_disabled("add_landmark_image_disabled"), - add_landmark_image_hover("add_landmark_image_hover"), - add_landmark_image_selected("add_landmark_image_selected"), - add_landmark_hpad("add_landmark_hpad", 0), - icon_hpad("icon_hpad", 0), - add_landmark_button("add_landmark_button"), - for_sale_button("for_sale_button"), - info_button("info_button"), - maturity_button("maturity_button"), - voice_icon("voice_icon"), - fly_icon("fly_icon"), - push_icon("push_icon"), - build_icon("build_icon"), - scripts_icon("scripts_icon"), - damage_icon("damage_icon"), - damage_text("damage_text"), - see_avatars_icon("see_avatars_icon"), - maturity_help_topic("maturity_help_topic"), - pathfinding_dirty_icon("pathfinding_dirty_icon"), - pathfinding_disabled_icon("pathfinding_disabled_icon") -{ -} - -LLLocationInputCtrl::LLLocationInputCtrl(const LLLocationInputCtrl::Params& p) -: LLComboBox(p), - mIconHPad(p.icon_hpad), - mAddLandmarkHPad(p.add_landmark_hpad), - mLocationContextMenu(NULL), - mAddLandmarkBtn(NULL), - mForSaleBtn(NULL), - mInfoBtn(NULL), - mRegionCrossingSlot(), - mNavMeshSlot(), - mIsNavMeshDirty(false), - mLandmarkImageOn(NULL), - mLandmarkImageOff(NULL), - mIconMaturityGeneral(NULL), - mIconMaturityAdult(NULL), - mIconMaturityModerate(NULL), - mMaturityHelpTopic(p.maturity_help_topic) -{ - // Lets replace default LLLineEditor with LLLocationLineEditor - // to make needed escaping while copying and cutting url - delete mTextEntry; - - // Can't access old mTextEntry fields as they are protected, so lets build new params - // That is C&P from LLComboBox::createLineEditor function - static LLUICachedControl<S32> drop_shadow_button ("DropShadowButton", 0); - S32 arrow_width = mArrowImage ? mArrowImage->getWidth() : 0; - LLRect text_entry_rect(0, getRect().getHeight(), getRect().getWidth(), 0); - text_entry_rect.mRight -= llmax(8,arrow_width) + 2 * drop_shadow_button; - - LLLineEditor::Params params = p.combo_editor; - params.rect(text_entry_rect); - params.default_text(LLStringUtil::null); - params.max_length.bytes(p.max_chars); - params.keystroke_callback(boost::bind(&LLLocationInputCtrl::onTextEntry, this, _1)); - params.commit_on_focus_lost(false); - params.follows.flags(FOLLOWS_ALL); - mTextEntry = LLUICtrlFactory::create<LLURLLineEditor>(params); - mTextEntry->resetContextMenu(); - addChild(mTextEntry); - // LLLineEditor is replaced with LLLocationLineEditor - - // "Place information" button. - LLButton::Params info_params = p.info_button; - mInfoBtn = LLUICtrlFactory::create<LLButton>(info_params); - mInfoBtn->setClickedCallback(boost::bind(&LLLocationInputCtrl::onInfoButtonClicked, this)); - addChild(mInfoBtn); - - // "Add landmark" button. - LLButton::Params al_params = p.add_landmark_button; - - // Image for unselected state will be set in updateAddLandmarkButton(), - // it will be either mLandmarkOn or mLandmarkOff - if (p.add_landmark_image_enabled()) - { - mLandmarkImageOn = p.add_landmark_image_enabled; - } - if (p.add_landmark_image_disabled()) - { - mLandmarkImageOff = p.add_landmark_image_disabled; - } - - if(p.add_landmark_image_selected) - { - al_params.image_selected = p.add_landmark_image_selected; - } - if (p.add_landmark_image_hover()) - { - al_params.image_hover_unselected = p.add_landmark_image_hover; - } - - al_params.click_callback.function(boost::bind(&LLLocationInputCtrl::onAddLandmarkButtonClicked, this)); - mAddLandmarkBtn = LLUICtrlFactory::create<LLButton>(al_params); - enableAddLandmarkButton(true); - addChild(mAddLandmarkBtn); - - if (p.icon_maturity_general()) - { - mIconMaturityGeneral = p.icon_maturity_general; - } - if (p.icon_maturity_adult()) - { - mIconMaturityAdult = p.icon_maturity_adult; - } - if(p.icon_maturity_moderate()) - { - mIconMaturityModerate = p.icon_maturity_moderate; - } - - LLButton::Params maturity_button = p.maturity_button; - mMaturityButton = LLUICtrlFactory::create<LLButton>(maturity_button); - addChild(mMaturityButton); - - LLButton::Params for_sale_button = p.for_sale_button; - for_sale_button.tool_tip = LLTrans::getString("LocationCtrlForSaleTooltip"); - for_sale_button.click_callback.function( - boost::bind(&LLLocationInputCtrl::onForSaleButtonClicked, this)); - mForSaleBtn = LLUICtrlFactory::create<LLButton>( for_sale_button ); - addChild(mForSaleBtn); - - // Parcel property icons - // Must be mouse-opaque so cursor stays as an arrow when hovering to - // see tooltip. - LLIconCtrl::Params voice_icon = p.voice_icon; - voice_icon.tool_tip = LLTrans::getString("LocationCtrlVoiceTooltip"); - voice_icon.mouse_opaque = true; - mParcelIcon[VOICE_ICON] = LLUICtrlFactory::create<LLIconCtrl>(voice_icon); - mParcelIcon[VOICE_ICON]->setMouseDownCallback(boost::bind(&LLLocationInputCtrl::onParcelIconClick, this, VOICE_ICON)); - addChild(mParcelIcon[VOICE_ICON]); - - LLIconCtrl::Params fly_icon = p.fly_icon; - fly_icon.tool_tip = LLTrans::getString("LocationCtrlFlyTooltip"); - fly_icon.mouse_opaque = true; - mParcelIcon[FLY_ICON] = LLUICtrlFactory::create<LLIconCtrl>(fly_icon); - mParcelIcon[FLY_ICON]->setMouseDownCallback(boost::bind(&LLLocationInputCtrl::onParcelIconClick, this, FLY_ICON)); - addChild(mParcelIcon[FLY_ICON]); - - LLIconCtrl::Params push_icon = p.push_icon; - push_icon.tool_tip = LLTrans::getString("LocationCtrlPushTooltip"); - push_icon.mouse_opaque = true; - mParcelIcon[PUSH_ICON] = LLUICtrlFactory::create<LLIconCtrl>(push_icon); - mParcelIcon[PUSH_ICON]->setMouseDownCallback(boost::bind(&LLLocationInputCtrl::onParcelIconClick, this, PUSH_ICON)); - addChild(mParcelIcon[PUSH_ICON]); - - LLIconCtrl::Params build_icon = p.build_icon; - build_icon.tool_tip = LLTrans::getString("LocationCtrlBuildTooltip"); - build_icon.mouse_opaque = true; - mParcelIcon[BUILD_ICON] = LLUICtrlFactory::create<LLIconCtrl>(build_icon); - mParcelIcon[BUILD_ICON]->setMouseDownCallback(boost::bind(&LLLocationInputCtrl::onParcelIconClick, this, BUILD_ICON)); - addChild(mParcelIcon[BUILD_ICON]); - - LLIconCtrl::Params scripts_icon = p.scripts_icon; - scripts_icon.tool_tip = LLTrans::getString("LocationCtrlScriptsTooltip"); - scripts_icon.mouse_opaque = true; - mParcelIcon[SCRIPTS_ICON] = LLUICtrlFactory::create<LLIconCtrl>(scripts_icon); - mParcelIcon[SCRIPTS_ICON]->setMouseDownCallback(boost::bind(&LLLocationInputCtrl::onParcelIconClick, this, SCRIPTS_ICON)); - addChild(mParcelIcon[SCRIPTS_ICON]); - - LLIconCtrl::Params damage_icon = p.damage_icon; - damage_icon.tool_tip = LLTrans::getString("LocationCtrlDamageTooltip"); - damage_icon.mouse_opaque = true; - mParcelIcon[DAMAGE_ICON] = LLUICtrlFactory::create<LLIconCtrl>(damage_icon); - mParcelIcon[DAMAGE_ICON]->setMouseDownCallback(boost::bind(&LLLocationInputCtrl::onParcelIconClick, this, DAMAGE_ICON)); - addChild(mParcelIcon[DAMAGE_ICON]); - - LLIconCtrl::Params pathfinding_dirty_icon = p.pathfinding_dirty_icon; - pathfinding_dirty_icon.tool_tip = LLTrans::getString("LocationCtrlPathfindingDirtyTooltip"); - pathfinding_dirty_icon.mouse_opaque = true; - mParcelIcon[PATHFINDING_DIRTY_ICON] = LLUICtrlFactory::create<LLIconCtrl>(pathfinding_dirty_icon); - mParcelIcon[PATHFINDING_DIRTY_ICON]->setMouseDownCallback(boost::bind(&LLLocationInputCtrl::onParcelIconClick, this, PATHFINDING_DIRTY_ICON)); - addChild(mParcelIcon[PATHFINDING_DIRTY_ICON]); - - LLIconCtrl::Params pathfinding_disabled_icon = p.pathfinding_disabled_icon; - pathfinding_disabled_icon.tool_tip = LLTrans::getString("LocationCtrlPathfindingDisabledTooltip"); - pathfinding_disabled_icon.mouse_opaque = true; - mParcelIcon[PATHFINDING_DISABLED_ICON] = LLUICtrlFactory::create<LLIconCtrl>(pathfinding_disabled_icon); - mParcelIcon[PATHFINDING_DISABLED_ICON]->setMouseDownCallback(boost::bind(&LLLocationInputCtrl::onParcelIconClick, this, PATHFINDING_DISABLED_ICON)); - addChild(mParcelIcon[PATHFINDING_DISABLED_ICON]); - - LLTextBox::Params damage_text = p.damage_text; - damage_text.tool_tip = LLTrans::getString("LocationCtrlDamageTooltip"); - damage_text.mouse_opaque = true; - mDamageText = LLUICtrlFactory::create<LLTextBox>(damage_text); - addChild(mDamageText); - - LLIconCtrl::Params see_avatars_icon = p.see_avatars_icon; - see_avatars_icon.tool_tip = LLTrans::getString("LocationCtrlSeeAVsTooltip"); - see_avatars_icon.mouse_opaque = true; - mParcelIcon[SEE_AVATARS_ICON] = LLUICtrlFactory::create<LLIconCtrl>(see_avatars_icon); - mParcelIcon[SEE_AVATARS_ICON]->setMouseDownCallback(boost::bind(&LLLocationInputCtrl::onParcelIconClick, this, SEE_AVATARS_ICON)); - addChild(mParcelIcon[SEE_AVATARS_ICON]); - - // Register callbacks and load the location field context menu (NB: the order matters). - LLUICtrl::CommitCallbackRegistry::currentRegistrar().add("Navbar.Action", boost::bind(&LLLocationInputCtrl::onLocationContextMenuItemClicked, this, _2)); - LLUICtrl::EnableCallbackRegistry::currentRegistrar().add("Navbar.EnableMenuItem", boost::bind(&LLLocationInputCtrl::onLocationContextMenuItemEnabled, this, _2)); - - setPrearrangeCallback(boost::bind(&LLLocationInputCtrl::onLocationPrearrange, this, _2)); - getTextEntry()->setMouseUpCallback(boost::bind(&LLLocationInputCtrl::changeLocationPresentation, this)); - - // Load the location field context menu - mLocationContextMenu = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>("menu_navbar.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); - if (!mLocationContextMenu) - { - LL_WARNS() << "Error loading navigation bar context menu" << LL_ENDL; - - } - //don't show default context menu - getTextEntry()->setShowContextMenu(false); - getTextEntry()->setRightMouseDownCallback(boost::bind(&LLLocationInputCtrl::onTextEditorRightClicked, this, _2, _3, _4)); - updateWidgetlayout(); - - // Connecting signal for updating location on "Show Coordinates" setting change. - LLControlVariable* coordinates_control = gSavedSettings.getControl("NavBarShowCoordinates").get(); - if (coordinates_control) - { - mCoordinatesControlConnection = coordinates_control->getSignal()->connect(boost::bind(&LLLocationInputCtrl::refreshLocation, this)); - } - - // Connecting signal for updating parcel icons on "Show Parcel Properties" setting change. - LLControlVariable* parcel_properties_control = gSavedSettings.getControl("NavBarShowParcelProperties").get(); - if (parcel_properties_control) - { - mParcelPropertiesControlConnection = parcel_properties_control->getSignal()->connect(boost::bind(&LLLocationInputCtrl::refreshParcelIcons, this)); - } - - // - Make the "Add landmark" button updated when either current parcel gets changed - // or a landmark gets created or removed from the inventory. - // - Update the location string on parcel change. - mParcelMgrConnection = gAgent.addParcelChangedCallback( - boost::bind(&LLLocationInputCtrl::onAgentParcelChange, this)); - // LLLocationHistory instance is being created before the location input control, so we have to update initial state of button manually. - mButton->setEnabled(LLLocationHistory::instance().getItemCount() > 0); - mLocationHistoryConnection = LLLocationHistory::getInstance()->setChangedCallback( - boost::bind(&LLLocationInputCtrl::onLocationHistoryChanged, this,_1)); - - mRegionCrossingSlot = gAgent.addRegionChangedCallback(boost::bind(&LLLocationInputCtrl::onRegionBoundaryCrossed, this)); - createNavMeshStatusListenerForCurrentRegion(); - - mRemoveLandmarkObserver = new LLRemoveLandmarkObserver(this); - mAddLandmarkObserver = new LLAddLandmarkObserver(this); - gInventory.addObserver(mRemoveLandmarkObserver); - gInventory.addObserver(mAddLandmarkObserver); - - mParcelChangeObserver = new LLParcelChangeObserver(this); - LLViewerParcelMgr::getInstance()->addObserver(mParcelChangeObserver); - - mAddLandmarkTooltip = LLTrans::getString("LocationCtrlAddLandmarkTooltip"); - mEditLandmarkTooltip = LLTrans::getString("LocationCtrlEditLandmarkTooltip"); - mButton->setToolTip(LLTrans::getString("LocationCtrlComboBtnTooltip")); - mInfoBtn->setToolTip(LLTrans::getString("LocationCtrlInfoBtnTooltip")); -} - -LLLocationInputCtrl::~LLLocationInputCtrl() -{ - gInventory.removeObserver(mRemoveLandmarkObserver); - gInventory.removeObserver(mAddLandmarkObserver); - delete mRemoveLandmarkObserver; - delete mAddLandmarkObserver; - - LLViewerParcelMgr::getInstance()->removeObserver(mParcelChangeObserver); - delete mParcelChangeObserver; - - mRegionCrossingSlot.disconnect(); - mNavMeshSlot.disconnect(); - mCoordinatesControlConnection.disconnect(); - mParcelPropertiesControlConnection.disconnect(); - mParcelMgrConnection.disconnect(); - mLocationHistoryConnection.disconnect(); -} - -void LLLocationInputCtrl::setEnabled(bool enabled) -{ - LLComboBox::setEnabled(enabled); - mAddLandmarkBtn->setEnabled(enabled); -} - -void LLLocationInputCtrl::hideList() -{ - LLComboBox::hideList(); - if (mTextEntry && hasFocus()) - focusTextEntry(); -} - -bool LLLocationInputCtrl::handleToolTip(S32 x, S32 y, MASK mask) -{ - - if(mAddLandmarkBtn->parentPointInView(x,y)) - { - updateAddLandmarkTooltip(); - } - // Let the buttons show their tooltips. - if (LLUICtrl::handleToolTip(x, y, mask)) - { - if (mList->getRect().pointInRect(x, y)) - { - S32 loc_x, loc_y; - //x,y - contain coordinates related to the location input control, but without taking the expanded list into account - //So we have to convert it again into local coordinates of mList - localPointToOtherView(x,y,&loc_x,&loc_y,mList); - - LLScrollListItem* item = mList->hitItem(loc_x,loc_y); - if (item) - { - LLSD value = item->getValue(); - if (value.has("tooltip")) - { - LLToolTipMgr::instance().show(value["tooltip"]); - } - } - } - - return true; - } - - return false; -} - -bool LLLocationInputCtrl::handleKeyHere(KEY key, MASK mask) -{ - bool result = LLComboBox::handleKeyHere(key, mask); - - if (key == KEY_DOWN && hasFocus() && mList->getItemCount() != 0 && !mList->getVisible()) - { - showList(); - } - - return result; -} - -void LLLocationInputCtrl::onTextEntry(LLLineEditor* line_editor) -{ - KEY key = gKeyboard->currentKey(); - MASK mask = gKeyboard->currentMask(true); - - // Typing? (moving cursor should not affect showing the list) - bool typing = mask != MASK_CONTROL && key != KEY_LEFT && key != KEY_RIGHT && key != KEY_HOME && key != KEY_END; - bool pasting = mask == MASK_CONTROL && key == 'V'; - - if (line_editor->getText().empty()) - { - prearrangeList(); // resets filter - hideList(); - } - else if (typing || pasting) - { - prearrangeList(line_editor->getText()); - if (mList->getItemCount() != 0) - { - showList(); - focusTextEntry(); - } - else - { - // Hide the list if it's empty. - hideList(); - } - } - - LLComboBox::onTextEntry(line_editor); -} - -/** - * 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); - } - mHasAutocompletedText = false; -} - -void LLLocationInputCtrl::setFocus(bool b) -{ - LLComboBox::setFocus(b); - - if (mTextEntry && b && !mList->getVisible()) - { - mTextEntry->setFocus(true); - } -} - -void LLLocationInputCtrl::handleLoginComplete() -{ - // An agent parcel update hasn't occurred yet, so we have to - // manually set location and the appropriate "Add landmark" icon. - refresh(); -} - -//== private methods ========================================================= - -void LLLocationInputCtrl::onFocusReceived() -{ - prearrangeList(); -} - -void LLLocationInputCtrl::onFocusLost() -{ - LLUICtrl::onFocusLost(); - refreshLocation(); - - // Setting cursor to 0 to show the left edge of the text. See STORM-370. - mTextEntry->setCursor(0); - - if(mTextEntry->hasSelection()){ - mTextEntry->deselect(); - } -} - -void LLLocationInputCtrl::draw() -{ - static LLUICachedControl<bool> show_coords("NavBarShowCoordinates", false); - if(!hasFocus() && show_coords) - { - refreshLocation(); - } - - static LLUICachedControl<bool> show_icons("NavBarShowParcelProperties", false); - if (show_icons) - { - refreshHealth(); - } - LLComboBox::draw(); -} - -void LLLocationInputCtrl::reshape(S32 width, S32 height, bool called_from_parent) -{ - LLComboBox::reshape(width, height, called_from_parent); - - // Setting cursor to 0 to show the left edge of the text. See EXT-4967. - mTextEntry->setCursor(0); - if (mTextEntry->hasSelection()) - { - // Deselecting because selection position is changed together with - // cursor position change. - mTextEntry->deselect(); - } - - if (isHumanReadableLocationVisible) - { - refreshMaturityButton(); - } -} - -void LLLocationInputCtrl::onInfoButtonClicked() -{ - LLFloaterSidePanelContainer::showPanel("places", LLSD().with("type", "agent")); -} - -void LLLocationInputCtrl::onForSaleButtonClicked() -{ - handle_buy_land(); -} - -void LLLocationInputCtrl::onAddLandmarkButtonClicked() -{ - LLViewerInventoryItem* landmark = LLLandmarkActions::findLandmarkForAgentPos(); - // Landmark exists, open it for preview and edit - if(landmark && landmark->getUUID().notNull()) - { - LLSD key; - key["type"] = "landmark"; - key["id"] = landmark->getUUID(); - - LLFloaterSidePanelContainer::showPanel("places", key); - } - else - { - LLFloaterReg::showInstance("add_landmark"); - } -} - -void LLLocationInputCtrl::onAgentParcelChange() -{ - refresh(); -} - -void LLLocationInputCtrl::onRegionBoundaryCrossed() -{ - createNavMeshStatusListenerForCurrentRegion(); -} - -void LLLocationInputCtrl::onNavMeshStatusChange(const LLPathfindingNavMeshStatus &pNavMeshStatus) -{ - mIsNavMeshDirty = pNavMeshStatus.isValid() && (pNavMeshStatus.getStatus() != LLPathfindingNavMeshStatus::kComplete); - refreshParcelIcons(); -} - -void LLLocationInputCtrl::onLandmarkLoaded(LLLandmark* lm) -{ - (void) lm; - updateAddLandmarkButton(); -} - -void LLLocationInputCtrl::onLocationHistoryChanged(LLLocationHistory::EChangeType event) -{ - if(event == LLLocationHistory::LOAD) - { - rebuildLocationHistory(); - } - mButton->setEnabled(LLLocationHistory::instance().getItemCount() > 0); -} - -void LLLocationInputCtrl::onLocationPrearrange(const LLSD& data) -{ - std::string filter = data.asString(); - rebuildLocationHistory(filter); - - //Let's add landmarks to the top of the list if any - if(!filter.empty() ) - { - LLInventoryModel::item_array_t landmark_items = LLLandmarkActions::fetchLandmarksByName(filter, true); - - for(U32 i=0; i < landmark_items.size(); i++) - { - LLSD value; - //TODO:: DO we need tooltip for Landmark?? - - value["item_type"] = LANDMARK; - value["AssetUUID"] = landmark_items[i]->getAssetUUID(); - add(landmark_items[i]->getName(), value); - - } - //Let's add teleport history items - LLTeleportHistory* th = LLTeleportHistory::getInstance(); - LLTeleportHistory::slurl_list_t th_items = th->getItems(); - - std::set<std::string> new_item_titles;// duplicate control - LLTeleportHistory::slurl_list_t::iterator result = std::find_if( - th_items.begin(), th_items.end(), boost::bind( - &LLLocationInputCtrl::findTeleportItemsByTitle, this, - _1, filter)); - - while (result != th_items.end()) - { - //mTitile format - region_name[, parcel_name] - //mFullTitile format - region_name[, parcel_name] (local_x,local_y, local_z) - if (new_item_titles.insert(result->mFullTitle).second) - { - LLSD value; - value["item_type"] = TELEPORT_HISTORY; - value["global_pos"] = result->mGlobalPos.getValue(); - std::string region_name = result->mTitle.substr(0, result->mTitle.find(',')); - //TODO*: add Surl to teleportitem or parse region name from title - value["tooltip"] = LLSLURL(region_name, result->mGlobalPos).getSLURLString(); - add(result->getTitle(), value); - } - result = std::find_if(result + 1, th_items.end(), boost::bind( - &LLLocationInputCtrl::findTeleportItemsByTitle, this, - _1, filter)); - } - } - sortByName(); - - mList->mouseOverHighlightNthItem(-1); // Clear highlight on the last selected item. -} - -bool LLLocationInputCtrl::findTeleportItemsByTitle(const LLTeleportHistoryItem& item, const std::string& filter) -{ - return item.mTitle.find(filter) != std::string::npos; -} - -void LLLocationInputCtrl::onTextEditorRightClicked(S32 x, S32 y, MASK mask) -{ - if (mLocationContextMenu) - { - updateContextMenu(); - mLocationContextMenu->buildDrawLabels(); - mLocationContextMenu->updateParent(LLMenuGL::sMenuContainer); - hideList(); - setFocus(true); - changeLocationPresentation(); - LLMenuGL::showPopup(this, mLocationContextMenu, x, y); - } -} - -void LLLocationInputCtrl::refresh() -{ - refreshLocation(); // update location string - refreshParcelIcons(); - updateAddLandmarkButton(); // indicate whether current parcel has been landmarked -} - -void LLLocationInputCtrl::refreshLocation() -{ - // Is one of our children focused? - if (LLUICtrl::hasFocus() || mButton->hasFocus() || mList->hasFocus() || - (mTextEntry && mTextEntry->hasFocus()) || - (mAddLandmarkBtn->hasFocus())) - { - LL_WARNS() << "Location input should not be refreshed when having focus" << LL_ENDL; - return; - } - - // Update location field. - std::string location_name; - LLAgentUI::ELocationFormat format = - (gSavedSettings.getBOOL("NavBarShowCoordinates") - ? LLAgentUI::LOCATION_FORMAT_FULL - : LLAgentUI::LOCATION_FORMAT_NO_COORDS); - - if (!LLAgentUI::buildLocationString(location_name, format)) - { - location_name = "???"; - } - // store human-readable location to compare it in changeLocationPresentation() - mHumanReadableLocation = location_name; - setText(location_name); - isHumanReadableLocationVisible = true; - - refreshMaturityButton(); -} - -// returns new right edge -static S32 layout_widget(LLUICtrl* widget, S32 right) -{ - if (widget->getVisible()) - { - LLRect rect = widget->getRect(); - rect.mLeft = right - rect.getWidth(); - rect.mRight = right; - widget->setRect( rect ); - right -= rect.getWidth(); - } - return right; -} - -void LLLocationInputCtrl::refreshParcelIcons() -{ - // Our "cursor" moving right to left - S32 x = mAddLandmarkBtn->getRect().mLeft; - - LLViewerParcelMgr* vpm = LLViewerParcelMgr::getInstance(); - - LLViewerRegion* agent_region = gAgent.getRegion(); - LLParcel* agent_parcel = vpm->getAgentParcel(); - if (!agent_region || !agent_parcel) - return; - - mForSaleBtn->setVisible(vpm->canAgentBuyParcel(agent_parcel, false)); - - x = layout_widget(mForSaleBtn, x); - - if (gSavedSettings.getBOOL("NavBarShowParcelProperties")) - { - LLParcel* current_parcel; - LLViewerRegion* selection_region = vpm->getSelectionRegion(); - LLParcel* selected_parcel = vpm->getParcelSelection()->getParcel(); - - // If agent is in selected parcel we use its properties because - // they are updated more often by LLViewerParcelMgr than agent parcel properties. - // See LLViewerParcelMgr::processParcelProperties(). - // This is needed to reflect parcel restrictions changes without having to leave - // the parcel and then enter it again. See EXT-2987 - if (selected_parcel && selected_parcel->getLocalID() == agent_parcel->getLocalID() - && selection_region == agent_region) - { - current_parcel = selected_parcel; - } - else - { - current_parcel = agent_parcel; - } - - bool allow_voice = vpm->allowAgentVoice(agent_region, current_parcel); - bool allow_fly = vpm->allowAgentFly(agent_region, current_parcel); - bool allow_push = vpm->allowAgentPush(agent_region, current_parcel); - bool allow_build = vpm->allowAgentBuild(current_parcel); // true when anyone is allowed to build. See EXT-4610. - bool allow_scripts = vpm->allowAgentScripts(agent_region, current_parcel); - bool allow_damage = vpm->allowAgentDamage(agent_region, current_parcel); - bool see_avs = current_parcel->getSeeAVs(); - bool pathfinding_dynamic_enabled = agent_region->dynamicPathfindingEnabled(); - - // Most icons are "block this ability" - mParcelIcon[VOICE_ICON]->setVisible( !allow_voice ); - mParcelIcon[FLY_ICON]->setVisible( !allow_fly ); - mParcelIcon[PUSH_ICON]->setVisible( !allow_push ); - mParcelIcon[BUILD_ICON]->setVisible( !allow_build ); - mParcelIcon[SCRIPTS_ICON]->setVisible( !allow_scripts ); - mParcelIcon[DAMAGE_ICON]->setVisible( allow_damage ); - mParcelIcon[PATHFINDING_DIRTY_ICON]->setVisible(mIsNavMeshDirty); - mParcelIcon[PATHFINDING_DISABLED_ICON]->setVisible(!mIsNavMeshDirty && !pathfinding_dynamic_enabled); - - mDamageText->setVisible(allow_damage); - mParcelIcon[SEE_AVATARS_ICON]->setVisible( !see_avs ); - - // Padding goes to left of both landmark star and for sale btn - x -= mAddLandmarkHPad; - - // Slide the parcel icons rect from right to left, adjusting rectangles - for (S32 i = 0; i < ICON_COUNT; ++i) - { - x = layout_widget(mParcelIcon[i], x); - x -= mIconHPad; - } - x = layout_widget(mDamageText, x); - x -= mIconHPad; - } - else - { - for (S32 i = 0; i < ICON_COUNT; ++i) - { - mParcelIcon[i]->setVisible(false); - } - mDamageText->setVisible(false); - } - - if (mTextEntry) - { - S32 left_pad, right_pad; - mTextEntry->getTextPadding(&left_pad, &right_pad); - right_pad = mTextEntry->getRect().mRight - x; - mTextEntry->setTextPadding(left_pad, right_pad); - } -} - -void LLLocationInputCtrl::refreshHealth() -{ - // *FIXME: Status bar owns health information, should be in agent - if (gStatusBar) - { - static S32 last_health = -1; - S32 health = gStatusBar->getHealth(); - if (health != last_health) - { - std::string text = llformat("%d%%", health); - mDamageText->setText(text); - last_health = health; - } - } -} - -void LLLocationInputCtrl::refreshMaturityButton() -{ - // Updating maturity rating icon. - LLViewerRegion* region = gAgent.getRegion(); - if (!region) - return; - - bool button_visible = true; - LLPointer<LLUIImage> rating_image = NULL; - std::string rating_tooltip; - - U8 sim_access = region->getSimAccess(); - switch(sim_access) - { - case SIM_ACCESS_PG: - rating_image = mIconMaturityGeneral; - rating_tooltip = LLTrans::getString("LocationCtrlGeneralIconTooltip"); - break; - - case SIM_ACCESS_ADULT: - rating_image = mIconMaturityAdult; - rating_tooltip = LLTrans::getString("LocationCtrlAdultIconTooltip"); - break; - - case SIM_ACCESS_MATURE: - rating_image = mIconMaturityModerate; - rating_tooltip = LLTrans::getString("LocationCtrlModerateIconTooltip"); - break; - - default: - button_visible = false; - break; - } - - mMaturityButton->setVisible(button_visible); - mMaturityButton->setToolTip(rating_tooltip); - if(rating_image) - { - mMaturityButton->setImageUnselected(rating_image); - mMaturityButton->setImagePressed(rating_image); - } - if (mMaturityButton->getVisible()) - { - positionMaturityButton(); - } -} - -void LLLocationInputCtrl::positionMaturityButton() -{ - const LLFontGL* font = mTextEntry->getFont(); - if (!font) - return; - - S32 left_pad, right_pad; - mTextEntry->getTextPadding(&left_pad, &right_pad); - - // Calculate the right edge of rendered text + a whitespace. - left_pad = left_pad + font->getWidth(mTextEntry->getText()) + font->getWidth(" "); - - LLRect rect = mMaturityButton->getRect(); - mMaturityButton->setRect(rect.setOriginAndSize(left_pad, rect.mBottom, rect.getWidth(), rect.getHeight())); - - // Hide icon if it text area is not width enough to display it, show otherwise. - mMaturityButton->setVisible(rect.mRight < mTextEntry->getRect().getWidth() - right_pad); -} - -void LLLocationInputCtrl::rebuildLocationHistory(const std::string& filter) -{ - LLLocationHistory::location_list_t filtered_items; - const LLLocationHistory::location_list_t* itemsp = NULL; - LLLocationHistory* lh = LLLocationHistory::getInstance(); - - if (filter.empty()) - { - itemsp = &lh->getItems(); - } - else - { - lh->getMatchingItems(filter, filtered_items); - itemsp = &filtered_items; - } - - removeall(); - for (LLLocationHistory::location_list_t::const_reverse_iterator it = itemsp->rbegin(); it != itemsp->rend(); it++) - { - LLSD value; - value["tooltip"] = it->getToolTip(); - //location history can contain only typed locations - value["item_type"] = TYPED_REGION_SLURL; - value["global_pos"] = it->mGlobalPos.getValue(); - add(it->getLocation(), value); - } -} - -void LLLocationInputCtrl::focusTextEntry() -{ - // We can't use "mTextEntry->setFocus(true)" instead because - // if the "select_on_focus" parameter is true it places the cursor - // at the beginning (after selecting text), thus screwing up updateSelection(). - if (mTextEntry) - { - gFocusMgr.setKeyboardFocus(mTextEntry); - - // Enable the text entry to handle accelerator keys (EXT-8104). - LLEditMenuHandler::gEditMenuHandler = mTextEntry; - } -} - -void LLLocationInputCtrl::enableAddLandmarkButton(bool val) -{ - // We don't want to disable the button because it should be click able at any time, - // instead switch images. - LLUIImage* img = val ? mLandmarkImageOn : mLandmarkImageOff; - if(img) - { - mAddLandmarkBtn->setImageUnselected(img); - } -} - -// Change the "Add landmark" button image -// depending on whether current parcel has been landmarked. -void LLLocationInputCtrl::updateAddLandmarkButton() -{ - enableAddLandmarkButton(LLLandmarkActions::hasParcelLandmark()); -} -void LLLocationInputCtrl::updateAddLandmarkTooltip() -{ - std::string tooltip; - if(LLLandmarkActions::landmarkAlreadyExists()) - { - tooltip = mEditLandmarkTooltip; - } - else - { - tooltip = mAddLandmarkTooltip; - } - mAddLandmarkBtn->setToolTip(tooltip); -} - -void LLLocationInputCtrl::updateContextMenu(){ - - if (mLocationContextMenu) - { - LLMenuItemGL* landmarkItem = mLocationContextMenu->getChild<LLMenuItemGL>("Landmark"); - if (!LLLandmarkActions::landmarkAlreadyExists()) - { - landmarkItem->setLabel(LLTrans::getString("AddLandmarkNavBarMenu")); - } - else - { - landmarkItem->setLabel(LLTrans::getString("EditLandmarkNavBarMenu")); - } - } -} -void LLLocationInputCtrl::updateWidgetlayout() -{ - const LLRect& rect = getLocalRect(); - const LLRect& hist_btn_rect = mButton->getRect(); - - // Info button is set in the XUI XML location_input.xml - - // "Add Landmark" button - LLRect al_btn_rect = mAddLandmarkBtn->getRect(); - al_btn_rect.translate( - hist_btn_rect.mLeft - mIconHPad - al_btn_rect.getWidth(), - (rect.getHeight() - al_btn_rect.getHeight()) / 2); - mAddLandmarkBtn->setRect(al_btn_rect); -} - -void LLLocationInputCtrl::changeLocationPresentation() -{ - if (!mTextEntry) - return; - - //change location presentation only if user does not select/paste anything and - //human-readable region name is being displayed - if(!mTextEntry->hasSelection() && mTextEntry->getText() == mHumanReadableLocation) - { - //needs unescaped one - LLSLURL slurl; - LLAgentUI::buildSLURL(slurl, false); - mTextEntry->setText(LLURI::unescape(slurl.getSLURLString())); - mTextEntry->selectAll(); - - mMaturityButton->setVisible(false); - - isHumanReadableLocationVisible = false; - } -} - -void LLLocationInputCtrl::onLocationContextMenuItemClicked(const LLSD& userdata) -{ - std::string item = userdata.asString(); - - if (item == "show_coordinates") - { - gSavedSettings.setBOOL("NavBarShowCoordinates",!gSavedSettings.getBOOL("NavBarShowCoordinates")); - } - else if (item == "show_properties") - { - gSavedSettings.setBOOL("NavBarShowParcelProperties", - !gSavedSettings.getBOOL("NavBarShowParcelProperties")); - } - else if (item == "landmark") - { - LLViewerInventoryItem* landmark = LLLandmarkActions::findLandmarkForAgentPos(); - - if(!landmark) - { - LLFloaterReg::showInstance("add_landmark"); - } - else - { - LLFloaterSidePanelContainer::showPanel("places", LLSD().with("type", "landmark").with("id",landmark->getUUID())); - - } - } - else if (item == "cut") - { - mTextEntry->cut(); - } - else if (item == "copy") - { - mTextEntry->copy(); - } - else if (item == "paste") - { - mTextEntry->paste(); - } - else if (item == "delete") - { - mTextEntry->deleteSelection(); - } - else if (item == "select_all") - { - mTextEntry->selectAll(); - } -} - -bool LLLocationInputCtrl::onLocationContextMenuItemEnabled(const LLSD& userdata) -{ - std::string item = userdata.asString(); - - if (item == "can_cut") - { - return mTextEntry->canCut(); - } - else if (item == "can_copy") - { - return mTextEntry->canCopy(); - } - else if (item == "can_paste") - { - return mTextEntry->canPaste(); - } - else if (item == "can_delete") - { - return mTextEntry->canDeselect(); - } - else if (item == "can_select_all") - { - return mTextEntry->canSelectAll() && (mTextEntry->getLength() > 0); - } - else if(item == "show_coordinates") - { - return gSavedSettings.getBOOL("NavBarShowCoordinates"); - } - - return false; -} - -void LLLocationInputCtrl::callbackRebakeRegion(const LLSD& notification, const LLSD& response) -{ - S32 option = LLNotificationsUtil::getSelectedOption(notification, response); - if (option == 0) // OK - { - if (LLPathfindingManager::getInstance() != NULL) - { - LLMenuOptionPathfindingRebakeNavmesh::getInstance()->sendRequestRebakeNavmesh(); - } - } -} - -void LLLocationInputCtrl::onParcelIconClick(EParcelIcon icon) -{ - switch (icon) - { - case VOICE_ICON: - LLNotificationsUtil::add("NoVoice"); - break; - case FLY_ICON: - LLNotificationsUtil::add("NoFly"); - break; - case PUSH_ICON: - LLNotificationsUtil::add("PushRestricted"); - break; - case BUILD_ICON: - LLNotificationsUtil::add("NoBuild"); - break; - case PATHFINDING_DIRTY_ICON: - if (LLPathfindingManager::getInstance() != NULL) - { - LLMenuOptionPathfindingRebakeNavmesh *rebakeInstance = LLMenuOptionPathfindingRebakeNavmesh::getInstance(); - if (rebakeInstance && rebakeInstance->canRebakeRegion() && (rebakeInstance->getMode() == LLMenuOptionPathfindingRebakeNavmesh::kRebakeNavMesh_Available)) - { - LLNotificationsUtil::add("PathfindingDirtyRebake", LLSD(), LLSD(), - boost::bind(&LLLocationInputCtrl::callbackRebakeRegion, this, _1, _2)); - break; - } - } - LLNotificationsUtil::add("PathfindingDirty"); - break; - case PATHFINDING_DISABLED_ICON: - LLNotificationsUtil::add("DynamicPathfindingDisabled"); - break; - case SCRIPTS_ICON: - { - LLViewerRegion* region = gAgent.getRegion(); - if(region && region->getRegionFlag(REGION_FLAGS_ESTATE_SKIP_SCRIPTS)) - { - LLNotificationsUtil::add("ScriptsStopped"); - } - else if(region && region->getRegionFlag(REGION_FLAGS_SKIP_SCRIPTS)) - { - LLNotificationsUtil::add("ScriptsNotRunning"); - } - else - { - LLNotificationsUtil::add("NoOutsideScripts"); - } - break; - } - case DAMAGE_ICON: - LLNotificationsUtil::add("NotSafe"); - break; - case SEE_AVATARS_ICON: - LLNotificationsUtil::add("SeeAvatars"); - break; - case ICON_COUNT: - break; - // no default to get compiler warning when a new icon gets added - } -} - -void LLLocationInputCtrl::createNavMeshStatusListenerForCurrentRegion() -{ - if (mNavMeshSlot.connected()) - { - mNavMeshSlot.disconnect(); - } - - LLViewerRegion *currentRegion = gAgent.getRegion(); - if (currentRegion != NULL) - { - mNavMeshSlot = LLPathfindingManager::getInstance()->registerNavMeshListenerForRegion(currentRegion, boost::bind(&LLLocationInputCtrl::onNavMeshStatusChange, this, _2)); - LLPathfindingManager::getInstance()->requestGetNavMeshForRegion(currentRegion, true); - } -} +/**
+ * @file lllocationinputctrl.cpp
+ * @brief Combobox-like location input control
+ *
+ * $LicenseInfo:firstyear=2009&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+// file includes
+#include "lllocationinputctrl.h"
+
+// common includes
+#include "llbutton.h"
+#include "llfocusmgr.h"
+#include "llhelp.h"
+#include "llmenugl.h"
+#include "llparcel.h"
+#include "llstring.h"
+#include "lltrans.h"
+#include "lluictrlfactory.h"
+#include "lltooltip.h"
+#include "llnotificationsutil.h"
+#include "llregionflags.h"
+
+// newview includes
+#include "llagent.h"
+#include "llfloaterreg.h"
+#include "llfloatersidepanelcontainer.h"
+#include "llinventoryobserver.h"
+#include "lllandmarkactions.h"
+#include "lllandmarklist.h"
+#include "llpathfindingmanager.h"
+#include "llpathfindingnavmesh.h"
+#include "llpathfindingnavmeshstatus.h"
+#include "llteleporthistory.h"
+#include "llslurl.h"
+#include "llstatusbar.h" // getHealth()
+#include "lltrans.h"
+#include "llviewerinventory.h"
+#include "llviewerparcelmgr.h"
+#include "llviewerregion.h"
+#include "llviewercontrol.h"
+#include "llviewermenu.h"
+#include "llurllineeditorctrl.h"
+#include "llagentui.h"
+
+#include "llmenuoptionpathfindingrebakenavmesh.h"
+#include "llpathfindingmanager.h"
+
+//============================================================================
+/*
+ * "ADD LANDMARK" BUTTON UPDATING LOGIC
+ *
+ * If the current parcel has been landmarked, we should draw
+ * a special image on the button.
+ *
+ * To avoid determining the appropriate image on every draw() we do that
+ * only in the following cases:
+ * 1) Navbar is shown for the first time after login.
+ * 2) Agent moves to another parcel.
+ * 3) A landmark is created or removed.
+ *
+ * The first case is handled by the handleLoginComplete() method.
+ *
+ * The second case is handled by setting the "agent parcel changed" callback
+ * on LLViewerParcelMgr.
+ *
+ * The third case is the most complex one. We have two inventory observers for that:
+ * one is designated to handle adding landmarks, the other handles removal.
+ * Let's see how the former works.
+ *
+ * When we get notified about landmark addition, the landmark position is unknown yet. What we can
+ * do at that point is initiate loading the landmark data by LLLandmarkList and set the
+ * "loading finished" callback on it. Finally, when the callback is triggered,
+ * we can determine whether the landmark refers to a point within the current parcel
+ * and choose the appropriate image for the "Add landmark" button.
+ */
+
+/**
+ * Initiates loading the landmarks that have been just added.
+ *
+ * Once the loading is complete we'll be notified
+ * with the callback we set for LLLandmarkList.
+ */
+class LLAddLandmarkObserver : public LLInventoryAddedObserver
+{
+public:
+ LLAddLandmarkObserver(LLLocationInputCtrl* input) : mInput(input) {}
+
+private:
+ /*virtual*/ void done()
+ {
+ const uuid_set_t& added = gInventory.getAddedIDs();
+ for (uuid_set_t::const_iterator it = added.begin(); it != added.end(); ++it)
+ {
+ LLInventoryItem* item = gInventory.getItem(*it);
+ if (!item || item->getType() != LLAssetType::AT_LANDMARK)
+ continue;
+
+ // Start loading the landmark.
+ LLLandmark* lm = gLandmarkList.getAsset(
+ item->getAssetUUID(),
+ boost::bind(&LLLocationInputCtrl::onLandmarkLoaded, mInput, _1));
+ if (lm)
+ {
+ // Already loaded? Great, handle it immediately (the callback won't be called).
+ mInput->onLandmarkLoaded(lm);
+ }
+ }
+ }
+
+ LLLocationInputCtrl* mInput;
+};
+
+/**
+ * Updates the "Add landmark" button once a landmark gets removed.
+ */
+class LLRemoveLandmarkObserver : public LLInventoryObserver
+{
+public:
+ LLRemoveLandmarkObserver(LLLocationInputCtrl* input) : mInput(input) {}
+
+private:
+ /*virtual*/ void changed(U32 mask)
+ {
+ if (mask & (~(LLInventoryObserver::LABEL|
+ LLInventoryObserver::INTERNAL|
+ LLInventoryObserver::ADD|
+ LLInventoryObserver::CREATE|
+ LLInventoryObserver::UPDATE_CREATE)))
+ {
+ mInput->updateAddLandmarkButton();
+ }
+ }
+
+ LLLocationInputCtrl* mInput;
+};
+
+class LLParcelChangeObserver : public LLParcelObserver
+{
+public:
+ LLParcelChangeObserver(LLLocationInputCtrl* input) : mInput(input) {}
+
+private:
+ /*virtual*/ void changed()
+ {
+ if (mInput)
+ {
+ mInput->refreshParcelIcons();
+ }
+ }
+
+ LLLocationInputCtrl* mInput;
+};
+
+//============================================================================
+
+
+static LLDefaultChildRegistry::Register<LLLocationInputCtrl> r("location_input");
+
+LLLocationInputCtrl::Params::Params()
+: icon_maturity_general("icon_maturity_general"),
+ icon_maturity_adult("icon_maturity_adult"),
+ icon_maturity_moderate("icon_maturity_moderate"),
+ add_landmark_image_enabled("add_landmark_image_enabled"),
+ add_landmark_image_disabled("add_landmark_image_disabled"),
+ add_landmark_image_hover("add_landmark_image_hover"),
+ add_landmark_image_selected("add_landmark_image_selected"),
+ add_landmark_hpad("add_landmark_hpad", 0),
+ icon_hpad("icon_hpad", 0),
+ add_landmark_button("add_landmark_button"),
+ for_sale_button("for_sale_button"),
+ info_button("info_button"),
+ maturity_button("maturity_button"),
+ voice_icon("voice_icon"),
+ fly_icon("fly_icon"),
+ push_icon("push_icon"),
+ build_icon("build_icon"),
+ scripts_icon("scripts_icon"),
+ damage_icon("damage_icon"),
+ damage_text("damage_text"),
+ see_avatars_icon("see_avatars_icon"),
+ maturity_help_topic("maturity_help_topic"),
+ pathfinding_dirty_icon("pathfinding_dirty_icon"),
+ pathfinding_disabled_icon("pathfinding_disabled_icon")
+{
+}
+
+LLLocationInputCtrl::LLLocationInputCtrl(const LLLocationInputCtrl::Params& p)
+: LLComboBox(p),
+ mIconHPad(p.icon_hpad),
+ mAddLandmarkHPad(p.add_landmark_hpad),
+ mLocationContextMenu(NULL),
+ mAddLandmarkBtn(NULL),
+ mForSaleBtn(NULL),
+ mInfoBtn(NULL),
+ mRegionCrossingSlot(),
+ mNavMeshSlot(),
+ mIsNavMeshDirty(false),
+ mLandmarkImageOn(NULL),
+ mLandmarkImageOff(NULL),
+ mIconMaturityGeneral(NULL),
+ mIconMaturityAdult(NULL),
+ mIconMaturityModerate(NULL),
+ mMaturityHelpTopic(p.maturity_help_topic)
+{
+ // Lets replace default LLLineEditor with LLLocationLineEditor
+ // to make needed escaping while copying and cutting url
+ delete mTextEntry;
+
+ // Can't access old mTextEntry fields as they are protected, so lets build new params
+ // That is C&P from LLComboBox::createLineEditor function
+ S32 arrow_width = mArrowImage ? mArrowImage->getWidth() : 0;
+ LLRect text_entry_rect(0, getRect().getHeight(), getRect().getWidth(), 0);
+ text_entry_rect.mRight -= llmax(8,arrow_width) + 2 * BTN_DROP_SHADOW;
+
+ LLLineEditor::Params params = p.combo_editor;
+ params.rect(text_entry_rect);
+ params.default_text(LLStringUtil::null);
+ params.max_length.bytes(p.max_chars);
+ params.keystroke_callback(boost::bind(&LLLocationInputCtrl::onTextEntry, this, _1));
+ params.commit_on_focus_lost(false);
+ params.follows.flags(FOLLOWS_ALL);
+ mTextEntry = LLUICtrlFactory::create<LLURLLineEditor>(params);
+ mTextEntry->resetContextMenu();
+ addChild(mTextEntry);
+ // LLLineEditor is replaced with LLLocationLineEditor
+
+ // "Place information" button.
+ LLButton::Params info_params = p.info_button;
+ mInfoBtn = LLUICtrlFactory::create<LLButton>(info_params);
+ mInfoBtn->setClickedCallback(boost::bind(&LLLocationInputCtrl::onInfoButtonClicked, this));
+ addChild(mInfoBtn);
+
+ // "Add landmark" button.
+ LLButton::Params al_params = p.add_landmark_button;
+
+ // Image for unselected state will be set in updateAddLandmarkButton(),
+ // it will be either mLandmarkOn or mLandmarkOff
+ if (p.add_landmark_image_enabled())
+ {
+ mLandmarkImageOn = p.add_landmark_image_enabled;
+ }
+ if (p.add_landmark_image_disabled())
+ {
+ mLandmarkImageOff = p.add_landmark_image_disabled;
+ }
+
+ if(p.add_landmark_image_selected)
+ {
+ al_params.image_selected = p.add_landmark_image_selected;
+ }
+ if (p.add_landmark_image_hover())
+ {
+ al_params.image_hover_unselected = p.add_landmark_image_hover;
+ }
+
+ al_params.click_callback.function(boost::bind(&LLLocationInputCtrl::onAddLandmarkButtonClicked, this));
+ mAddLandmarkBtn = LLUICtrlFactory::create<LLButton>(al_params);
+ enableAddLandmarkButton(true);
+ addChild(mAddLandmarkBtn);
+
+ if (p.icon_maturity_general())
+ {
+ mIconMaturityGeneral = p.icon_maturity_general;
+ }
+ if (p.icon_maturity_adult())
+ {
+ mIconMaturityAdult = p.icon_maturity_adult;
+ }
+ if(p.icon_maturity_moderate())
+ {
+ mIconMaturityModerate = p.icon_maturity_moderate;
+ }
+
+ LLButton::Params maturity_button = p.maturity_button;
+ mMaturityButton = LLUICtrlFactory::create<LLButton>(maturity_button);
+ addChild(mMaturityButton);
+
+ LLButton::Params for_sale_button = p.for_sale_button;
+ for_sale_button.tool_tip = LLTrans::getString("LocationCtrlForSaleTooltip");
+ for_sale_button.click_callback.function(
+ boost::bind(&LLLocationInputCtrl::onForSaleButtonClicked, this));
+ mForSaleBtn = LLUICtrlFactory::create<LLButton>( for_sale_button );
+ addChild(mForSaleBtn);
+
+ // Parcel property icons
+ // Must be mouse-opaque so cursor stays as an arrow when hovering to
+ // see tooltip.
+ LLIconCtrl::Params voice_icon = p.voice_icon;
+ voice_icon.tool_tip = LLTrans::getString("LocationCtrlVoiceTooltip");
+ voice_icon.mouse_opaque = true;
+ mParcelIcon[VOICE_ICON] = LLUICtrlFactory::create<LLIconCtrl>(voice_icon);
+ mParcelIcon[VOICE_ICON]->setMouseDownCallback(boost::bind(&LLLocationInputCtrl::onParcelIconClick, this, VOICE_ICON));
+ addChild(mParcelIcon[VOICE_ICON]);
+
+ LLIconCtrl::Params fly_icon = p.fly_icon;
+ fly_icon.tool_tip = LLTrans::getString("LocationCtrlFlyTooltip");
+ fly_icon.mouse_opaque = true;
+ mParcelIcon[FLY_ICON] = LLUICtrlFactory::create<LLIconCtrl>(fly_icon);
+ mParcelIcon[FLY_ICON]->setMouseDownCallback(boost::bind(&LLLocationInputCtrl::onParcelIconClick, this, FLY_ICON));
+ addChild(mParcelIcon[FLY_ICON]);
+
+ LLIconCtrl::Params push_icon = p.push_icon;
+ push_icon.tool_tip = LLTrans::getString("LocationCtrlPushTooltip");
+ push_icon.mouse_opaque = true;
+ mParcelIcon[PUSH_ICON] = LLUICtrlFactory::create<LLIconCtrl>(push_icon);
+ mParcelIcon[PUSH_ICON]->setMouseDownCallback(boost::bind(&LLLocationInputCtrl::onParcelIconClick, this, PUSH_ICON));
+ addChild(mParcelIcon[PUSH_ICON]);
+
+ LLIconCtrl::Params build_icon = p.build_icon;
+ build_icon.tool_tip = LLTrans::getString("LocationCtrlBuildTooltip");
+ build_icon.mouse_opaque = true;
+ mParcelIcon[BUILD_ICON] = LLUICtrlFactory::create<LLIconCtrl>(build_icon);
+ mParcelIcon[BUILD_ICON]->setMouseDownCallback(boost::bind(&LLLocationInputCtrl::onParcelIconClick, this, BUILD_ICON));
+ addChild(mParcelIcon[BUILD_ICON]);
+
+ LLIconCtrl::Params scripts_icon = p.scripts_icon;
+ scripts_icon.tool_tip = LLTrans::getString("LocationCtrlScriptsTooltip");
+ scripts_icon.mouse_opaque = true;
+ mParcelIcon[SCRIPTS_ICON] = LLUICtrlFactory::create<LLIconCtrl>(scripts_icon);
+ mParcelIcon[SCRIPTS_ICON]->setMouseDownCallback(boost::bind(&LLLocationInputCtrl::onParcelIconClick, this, SCRIPTS_ICON));
+ addChild(mParcelIcon[SCRIPTS_ICON]);
+
+ LLIconCtrl::Params damage_icon = p.damage_icon;
+ damage_icon.tool_tip = LLTrans::getString("LocationCtrlDamageTooltip");
+ damage_icon.mouse_opaque = true;
+ mParcelIcon[DAMAGE_ICON] = LLUICtrlFactory::create<LLIconCtrl>(damage_icon);
+ mParcelIcon[DAMAGE_ICON]->setMouseDownCallback(boost::bind(&LLLocationInputCtrl::onParcelIconClick, this, DAMAGE_ICON));
+ addChild(mParcelIcon[DAMAGE_ICON]);
+
+ LLIconCtrl::Params pathfinding_dirty_icon = p.pathfinding_dirty_icon;
+ pathfinding_dirty_icon.tool_tip = LLTrans::getString("LocationCtrlPathfindingDirtyTooltip");
+ pathfinding_dirty_icon.mouse_opaque = true;
+ mParcelIcon[PATHFINDING_DIRTY_ICON] = LLUICtrlFactory::create<LLIconCtrl>(pathfinding_dirty_icon);
+ mParcelIcon[PATHFINDING_DIRTY_ICON]->setMouseDownCallback(boost::bind(&LLLocationInputCtrl::onParcelIconClick, this, PATHFINDING_DIRTY_ICON));
+ addChild(mParcelIcon[PATHFINDING_DIRTY_ICON]);
+
+ LLIconCtrl::Params pathfinding_disabled_icon = p.pathfinding_disabled_icon;
+ pathfinding_disabled_icon.tool_tip = LLTrans::getString("LocationCtrlPathfindingDisabledTooltip");
+ pathfinding_disabled_icon.mouse_opaque = true;
+ mParcelIcon[PATHFINDING_DISABLED_ICON] = LLUICtrlFactory::create<LLIconCtrl>(pathfinding_disabled_icon);
+ mParcelIcon[PATHFINDING_DISABLED_ICON]->setMouseDownCallback(boost::bind(&LLLocationInputCtrl::onParcelIconClick, this, PATHFINDING_DISABLED_ICON));
+ addChild(mParcelIcon[PATHFINDING_DISABLED_ICON]);
+
+ LLTextBox::Params damage_text = p.damage_text;
+ damage_text.tool_tip = LLTrans::getString("LocationCtrlDamageTooltip");
+ damage_text.mouse_opaque = true;
+ mDamageText = LLUICtrlFactory::create<LLTextBox>(damage_text);
+ addChild(mDamageText);
+
+ LLIconCtrl::Params see_avatars_icon = p.see_avatars_icon;
+ see_avatars_icon.tool_tip = LLTrans::getString("LocationCtrlSeeAVsTooltip");
+ see_avatars_icon.mouse_opaque = true;
+ mParcelIcon[SEE_AVATARS_ICON] = LLUICtrlFactory::create<LLIconCtrl>(see_avatars_icon);
+ mParcelIcon[SEE_AVATARS_ICON]->setMouseDownCallback(boost::bind(&LLLocationInputCtrl::onParcelIconClick, this, SEE_AVATARS_ICON));
+ addChild(mParcelIcon[SEE_AVATARS_ICON]);
+
+ // Register callbacks and load the location field context menu (NB: the order matters).
+ LLUICtrl::CommitCallbackRegistry::currentRegistrar().add("Navbar.Action", boost::bind(&LLLocationInputCtrl::onLocationContextMenuItemClicked, this, _2));
+ LLUICtrl::EnableCallbackRegistry::currentRegistrar().add("Navbar.EnableMenuItem", boost::bind(&LLLocationInputCtrl::onLocationContextMenuItemEnabled, this, _2));
+
+ setPrearrangeCallback(boost::bind(&LLLocationInputCtrl::onLocationPrearrange, this, _2));
+ getTextEntry()->setMouseUpCallback(boost::bind(&LLLocationInputCtrl::changeLocationPresentation, this));
+
+ // Load the location field context menu
+ mLocationContextMenu = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>("menu_navbar.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance());
+ if (!mLocationContextMenu)
+ {
+ LL_WARNS() << "Error loading navigation bar context menu" << LL_ENDL;
+
+ }
+ //don't show default context menu
+ getTextEntry()->setShowContextMenu(false);
+ getTextEntry()->setRightMouseDownCallback(boost::bind(&LLLocationInputCtrl::onTextEditorRightClicked, this, _2, _3, _4));
+ updateWidgetlayout();
+
+ // Connecting signal for updating location on "Show Coordinates" setting change.
+ LLControlVariable* coordinates_control = gSavedSettings.getControl("NavBarShowCoordinates").get();
+ if (coordinates_control)
+ {
+ mCoordinatesControlConnection = coordinates_control->getSignal()->connect(boost::bind(&LLLocationInputCtrl::refreshLocation, this));
+ }
+
+ // Connecting signal for updating parcel icons on "Show Parcel Properties" setting change.
+ LLControlVariable* parcel_properties_control = gSavedSettings.getControl("NavBarShowParcelProperties").get();
+ if (parcel_properties_control)
+ {
+ mParcelPropertiesControlConnection = parcel_properties_control->getSignal()->connect(boost::bind(&LLLocationInputCtrl::refreshParcelIcons, this));
+ }
+
+ // - Make the "Add landmark" button updated when either current parcel gets changed
+ // or a landmark gets created or removed from the inventory.
+ // - Update the location string on parcel change.
+ mParcelMgrConnection = gAgent.addParcelChangedCallback(
+ boost::bind(&LLLocationInputCtrl::onAgentParcelChange, this));
+ // LLLocationHistory instance is being created before the location input control, so we have to update initial state of button manually.
+ mButton->setEnabled(LLLocationHistory::instance().getItemCount() > 0);
+ mLocationHistoryConnection = LLLocationHistory::getInstance()->setChangedCallback(
+ boost::bind(&LLLocationInputCtrl::onLocationHistoryChanged, this,_1));
+
+ mRegionCrossingSlot = gAgent.addRegionChangedCallback(boost::bind(&LLLocationInputCtrl::onRegionBoundaryCrossed, this));
+ createNavMeshStatusListenerForCurrentRegion();
+
+ mRemoveLandmarkObserver = new LLRemoveLandmarkObserver(this);
+ mAddLandmarkObserver = new LLAddLandmarkObserver(this);
+ gInventory.addObserver(mRemoveLandmarkObserver);
+ gInventory.addObserver(mAddLandmarkObserver);
+
+ mParcelChangeObserver = new LLParcelChangeObserver(this);
+ LLViewerParcelMgr::getInstance()->addObserver(mParcelChangeObserver);
+
+ mAddLandmarkTooltip = LLTrans::getString("LocationCtrlAddLandmarkTooltip");
+ mEditLandmarkTooltip = LLTrans::getString("LocationCtrlEditLandmarkTooltip");
+ mButton->setToolTip(LLTrans::getString("LocationCtrlComboBtnTooltip"));
+ mInfoBtn->setToolTip(LLTrans::getString("LocationCtrlInfoBtnTooltip"));
+}
+
+LLLocationInputCtrl::~LLLocationInputCtrl()
+{
+ gInventory.removeObserver(mRemoveLandmarkObserver);
+ gInventory.removeObserver(mAddLandmarkObserver);
+ delete mRemoveLandmarkObserver;
+ delete mAddLandmarkObserver;
+
+ LLViewerParcelMgr::getInstance()->removeObserver(mParcelChangeObserver);
+ delete mParcelChangeObserver;
+
+ mRegionCrossingSlot.disconnect();
+ mNavMeshSlot.disconnect();
+ mCoordinatesControlConnection.disconnect();
+ mParcelPropertiesControlConnection.disconnect();
+ mParcelMgrConnection.disconnect();
+ mLocationHistoryConnection.disconnect();
+}
+
+void LLLocationInputCtrl::setEnabled(bool enabled)
+{
+ LLComboBox::setEnabled(enabled);
+ mAddLandmarkBtn->setEnabled(enabled);
+}
+
+void LLLocationInputCtrl::hideList()
+{
+ LLComboBox::hideList();
+ if (mTextEntry && hasFocus())
+ focusTextEntry();
+}
+
+bool LLLocationInputCtrl::handleToolTip(S32 x, S32 y, MASK mask)
+{
+
+ if(mAddLandmarkBtn->parentPointInView(x,y))
+ {
+ updateAddLandmarkTooltip();
+ }
+ // Let the buttons show their tooltips.
+ if (LLUICtrl::handleToolTip(x, y, mask))
+ {
+ if (mList->getRect().pointInRect(x, y))
+ {
+ S32 loc_x, loc_y;
+ //x,y - contain coordinates related to the location input control, but without taking the expanded list into account
+ //So we have to convert it again into local coordinates of mList
+ localPointToOtherView(x,y,&loc_x,&loc_y,mList);
+
+ LLScrollListItem* item = mList->hitItem(loc_x,loc_y);
+ if (item)
+ {
+ LLSD value = item->getValue();
+ if (value.has("tooltip"))
+ {
+ LLToolTipMgr::instance().show(value["tooltip"]);
+ }
+ }
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+bool LLLocationInputCtrl::handleKeyHere(KEY key, MASK mask)
+{
+ bool result = LLComboBox::handleKeyHere(key, mask);
+
+ if (key == KEY_DOWN && hasFocus() && mList->getItemCount() != 0 && !mList->getVisible())
+ {
+ showList();
+ }
+
+ return result;
+}
+
+void LLLocationInputCtrl::onTextEntry(LLLineEditor* line_editor)
+{
+ KEY key = gKeyboard->currentKey();
+ MASK mask = gKeyboard->currentMask(true);
+
+ // Typing? (moving cursor should not affect showing the list)
+ bool typing = mask != MASK_CONTROL && key != KEY_LEFT && key != KEY_RIGHT && key != KEY_HOME && key != KEY_END;
+ bool pasting = mask == MASK_CONTROL && key == 'V';
+
+ if (line_editor->getText().empty())
+ {
+ prearrangeList(); // resets filter
+ hideList();
+ }
+ else if (typing || pasting)
+ {
+ prearrangeList(line_editor->getText());
+ if (mList->getItemCount() != 0)
+ {
+ showList();
+ focusTextEntry();
+ }
+ else
+ {
+ // Hide the list if it's empty.
+ hideList();
+ }
+ }
+
+ LLComboBox::onTextEntry(line_editor);
+}
+
+/**
+ * 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);
+ }
+ mHasAutocompletedText = false;
+}
+
+void LLLocationInputCtrl::setFocus(bool b)
+{
+ LLComboBox::setFocus(b);
+
+ if (mTextEntry && b && !mList->getVisible())
+ {
+ mTextEntry->setFocus(true);
+ }
+}
+
+void LLLocationInputCtrl::handleLoginComplete()
+{
+ // An agent parcel update hasn't occurred yet, so we have to
+ // manually set location and the appropriate "Add landmark" icon.
+ refresh();
+}
+
+//== private methods =========================================================
+
+void LLLocationInputCtrl::onFocusReceived()
+{
+ prearrangeList();
+}
+
+void LLLocationInputCtrl::onFocusLost()
+{
+ LLUICtrl::onFocusLost();
+ refreshLocation();
+
+ // Setting cursor to 0 to show the left edge of the text. See STORM-370.
+ mTextEntry->setCursor(0);
+
+ if(mTextEntry->hasSelection()){
+ mTextEntry->deselect();
+ }
+}
+
+void LLLocationInputCtrl::draw()
+{
+ static LLUICachedControl<bool> show_coords("NavBarShowCoordinates", false);
+ if(!hasFocus() && show_coords)
+ {
+ refreshLocation();
+ }
+
+ static LLUICachedControl<bool> show_icons("NavBarShowParcelProperties", false);
+ if (show_icons)
+ {
+ refreshHealth();
+ }
+ LLComboBox::draw();
+}
+
+void LLLocationInputCtrl::reshape(S32 width, S32 height, bool called_from_parent)
+{
+ LLComboBox::reshape(width, height, called_from_parent);
+
+ // Setting cursor to 0 to show the left edge of the text. See EXT-4967.
+ mTextEntry->setCursor(0);
+ if (mTextEntry->hasSelection())
+ {
+ // Deselecting because selection position is changed together with
+ // cursor position change.
+ mTextEntry->deselect();
+ }
+
+ if (isHumanReadableLocationVisible)
+ {
+ refreshMaturityButton();
+ }
+}
+
+void LLLocationInputCtrl::onInfoButtonClicked()
+{
+ LLFloaterSidePanelContainer::showPanel("places", LLSD().with("type", "agent"));
+}
+
+void LLLocationInputCtrl::onForSaleButtonClicked()
+{
+ handle_buy_land();
+}
+
+void LLLocationInputCtrl::onAddLandmarkButtonClicked()
+{
+ LLViewerInventoryItem* landmark = LLLandmarkActions::findLandmarkForAgentPos();
+ // Landmark exists, open it for preview and edit
+ if(landmark && landmark->getUUID().notNull())
+ {
+ LLSD key;
+ key["type"] = "landmark";
+ key["id"] = landmark->getUUID();
+
+ LLFloaterSidePanelContainer::showPanel("places", key);
+ }
+ else
+ {
+ LLFloaterReg::showInstance("add_landmark");
+ }
+}
+
+void LLLocationInputCtrl::onAgentParcelChange()
+{
+ refresh();
+}
+
+void LLLocationInputCtrl::onRegionBoundaryCrossed()
+{
+ createNavMeshStatusListenerForCurrentRegion();
+}
+
+void LLLocationInputCtrl::onNavMeshStatusChange(const LLPathfindingNavMeshStatus &pNavMeshStatus)
+{
+ mIsNavMeshDirty = pNavMeshStatus.isValid() && (pNavMeshStatus.getStatus() != LLPathfindingNavMeshStatus::kComplete);
+ refreshParcelIcons();
+}
+
+void LLLocationInputCtrl::onLandmarkLoaded(LLLandmark* lm)
+{
+ (void) lm;
+ updateAddLandmarkButton();
+}
+
+void LLLocationInputCtrl::onLocationHistoryChanged(LLLocationHistory::EChangeType event)
+{
+ if(event == LLLocationHistory::LOAD)
+ {
+ rebuildLocationHistory();
+ }
+ mButton->setEnabled(LLLocationHistory::instance().getItemCount() > 0);
+}
+
+void LLLocationInputCtrl::onLocationPrearrange(const LLSD& data)
+{
+ std::string filter = data.asString();
+ rebuildLocationHistory(filter);
+
+ //Let's add landmarks to the top of the list if any
+ if(!filter.empty() )
+ {
+ LLInventoryModel::item_array_t landmark_items = LLLandmarkActions::fetchLandmarksByName(filter, true);
+
+ for(U32 i=0; i < landmark_items.size(); i++)
+ {
+ LLSD value;
+ //TODO:: DO we need tooltip for Landmark??
+
+ value["item_type"] = LANDMARK;
+ value["AssetUUID"] = landmark_items[i]->getAssetUUID();
+ addLocationHistoryEntry(landmark_items[i]->getName(), value);
+
+ }
+ //Let's add teleport history items
+ LLTeleportHistory* th = LLTeleportHistory::getInstance();
+ LLTeleportHistory::slurl_list_t th_items = th->getItems();
+
+ std::set<std::string> new_item_titles;// duplicate control
+ LLTeleportHistory::slurl_list_t::iterator result = std::find_if(
+ th_items.begin(), th_items.end(), boost::bind(
+ &LLLocationInputCtrl::findTeleportItemsByTitle, this,
+ _1, filter));
+
+ while (result != th_items.end())
+ {
+ //mTitile format - region_name[, parcel_name]
+ //mFullTitile format - region_name[, parcel_name] (local_x,local_y, local_z)
+ if (new_item_titles.insert(result->mFullTitle).second)
+ {
+ LLSD value;
+ value["item_type"] = TELEPORT_HISTORY;
+ value["global_pos"] = result->mGlobalPos.getValue();
+ std::string region_name = result->mTitle.substr(0, result->mTitle.find(','));
+ //TODO*: add Surl to teleportitem or parse region name from title
+ value["tooltip"] = LLSLURL(region_name, result->mGlobalPos).getSLURLString();
+ addLocationHistoryEntry(result->getTitle(), value);
+ }
+ result = std::find_if(result + 1, th_items.end(), boost::bind(
+ &LLLocationInputCtrl::findTeleportItemsByTitle, this,
+ _1, filter));
+ }
+ }
+ sortByName();
+
+ mList->mouseOverHighlightNthItem(-1); // Clear highlight on the last selected item.
+}
+
+bool LLLocationInputCtrl::findTeleportItemsByTitle(const LLTeleportHistoryItem& item, const std::string& filter)
+{
+ return item.mTitle.find(filter) != std::string::npos;
+}
+
+void LLLocationInputCtrl::onTextEditorRightClicked(S32 x, S32 y, MASK mask)
+{
+ if (mLocationContextMenu)
+ {
+ updateContextMenu();
+ mLocationContextMenu->buildDrawLabels();
+ mLocationContextMenu->updateParent(LLMenuGL::sMenuContainer);
+ hideList();
+ setFocus(true);
+ changeLocationPresentation();
+ LLMenuGL::showPopup(this, mLocationContextMenu, x, y);
+ }
+}
+
+void LLLocationInputCtrl::refresh()
+{
+ refreshLocation(); // update location string
+ refreshParcelIcons();
+ updateAddLandmarkButton(); // indicate whether current parcel has been landmarked
+}
+
+void LLLocationInputCtrl::refreshLocation()
+{
+ // Is one of our children focused?
+ if (LLUICtrl::hasFocus() || mButton->hasFocus() || mList->hasFocus() ||
+ (mTextEntry && mTextEntry->hasFocus()) ||
+ (mAddLandmarkBtn->hasFocus()))
+ {
+ LL_WARNS() << "Location input should not be refreshed when having focus" << LL_ENDL;
+ return;
+ }
+
+ // Update location field.
+ std::string location_name;
+ LLAgentUI::ELocationFormat format =
+ (gSavedSettings.getBOOL("NavBarShowCoordinates")
+ ? LLAgentUI::LOCATION_FORMAT_FULL
+ : LLAgentUI::LOCATION_FORMAT_NO_COORDS);
+
+ if (!LLAgentUI::buildLocationString(location_name, format))
+ {
+ location_name = "???";
+ }
+ // store human-readable location to compare it in changeLocationPresentation()
+ mHumanReadableLocation = location_name;
+ setText(location_name);
+ isHumanReadableLocationVisible = true;
+
+ refreshMaturityButton();
+}
+
+// returns new right edge
+static S32 layout_widget(LLUICtrl* widget, S32 right)
+{
+ if (widget->getVisible())
+ {
+ LLRect rect = widget->getRect();
+ rect.mLeft = right - rect.getWidth();
+ rect.mRight = right;
+ widget->setRect( rect );
+ right -= rect.getWidth();
+ }
+ return right;
+}
+
+void LLLocationInputCtrl::refreshParcelIcons()
+{
+ // Our "cursor" moving right to left
+ S32 x = mAddLandmarkBtn->getRect().mLeft;
+
+ LLViewerParcelMgr* vpm = LLViewerParcelMgr::getInstance();
+
+ LLViewerRegion* agent_region = gAgent.getRegion();
+ LLParcel* agent_parcel = vpm->getAgentParcel();
+ if (!agent_region || !agent_parcel)
+ return;
+
+ mForSaleBtn->setVisible(vpm->canAgentBuyParcel(agent_parcel, false));
+
+ x = layout_widget(mForSaleBtn, x);
+
+ if (gSavedSettings.getBOOL("NavBarShowParcelProperties"))
+ {
+ LLParcel* current_parcel;
+ LLViewerRegion* selection_region = vpm->getSelectionRegion();
+ LLParcel* selected_parcel = vpm->getParcelSelection()->getParcel();
+
+ // If agent is in selected parcel we use its properties because
+ // they are updated more often by LLViewerParcelMgr than agent parcel properties.
+ // See LLViewerParcelMgr::processParcelProperties().
+ // This is needed to reflect parcel restrictions changes without having to leave
+ // the parcel and then enter it again. See EXT-2987
+ if (selected_parcel && selected_parcel->getLocalID() == agent_parcel->getLocalID()
+ && selection_region == agent_region)
+ {
+ current_parcel = selected_parcel;
+ }
+ else
+ {
+ current_parcel = agent_parcel;
+ }
+
+ bool allow_voice = vpm->allowAgentVoice(agent_region, current_parcel);
+ bool allow_fly = vpm->allowAgentFly(agent_region, current_parcel);
+ bool allow_push = vpm->allowAgentPush(agent_region, current_parcel);
+ bool allow_build = vpm->allowAgentBuild(current_parcel); // true when anyone is allowed to build. See EXT-4610.
+ bool allow_scripts = vpm->allowAgentScripts(agent_region, current_parcel);
+ bool allow_damage = vpm->allowAgentDamage(agent_region, current_parcel);
+ bool see_avs = current_parcel->getSeeAVs();
+ bool pathfinding_dynamic_enabled = agent_region->dynamicPathfindingEnabled();
+
+ // Most icons are "block this ability"
+ mParcelIcon[VOICE_ICON]->setVisible( !allow_voice );
+ mParcelIcon[FLY_ICON]->setVisible( !allow_fly );
+ mParcelIcon[PUSH_ICON]->setVisible( !allow_push );
+ mParcelIcon[BUILD_ICON]->setVisible( !allow_build );
+ mParcelIcon[SCRIPTS_ICON]->setVisible( !allow_scripts );
+ mParcelIcon[DAMAGE_ICON]->setVisible( allow_damage );
+ mParcelIcon[PATHFINDING_DIRTY_ICON]->setVisible(mIsNavMeshDirty);
+ mParcelIcon[PATHFINDING_DISABLED_ICON]->setVisible(!mIsNavMeshDirty && !pathfinding_dynamic_enabled);
+
+ mDamageText->setVisible(allow_damage);
+ mParcelIcon[SEE_AVATARS_ICON]->setVisible( !see_avs );
+
+ // Padding goes to left of both landmark star and for sale btn
+ x -= mAddLandmarkHPad;
+
+ // Slide the parcel icons rect from right to left, adjusting rectangles
+ for (S32 i = 0; i < ICON_COUNT; ++i)
+ {
+ x = layout_widget(mParcelIcon[i], x);
+ x -= mIconHPad;
+ }
+ x = layout_widget(mDamageText, x);
+ x -= mIconHPad;
+ }
+ else
+ {
+ for (S32 i = 0; i < ICON_COUNT; ++i)
+ {
+ mParcelIcon[i]->setVisible(false);
+ }
+ mDamageText->setVisible(false);
+ }
+
+ if (mTextEntry)
+ {
+ S32 left_pad, right_pad;
+ mTextEntry->getTextPadding(&left_pad, &right_pad);
+ right_pad = mTextEntry->getRect().mRight - x;
+ mTextEntry->setTextPadding(left_pad, right_pad);
+ }
+}
+
+void LLLocationInputCtrl::refreshHealth()
+{
+ // *FIXME: Status bar owns health information, should be in agent
+ if (gStatusBar)
+ {
+ static S32 last_health = -1;
+ S32 health = gStatusBar->getHealth();
+ if (health != last_health)
+ {
+ std::string text = llformat("%d%%", health);
+ mDamageText->setText(text);
+ last_health = health;
+ }
+ }
+}
+
+void LLLocationInputCtrl::refreshMaturityButton()
+{
+ // Updating maturity rating icon.
+ LLViewerRegion* region = gAgent.getRegion();
+ if (!region)
+ return;
+
+ bool button_visible = true;
+ LLPointer<LLUIImage> rating_image = NULL;
+ std::string rating_tooltip;
+
+ U8 sim_access = region->getSimAccess();
+ switch(sim_access)
+ {
+ case SIM_ACCESS_PG:
+ rating_image = mIconMaturityGeneral;
+ rating_tooltip = LLTrans::getString("LocationCtrlGeneralIconTooltip");
+ break;
+
+ case SIM_ACCESS_ADULT:
+ rating_image = mIconMaturityAdult;
+ rating_tooltip = LLTrans::getString("LocationCtrlAdultIconTooltip");
+ break;
+
+ case SIM_ACCESS_MATURE:
+ rating_image = mIconMaturityModerate;
+ rating_tooltip = LLTrans::getString("LocationCtrlModerateIconTooltip");
+ break;
+
+ default:
+ button_visible = false;
+ break;
+ }
+
+ mMaturityButton->setVisible(button_visible);
+ mMaturityButton->setToolTip(rating_tooltip);
+ if(rating_image)
+ {
+ mMaturityButton->setImageUnselected(rating_image);
+ mMaturityButton->setImagePressed(rating_image);
+ }
+ if (mMaturityButton->getVisible())
+ {
+ positionMaturityButton();
+ }
+}
+
+void LLLocationInputCtrl::positionMaturityButton()
+{
+ const LLFontGL* font = mTextEntry->getFont();
+ if (!font)
+ return;
+
+ S32 left_pad, right_pad;
+ mTextEntry->getTextPadding(&left_pad, &right_pad);
+
+ // Calculate the right edge of rendered text + a whitespace.
+ left_pad = left_pad + font->getWidth(mTextEntry->getText()) + font->getWidth(" ");
+
+ LLRect rect = mMaturityButton->getRect();
+ mMaturityButton->setRect(rect.setOriginAndSize(left_pad, rect.mBottom, rect.getWidth(), rect.getHeight()));
+
+ // Hide icon if it text area is not width enough to display it, show otherwise.
+ mMaturityButton->setVisible(rect.mRight < mTextEntry->getRect().getWidth() - right_pad);
+}
+
+void LLLocationInputCtrl::addLocationHistoryEntry(const std::string& title, const LLSD& value)
+{
+ // SL-20286 : Duplication of autocomplete results occurs when entering some search queries in the navigation bar
+ // Exclude visual duplicates (items with the same titles) in the dropdown list
+ LLScrollListItem* item = mList->getItemByLabel(title);
+ if (!item)
+ {
+ add(title, value);
+ }
+}
+
+void LLLocationInputCtrl::rebuildLocationHistory(const std::string& filter)
+{
+ LLLocationHistory::location_list_t filtered_items;
+ const LLLocationHistory::location_list_t* itemsp = NULL;
+ LLLocationHistory* lh = LLLocationHistory::getInstance();
+
+ if (filter.empty())
+ {
+ itemsp = &lh->getItems();
+ }
+ else
+ {
+ lh->getMatchingItems(filter, filtered_items);
+ itemsp = &filtered_items;
+ }
+
+ removeall();
+ for (LLLocationHistory::location_list_t::const_reverse_iterator it = itemsp->rbegin(); it != itemsp->rend(); it++)
+ {
+ LLSD value;
+ value["tooltip"] = it->getToolTip();
+ //location history can contain only typed locations
+ value["item_type"] = TYPED_REGION_SLURL;
+ value["global_pos"] = it->mGlobalPos.getValue();
+ addLocationHistoryEntry(it->getLocation(), value);
+ }
+}
+
+void LLLocationInputCtrl::focusTextEntry()
+{
+ // We can't use "mTextEntry->setFocus(true)" instead because
+ // if the "select_on_focus" parameter is true it places the cursor
+ // at the beginning (after selecting text), thus screwing up updateSelection().
+ if (mTextEntry)
+ {
+ gFocusMgr.setKeyboardFocus(mTextEntry);
+
+ // Enable the text entry to handle accelerator keys (EXT-8104).
+ LLEditMenuHandler::gEditMenuHandler = mTextEntry;
+ }
+}
+
+void LLLocationInputCtrl::enableAddLandmarkButton(bool val)
+{
+ // We don't want to disable the button because it should be click able at any time,
+ // instead switch images.
+ LLUIImage* img = val ? mLandmarkImageOn : mLandmarkImageOff;
+ if(img)
+ {
+ mAddLandmarkBtn->setImageUnselected(img);
+ }
+}
+
+// Change the "Add landmark" button image
+// depending on whether current parcel has been landmarked.
+void LLLocationInputCtrl::updateAddLandmarkButton()
+{
+ enableAddLandmarkButton(LLLandmarkActions::hasParcelLandmark());
+}
+void LLLocationInputCtrl::updateAddLandmarkTooltip()
+{
+ std::string tooltip;
+ if(LLLandmarkActions::landmarkAlreadyExists())
+ {
+ tooltip = mEditLandmarkTooltip;
+ }
+ else
+ {
+ tooltip = mAddLandmarkTooltip;
+ }
+ mAddLandmarkBtn->setToolTip(tooltip);
+}
+
+void LLLocationInputCtrl::updateContextMenu(){
+
+ if (mLocationContextMenu)
+ {
+ LLMenuItemGL* landmarkItem = mLocationContextMenu->getChild<LLMenuItemGL>("Landmark");
+ if (!LLLandmarkActions::landmarkAlreadyExists())
+ {
+ landmarkItem->setLabel(LLTrans::getString("AddLandmarkNavBarMenu"));
+ }
+ else
+ {
+ landmarkItem->setLabel(LLTrans::getString("EditLandmarkNavBarMenu"));
+ }
+ }
+}
+void LLLocationInputCtrl::updateWidgetlayout()
+{
+ const LLRect& rect = getLocalRect();
+ const LLRect& hist_btn_rect = mButton->getRect();
+
+ // Info button is set in the XUI XML location_input.xml
+
+ // "Add Landmark" button
+ LLRect al_btn_rect = mAddLandmarkBtn->getRect();
+ al_btn_rect.translate(
+ hist_btn_rect.mLeft - mIconHPad - al_btn_rect.getWidth(),
+ (rect.getHeight() - al_btn_rect.getHeight()) / 2);
+ mAddLandmarkBtn->setRect(al_btn_rect);
+}
+
+void LLLocationInputCtrl::changeLocationPresentation()
+{
+ if (!mTextEntry)
+ return;
+
+ //change location presentation only if user does not select/paste anything and
+ //human-readable region name is being displayed
+ if(!mTextEntry->hasSelection() && mTextEntry->getText() == mHumanReadableLocation)
+ {
+ //needs unescaped one
+ LLSLURL slurl;
+ LLAgentUI::buildSLURL(slurl, false);
+ mTextEntry->setText(LLURI::unescape(slurl.getSLURLString()));
+ mTextEntry->selectAll();
+
+ mMaturityButton->setVisible(false);
+
+ isHumanReadableLocationVisible = false;
+ }
+}
+
+void LLLocationInputCtrl::onLocationContextMenuItemClicked(const LLSD& userdata)
+{
+ std::string item = userdata.asString();
+
+ if (item == "show_coordinates")
+ {
+ gSavedSettings.setBOOL("NavBarShowCoordinates",!gSavedSettings.getBOOL("NavBarShowCoordinates"));
+ }
+ else if (item == "show_properties")
+ {
+ gSavedSettings.setBOOL("NavBarShowParcelProperties",
+ !gSavedSettings.getBOOL("NavBarShowParcelProperties"));
+ }
+ else if (item == "landmark")
+ {
+ LLViewerInventoryItem* landmark = LLLandmarkActions::findLandmarkForAgentPos();
+
+ if(!landmark)
+ {
+ LLFloaterReg::showInstance("add_landmark");
+ }
+ else
+ {
+ LLFloaterSidePanelContainer::showPanel("places", LLSD().with("type", "landmark").with("id",landmark->getUUID()));
+
+ }
+ }
+ else if (item == "cut")
+ {
+ mTextEntry->cut();
+ }
+ else if (item == "copy")
+ {
+ mTextEntry->copy();
+ }
+ else if (item == "paste")
+ {
+ mTextEntry->paste();
+ }
+ else if (item == "delete")
+ {
+ mTextEntry->deleteSelection();
+ }
+ else if (item == "select_all")
+ {
+ mTextEntry->selectAll();
+ }
+}
+
+bool LLLocationInputCtrl::onLocationContextMenuItemEnabled(const LLSD& userdata)
+{
+ std::string item = userdata.asString();
+
+ if (item == "can_cut")
+ {
+ return mTextEntry->canCut();
+ }
+ else if (item == "can_copy")
+ {
+ return mTextEntry->canCopy();
+ }
+ else if (item == "can_paste")
+ {
+ return mTextEntry->canPaste();
+ }
+ else if (item == "can_delete")
+ {
+ return mTextEntry->canDeselect();
+ }
+ else if (item == "can_select_all")
+ {
+ return mTextEntry->canSelectAll() && (mTextEntry->getLength() > 0);
+ }
+ else if(item == "show_coordinates")
+ {
+ return gSavedSettings.getBOOL("NavBarShowCoordinates");
+ }
+
+ return false;
+}
+
+void LLLocationInputCtrl::callbackRebakeRegion(const LLSD& notification, const LLSD& response)
+{
+ S32 option = LLNotificationsUtil::getSelectedOption(notification, response);
+ if (option == 0) // OK
+ {
+ if (LLPathfindingManager::getInstance() != NULL)
+ {
+ LLMenuOptionPathfindingRebakeNavmesh::getInstance()->sendRequestRebakeNavmesh();
+ }
+ }
+}
+
+void LLLocationInputCtrl::onParcelIconClick(EParcelIcon icon)
+{
+ switch (icon)
+ {
+ case VOICE_ICON:
+ LLNotificationsUtil::add("NoVoice");
+ break;
+ case FLY_ICON:
+ LLNotificationsUtil::add("NoFly");
+ break;
+ case PUSH_ICON:
+ LLNotificationsUtil::add("PushRestricted");
+ break;
+ case BUILD_ICON:
+ LLNotificationsUtil::add("NoBuild");
+ break;
+ case PATHFINDING_DIRTY_ICON:
+ if (LLPathfindingManager::getInstance() != NULL)
+ {
+ LLMenuOptionPathfindingRebakeNavmesh *rebakeInstance = LLMenuOptionPathfindingRebakeNavmesh::getInstance();
+ if (rebakeInstance && rebakeInstance->canRebakeRegion() && (rebakeInstance->getMode() == LLMenuOptionPathfindingRebakeNavmesh::kRebakeNavMesh_Available))
+ {
+ LLNotificationsUtil::add("PathfindingDirtyRebake", LLSD(), LLSD(),
+ boost::bind(&LLLocationInputCtrl::callbackRebakeRegion, this, _1, _2));
+ break;
+ }
+ }
+ LLNotificationsUtil::add("PathfindingDirty");
+ break;
+ case PATHFINDING_DISABLED_ICON:
+ LLNotificationsUtil::add("DynamicPathfindingDisabled");
+ break;
+ case SCRIPTS_ICON:
+ {
+ LLViewerRegion* region = gAgent.getRegion();
+ if(region && region->getRegionFlag(REGION_FLAGS_ESTATE_SKIP_SCRIPTS))
+ {
+ LLNotificationsUtil::add("ScriptsStopped");
+ }
+ else if(region && region->getRegionFlag(REGION_FLAGS_SKIP_SCRIPTS))
+ {
+ LLNotificationsUtil::add("ScriptsNotRunning");
+ }
+ else
+ {
+ LLNotificationsUtil::add("NoOutsideScripts");
+ }
+ break;
+ }
+ case DAMAGE_ICON:
+ LLNotificationsUtil::add("NotSafe");
+ break;
+ case SEE_AVATARS_ICON:
+ LLNotificationsUtil::add("SeeAvatars");
+ break;
+ case ICON_COUNT:
+ break;
+ // no default to get compiler warning when a new icon gets added
+ }
+}
+
+void LLLocationInputCtrl::createNavMeshStatusListenerForCurrentRegion()
+{
+ if (mNavMeshSlot.connected())
+ {
+ mNavMeshSlot.disconnect();
+ }
+
+ LLViewerRegion *currentRegion = gAgent.getRegion();
+ if (currentRegion != NULL)
+ {
+ mNavMeshSlot = LLPathfindingManager::getInstance()->registerNavMeshListenerForRegion(currentRegion, boost::bind(&LLLocationInputCtrl::onNavMeshStatusChange, this, _2));
+ LLPathfindingManager::getInstance()->requestGetNavMeshForRegion(currentRegion, true);
+ }
+}
|