2196 lines
70 KiB
C++
2196 lines
70 KiB
C++
/**
|
|
* @file llfloaterworldmap.cpp
|
|
* @author James Cook, Tom Yedwab
|
|
* @brief LLFloaterWorldMap class implementation
|
|
*
|
|
* $LicenseInfo:firstyear=2003&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$
|
|
*/
|
|
|
|
/*
|
|
* Map of the entire world, with multiple background images,
|
|
* avatar tracking, teleportation by double-click, etc.
|
|
*/
|
|
|
|
#include "llviewerprecompiledheaders.h"
|
|
|
|
#include "llfloaterworldmap.h"
|
|
|
|
#include "llagent.h"
|
|
#include "llagentcamera.h"
|
|
#include "llbutton.h"
|
|
#include "llcallingcard.h"
|
|
#include "llcombobox.h"
|
|
#include "llcheckboxctrl.h"
|
|
#include "llsliderctrl.h"
|
|
#include "llviewercontrol.h"
|
|
#include "llcommandhandler.h"
|
|
#include "lldraghandle.h"
|
|
//#include "llfirstuse.h"
|
|
#include "llfloaterreg.h" // getTypedInstance()
|
|
#include "llfocusmgr.h"
|
|
//#include "lliconctrl.h" // <FS:Ansariel> Use own expand/collapse function
|
|
#include "llinventoryfunctions.h"
|
|
#include "llinventorymodel.h"
|
|
#include "llinventorymodelbackgroundfetch.h"
|
|
#include "llinventoryobserver.h"
|
|
#include "lllandmarklist.h"
|
|
#include "llsearcheditor.h"
|
|
#include "llnotificationsutil.h"
|
|
#include "llregionhandle.h"
|
|
#include "llscrolllistctrl.h"
|
|
#include "llviewernetwork.h" // <FS/> Access to GridManager
|
|
#include "lfsimfeaturehandler.h" // <FS/> hyperGridURL()
|
|
#include "llslurl.h"
|
|
#include "lltextbox.h"
|
|
#include "lltoolbarview.h"
|
|
#include "lltracker.h"
|
|
#include "lltrans.h"
|
|
#include "llviewerinventory.h" // LLViewerInventoryItem
|
|
#include "llviewermenu.h"
|
|
#include "llviewerparcelmgr.h"
|
|
#include "llviewerregion.h"
|
|
#include "llviewerstats.h"
|
|
#include "llviewertexture.h"
|
|
#include "llviewerwindow.h"
|
|
#include "llworldmap.h"
|
|
#include "llworldmapmessage.h"
|
|
#include "llworldmapview.h"
|
|
#include "lluictrlfactory.h"
|
|
#include "llappviewer.h"
|
|
#include "llmapimagetype.h"
|
|
#include "llweb.h"
|
|
#include "llsliderctrl.h"
|
|
#include "llspinctrl.h" // <FS/> setMinValue
|
|
#include "message.h"
|
|
#include "llwindow.h" // copyTextToClipboard()
|
|
#include <algorithm>
|
|
|
|
// [RLVa:KB] - Checked: 2010-08-22 (RLVa-1.2.1a)
|
|
#include "rlvhandler.h"
|
|
// [/RLVa:KB]
|
|
#include "llsdutil.h"
|
|
#include "llsdutil_math.h"
|
|
#include "alfloaterregiontracker.h"
|
|
#include "llstartup.h"
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Constants
|
|
//---------------------------------------------------------------------------
|
|
|
|
// Merov: we switched from using the "world size" (which varies depending where the user went) to a fixed
|
|
// width of 512 regions max visible at a time. This makes the zoom slider works in a consistent way across
|
|
// sessions and doesn't prevent the user to pan the world if it was to grow a lot beyond that limit.
|
|
// Currently (01/26/09), this value allows the whole grid to be visible in a 1024x1024 window.
|
|
static const S32 MAX_VISIBLE_REGIONS = 512;
|
|
|
|
|
|
const S32 HIDE_BEACON_PAD = 133;
|
|
|
|
// It would be more logical to have this inside the method where it is used but to compile under gcc this
|
|
// struct has to be here.
|
|
struct SortRegionNames
|
|
{
|
|
inline bool operator ()(std::pair <U64, LLSimInfo*> const& _left, std::pair <U64, LLSimInfo*> const& _right)
|
|
{
|
|
return(LLStringUtil::compareInsensitive(_left.second->getName(), _right.second->getName()) < 0);
|
|
}
|
|
};
|
|
|
|
enum EPanDirection
|
|
{
|
|
PAN_UP,
|
|
PAN_DOWN,
|
|
PAN_LEFT,
|
|
PAN_RIGHT
|
|
};
|
|
|
|
// Values in pixels per region
|
|
static const F32 ZOOM_MAX = 128.f;
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Globals
|
|
//---------------------------------------------------------------------------
|
|
|
|
// handle secondlife:///app/worldmap/{NAME}/{COORDS} URLs
|
|
class LLWorldMapHandler : public LLCommandHandler
|
|
{
|
|
public:
|
|
LLWorldMapHandler() : LLCommandHandler("worldmap", UNTRUSTED_THROTTLE)
|
|
{
|
|
}
|
|
|
|
virtual bool canHandleUntrusted(
|
|
const LLSD& params,
|
|
const LLSD& query_map,
|
|
LLMediaCtrl* web,
|
|
const std::string& nav_type)
|
|
{
|
|
if (nav_type == NAV_TYPE_CLICKED
|
|
|| nav_type == NAV_TYPE_EXTERNAL)
|
|
{
|
|
// NAV_TYPE_EXTERNAL will be throttled
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool handle(const LLSD& params,
|
|
const LLSD& query_map,
|
|
const std::string& grid,
|
|
LLMediaCtrl* web)
|
|
{
|
|
// <FS:Ansariel> FIRE-17927: Viewer crashes when opening worldmap SLURL command from login panel
|
|
if (LLStartUp::getStartupState() < STATE_STARTED)
|
|
{
|
|
return true;
|
|
}
|
|
// </FS:Ansariel>
|
|
|
|
if (params.size() == 0)
|
|
{
|
|
// support the secondlife:///app/worldmap SLapp
|
|
LLFloaterReg::showInstance("world_map", "center");
|
|
return true;
|
|
}
|
|
|
|
// support the secondlife:///app/worldmap/{LOCATION}/{COORDS} SLapp
|
|
const std::string region_name = LLURI::unescape(params[0].asString());
|
|
S32 x = (params.size() > 1) ? params[1].asInteger() : 128;
|
|
S32 y = (params.size() > 2) ? params[2].asInteger() : 128;
|
|
S32 z = (params.size() > 3) ? params[3].asInteger() : 0;
|
|
|
|
LLFloaterWorldMap::getInstance()->trackURL(region_name, x, y, z);
|
|
LLFloaterReg::showInstance("world_map", "center");
|
|
|
|
return true;
|
|
}
|
|
};
|
|
LLWorldMapHandler gWorldMapHandler;
|
|
|
|
// handle secondlife:///app/worldmap_global/{GLOBAL_COORDS} URLs
|
|
class LLWorldMapGlobalHandler : public LLCommandHandler
|
|
{
|
|
public:
|
|
LLWorldMapGlobalHandler() : LLCommandHandler("worldmap_global", UNTRUSTED_THROTTLE)
|
|
{}
|
|
|
|
virtual bool canHandleUntrusted(
|
|
const LLSD& params,
|
|
const LLSD& query_map,
|
|
LLMediaCtrl* web,
|
|
const std::string& nav_type)
|
|
{
|
|
if (nav_type == NAV_TYPE_CLICKED
|
|
|| nav_type == NAV_TYPE_EXTERNAL)
|
|
{
|
|
// NAV_TYPE_EXTERNAL will be throttled
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool handle(const LLSD& params,
|
|
const LLSD& query_map,
|
|
const std::string& grid,
|
|
LLMediaCtrl* web)
|
|
{
|
|
if (params.size() < 3)
|
|
{
|
|
LL_WARNS() << "Correct global coordinates are not provided." << LL_ENDL;
|
|
return true;
|
|
}
|
|
|
|
LLVector3d parcel_global_pos = LLVector3d(params[0].asInteger(), params[1].asInteger(), params[2].asInteger());
|
|
LLFloaterWorldMap* worldmap_instance = LLFloaterWorldMap::getInstance();
|
|
if (!parcel_global_pos.isExactlyZero() && worldmap_instance)
|
|
{
|
|
worldmap_instance->trackLocation(parcel_global_pos);
|
|
LLFloaterReg::showInstance("world_map", "center");
|
|
}
|
|
return true;
|
|
}
|
|
};
|
|
LLWorldMapGlobalHandler gWorldMapGlobalHandler;
|
|
|
|
// SocialMap handler secondlife:///app/maptrackavatar/id
|
|
class LLMapTrackAvatarHandler : public LLCommandHandler
|
|
{
|
|
public:
|
|
LLMapTrackAvatarHandler() : LLCommandHandler("maptrackavatar", UNTRUSTED_THROTTLE)
|
|
{
|
|
}
|
|
|
|
virtual bool canHandleUntrusted(
|
|
const LLSD& params,
|
|
const LLSD& query_map,
|
|
LLMediaCtrl* web,
|
|
const std::string& nav_type)
|
|
{
|
|
if (params.size() < 1)
|
|
{
|
|
return true; // don't block, will fail later
|
|
}
|
|
|
|
if (nav_type == NAV_TYPE_CLICKED
|
|
|| nav_type == NAV_TYPE_EXTERNAL)
|
|
{
|
|
// NAV_TYPE_EXTERNAL will be throttled
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool handle(const LLSD& params,
|
|
const LLSD& query_map,
|
|
const std::string& grid,
|
|
LLMediaCtrl* web)
|
|
{
|
|
//Make sure we have some parameters
|
|
if (params.size() == 0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
//Get the ID
|
|
LLUUID id;
|
|
if (!id.set( params[0], false ))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
LLFloaterWorldMap::getInstance()->avatarTrackFromSlapp( id );
|
|
LLFloaterReg::showInstance( "world_map", "center" );
|
|
|
|
return true;
|
|
}
|
|
};
|
|
LLMapTrackAvatarHandler gMapTrackAvatar;
|
|
|
|
LLFloaterWorldMap* gFloaterWorldMap = NULL;
|
|
|
|
class LLMapInventoryObserver : public LLInventoryObserver
|
|
{
|
|
public:
|
|
LLMapInventoryObserver() {}
|
|
virtual ~LLMapInventoryObserver() {}
|
|
virtual void changed(U32 mask);
|
|
};
|
|
|
|
void LLMapInventoryObserver::changed(U32 mask)
|
|
{
|
|
// if there's a change we're interested in.
|
|
if((mask & (LLInventoryObserver::CALLING_CARD | LLInventoryObserver::ADD |
|
|
LLInventoryObserver::REMOVE)) != 0)
|
|
{
|
|
gFloaterWorldMap->inventoryChanged();
|
|
}
|
|
}
|
|
|
|
class LLMapFriendObserver : public LLFriendObserver
|
|
{
|
|
public:
|
|
LLMapFriendObserver() {}
|
|
virtual ~LLMapFriendObserver() {}
|
|
virtual void changed(U32 mask);
|
|
};
|
|
|
|
void LLMapFriendObserver::changed(U32 mask)
|
|
{
|
|
// if there's a change we're interested in.
|
|
if((mask & (LLFriendObserver::ADD | LLFriendObserver::REMOVE | LLFriendObserver::ONLINE | LLFriendObserver::POWERS)) != 0)
|
|
{
|
|
gFloaterWorldMap->friendsChanged();
|
|
}
|
|
}
|
|
|
|
LLWorldMapParcelInfoObserver::LLWorldMapParcelInfoObserver(const LLVector3d& pos_global)
|
|
: LLRemoteParcelInfoObserver(),
|
|
mPosGlobal(pos_global),
|
|
mParcelID(LLUUID::null)
|
|
{ }
|
|
|
|
LLWorldMapParcelInfoObserver::~LLWorldMapParcelInfoObserver()
|
|
{
|
|
if (mParcelID.notNull())
|
|
{
|
|
LLRemoteParcelInfoProcessor::getInstance()->removeObserver(mParcelID, this);
|
|
}
|
|
}
|
|
|
|
void LLWorldMapParcelInfoObserver::processParcelInfo(const LLParcelData& parcel_data)
|
|
{
|
|
if (parcel_data.parcel_id == mParcelID)
|
|
{
|
|
LLRemoteParcelInfoProcessor::getInstance()->removeObserver(mParcelID, this);
|
|
|
|
if (gFloaterWorldMap)
|
|
{
|
|
gFloaterWorldMap->processParcelInfo(parcel_data, mPosGlobal);
|
|
}
|
|
}
|
|
}
|
|
|
|
// virtual
|
|
void LLWorldMapParcelInfoObserver::setParcelID(const LLUUID& parcel_id)
|
|
{
|
|
mParcelID = parcel_id;
|
|
auto instance = LLRemoteParcelInfoProcessor::getInstance();
|
|
instance->addObserver(mParcelID, this);
|
|
instance->sendParcelInfoRequest(mParcelID);
|
|
}
|
|
|
|
// virtual
|
|
void LLWorldMapParcelInfoObserver::setErrorStatus(S32 status, const std::string& reason)
|
|
{
|
|
LL_WARNS("LLWorldMapParcelInfoObserver") << "Can't handle remote parcel request." << " Http Status: " << status << ". Reason : " << reason << LL_ENDL;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Statics
|
|
//---------------------------------------------------------------------------
|
|
|
|
// Used as a pretend asset and inventory id to mean "landmark at my home location."
|
|
const LLUUID LLFloaterWorldMap::sHomeID( "10000000-0000-0000-0000-000000000001" );
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Construction and destruction
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
|
LLFloaterWorldMap::LLFloaterWorldMap(const LLSD& key)
|
|
: LLFloater(key),
|
|
mInventory(nullptr),
|
|
mInventoryObserver(nullptr),
|
|
mFriendObserver(nullptr),
|
|
mCompletingRegionName(),
|
|
mCompletingRegionPos(),
|
|
mWaitingForTracker(false),
|
|
mIsClosing(false),
|
|
mSetToUserPosition(true),
|
|
mProcessingSearchUpdate(false),
|
|
mTrackedLocation(0.0,0.0,0.0),
|
|
mTrackedStatus(LLTracker::TRACKING_NOTHING),
|
|
mParcelInfoObserver(nullptr),
|
|
mShowParcelInfo(false)
|
|
{
|
|
gFloaterWorldMap = this;
|
|
|
|
mFactoryMap["objects_mapview"] = LLCallbackMap(createWorldMapView, nullptr);
|
|
|
|
mCommitCallbackRegistrar.add("WMap.Coordinates", boost::bind(&LLFloaterWorldMap::onCoordinatesCommit, this));
|
|
mCommitCallbackRegistrar.add("WMap.Location", boost::bind(&LLFloaterWorldMap::onLocationCommit, this));
|
|
mCommitCallbackRegistrar.add("WMap.AvatarCombo", boost::bind(&LLFloaterWorldMap::onAvatarComboCommit, this));
|
|
mCommitCallbackRegistrar.add("WMap.Landmark", boost::bind(&LLFloaterWorldMap::onLandmarkComboCommit, this));
|
|
mCommitCallbackRegistrar.add("WMap.SearchResult", [this](LLUICtrl* ctrl, const LLSD& data) { LLFloaterWorldMap::onCommitSearchResult(false); });
|
|
mCommitCallbackRegistrar.add("WMap.GoHome", boost::bind(&LLFloaterWorldMap::onGoHome, this));
|
|
mCommitCallbackRegistrar.add("WMap.Teleport", boost::bind(&LLFloaterWorldMap::onClickTeleportBtn, this));
|
|
mCommitCallbackRegistrar.add("WMap.ShowTarget", boost::bind(&LLFloaterWorldMap::onShowTargetBtn, this));
|
|
mCommitCallbackRegistrar.add("WMap.ShowAgent", boost::bind(&LLFloaterWorldMap::onShowAgentBtn, this));
|
|
mCommitCallbackRegistrar.add("WMap.Clear", boost::bind(&LLFloaterWorldMap::onClearBtn, this));
|
|
mCommitCallbackRegistrar.add("WMap.CopySLURL", boost::bind(&LLFloaterWorldMap::onCopySLURL, this));
|
|
// <FS:Ansariel> Alchemy region tracker
|
|
mCommitCallbackRegistrar.add("WMap.TrackRegion", boost::bind(&LLFloaterWorldMap::onTrackRegion, this));
|
|
|
|
gSavedSettings.getControl("PreferredMaturity")->getSignal()->connect(boost::bind(&LLFloaterWorldMap::onChangeMaturity, this));
|
|
}
|
|
|
|
// static
|
|
void* LLFloaterWorldMap::createWorldMapView(void* data)
|
|
{
|
|
return new LLWorldMapView();
|
|
}
|
|
|
|
bool LLFloaterWorldMap::postBuild()
|
|
{
|
|
mMapView = dynamic_cast<LLWorldMapView*>(getChild<LLPanel>("objects_mapview"));
|
|
mMapView->setPan(0, 0, true);
|
|
|
|
mTeleportButton = getChild<LLButton>("Teleport");
|
|
mShowDestinationButton = getChild<LLButton>("Show Destination");
|
|
mCopySlurlButton = getChild<LLButton>("copy_slurl");
|
|
mGoHomeButton = getChild<LLButton>("Go Home");
|
|
|
|
mPeopleCheck = getChild<LLCheckBoxCtrl>("people_chk");
|
|
mInfohubCheck = getChild<LLCheckBoxCtrl>("infohub_chk");
|
|
mLandSaleCheck = getChild<LLCheckBoxCtrl>("land_for_sale_chk");
|
|
mEventsCheck = getChild<LLCheckBoxCtrl>("event_chk");
|
|
mEventsMatureCheck = getChild<LLCheckBoxCtrl>("events_mature_chk");
|
|
mEventsAdultCheck = getChild<LLCheckBoxCtrl>("events_adult_chk");
|
|
|
|
mAvatarIcon = getChild<LLUICtrl>("friends_icon"); // <FS:Ansariel> Used to be avatar_icon
|
|
mLandmarkIcon = getChild<LLUICtrl>("landmark_icon");
|
|
mLocationIcon = getChild<LLUICtrl>("location_icon");
|
|
|
|
// <FS> [FIRE-35333] OpenSim needs to be able to adjust the minValue
|
|
/*
|
|
mTeleportCoordSpinX = getChild<LLUICtrl>("teleport_coordinate_x");
|
|
mTeleportCoordSpinY = getChild<LLUICtrl>("teleport_coordinate_y");
|
|
mTeleportCoordSpinZ = getChild<LLUICtrl>("teleport_coordinate_z");
|
|
*/
|
|
mTeleportCoordSpinX = getChild<LLSpinCtrl>("teleport_coordinate_x");
|
|
mTeleportCoordSpinY = getChild<LLSpinCtrl>("teleport_coordinate_y");
|
|
mTeleportCoordSpinZ = getChild<LLSpinCtrl>("teleport_coordinate_z");
|
|
// </FS>
|
|
|
|
mFriendCombo = getChild<LLComboBox>("friend combo");
|
|
mFriendCombo->selectFirstItem();
|
|
mFriendCombo->setPrearrangeCallback(boost::bind(&LLFloaterWorldMap::onAvatarComboPrearrange, this));
|
|
mFriendCombo->setTextChangedCallback(boost::bind(&LLFloaterWorldMap::onComboTextEntry, this));
|
|
|
|
mLocationEditor = getChild<LLSearchEditor>("location");
|
|
mLocationEditor->setFocusChangedCallback(boost::bind(&LLFloaterWorldMap::onLocationFocusChanged, this, _1));
|
|
mLocationEditor->setTextChangedCallback(boost::bind(&LLFloaterWorldMap::onSearchTextEntry, this));
|
|
|
|
mSearchResults = getChild<LLScrollListCtrl>("search_results");
|
|
mSearchResults->setDoubleClickCallback(boost::bind(&LLFloaterWorldMap::onClickTeleportBtn, this));
|
|
|
|
mLandmarkCombo = getChild<LLComboBox>("landmark combo");
|
|
mLandmarkCombo->selectFirstItem();
|
|
mLandmarkCombo->setPrearrangeCallback(boost::bind(&LLFloaterWorldMap::onLandmarkComboPrearrange, this));
|
|
mLandmarkCombo->setTextChangedCallback(boost::bind(&LLFloaterWorldMap::onComboTextEntry, this));
|
|
|
|
mZoomSlider = getChild<LLSliderCtrl>("zoom slider");
|
|
F32 slider_zoom = mMapView->getZoom();
|
|
mZoomSlider->setValue(slider_zoom);
|
|
|
|
mTrackCtrlsPanel = getChild<LLPanel>("layout_panel_4");
|
|
mSearchButton = getChild<LLButton>("DoSearch");
|
|
|
|
// <FS:Ansariel> Use own expand/collapse function
|
|
//getChild<LLPanel>("expand_btn_panel")->setMouseDownCallback(boost::bind(&LLFloaterWorldMap::onExpandCollapseBtn, this));
|
|
|
|
mTrackCtrlsPanel->setDefaultBtn(nullptr);
|
|
|
|
onChangeMaturity();
|
|
|
|
return true;
|
|
}
|
|
|
|
// virtual
|
|
LLFloaterWorldMap::~LLFloaterWorldMap()
|
|
{
|
|
if (mParcelInfoObserver)
|
|
{
|
|
delete mParcelInfoObserver;
|
|
}
|
|
|
|
// All cleaned up by LLView destructor
|
|
mMapView = NULL;
|
|
|
|
// Inventory deletes all observers on shutdown
|
|
mInventory = NULL;
|
|
mInventoryObserver = NULL;
|
|
|
|
// avatar tracker will delete this for us.
|
|
mFriendObserver = NULL;
|
|
|
|
gFloaterWorldMap = NULL;
|
|
|
|
mTeleportFinishConnection.disconnect();
|
|
}
|
|
|
|
//static
|
|
LLFloaterWorldMap* LLFloaterWorldMap::getInstance()
|
|
{
|
|
return LLFloaterReg::getTypedInstance<LLFloaterWorldMap>("world_map");
|
|
}
|
|
|
|
// virtual
|
|
void LLFloaterWorldMap::onClose(bool app_quitting)
|
|
{
|
|
// While we're not visible, discard the overlay images we're using
|
|
LLWorldMap::getInstance()->clearImageRefs();
|
|
mTeleportFinishConnection.disconnect();
|
|
}
|
|
|
|
// virtual
|
|
void LLFloaterWorldMap::onOpen(const LLSD& key)
|
|
{
|
|
mTeleportFinishConnection = LLViewerParcelMgr::getInstance()->
|
|
setTeleportFinishedCallback(boost::bind(&LLFloaterWorldMap::onTeleportFinished, this));
|
|
|
|
bool center_on_target = (key.asString() == "center");
|
|
|
|
mIsClosing = false;
|
|
|
|
mMapView->clearLastClick();
|
|
|
|
{
|
|
// reset pan on show, so it centers on you again
|
|
if (!center_on_target)
|
|
{
|
|
mMapView->setPan(0, 0, true);
|
|
}
|
|
mMapView->updateVisibleBlocks();
|
|
|
|
// Reload items as they may have changed
|
|
LLWorldMap::getInstance()->reloadItems();
|
|
|
|
// We may already have a bounding box for the regions of the world,
|
|
// so use that to adjust the view.
|
|
adjustZoomSliderBounds();
|
|
|
|
// Could be first show
|
|
//LLFirstUse::useMap();
|
|
|
|
// Start speculative download of landmarks
|
|
const LLUUID landmark_folder_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_LANDMARK);
|
|
LLInventoryModelBackgroundFetch::instance().start(landmark_folder_id);
|
|
|
|
if (hasFocus())
|
|
{
|
|
mLocationEditor->setFocus( true);
|
|
gFocusMgr.triggerFocusFlash();
|
|
}
|
|
|
|
buildAvatarIDList();
|
|
buildLandmarkIDLists();
|
|
|
|
// If nothing is being tracked, set flag so the user position will be found
|
|
mSetToUserPosition = ( LLTracker::getTrackingStatus() == LLTracker::TRACKING_NOTHING );
|
|
}
|
|
|
|
if (center_on_target)
|
|
{
|
|
centerOnTarget(false);
|
|
}
|
|
|
|
// <FS> [FIRE-35333] OpenSim allows Z coordinates to be negative based on MinSimHeight
|
|
if (!LLGridManager::getInstance()->isInSecondLife())
|
|
{
|
|
LLViewerRegion* regionp = gAgent.getRegion();
|
|
F32 min_sim_height = regionp ? regionp->getMinSimHeight() : 0.f;
|
|
mTeleportCoordSpinZ->setMinValue(min_sim_height);
|
|
}
|
|
// </FS>
|
|
}
|
|
|
|
// static
|
|
void LLFloaterWorldMap::reloadIcons(void*)
|
|
{
|
|
LLWorldMap::getInstance()->reloadItems();
|
|
}
|
|
|
|
// virtual
|
|
bool LLFloaterWorldMap::handleHover(S32 x, S32 y, MASK mask)
|
|
{
|
|
bool handled;
|
|
handled = LLFloater::handleHover(x, y, mask);
|
|
return handled;
|
|
}
|
|
|
|
bool LLFloaterWorldMap::handleScrollWheel(S32 x, S32 y, S32 clicks)
|
|
{
|
|
if (!isMinimized() && isFrontmost())
|
|
{
|
|
S32 map_x = x - mMapView->getRect().mLeft;
|
|
S32 map_y = y - mMapView->getRect().mBottom;
|
|
if (mMapView->pointInView(map_x, map_y))
|
|
{
|
|
F32 old_slider_zoom = (F32) mZoomSlider->getValue().asReal();
|
|
F32 slider_zoom = old_slider_zoom + ((F32) clicks * -0.3333f);
|
|
mZoomSlider->setValue(LLSD(slider_zoom));
|
|
mMapView->zoomWithPivot(slider_zoom, map_x, map_y);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return LLFloater::handleScrollWheel(x, y, clicks);
|
|
}
|
|
|
|
|
|
// virtual
|
|
void LLFloaterWorldMap::reshape( S32 width, S32 height, bool called_from_parent )
|
|
{
|
|
LLFloater::reshape( width, height, called_from_parent );
|
|
}
|
|
|
|
|
|
// virtual
|
|
void LLFloaterWorldMap::draw()
|
|
{
|
|
// <FS:Ansariel> Performance improvement
|
|
static LLView* show_destination_btn = getChildView("Show Destination");
|
|
static LLUICtrl* zoom_slider = getChild<LLUICtrl>("zoom slider");
|
|
// </FS:Ansariel> Performance improvement
|
|
|
|
static LLUIColor map_track_color = LLUIColorTable::instance().getColor("MapTrackColor", LLColor4::white);
|
|
static LLUIColor map_track_disabled_color = LLUIColorTable::instance().getColor("MapTrackDisabledColor", LLColor4::white);
|
|
|
|
// On orientation island, users don't have a home location yet, so don't
|
|
// let them teleport "home". It dumps them in an often-crowed welcome
|
|
// area (infohub) and they get confused. JC
|
|
LLViewerRegion* regionp = gAgent.getRegion();
|
|
bool agent_on_prelude = (regionp && regionp->isPrelude());
|
|
bool enable_go_home = gAgent.isGodlike() || !agent_on_prelude;
|
|
mGoHomeButton->setEnabled(enable_go_home);
|
|
|
|
updateLocation();
|
|
|
|
LLTracker::ETrackingStatus tracking_status = LLTracker::getTrackingStatus();
|
|
if (LLTracker::TRACKING_AVATAR == tracking_status)
|
|
{
|
|
mAvatarIcon->setColor( map_track_color);
|
|
}
|
|
else
|
|
{
|
|
mAvatarIcon->setColor( map_track_disabled_color);
|
|
}
|
|
|
|
if (LLTracker::TRACKING_LANDMARK == tracking_status)
|
|
{
|
|
mLandmarkIcon->setColor( map_track_color);
|
|
}
|
|
else
|
|
{
|
|
mLandmarkIcon->setColor( map_track_disabled_color);
|
|
}
|
|
|
|
if (LLTracker::TRACKING_LOCATION == tracking_status)
|
|
{
|
|
mLocationIcon->setColor( map_track_color);
|
|
}
|
|
else
|
|
{
|
|
if (mCompletingRegionName != "")
|
|
{
|
|
F64 seconds = LLTimer::getElapsedSeconds();
|
|
double value = fmod(seconds, 2);
|
|
value = 0.5 + 0.5*cos(value * F_PI);
|
|
LLColor4 loading_color(0.0, F32(value/2), F32(value), 1.0);
|
|
mLocationIcon->setColor( loading_color);
|
|
}
|
|
else
|
|
{
|
|
mLocationIcon->setColor( map_track_disabled_color);
|
|
}
|
|
}
|
|
|
|
// check for completion of tracking data
|
|
if (mWaitingForTracker)
|
|
{
|
|
centerOnTarget(true);
|
|
}
|
|
|
|
// <FS:Ansariel> Performance improvement
|
|
// getChildView("Teleport")->setEnabled((bool)tracking_status);
|
|
// // getChildView("Clear")->setEnabled((bool)tracking_status);
|
|
// getChildView("Show Destination")->setEnabled((bool)tracking_status || LLWorldMap::getInstance()->isTracking());
|
|
// getChildView("copy_slurl")->setEnabled((mSLURL.isValid()) );
|
|
//// [RLVa:KB] - Checked: 2010-08-22 (RLVa-1.2.1a) | Added: RLVa-1.2.1a
|
|
// childSetEnabled("Go Home",
|
|
// (!rlv_handler_t::isEnabled()) || !(gRlvHandler.hasBehaviour(RLV_BHVR_TPLM) && gRlvHandler.hasBehaviour(RLV_BHVR_TPLOC)));
|
|
//// [/RLVa:KB]
|
|
mTeleportButton->setEnabled((bool)tracking_status);
|
|
mShowDestinationButton->setEnabled((bool)tracking_status || LLWorldMap::getInstance()->isTracking());
|
|
mCopySlurlButton->setEnabled((mSLURL.isValid()) );
|
|
mGoHomeButton->setEnabled((!rlv_handler_t::isEnabled()) || !(gRlvHandler.hasBehaviour(RLV_BHVR_TPLM) && gRlvHandler.hasBehaviour(RLV_BHVR_TPLOC)));
|
|
// </FS:Ansariel> Performance improvement
|
|
// <FS:Ansariel> Alchemy region tracker
|
|
getChild<LLButton>("track_region")->setEnabled((bool) tracking_status || LLWorldMap::getInstance()->isTracking());
|
|
|
|
setMouseOpaque(true);
|
|
getDragHandle()->setMouseOpaque(true);
|
|
|
|
mMapView->zoom((F32)mZoomSlider->getValue().asReal());
|
|
|
|
// Enable/disable checkboxes depending on the zoom level
|
|
// If above threshold level (i.e. low res) -> Disable all checkboxes
|
|
// If under threshold level (i.e. high res) -> Enable all checkboxes
|
|
bool enable = mMapView->showRegionInfo();
|
|
mPeopleCheck->setEnabled(enable);
|
|
mInfohubCheck->setEnabled(enable);
|
|
mLandSaleCheck->setEnabled(enable);
|
|
mEventsCheck->setEnabled(enable);
|
|
mEventsMatureCheck->setEnabled(enable);
|
|
mEventsAdultCheck->setEnabled(enable);
|
|
|
|
LLFloater::draw();
|
|
}
|
|
|
|
|
|
//-------------------------------------------------------------------------
|
|
// Internal utility functions
|
|
//-------------------------------------------------------------------------
|
|
|
|
void LLFloaterWorldMap::processParcelInfo(const LLParcelData& parcel_data, const LLVector3d& pos_global) const
|
|
{
|
|
LLVector3d tracker_pos = LLTracker::getTrackedPositionGlobal();
|
|
if (!mShowParcelInfo ||
|
|
(tracker_pos.mdV[VX] != pos_global.mdV[VX] && tracker_pos.mdV[VY] != pos_global.mdV[VY]) ||
|
|
LLTracker::getTrackedLocationType() != LLTracker::LOCATION_NOTHING ||
|
|
LLTracker::getTrackingStatus() != LLTracker::TRACKING_LOCATION)
|
|
{
|
|
return;
|
|
}
|
|
|
|
LLSimInfo* sim_info = LLWorldMap::getInstance()->simInfoFromPosGlobal(pos_global);
|
|
if (!sim_info)
|
|
{
|
|
return;
|
|
}
|
|
|
|
std::string sim_name = sim_info->getName();
|
|
U32 locX, locY;
|
|
from_region_handle(sim_info->getHandle(), &locX, &locY);
|
|
F32 region_x = (F32)(pos_global.mdV[VX] - locX);
|
|
F32 region_y = (F32)(pos_global.mdV[VY] - locY);
|
|
std::string full_name = llformat("%s (%d, %d, %d)",
|
|
sim_name.c_str(),
|
|
ll_round(region_x),
|
|
ll_round(region_y),
|
|
ll_round((F32)pos_global.mdV[VZ]));
|
|
|
|
LLTracker::trackLocation(pos_global, parcel_data.name.empty() ? getString("UnnamedParcel") : parcel_data.name, full_name);
|
|
}
|
|
|
|
void LLFloaterWorldMap::requestParcelInfo(const LLVector3d& pos_global, const LLVector3d& region_origin)
|
|
{
|
|
if (pos_global == mRequestedGlobalPos)
|
|
{
|
|
return;
|
|
}
|
|
|
|
LLViewerRegion* region = gAgent.getRegion();
|
|
if (!region)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (std::string url = region->getCapability("RemoteParcelRequest"); !url.empty())
|
|
{
|
|
mRequestedGlobalPos = pos_global;
|
|
if (mParcelInfoObserver)
|
|
{
|
|
delete mParcelInfoObserver;
|
|
}
|
|
mParcelInfoObserver = new LLWorldMapParcelInfoObserver(pos_global);
|
|
|
|
auto pos_region = LLVector3(pos_global - region_origin);
|
|
LLRemoteParcelInfoProcessor::instance().requestRegionParcelInfo(url,
|
|
region->getRegionID(), pos_region, pos_global,
|
|
mParcelInfoObserver->getObserverHandle());
|
|
}
|
|
else
|
|
{
|
|
LL_WARNS() << "Cannot request parcel details: Cap not found" << LL_ENDL;
|
|
}
|
|
}
|
|
|
|
void LLFloaterWorldMap::trackAvatar(const LLUUID& avatar_id, const std::string& name)
|
|
{
|
|
mShowParcelInfo = false;
|
|
|
|
buildAvatarIDList();
|
|
if (mFriendCombo->setCurrentByID(avatar_id) || gAgent.isGodlike())
|
|
{
|
|
// *HACK: Adjust Z values automatically for liaisons & gods so
|
|
// they swoop down when they click on the map. Requested
|
|
// convenience.
|
|
if (gAgent.isGodlike())
|
|
{
|
|
mTeleportCoordSpinZ->setValue(LLSD(200.f));
|
|
}
|
|
// Don't re-request info if we already have it or we won't have it in time to teleport
|
|
if (mTrackedStatus != LLTracker::TRACKING_AVATAR || avatar_id != mTrackedAvatarID)
|
|
{
|
|
mTrackedStatus = LLTracker::TRACKING_AVATAR;
|
|
mTrackedAvatarID = avatar_id;
|
|
LLTracker::trackAvatar(avatar_id, name);
|
|
centerOnTarget(true);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
LLTracker::stopTracking(false);
|
|
}
|
|
mTrackCtrlsPanel->setDefaultBtn(mTeleportButton);
|
|
}
|
|
|
|
void LLFloaterWorldMap::trackLandmark(const LLUUID& landmark_item_id)
|
|
{
|
|
mShowParcelInfo = false;
|
|
|
|
buildLandmarkIDLists();
|
|
bool found = false;
|
|
S32 idx;
|
|
for (idx = 0; idx < mLandmarkItemIDList.size(); idx++)
|
|
{
|
|
if (mLandmarkItemIDList.at(idx) == landmark_item_id)
|
|
{
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (found && mLandmarkCombo->setCurrentByID(landmark_item_id))
|
|
{
|
|
LLUUID asset_id = mLandmarkAssetIDList.at(idx);
|
|
std::string name = mLandmarkCombo->getSimple();
|
|
mTrackedStatus = LLTracker::TRACKING_LANDMARK;
|
|
LLTracker::trackLandmark(mLandmarkAssetIDList.at(idx), // assetID
|
|
mLandmarkItemIDList.at(idx), // itemID
|
|
name); // name
|
|
|
|
if (asset_id != sHomeID)
|
|
{
|
|
// start the download process
|
|
gLandmarkList.getAsset(asset_id);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
LLTracker::stopTracking(false);
|
|
}
|
|
mTrackCtrlsPanel->setDefaultBtn(mTeleportButton);
|
|
}
|
|
|
|
|
|
void LLFloaterWorldMap::trackEvent(const LLItemInfo &event_info)
|
|
{
|
|
mShowParcelInfo = false;
|
|
mTrackedStatus = LLTracker::TRACKING_LOCATION;
|
|
LLTracker::trackLocation(event_info.getGlobalPosition(), event_info.getName(), event_info.getToolTip(), LLTracker::LOCATION_EVENT);
|
|
mTrackCtrlsPanel->setDefaultBtn(mTeleportButton);
|
|
}
|
|
|
|
void LLFloaterWorldMap::trackGenericItem(const LLItemInfo &item)
|
|
{
|
|
mShowParcelInfo = false;
|
|
mTrackedStatus = LLTracker::TRACKING_LOCATION;
|
|
LLTracker::trackLocation(item.getGlobalPosition(), item.getName(), item.getToolTip(), LLTracker::LOCATION_ITEM);
|
|
mTrackCtrlsPanel->setDefaultBtn(mTeleportButton);
|
|
}
|
|
|
|
void LLFloaterWorldMap::trackLocation(const LLVector3d& pos_global)
|
|
{
|
|
mProcessingSearchUpdate = false;
|
|
LLSimInfo* sim_info = LLWorldMap::getInstance()->simInfoFromPosGlobal(pos_global);
|
|
if (!sim_info)
|
|
{
|
|
// We haven't found a region for that point yet, leave the tracking to the world map
|
|
LLTracker::stopTracking(false);
|
|
LLWorldMap::getInstance()->setTracking(pos_global);
|
|
S32 world_x = S32(pos_global.mdV[0] / 256);
|
|
S32 world_y = S32(pos_global.mdV[1] / 256);
|
|
LLWorldMapMessage::getInstance()->sendMapBlockRequest(world_x, world_y, world_x, world_y, true);
|
|
mTrackCtrlsPanel->setDefaultBtn(nullptr);
|
|
|
|
// clicked on a non-region - turn off coord display
|
|
enableTeleportCoordsDisplay( false );
|
|
|
|
return;
|
|
}
|
|
if (sim_info->isDown())
|
|
{
|
|
// Down region. Show the blue circle of death!
|
|
// i.e. let the world map that this and tell it it's invalid
|
|
LLTracker::stopTracking(false);
|
|
LLWorldMap::getInstance()->setTracking(pos_global);
|
|
LLWorldMap::getInstance()->setTrackingInvalid();
|
|
mTrackCtrlsPanel->setDefaultBtn(nullptr);
|
|
|
|
// clicked on a down region - turn off coord display
|
|
enableTeleportCoordsDisplay( false );
|
|
|
|
return;
|
|
}
|
|
|
|
std::string sim_name = sim_info->getName();
|
|
// <FS:CR> Aurora-sim var region teleports
|
|
//F32 region_x = (F32)fmod( pos_global.mdV[VX], (F64)REGION_WIDTH_METERS );
|
|
//F32 region_y = (F32)fmod( pos_global.mdV[VY], (F64)REGION_WIDTH_METERS );
|
|
U32 locX, locY;
|
|
from_region_handle(sim_info->getHandle(), &locX, &locY);
|
|
F32 region_x = (F32)(pos_global.mdV[VX] - locX);
|
|
F32 region_y = (F32)(pos_global.mdV[VY] - locY);
|
|
// </FS:CR>
|
|
std::string full_name = llformat("%s (%d, %d, %d)",
|
|
sim_name.c_str(),
|
|
ll_round(region_x),
|
|
ll_round(region_y),
|
|
ll_round((F32)pos_global.mdV[VZ]));
|
|
|
|
std::string tooltip("");
|
|
mTrackedStatus = LLTracker::TRACKING_LOCATION;
|
|
// [RLVa:KB] - Checked: 2012-02-08 (RLVa-1.4.5) | Added: RLVa-1.4.5
|
|
LLTracker::trackLocation(pos_global, (!gRlvHandler.hasBehaviour(RLV_BHVR_SHOWLOC)) ? full_name : RlvStrings::getString(RlvStringKeys::Hidden::Generic).c_str(), tooltip);
|
|
// [/RLVa:KB]
|
|
// LLTracker::trackLocation(pos_global, full_name, tooltip);
|
|
LLWorldMap::getInstance()->cancelTracking(); // The floater is taking over the tracking
|
|
|
|
// <FS:Ansariel> RLVa support
|
|
//mShowParcelInfo = true;
|
|
//requestParcelInfo(pos_global, sim_info->getGlobalOrigin());
|
|
if (!gRlvHandler.hasBehaviour(RLV_BHVR_SHOWLOC))
|
|
{
|
|
mShowParcelInfo = true;
|
|
requestParcelInfo(pos_global, sim_info->getGlobalOrigin());
|
|
}
|
|
else
|
|
{
|
|
mShowParcelInfo = false;
|
|
}
|
|
// </FS:Ansariel> RLVa support
|
|
|
|
LLVector3d coord_pos = LLTracker::getTrackedPositionGlobal();
|
|
updateTeleportCoordsDisplay( coord_pos );
|
|
|
|
// we have a valid region - turn on coord display
|
|
enableTeleportCoordsDisplay( true );
|
|
|
|
mTrackCtrlsPanel->setDefaultBtn(mTeleportButton);
|
|
}
|
|
|
|
// enable/disable teleport destination coordinates
|
|
void LLFloaterWorldMap::enableTeleportCoordsDisplay( bool enabled )
|
|
{
|
|
// [RLVa:KB] - Checked: 2012-02-08 (RLVa-1.4.5) | Added: RLVa-1.4.5
|
|
LLUICtrl* pCtrl = getChild<LLUICtrl>("events_label");
|
|
pCtrl->setVisible(!gRlvHandler.hasBehaviour(RLV_BHVR_SHOWLOC));
|
|
|
|
mTeleportCoordSpinX->setVisible(!gRlvHandler.hasBehaviour(RLV_BHVR_SHOWLOC));
|
|
mTeleportCoordSpinX->setEnabled(enabled);
|
|
|
|
mTeleportCoordSpinY->setVisible(!gRlvHandler.hasBehaviour(RLV_BHVR_SHOWLOC));
|
|
mTeleportCoordSpinY->setEnabled(enabled);
|
|
|
|
mTeleportCoordSpinZ->setVisible(!gRlvHandler.hasBehaviour(RLV_BHVR_SHOWLOC));
|
|
mTeleportCoordSpinZ->setEnabled(enabled);
|
|
// [/RLVa:KB]
|
|
// mTeleportCoordSpinX->setEnabled(enabled);
|
|
// mTeleportCoordSpinY->setEnabled(enabled);
|
|
// mTeleportCoordSpinZ->setEnabled(enabled);
|
|
}
|
|
|
|
// update display of teleport destination coordinates - pos is in global coordinates
|
|
void LLFloaterWorldMap::updateTeleportCoordsDisplay( const LLVector3d& pos )
|
|
{
|
|
// convert global specified position to a local one
|
|
// <FS:CR> Aurora-Sim var region support
|
|
//F32 region_local_x = (F32)fmod( pos.mdV[VX], (F64)REGION_WIDTH_METERS );
|
|
//F32 region_local_y = (F32)fmod( pos.mdV[VY], (F64)REGION_WIDTH_METERS );
|
|
LLSimInfo* sim_info = LLWorldMap::getInstance()->simInfoFromPosGlobal(pos);
|
|
if (sim_info)
|
|
{
|
|
// if we're going to update their value, we should also enable them
|
|
enableTeleportCoordsDisplay( true );
|
|
|
|
U32 locX, locY;
|
|
from_region_handle(sim_info->getHandle(), &locX, &locY);
|
|
F32 region_local_x = (F32)(pos.mdV[VX] - locX);
|
|
F32 region_local_y = (F32)(pos.mdV[VY] - locY);
|
|
//F32 region_local_z = (F32)fmod( pos.mdV[VZ], (F64)REGION_WIDTH_METERS );
|
|
F32 region_local_z = (F32)pos.mdV[VZ];
|
|
|
|
// write in the values
|
|
mTeleportCoordSpinX->setValue(region_local_x);
|
|
mTeleportCoordSpinY->setValue(region_local_y);
|
|
mTeleportCoordSpinZ->setValue(region_local_z);
|
|
}
|
|
// </FS:CR>
|
|
}
|
|
|
|
void LLFloaterWorldMap::updateLocation()
|
|
{
|
|
bool gotSimName;
|
|
|
|
LLTracker::ETrackingStatus status = LLTracker::getTrackingStatus();
|
|
|
|
// These values may get updated by a message, so need to check them every frame
|
|
// The fields may be changed by the user, so only update them if the data changes
|
|
LLVector3d pos_global = LLTracker::getTrackedPositionGlobal();
|
|
if (pos_global.isExactlyZero())
|
|
{
|
|
LLVector3d agentPos = gAgent.getPositionGlobal();
|
|
|
|
// Set to avatar's current postion if nothing is selected
|
|
if ( status == LLTracker::TRACKING_NOTHING && mSetToUserPosition )
|
|
{
|
|
// Make sure we know where we are before setting the current user position
|
|
std::string agent_sim_name;
|
|
gotSimName = LLWorldMap::getInstance()->simNameFromPosGlobal( agentPos, agent_sim_name );
|
|
// [RLVa:KB] - Checked: 2012-02-08 (RLVa-1.4.5) | Added: RLVa-1.4.5
|
|
if (gRlvHandler.hasBehaviour(RLV_BHVR_SHOWLOC))
|
|
{
|
|
mSetToUserPosition = false;
|
|
|
|
// Fill out the location field
|
|
getChild<LLUICtrl>("location")->setValue(RlvStrings::getString(RlvStringKeys::Hidden::Region));
|
|
|
|
// update the coordinate display with location of avatar in region
|
|
updateTeleportCoordsDisplay( agentPos );
|
|
|
|
mSLURL = LLSLURL();
|
|
}
|
|
else if (gotSimName)
|
|
// [/RLVa:KB]
|
|
// if ( gotSimName )
|
|
{
|
|
mSetToUserPosition = false;
|
|
|
|
// Fill out the location field
|
|
mLocationEditor->setValue(agent_sim_name);
|
|
|
|
// update the coordinate display with location of avatar in region
|
|
updateTeleportCoordsDisplay( agentPos );
|
|
|
|
// Figure out where user is
|
|
// Set the current SLURL
|
|
// <FS:CR> Aurora-sim var region teleports
|
|
//mSLURL = LLSLURL(agent_sim_name, gAgent.getPositionGlobal());
|
|
// <FS> [FIRE-35268] OpenSim support for when on other grids
|
|
if (LLGridManager::getInstance()->isInSecondLife())
|
|
mSLURL = LLSLURL(agent_sim_name, gAgent.getPositionAgent());
|
|
else
|
|
mSLURL = LLSLURL(LFSimFeatureHandler::instance().hyperGridURL(), agent_sim_name, gAgent.getPositionAgent());
|
|
// </FS>
|
|
// </FS:CR>
|
|
}
|
|
}
|
|
|
|
return; // invalid location
|
|
}
|
|
std::string sim_name;
|
|
// <FS:Beq pp Oren> FIRE-30768: SLURL's don't work in VarRegions
|
|
//gotSimName = LLWorldMap::getInstance()->simNameFromPosGlobal( pos_global, sim_name );
|
|
LLSimInfo* sim_info = LLWorldMap::getInstance()->simInfoFromPosGlobal(pos_global);
|
|
if (sim_info)
|
|
sim_name = sim_info->getName();
|
|
// </FS:Beq pp Oren>
|
|
if ((status != LLTracker::TRACKING_NOTHING) &&
|
|
(status != mTrackedStatus || pos_global != mTrackedLocation || sim_name != mTrackedSimName))
|
|
{
|
|
mTrackedStatus = status;
|
|
mTrackedLocation = pos_global;
|
|
mTrackedSimName = sim_name;
|
|
|
|
if (status == LLTracker::TRACKING_AVATAR)
|
|
{
|
|
// *HACK: Adjust Z values automatically for liaisons &
|
|
// gods so they swoop down when they click on the
|
|
// map. Requested convenience.
|
|
if(gAgent.isGodlike())
|
|
{
|
|
pos_global[2] = 200;
|
|
}
|
|
}
|
|
|
|
if (!mProcessingSearchUpdate)
|
|
{
|
|
mLocationEditor->setValue(sim_name);
|
|
}
|
|
|
|
// refresh coordinate display to reflect where user clicked.
|
|
LLVector3d coord_pos = LLTracker::getTrackedPositionGlobal();
|
|
updateTeleportCoordsDisplay( coord_pos );
|
|
|
|
// simNameFromPosGlobal can fail, so don't give the user an invalid SLURL
|
|
// [RLVa:KB] - Checked: 2012-02-08 (RLVa-1.4.5) | Added: RLVa-1.4.5
|
|
if (gRlvHandler.hasBehaviour(RLV_BHVR_SHOWLOC))
|
|
{
|
|
mSLURL = LLSLURL();
|
|
|
|
childSetValue("location", RlvStrings::getString(RlvStringKeys::Hidden::Region));
|
|
}
|
|
// <FS:Beq pp Oren> FORE-30768 Slurls in Var regions are b0rked
|
|
// else if (gotSimName)
|
|
else if (sim_info)
|
|
// [/RLVa:KB]
|
|
// if ( gotSimName )
|
|
{
|
|
// mSLURL = LLSLURL(sim_name, pos_global);
|
|
// <FS> [FIRE-35268] OpenSim support for when on other grids
|
|
if (LLGridManager::getInstance()->isInSecondLife())
|
|
mSLURL = LLSLURL(sim_info->getName(), sim_info->getGlobalOrigin(), pos_global);
|
|
else
|
|
mSLURL = LLSLURL(LFSimFeatureHandler::instance().hyperGridURL(), sim_info->getName(), sim_info->getGlobalOrigin(), pos_global);
|
|
// </FS>
|
|
}
|
|
// </FS:Beq pp Oren>
|
|
else
|
|
{ // Empty SLURL will disable the "Copy SLURL to clipboard" button
|
|
mSLURL = LLSLURL();
|
|
}
|
|
|
|
// [RLVa:KB] - Checked: 2009-07-04 (RLVa-1.0.0a)
|
|
/*
|
|
if (gRlvHandler.hasBehaviour(RLV_BHVR_SHOWLOC))
|
|
{
|
|
childSetValue("location", RlvStrings::getString(RLV_STRING_HIDDEN_REGION));
|
|
mSLURL.clear();
|
|
}
|
|
*/
|
|
// [/RLVa:KB]
|
|
}
|
|
}
|
|
|
|
void LLFloaterWorldMap::trackURL(const std::string& region_name, S32 x_coord, S32 y_coord, S32 z_coord)
|
|
{
|
|
LLSimInfo* sim_info = LLWorldMap::getInstance()->simInfoFromName(region_name);
|
|
// <FS> [FIRE-35333] OpenSim allows Z coordinates to be negative based on MinSimHeight
|
|
if (!LLGridManager::getInstance()->isInSecondLife())
|
|
{
|
|
LLViewerRegion* regionp = gAgent.getRegion();
|
|
F32 min_sim_height = regionp ? regionp->getMinSimHeight() : 0.f;
|
|
z_coord = llclamp(z_coord, min_sim_height, 4096);
|
|
}
|
|
else
|
|
{
|
|
z_coord = llclamp(z_coord, 0, 4096);
|
|
}
|
|
// </FS>
|
|
if (sim_info)
|
|
{
|
|
LLVector3 local_pos;
|
|
local_pos.mV[VX] = (F32)x_coord;
|
|
local_pos.mV[VY] = (F32)y_coord;
|
|
local_pos.mV[VZ] = (F32)z_coord;
|
|
LLVector3d global_pos = sim_info->getGlobalPos(local_pos);
|
|
trackLocation(global_pos);
|
|
mTrackCtrlsPanel->setDefaultBtn(mTeleportButton);
|
|
}
|
|
else
|
|
{
|
|
// fill in UI based on URL
|
|
mLocationEditor->setValue(region_name);
|
|
|
|
// Save local coords to highlight position after region global
|
|
// position is returned.
|
|
mCompletingRegionPos.set(
|
|
(F32)x_coord, (F32)y_coord, (F32)z_coord);
|
|
|
|
// pass sim name to combo box
|
|
mCompletingRegionName = region_name;
|
|
LLWorldMapMessage::getInstance()->sendNamedRegionRequest(region_name);
|
|
LLStringUtil::toLower(mCompletingRegionName);
|
|
LLWorldMap::getInstance()->setTrackingCommit();
|
|
}
|
|
}
|
|
|
|
void LLFloaterWorldMap::observeInventory(LLInventoryModel* model)
|
|
{
|
|
if(mInventory)
|
|
{
|
|
mInventory->removeObserver(mInventoryObserver);
|
|
delete mInventoryObserver;
|
|
mInventory = NULL;
|
|
mInventoryObserver = NULL;
|
|
}
|
|
if(model)
|
|
{
|
|
mInventory = model;
|
|
mInventoryObserver = new LLMapInventoryObserver;
|
|
// Inventory deletes all observers on shutdown
|
|
mInventory->addObserver(mInventoryObserver);
|
|
inventoryChanged();
|
|
}
|
|
}
|
|
|
|
void LLFloaterWorldMap::inventoryChanged()
|
|
{
|
|
if(!LLTracker::getTrackedLandmarkItemID().isNull())
|
|
{
|
|
LLUUID item_id = LLTracker::getTrackedLandmarkItemID();
|
|
buildLandmarkIDLists();
|
|
trackLandmark(item_id);
|
|
}
|
|
}
|
|
|
|
void LLFloaterWorldMap::observeFriends()
|
|
{
|
|
if(!mFriendObserver)
|
|
{
|
|
mFriendObserver = new LLMapFriendObserver;
|
|
LLAvatarTracker::instance().addObserver(mFriendObserver);
|
|
friendsChanged();
|
|
}
|
|
}
|
|
|
|
void LLFloaterWorldMap::friendsChanged()
|
|
{
|
|
LLAvatarTracker& t = LLAvatarTracker::instance();
|
|
const LLUUID& avatar_id = t.getAvatarID();
|
|
buildAvatarIDList();
|
|
if (avatar_id.notNull())
|
|
{
|
|
const LLRelationship* buddy_info = t.getBuddyInfo(avatar_id);
|
|
if (!mFriendCombo->setCurrentByID(avatar_id) ||
|
|
(buddy_info && !buddy_info->isRightGrantedFrom(LLRelationship::GRANT_MAP_LOCATION)) || gAgent.isGodlike())
|
|
{
|
|
LLTracker::stopTracking(false);
|
|
}
|
|
}
|
|
}
|
|
|
|
// No longer really builds a list. Instead, just updates mAvatarCombo.
|
|
void LLFloaterWorldMap::buildAvatarIDList()
|
|
{
|
|
// Delete all but the "None" entry
|
|
S32 list_size = mFriendCombo->getItemCount();
|
|
if (list_size > 1)
|
|
{
|
|
mFriendCombo->selectItemRange(1, -1);
|
|
mFriendCombo->operateOnSelection(LLCtrlListInterface::OP_DELETE);
|
|
}
|
|
|
|
// Get all of the calling cards for avatar that are currently online
|
|
LLCollectMappableBuddies collector;
|
|
LLAvatarTracker::instance().applyFunctor(collector);
|
|
LLCollectMappableBuddies::buddy_map_t::iterator it;
|
|
LLCollectMappableBuddies::buddy_map_t::iterator end;
|
|
it = collector.mMappable.begin();
|
|
end = collector.mMappable.end();
|
|
// <FS:Ansariel> Sort friend list alphabetically
|
|
//for(; it != end; ++it)
|
|
//{
|
|
// mFriendCombo->addSimpleElement((*it).second, ADD_BOTTOM, (*it).first);
|
|
//}
|
|
|
|
std::multimap<std::string, LLUUID> buddymap;
|
|
for(; it != end; ++it)
|
|
{
|
|
buddymap.insert(std::make_pair((*it).second, (*it).first));
|
|
}
|
|
for (std::multimap<std::string, LLUUID>::iterator bit = buddymap.begin(); bit != buddymap.end(); ++bit)
|
|
{
|
|
mFriendCombo->addSimpleElement((*bit).first, ADD_BOTTOM, (*bit).second);
|
|
}
|
|
// </FS:Ansariel>
|
|
|
|
mFriendCombo->setCurrentByID(LLAvatarTracker::instance().getAvatarID());
|
|
mFriendCombo->selectFirstItem();
|
|
}
|
|
|
|
|
|
void LLFloaterWorldMap::buildLandmarkIDLists()
|
|
{
|
|
// Delete all but the "None" entry
|
|
S32 list_size = mLandmarkCombo->getItemCount();
|
|
if (list_size > 1)
|
|
{
|
|
mLandmarkCombo->selectItemRange(1, -1);
|
|
mLandmarkCombo->operateOnSelection(LLCtrlListInterface::OP_DELETE);
|
|
}
|
|
|
|
mLandmarkItemIDList.clear();
|
|
mLandmarkAssetIDList.clear();
|
|
|
|
// Get all of the current landmarks
|
|
mLandmarkAssetIDList.push_back( LLUUID::null );
|
|
mLandmarkItemIDList.push_back( LLUUID::null );
|
|
|
|
mLandmarkAssetIDList.push_back( sHomeID );
|
|
mLandmarkItemIDList.push_back( sHomeID );
|
|
|
|
LLInventoryModel::cat_array_t cats;
|
|
LLInventoryModel::item_array_t items;
|
|
LLIsType is_landmark(LLAssetType::AT_LANDMARK);
|
|
gInventory.collectDescendentsIf(gInventory.getRootFolderID(),
|
|
cats,
|
|
items,
|
|
LLInventoryModel::EXCLUDE_TRASH,
|
|
is_landmark);
|
|
|
|
std::sort(items.begin(), items.end(), LLViewerInventoryItem::comparePointers());
|
|
|
|
mLandmarkAssetIDList.reserve(mLandmarkAssetIDList.size() + items.size());
|
|
mLandmarkItemIDList.reserve(mLandmarkItemIDList.size() + items.size());
|
|
|
|
// <FS:Ansariel> Filter duplicate landmarks on world map
|
|
uuid_set_t used_landmarks;
|
|
bool filterLandmarks = gSavedSettings.getBOOL("WorldmapFilterDuplicateLandmarks");
|
|
// </FS:Ansariel>
|
|
auto count = items.size();
|
|
for(size_t i = 0; i < count; ++i)
|
|
{
|
|
LLInventoryItem* item = items.at(i);
|
|
|
|
// <FS:Ansariel> Filter duplicate landmarks on world map
|
|
if (filterLandmarks)
|
|
{
|
|
if (used_landmarks.find(item->getAssetUUID()) != used_landmarks.end())
|
|
{
|
|
continue;
|
|
}
|
|
used_landmarks.insert(item->getAssetUUID());
|
|
}
|
|
// </FS:Ansariel>
|
|
|
|
mLandmarkCombo->addSimpleElement(item->getName(), ADD_BOTTOM, item->getUUID());
|
|
|
|
mLandmarkAssetIDList.push_back( item->getAssetUUID() );
|
|
mLandmarkItemIDList.push_back( item->getUUID() );
|
|
}
|
|
|
|
mLandmarkCombo->selectFirstItem();
|
|
}
|
|
|
|
|
|
F32 LLFloaterWorldMap::getDistanceToDestination(const LLVector3d &destination,
|
|
F32 z_attenuation) const
|
|
{
|
|
LLVector3d delta = destination - gAgent.getPositionGlobal();
|
|
// by attenuating the z-component we effectively
|
|
// give more weight to the x-y plane
|
|
delta.mdV[VZ] *= z_attenuation;
|
|
F32 distance = (F32)delta.magVec();
|
|
return distance;
|
|
}
|
|
|
|
|
|
void LLFloaterWorldMap::clearLocationSelection(bool clear_ui, bool dest_reached)
|
|
{
|
|
if (!dest_reached || (mSearchResults->getItemCount() == 1))
|
|
{
|
|
mSearchResults->operateOnAll(LLCtrlListInterface::OP_DELETE);
|
|
}
|
|
LLWorldMap::getInstance()->cancelTracking();
|
|
mCompletingRegionName = "";
|
|
}
|
|
|
|
|
|
void LLFloaterWorldMap::clearLandmarkSelection(bool clear_ui)
|
|
{
|
|
if (clear_ui || !childHasKeyboardFocus("landmark combo"))
|
|
{
|
|
mLandmarkCombo->selectByValue("None");
|
|
}
|
|
}
|
|
|
|
|
|
void LLFloaterWorldMap::clearAvatarSelection(bool clear_ui)
|
|
{
|
|
if (clear_ui || !childHasKeyboardFocus("friend combo"))
|
|
{
|
|
mTrackedStatus = LLTracker::TRACKING_NOTHING;
|
|
if (mFriendCombo->getSelectedValue().asString() != "None")
|
|
{
|
|
mFriendCombo->selectByValue("None");
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// Adjust the maximally zoomed out limit of the zoom slider so you
|
|
// can see the whole world, plus a little.
|
|
void LLFloaterWorldMap::adjustZoomSliderBounds()
|
|
{
|
|
// Merov: we switched from using the "world size" (which varies depending where the user went) to a fixed
|
|
// width of 512 regions max visible at a time. This makes the zoom slider works in a consistent way across
|
|
// sessions and doesn't prevent the user to pan the world if it was to grow a lot beyond that limit.
|
|
// Currently (01/26/09), this value allows the whole grid to be visible in a 1024x1024 window.
|
|
S32 world_width_regions = MAX_VISIBLE_REGIONS;
|
|
S32 world_height_regions = MAX_VISIBLE_REGIONS;
|
|
|
|
// Find how much space we have to display the world
|
|
LLRect view_rect = mMapView->getRect();
|
|
|
|
// View size in pixels
|
|
S32 view_width = view_rect.getWidth();
|
|
S32 view_height = view_rect.getHeight();
|
|
|
|
// Pixels per region to display entire width/height
|
|
F32 width_pixels_per_region = (F32) view_width / (F32) world_width_regions;
|
|
F32 height_pixels_per_region = (F32) view_height / (F32) world_height_regions;
|
|
|
|
F32 pixels_per_region = llmin(width_pixels_per_region,
|
|
height_pixels_per_region);
|
|
|
|
// Round pixels per region to an even number of slider increments
|
|
S32 slider_units = llfloor(pixels_per_region / 0.2f);
|
|
pixels_per_region = slider_units * 0.2f;
|
|
|
|
// Make sure the zoom slider can be moved at least a little bit.
|
|
// Likewise, less than the increment pixels per region is just silly.
|
|
pixels_per_region = llclamp(pixels_per_region, 1.f, ZOOM_MAX);
|
|
|
|
F32 min_power = log(pixels_per_region/256.f)/log(2.f);
|
|
|
|
mZoomSlider->setMinValue(min_power);
|
|
}
|
|
|
|
|
|
//-------------------------------------------------------------------------
|
|
// User interface widget callbacks
|
|
//-------------------------------------------------------------------------
|
|
|
|
void LLFloaterWorldMap::onGoHome()
|
|
{
|
|
gAgent.teleportHome();
|
|
closeFloater();
|
|
mProcessingSearchUpdate = false;
|
|
}
|
|
|
|
|
|
void LLFloaterWorldMap::onLandmarkComboPrearrange()
|
|
{
|
|
if (mIsClosing)
|
|
{
|
|
return;
|
|
}
|
|
|
|
LLUUID current_choice = mLandmarkCombo->getCurrentID();
|
|
|
|
buildLandmarkIDLists();
|
|
|
|
if (current_choice.isNull() || !mLandmarkCombo->setCurrentByID(current_choice))
|
|
{
|
|
LLTracker::stopTracking(false);
|
|
}
|
|
}
|
|
|
|
void LLFloaterWorldMap::onComboTextEntry()
|
|
{
|
|
// Reset the tracking whenever we start typing into any of the search fields,
|
|
// so that hitting <enter> does an auto-complete versus teleporting us to the
|
|
// previously selected landmark/friend.
|
|
LLTracker::stopTracking(false);
|
|
}
|
|
|
|
void LLFloaterWorldMap::onSearchTextEntry( )
|
|
{
|
|
onComboTextEntry();
|
|
updateSearchEnabled();
|
|
}
|
|
|
|
|
|
void LLFloaterWorldMap::onLandmarkComboCommit()
|
|
{
|
|
if (mIsClosing)
|
|
{
|
|
return;
|
|
}
|
|
|
|
LLUUID asset_id;
|
|
LLUUID item_id = mLandmarkCombo->getCurrentID();
|
|
|
|
LLTracker::stopTracking(false);
|
|
|
|
// RN: stopTracking() clears current combobox selection, need to reassert it here
|
|
mLandmarkCombo->setCurrentByID(item_id);
|
|
|
|
if (item_id.isNull()) {}
|
|
else if (item_id == sHomeID)
|
|
{
|
|
asset_id = sHomeID;
|
|
}
|
|
else
|
|
{
|
|
LLInventoryItem* item = gInventory.getItem(item_id);
|
|
if (item)
|
|
{
|
|
asset_id = item->getAssetUUID();
|
|
}
|
|
else
|
|
{
|
|
// Something went wrong, so revert to a safe value.
|
|
item_id.setNull();
|
|
}
|
|
}
|
|
|
|
trackLandmark(item_id);
|
|
onShowTargetBtn();
|
|
|
|
// Reset to user postion if nothing is tracked
|
|
mSetToUserPosition = (LLTracker::getTrackingStatus() == LLTracker::TRACKING_NOTHING);
|
|
}
|
|
|
|
// static
|
|
void LLFloaterWorldMap::onAvatarComboPrearrange()
|
|
{
|
|
if (mIsClosing)
|
|
{
|
|
return;
|
|
}
|
|
|
|
LLUUID current_choice;
|
|
|
|
if (LLAvatarTracker::instance().haveTrackingInfo())
|
|
{
|
|
current_choice = LLAvatarTracker::instance().getAvatarID();
|
|
}
|
|
|
|
buildAvatarIDList();
|
|
|
|
if (!mFriendCombo->setCurrentByID(current_choice) || current_choice.isNull())
|
|
{
|
|
LLTracker::stopTracking(false);
|
|
}
|
|
}
|
|
|
|
void LLFloaterWorldMap::onAvatarComboCommit()
|
|
{
|
|
if (mIsClosing)
|
|
{
|
|
return;
|
|
}
|
|
|
|
const LLUUID& new_avatar_id = mFriendCombo->getCurrentID();
|
|
if (new_avatar_id.notNull())
|
|
{
|
|
std::string name = mFriendCombo->getSimple();
|
|
trackAvatar(new_avatar_id, name);
|
|
onShowTargetBtn();
|
|
}
|
|
else
|
|
{ // Reset to user postion if nothing is tracked
|
|
mSetToUserPosition = (LLTracker::getTrackingStatus() == LLTracker::TRACKING_NOTHING);
|
|
}
|
|
}
|
|
|
|
void LLFloaterWorldMap::avatarTrackFromSlapp( const LLUUID& id )
|
|
{
|
|
trackAvatar( id, "av" );
|
|
onShowTargetBtn();
|
|
}
|
|
|
|
void LLFloaterWorldMap::onLocationFocusChanged( LLFocusableElement* focus )
|
|
{
|
|
updateSearchEnabled();
|
|
}
|
|
|
|
void LLFloaterWorldMap::updateSearchEnabled()
|
|
{
|
|
if (childHasKeyboardFocus("location") &&
|
|
mLocationEditor->getValue().asString().length() > 0)
|
|
{
|
|
mTrackCtrlsPanel->setDefaultBtn(mSearchButton);
|
|
}
|
|
else
|
|
{
|
|
mTrackCtrlsPanel->setDefaultBtn(nullptr);
|
|
}
|
|
}
|
|
|
|
void LLFloaterWorldMap::onLocationCommit()
|
|
{
|
|
if( mIsClosing )
|
|
{
|
|
return;
|
|
}
|
|
|
|
clearLocationSelection(false);
|
|
mCompletingRegionName = "";
|
|
mLastRegionName = "";
|
|
|
|
std::string str = mLocationEditor->getValue().asString();
|
|
|
|
// Trim any leading and trailing spaces in the search target
|
|
std::string saved_str = str;
|
|
LLStringUtil::trim( str );
|
|
if ( str != saved_str )
|
|
{ // Set the value in the UI if any spaces were removed
|
|
mLocationEditor->setValue(str);
|
|
}
|
|
|
|
// Don't try completing empty name (STORM-1427).
|
|
if (str.empty())
|
|
{
|
|
return;
|
|
}
|
|
mProcessingSearchUpdate = true;
|
|
|
|
LLStringUtil::toLower(str);
|
|
mCompletingRegionName = str;
|
|
LLWorldMap::getInstance()->setTrackingCommit();
|
|
if (str.length() >= 3)
|
|
{
|
|
LLWorldMapMessage::getInstance()->sendNamedRegionRequest(str);
|
|
}
|
|
else
|
|
{
|
|
str += "#";
|
|
LLWorldMapMessage::getInstance()->sendNamedRegionRequest(str);
|
|
}
|
|
}
|
|
|
|
void LLFloaterWorldMap::onCoordinatesCommit()
|
|
{
|
|
if( mIsClosing )
|
|
{
|
|
return;
|
|
}
|
|
mProcessingSearchUpdate = false;
|
|
|
|
S32 x_coord = (S32)mTeleportCoordSpinX->getValue().asReal();
|
|
S32 y_coord = (S32)mTeleportCoordSpinY->getValue().asReal();
|
|
S32 z_coord = (S32)mTeleportCoordSpinZ->getValue().asReal();
|
|
|
|
const std::string region_name = mLocationEditor->getValue().asString();
|
|
|
|
trackURL( region_name, x_coord, y_coord, z_coord );
|
|
}
|
|
|
|
void LLFloaterWorldMap::onClearBtn()
|
|
{
|
|
mTrackedStatus = LLTracker::TRACKING_NOTHING;
|
|
mProcessingSearchUpdate = false;
|
|
LLTracker::stopTracking(true);
|
|
LLWorldMap::getInstance()->cancelTracking();
|
|
mSLURL = LLSLURL(); // Clear the SLURL since it's invalid
|
|
mSetToUserPosition = true; // Revert back to the current user position
|
|
}
|
|
|
|
void LLFloaterWorldMap::onShowTargetBtn()
|
|
{
|
|
centerOnTarget(true);
|
|
}
|
|
|
|
void LLFloaterWorldMap::onShowAgentBtn()
|
|
{
|
|
mMapView->setPanWithInterpTime(0, 0, false, 0.1f); // false == animate
|
|
// Set flag so user's location will be displayed if not tracking anything else
|
|
mSetToUserPosition = true;
|
|
mProcessingSearchUpdate = false;
|
|
}
|
|
|
|
void LLFloaterWorldMap::onClickTeleportBtn()
|
|
{
|
|
teleport();
|
|
}
|
|
|
|
void LLFloaterWorldMap::onCopySLURL()
|
|
{
|
|
getWindow()->copyTextToClipboard(utf8str_to_wstring(mSLURL.getSLURLString()));
|
|
|
|
LLSD args;
|
|
args["SLURL"] = mSLURL.getSLURLString();
|
|
|
|
LLNotificationsUtil::add("CopySLURL", args);
|
|
}
|
|
|
|
// <FS:Ansariel> Use own expand/collapse function
|
|
//void LLFloaterWorldMap::onExpandCollapseBtn()
|
|
//{
|
|
// LLLayoutStack* floater_stack = getChild<LLLayoutStack>("floater_map_stack");
|
|
// LLLayoutPanel* controls_panel = getChild<LLLayoutPanel>("controls_lp");
|
|
|
|
// bool toggle_collapse = !controls_panel->isCollapsed();
|
|
// floater_stack->collapsePanel(controls_panel, toggle_collapse);
|
|
// floater_stack->updateLayout();
|
|
|
|
// std::string image_name = getString(toggle_collapse ? "expand_icon" : "collapse_icon");
|
|
// std::string tooltip = getString(toggle_collapse ? "expand_tooltip" : "collapse_tooltip");
|
|
// LLIconCtrl* expandCollapseIcon = getChild<LLIconCtrl>("expand_collapse_icon");
|
|
// expandCollapseIcon->setImage(LLUI::getUIImage(image_name));
|
|
// expandCollapseIcon->setToolTip(tooltip);
|
|
// getChild<LLPanel>("expand_btn_panel")->setToolTip(tooltip);
|
|
//}
|
|
// </FS:Ansariel>
|
|
|
|
// <FS:Ansariel> Alchemy region tracker
|
|
void LLFloaterWorldMap::onTrackRegion()
|
|
{
|
|
ALFloaterRegionTracker* floaterp = LLFloaterReg::getTypedInstance<ALFloaterRegionTracker>("region_tracker");
|
|
if (floaterp)
|
|
{
|
|
if (LLTracker::getTrackingStatus() != LLTracker::TRACKING_NOTHING)
|
|
{
|
|
std::string sim_name;
|
|
LLWorldMap::getInstance()->simNameFromPosGlobal(LLTracker::getTrackedPositionGlobal(), sim_name);
|
|
if (!sim_name.empty())
|
|
{
|
|
const std::string& temp_label = floaterp->getRegionLabelIfExists(sim_name);
|
|
LLSD args, payload;
|
|
args["REGION"] = sim_name;
|
|
args["LABEL"] = !temp_label.empty() ? temp_label : sim_name;
|
|
payload["name"] = sim_name;
|
|
LLNotificationsUtil::add("RegionTrackerAdd", args, payload, boost::bind(&ALFloaterRegionTracker::onRegionAddedCallback, floaterp, _1, _2));
|
|
}
|
|
else
|
|
{
|
|
LL_WARNS() << "Cannot add region to region tracker because sim name is empty." << LL_ENDL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
LL_WARNS() << "Cannot add region to region tracker because we aren't tracking anything." << LL_ENDL;
|
|
}
|
|
}
|
|
}
|
|
// </FS:Ansariel>
|
|
|
|
// protected
|
|
void LLFloaterWorldMap::centerOnTarget(bool animate)
|
|
{
|
|
LLVector3d pos_global;
|
|
if(LLTracker::getTrackingStatus() != LLTracker::TRACKING_NOTHING)
|
|
{
|
|
LLVector3d tracked_position = LLTracker::getTrackedPositionGlobal();
|
|
//RN: tracker doesn't allow us to query completion, so we check for a tracking position of
|
|
// absolute zero, and keep trying in the draw loop
|
|
if (tracked_position.isExactlyZero())
|
|
{
|
|
mWaitingForTracker = true;
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
// We've got the position finally, so we're no longer busy. JC
|
|
// getWindow()->decBusyCount();
|
|
pos_global = LLTracker::getTrackedPositionGlobal() - gAgentCamera.getCameraPositionGlobal();
|
|
}
|
|
}
|
|
else if(LLWorldMap::getInstance()->isTracking())
|
|
{
|
|
pos_global = LLWorldMap::getInstance()->getTrackedPositionGlobal() - gAgentCamera.getCameraPositionGlobal();;
|
|
|
|
|
|
|
|
}
|
|
else
|
|
{
|
|
// default behavior = center on agent
|
|
pos_global.clearVec();
|
|
}
|
|
|
|
F64 map_scale = (F64)mMapView->getScale();
|
|
mMapView->setPanWithInterpTime(-llfloor((F32)(pos_global.mdV[VX] * map_scale / REGION_WIDTH_METERS)),
|
|
-llfloor((F32)(pos_global.mdV[VY] * map_scale / REGION_WIDTH_METERS)),
|
|
!animate, 0.1f);
|
|
mWaitingForTracker = false;
|
|
}
|
|
|
|
// protected
|
|
void LLFloaterWorldMap::fly()
|
|
{
|
|
LLVector3d pos_global = LLTracker::getTrackedPositionGlobal();
|
|
|
|
// Start the autopilot and close the floater,
|
|
// so we can see where we're flying
|
|
if (!pos_global.isExactlyZero())
|
|
{
|
|
gAgent.startAutoPilotGlobal( pos_global );
|
|
closeFloater();
|
|
}
|
|
else
|
|
{
|
|
make_ui_sound("UISndInvalidOp");
|
|
}
|
|
}
|
|
|
|
|
|
// protected
|
|
void LLFloaterWorldMap::teleport()
|
|
{
|
|
bool teleport_home = false;
|
|
LLVector3d pos_global;
|
|
LLAvatarTracker& av_tracker = LLAvatarTracker::instance();
|
|
|
|
LLTracker::ETrackingStatus tracking_status = LLTracker::getTrackingStatus();
|
|
if (LLTracker::TRACKING_AVATAR == tracking_status
|
|
&& av_tracker.haveTrackingInfo() )
|
|
{
|
|
pos_global = av_tracker.getGlobalPos();
|
|
pos_global.mdV[VZ] = mTeleportCoordSpinZ->getValue();
|
|
}
|
|
else if ( LLTracker::TRACKING_LANDMARK == tracking_status)
|
|
{
|
|
if( LLTracker::getTrackedLandmarkAssetID() == sHomeID )
|
|
{
|
|
teleport_home = true;
|
|
}
|
|
else
|
|
{
|
|
LLLandmark* landmark = gLandmarkList.getAsset( LLTracker::getTrackedLandmarkAssetID() );
|
|
LLUUID region_id;
|
|
if(landmark
|
|
&& !landmark->getGlobalPos(pos_global)
|
|
&& landmark->getRegionID(region_id))
|
|
{
|
|
LLLandmark::requestRegionHandle(
|
|
gMessageSystem,
|
|
gAgent.getRegionHost(),
|
|
region_id,
|
|
NULL);
|
|
}
|
|
}
|
|
}
|
|
else if ( LLTracker::TRACKING_LOCATION == tracking_status)
|
|
{
|
|
pos_global = LLTracker::getTrackedPositionGlobal();
|
|
}
|
|
else
|
|
{
|
|
make_ui_sound("UISndInvalidOp");
|
|
}
|
|
|
|
// Do the teleport, which will also close the floater
|
|
if (teleport_home)
|
|
{
|
|
gAgent.teleportHome();
|
|
}
|
|
else if (!pos_global.isExactlyZero())
|
|
{
|
|
if(LLTracker::TRACKING_LANDMARK == tracking_status)
|
|
{
|
|
gAgent.teleportViaLandmark(LLTracker::getTrackedLandmarkAssetID());
|
|
}
|
|
else
|
|
{
|
|
gAgent.teleportViaLocation( pos_global );
|
|
}
|
|
}
|
|
|
|
if (mProcessingSearchUpdate)
|
|
{
|
|
mProcessingSearchUpdate = false;
|
|
mTrackedSimName.clear();
|
|
}
|
|
}
|
|
|
|
void LLFloaterWorldMap::flyToLandmark()
|
|
{
|
|
LLVector3d destination_pos_global;
|
|
if( !LLTracker::getTrackedLandmarkAssetID().isNull() )
|
|
{
|
|
if (LLTracker::hasLandmarkPosition())
|
|
{
|
|
gAgent.startAutoPilotGlobal( LLTracker::getTrackedPositionGlobal() );
|
|
}
|
|
}
|
|
}
|
|
|
|
void LLFloaterWorldMap::teleportToLandmark()
|
|
{
|
|
bool has_destination = false;
|
|
LLUUID destination_id; // Null means "home"
|
|
|
|
if( LLTracker::getTrackedLandmarkAssetID() == sHomeID )
|
|
{
|
|
has_destination = true;
|
|
}
|
|
else
|
|
{
|
|
LLLandmark* landmark = gLandmarkList.getAsset( LLTracker::getTrackedLandmarkAssetID() );
|
|
LLVector3d global_pos;
|
|
if(landmark && landmark->getGlobalPos(global_pos))
|
|
{
|
|
destination_id = LLTracker::getTrackedLandmarkAssetID();
|
|
has_destination = true;
|
|
}
|
|
else if(landmark)
|
|
{
|
|
// pop up an anonymous request request.
|
|
LLUUID region_id;
|
|
if(landmark->getRegionID(region_id))
|
|
{
|
|
LLLandmark::requestRegionHandle(
|
|
gMessageSystem,
|
|
gAgent.getRegionHost(),
|
|
region_id,
|
|
NULL);
|
|
}
|
|
}
|
|
}
|
|
|
|
if( has_destination )
|
|
{
|
|
gAgent.teleportViaLandmark( destination_id );
|
|
}
|
|
}
|
|
|
|
|
|
void LLFloaterWorldMap::teleportToAvatar()
|
|
{
|
|
LLAvatarTracker& av_tracker = LLAvatarTracker::instance();
|
|
if(av_tracker.haveTrackingInfo())
|
|
{
|
|
LLVector3d pos_global = av_tracker.getGlobalPos();
|
|
gAgent.teleportViaLocation( pos_global );
|
|
}
|
|
}
|
|
|
|
|
|
void LLFloaterWorldMap::flyToAvatar()
|
|
{
|
|
if (LLAvatarTracker::instance().haveTrackingInfo())
|
|
{
|
|
gAgent.startAutoPilotGlobal(LLAvatarTracker::instance().getGlobalPos());
|
|
}
|
|
}
|
|
|
|
void LLFloaterWorldMap::updateSims(bool found_null_sim)
|
|
{
|
|
if (mCompletingRegionName == "")
|
|
{
|
|
return;
|
|
}
|
|
|
|
mSearchResults->operateOnAll(LLCtrlListInterface::OP_DELETE);
|
|
|
|
// auto name_length = mCompletingRegionName.length(); // <FS:Beq/> FIRE-23591 support map search partial matches (Patch by Kevin Cozens)
|
|
|
|
LLSD match;
|
|
|
|
S32 num_results = 0;
|
|
|
|
std::vector<std::pair <U64, LLSimInfo*> > sim_info_vec(LLWorldMap::getInstance()->getRegionMap().begin(), LLWorldMap::getInstance()->getRegionMap().end());
|
|
std::sort(sim_info_vec.begin(), sim_info_vec.end(), SortRegionNames());
|
|
|
|
for (std::vector<std::pair <U64, LLSimInfo*> >::const_iterator it = sim_info_vec.begin(); it != sim_info_vec.end(); ++it)
|
|
{
|
|
LLSimInfo* info = it->second;
|
|
std::string sim_name_lower = info->getName();
|
|
LLStringUtil::toLower(sim_name_lower);
|
|
// <FS:Beq> FIRE-23591 support map search partial matches (Patch by Kevin Cozens)
|
|
// if (sim_name_lower.substr(0, name_length) == mCompletingRegionName)
|
|
if (sim_name_lower.find(mCompletingRegionName) != std::string::npos)
|
|
// <FS:Beq>
|
|
{
|
|
if (sim_name_lower == mCompletingRegionName)
|
|
{
|
|
match = info->getName();
|
|
}
|
|
|
|
LLSD value;
|
|
value["id"] = info->getName();
|
|
value["columns"][0]["column"] = "sim_name";
|
|
value["columns"][0]["value"] = info->getName();
|
|
mSearchResults->addElement(value);
|
|
num_results++;
|
|
}
|
|
}
|
|
|
|
if (found_null_sim)
|
|
{
|
|
mCompletingRegionName = "";
|
|
}
|
|
|
|
if (num_results > 0)
|
|
{
|
|
// Ansariel: Let's sort the list to make it more user-friendly
|
|
mSearchResults->sortByColumn("sim_name", true);
|
|
|
|
// if match found, highlight it and go
|
|
if (!match.isUndefined())
|
|
{
|
|
mSearchResults->selectByValue(match);
|
|
mSearchResults->setFocus(true);
|
|
onCommitSearchResult(false /*fully commit the only option*/);
|
|
}
|
|
// else let user decide
|
|
else
|
|
{
|
|
mSearchResults->selectFirstItem();
|
|
mSearchResults->setFocus(true);
|
|
onCommitSearchResult(true /*don't update text field*/);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// if we found nothing, say "none"
|
|
mProcessingSearchUpdate = false;
|
|
mSearchResults->setCommentText(LLTrans::getString("worldmap_results_none_found"));
|
|
mSearchResults->operateOnAll(LLCtrlListInterface::OP_DESELECT);
|
|
}
|
|
}
|
|
|
|
void LLFloaterWorldMap::onTeleportFinished()
|
|
{
|
|
if(isInVisibleChain())
|
|
{
|
|
mMapView->setPan(0, 0, true);
|
|
}
|
|
}
|
|
|
|
void LLFloaterWorldMap::onCommitSearchResult(bool from_search)
|
|
{
|
|
std::string sim_name = mSearchResults->getSelectedValue().asString();
|
|
if (sim_name.empty())
|
|
{
|
|
return;
|
|
}
|
|
LLStringUtil::toLower(sim_name);
|
|
|
|
LLWorldMap::sim_info_map_t::const_iterator end = LLWorldMap::instance().getRegionMap().end();
|
|
for (LLWorldMap::sim_info_map_t::const_iterator it = LLWorldMap::getInstance()->getRegionMap().begin(); it != end; ++it)
|
|
{
|
|
LLSimInfo* info = it->second;
|
|
|
|
if (info->isName(sim_name))
|
|
{
|
|
LLVector3d pos_global = info->getGlobalOrigin();
|
|
|
|
constexpr F64 SIM_COORD_DEFAULT = 128.0;
|
|
LLVector3 pos_local(SIM_COORD_DEFAULT, SIM_COORD_DEFAULT, 0.0f);
|
|
|
|
// Did this value come from a trackURL() request?
|
|
if (!mCompletingRegionPos.isExactlyZero())
|
|
{
|
|
pos_local = mCompletingRegionPos;
|
|
mCompletingRegionPos.clear();
|
|
}
|
|
pos_global.mdV[VX] += (F64)pos_local.mV[VX];
|
|
pos_global.mdV[VY] += (F64)pos_local.mV[VY];
|
|
pos_global.mdV[VZ] = (F64)pos_local.mV[VZ];
|
|
|
|
// Commiting search string automatically selects first item in the search list,
|
|
// in such case onCommitSearchResult shouldn't modify search string
|
|
if (!from_search)
|
|
{
|
|
mLocationEditor->setValue(sim_name);
|
|
}
|
|
trackLocation(pos_global);
|
|
mProcessingSearchUpdate = from_search;
|
|
mTrackCtrlsPanel->setDefaultBtn(mTeleportButton);
|
|
break;
|
|
}
|
|
}
|
|
|
|
onShowTargetBtn();
|
|
}
|
|
|
|
void LLFloaterWorldMap::onChangeMaturity()
|
|
{
|
|
bool can_access_mature = gAgent.canAccessMature();
|
|
bool can_access_adult = gAgent.canAccessAdult();
|
|
|
|
getChildView("events_mature_icon")->setVisible( can_access_mature);
|
|
getChildView("events_mature_label")->setVisible( can_access_mature);
|
|
getChildView("events_mature_chk")->setVisible( can_access_mature);
|
|
|
|
getChildView("events_adult_icon")->setVisible( can_access_adult);
|
|
getChildView("events_adult_label")->setVisible( can_access_adult);
|
|
getChildView("events_adult_chk")->setVisible( can_access_adult);
|
|
|
|
// disable mature / adult events.
|
|
if (!can_access_mature)
|
|
{
|
|
gSavedSettings.setBOOL("ShowMatureEvents", false);
|
|
}
|
|
if (!can_access_adult)
|
|
{
|
|
gSavedSettings.setBOOL("ShowAdultEvents", false);
|
|
}
|
|
}
|
|
|
|
void LLFloaterWorldMap::onFocusLost()
|
|
{
|
|
gViewerWindow->showCursor();
|
|
mMapView->mPanning = false;
|
|
}
|
|
|
|
LLPanelHideBeacon::LLPanelHideBeacon() :
|
|
mHideButton(NULL)
|
|
{
|
|
}
|
|
|
|
// static
|
|
LLPanelHideBeacon* LLPanelHideBeacon::getInstance()
|
|
{
|
|
static LLPanelHideBeacon* panel = getPanelHideBeacon();
|
|
return panel;
|
|
}
|
|
|
|
|
|
bool LLPanelHideBeacon::postBuild()
|
|
{
|
|
mHideButton = getChild<LLButton>("hide_beacon_btn");
|
|
mHideButton->setCommitCallback(boost::bind(&LLPanelHideBeacon::onHideButtonClick, this));
|
|
|
|
gViewerWindow->setOnWorldViewRectUpdated(boost::bind(&LLPanelHideBeacon::updatePosition, this));
|
|
|
|
return true;
|
|
}
|
|
|
|
//virtual
|
|
void LLPanelHideBeacon::draw()
|
|
{
|
|
if (!LLTracker::isTracking(NULL))
|
|
{
|
|
mHideButton->setVisible(false);
|
|
return;
|
|
}
|
|
mHideButton->setVisible(true);
|
|
updatePosition();
|
|
LLPanel::draw();
|
|
}
|
|
|
|
//virtual
|
|
void LLPanelHideBeacon::setVisible(bool visible)
|
|
{
|
|
if (gAgentCamera.getCameraMode() == CAMERA_MODE_MOUSELOOK) visible = false;
|
|
|
|
if (visible)
|
|
{
|
|
updatePosition();
|
|
}
|
|
|
|
LLPanel::setVisible(visible);
|
|
}
|
|
|
|
|
|
//static
|
|
LLPanelHideBeacon* LLPanelHideBeacon::getPanelHideBeacon()
|
|
{
|
|
LLPanelHideBeacon* panel = new LLPanelHideBeacon();
|
|
panel->buildFromFile("panel_hide_beacon.xml");
|
|
|
|
LL_INFOS() << "Build LLPanelHideBeacon panel" << LL_ENDL;
|
|
|
|
panel->updatePosition();
|
|
return panel;
|
|
}
|
|
|
|
void LLPanelHideBeacon::onHideButtonClick()
|
|
{
|
|
LLFloaterWorldMap* instance = LLFloaterWorldMap::getInstance();
|
|
if (instance)
|
|
{
|
|
instance->onClearBtn();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Updates position of the panel (similar to Stand & Stop Flying panel).
|
|
*/
|
|
void LLPanelHideBeacon::updatePosition()
|
|
{
|
|
S32 bottom_tb_center = 0;
|
|
if (LLToolBar* toolbar_bottom = gToolBarView->getToolbar(LLToolBarEnums::TOOLBAR_BOTTOM))
|
|
{
|
|
bottom_tb_center = toolbar_bottom->getRect().getCenterX();
|
|
}
|
|
|
|
S32 left_tb_width = 0;
|
|
if (LLToolBar* toolbar_left = gToolBarView->getToolbar(LLToolBarEnums::TOOLBAR_LEFT))
|
|
{
|
|
left_tb_width = toolbar_left->getRect().getWidth();
|
|
}
|
|
|
|
if (gToolBarView != NULL && gToolBarView->getToolbar(LLToolBarEnums::TOOLBAR_LEFT)->hasButtons())
|
|
{
|
|
S32 x_pos = bottom_tb_center - getRect().getWidth() / 2 - left_tb_width;
|
|
setOrigin( x_pos + HIDE_BEACON_PAD, 0);
|
|
}
|
|
else
|
|
{
|
|
S32 x_pos = bottom_tb_center - getRect().getWidth() / 2;
|
|
setOrigin( x_pos + HIDE_BEACON_PAD, 0);
|
|
}
|
|
}
|