From c285f59ce2a05703e3a1232fcaf3ee3aea714b3f Mon Sep 17 00:00:00 2001 From: Ansariel Date: Sun, 18 Feb 2024 12:52:19 +0100 Subject: Replace BOOL with bool in llwindow and dependent classes --- indra/newview/lllocationinputctrl.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'indra/newview/lllocationinputctrl.cpp') diff --git a/indra/newview/lllocationinputctrl.cpp b/indra/newview/lllocationinputctrl.cpp index 9fa35e3bd9..99412c0b16 100644 --- a/indra/newview/lllocationinputctrl.cpp +++ b/indra/newview/lllocationinputctrl.cpp @@ -468,7 +468,7 @@ void LLLocationInputCtrl::hideList() focusTextEntry(); } -BOOL LLLocationInputCtrl::handleToolTip(S32 x, S32 y, MASK mask) +bool LLLocationInputCtrl::handleToolTip(S32 x, S32 y, MASK mask) { if(mAddLandmarkBtn->parentPointInView(x,y)) @@ -496,10 +496,10 @@ BOOL LLLocationInputCtrl::handleToolTip(S32 x, S32 y, MASK mask) } } - return TRUE; + return true; } - return FALSE; + return false; } BOOL LLLocationInputCtrl::handleKeyHere(KEY key, MASK mask) -- cgit v1.2.3 From a5261a5fa8fad810ecb5c260d92c3e771822bf58 Mon Sep 17 00:00:00 2001 From: Ansariel Date: Tue, 20 Feb 2024 23:46:23 +0100 Subject: Convert BOOL to bool in llui --- indra/newview/lllocationinputctrl.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'indra/newview/lllocationinputctrl.cpp') diff --git a/indra/newview/lllocationinputctrl.cpp b/indra/newview/lllocationinputctrl.cpp index 99412c0b16..c455a5e75b 100644 --- a/indra/newview/lllocationinputctrl.cpp +++ b/indra/newview/lllocationinputctrl.cpp @@ -455,7 +455,7 @@ LLLocationInputCtrl::~LLLocationInputCtrl() mLocationHistoryConnection.disconnect(); } -void LLLocationInputCtrl::setEnabled(BOOL enabled) +void LLLocationInputCtrl::setEnabled(bool enabled) { LLComboBox::setEnabled(enabled); mAddLandmarkBtn->setEnabled(enabled); @@ -502,9 +502,9 @@ bool LLLocationInputCtrl::handleToolTip(S32 x, S32 y, MASK mask) return false; } -BOOL LLLocationInputCtrl::handleKeyHere(KEY key, MASK mask) +bool LLLocationInputCtrl::handleKeyHere(KEY key, MASK mask) { - BOOL result = LLComboBox::handleKeyHere(key, mask); + bool result = LLComboBox::handleKeyHere(key, mask); if (key == KEY_DOWN && hasFocus() && mList->getItemCount() != 0 && !mList->getVisible()) { @@ -560,13 +560,13 @@ void LLLocationInputCtrl::setText(const LLStringExplicit& text) mHasAutocompletedText = FALSE; } -void LLLocationInputCtrl::setFocus(BOOL b) +void LLLocationInputCtrl::setFocus(bool b) { LLComboBox::setFocus(b); if (mTextEntry && b && !mList->getVisible()) { - mTextEntry->setFocus(TRUE); + mTextEntry->setFocus(true); } } @@ -613,7 +613,7 @@ void LLLocationInputCtrl::draw() LLComboBox::draw(); } -void LLLocationInputCtrl::reshape(S32 width, S32 height, BOOL called_from_parent) +void LLLocationInputCtrl::reshape(S32 width, S32 height, bool called_from_parent) { LLComboBox::reshape(width, height, called_from_parent); -- cgit v1.2.3 From 60d3dd98a44230c21803c1606552ee098ed9fa7c Mon Sep 17 00:00:00 2001 From: Ansariel Date: Wed, 21 Feb 2024 21:05:14 +0100 Subject: Convert remaining BOOL to bool --- indra/newview/lllocationinputctrl.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'indra/newview/lllocationinputctrl.cpp') diff --git a/indra/newview/lllocationinputctrl.cpp b/indra/newview/lllocationinputctrl.cpp index c455a5e75b..a64ee6857d 100644 --- a/indra/newview/lllocationinputctrl.cpp +++ b/indra/newview/lllocationinputctrl.cpp @@ -517,7 +517,7 @@ bool LLLocationInputCtrl::handleKeyHere(KEY key, MASK mask) void LLLocationInputCtrl::onTextEntry(LLLineEditor* line_editor) { KEY key = gKeyboard->currentKey(); - MASK mask = gKeyboard->currentMask(TRUE); + 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; @@ -557,7 +557,7 @@ void LLLocationInputCtrl::setText(const LLStringExplicit& text) { mTextEntry->setText(text); } - mHasAutocompletedText = FALSE; + mHasAutocompletedText = false; } void LLLocationInputCtrl::setFocus(bool b) @@ -699,7 +699,7 @@ void LLLocationInputCtrl::onLocationPrearrange(const LLSD& data) //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); + LLInventoryModel::item_array_t landmark_items = LLLandmarkActions::fetchLandmarksByName(filter, true); for(U32 i=0; i < landmark_items.size(); i++) { @@ -1016,7 +1016,7 @@ void LLLocationInputCtrl::rebuildLocationHistory(const std::string& filter) void LLLocationInputCtrl::focusTextEntry() { - // We can't use "mTextEntry->setFocus(TRUE)" instead because + // 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) @@ -1104,7 +1104,7 @@ void LLLocationInputCtrl::changeLocationPresentation() mTextEntry->setText(LLURI::unescape(slurl.getSLURLString())); mTextEntry->selectAll(); - mMaturityButton->setVisible(FALSE); + mMaturityButton->setVisible(false); isHumanReadableLocationVisible = false; } -- cgit v1.2.3 From e2e37cced861b98de8c1a7c9c0d3a50d2d90e433 Mon Sep 17 00:00:00 2001 From: Ansariel Date: Wed, 22 May 2024 21:25:21 +0200 Subject: Fix line endlings --- indra/newview/lllocationinputctrl.cpp | 2580 ++++++++++++++++----------------- 1 file changed, 1290 insertions(+), 1290 deletions(-) (limited to 'indra/newview/lllocationinputctrl.cpp') diff --git a/indra/newview/lllocationinputctrl.cpp b/indra/newview/lllocationinputctrl.cpp index 39e578eb80..54dd5792a0 100644 --- a/indra/newview/lllocationinputctrl.cpp +++ b/indra/newview/lllocationinputctrl.cpp @@ -1,1290 +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 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(params); - mTextEntry->resetContextMenu(); - addChild(mTextEntry); - // LLLineEditor is replaced with LLLocationLineEditor - - // "Place information" button. - LLButton::Params info_params = p.info_button; - mInfoBtn = LLUICtrlFactory::create(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(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(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( 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(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(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(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(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(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(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(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(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(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(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("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 show_coords("NavBarShowCoordinates", false); - if(!hasFocus() && show_coords) - { - refreshLocation(); - } - - static LLUICachedControl 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 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 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("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 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(params); + mTextEntry->resetContextMenu(); + addChild(mTextEntry); + // LLLineEditor is replaced with LLLocationLineEditor + + // "Place information" button. + LLButton::Params info_params = p.info_button; + mInfoBtn = LLUICtrlFactory::create(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(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(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( 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(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(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(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(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(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(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(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(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(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(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("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 show_coords("NavBarShowCoordinates", false); + if(!hasFocus() && show_coords) + { + refreshLocation(); + } + + static LLUICachedControl 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 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 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("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); + } +} -- cgit v1.2.3