Basic Summary: Issue 743: [Chat UI] Option to italicize actions (/me) in chat - Adds debug LiruItalicizeActions, and a checkbox to Adv. Chat->Chat UI preferences Issue 737: [Frosting] Annoyance Removal (Red beacon after teleport using LM's) - Adds debug ClearBeaconAfterTeleport, checkbox under System->General Issue 639: [Frosting] The agent isn't identified properly in chat - Oh what a silly issue this was, it's as though whoever wrote this didn't care. Fixes issue where names in logs do not match names in chat due to display name system Fixes the issue in which Unnamed objects got named by a hardcoded string under certain circumstances. Issue 813: [Frosting] When only accepting from friends, do not display incoming chat notification for nonfriends - Also broke the setting out, separating it from the voice calls friend only setting - Adds InstantMessagesFriendsOnly debug setting and checkbox in Adv. Chat->Chat/IM Issue 764: Copy SLURL from Map returns correct region but wrong coordinates. Satisfied the longstanding issue of inflexible autoresponse options. - Autoresponse now has its own tab in Adv. Chat preferences: Busy, Muted, nonfriends, and anyone (or just friends) can have separate responses, along with items of your choosing. - Prevent doubling up with the first repeated autoresponse due to typing message and normal message. Translator Summary: Adv. Chat->Chat UI->"Italicize action messages (/me)" System->General->"Clear red destination beacon after teleporting" Drop Targets for floater_ao.xml, panel_avatar.xml, panel_group_notices.xml, and panel_preferences_ascent_system.xml Adv. Chat->Chat/IM->"Only accept IMs from Friends" Please clean up the Busy Mode Response elements from panel_preferences_im.xml strings.xml now has "IM_autoresponse_minutes" Adv. Chat (panel_preferences_ascent_chat.xml) now has a new panel "Autoresponse", please clean up the old Autoresponse elements from Chat/IM tab and translate this panel. Developer Summary: Adds EChatStyle to LLChat, used for identifying what style a piece of chat is. Update settings_per_account.xml - Reorganized the ascent specific section. - Removes a few old and unused settings Better organize settings_per_account_ascent.xml - TODO: Actually get this include system working and remove the Ascent specific section in settings_per_account.xml Modernize LLDropTarget and make it more flexible and stand alone - The Text of drop targets is now a child of the target itself, meaning the necessity of having a static instance to the parent is eliminated - Drop targets are now one element in UI XML. - Drop targets now have fill_parent option which allows the target to spread over the parent, while the text, tool_tip, and border stays in place - If Drop Targets have a control_name, it is from the per account settings group, since Items must be in the inventory of the account in question. - All drop targets now use the common LLDropTarget class instead of their own. - LLGroupDropTarget is now derived from LLDropTarget and has its own tag group_drop_target. Cleaned up the focus functions we use to focus the input bar, setInputFocus exists for their purpose. Updated our llim* code to line up better with upstream and conform to styling. Polished LLTracker and LLFloaterWorldMap a bit Cleaned/Updated up LLStyleMap a bit. Optimized autoresponse code: - wildcards are now replaced via boost::algorithm::replace_all - Autoresponse and related chat enhancements are now performed inside their case, instead of by the large if block above.
1660 lines
45 KiB
C++
1660 lines
45 KiB
C++
/**
|
|
* @file llfloaterworldmap.cpp
|
|
* @author James Cook, Tom Yedwab
|
|
* @brief LLFloaterWorldMap class implementation
|
|
*
|
|
* $LicenseInfo:firstyear=2003&license=viewergpl$
|
|
*
|
|
* Copyright (c) 2003-2009, Linden Research, Inc.
|
|
*
|
|
* Second Life Viewer Source Code
|
|
* The source code in this file ("Source Code") is provided by Linden Lab
|
|
* to you under the terms of the GNU General Public License, version 2.0
|
|
* ("GPL"), unless you have obtained a separate licensing agreement
|
|
* ("Other License"), formally executed by you and Linden Lab. Terms of
|
|
* the GPL can be found in doc/GPL-license.txt in this distribution, or
|
|
* online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
|
|
*
|
|
* There are special exceptions to the terms and conditions of the GPL as
|
|
* it is applied to this Source Code. View the full text of the exception
|
|
* in the file doc/FLOSS-exception.txt in this software distribution, or
|
|
* online at
|
|
* http://secondlifegrid.net/programs/open_source/licensing/flossexception
|
|
*
|
|
* By copying, modifying or distributing this software, you acknowledge
|
|
* that you have read and understood your obligations described above,
|
|
* and agree to abide by those obligations.
|
|
*
|
|
* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
|
|
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
|
|
* COMPLETENESS OR PERFORMANCE.
|
|
* $/LicenseInfo$
|
|
*/
|
|
|
|
/*
|
|
* 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 "llcolorscheme.h"
|
|
#include "llcombobox.h"
|
|
#include "llviewercontrol.h"
|
|
#include "lldraghandle.h"
|
|
#include "llfirstuse.h"
|
|
#include "llfocusmgr.h"
|
|
#include "llinventoryfunctions.h"
|
|
#include "llinventorymodel.h"
|
|
#include "llinventorymodelbackgroundfetch.h"
|
|
#include "llinventoryobserver.h"
|
|
#include "lllandmarklist.h"
|
|
#include "lllineeditor.h"
|
|
#include "llnotificationsutil.h"
|
|
#include "llregionhandle.h"
|
|
#include "llscrolllistctrl.h"
|
|
#include "lltextbox.h"
|
|
#include "lltracker.h"
|
|
#include "lltrans.h"
|
|
#include "llurldispatcher.h"
|
|
#include "llviewermenu.h"
|
|
#include "llviewerregion.h"
|
|
#include "llviewerstats.h"
|
|
#include "llworldmap.h"
|
|
#include "llworldmapmessage.h"
|
|
#include "llworldmapview.h"
|
|
#include "lluictrlfactory.h"
|
|
#include "llappviewer.h"
|
|
#include "llmapimagetype.h"
|
|
#include "llweb.h"
|
|
#include "llwindow.h" // copyTextToClipboard()
|
|
|
|
|
|
// [RLVa:KB]
|
|
#include "rlvhandler.h"
|
|
// [/RLVa:KB]
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Constants
|
|
//---------------------------------------------------------------------------
|
|
static const F32 MAP_ZOOM_TIME = 0.2f;
|
|
|
|
// 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;
|
|
|
|
// 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;
|
|
|
|
static const F32 SIM_COORD_DEFAULT = 128.f;
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Globals
|
|
//---------------------------------------------------------------------------
|
|
|
|
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();
|
|
}
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
// 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()
|
|
: LLFloater(std::string("worldmap")),
|
|
mInventory(NULL),
|
|
mInventoryObserver(NULL),
|
|
mFriendObserver(NULL),
|
|
mCompletingRegionName(),
|
|
mCompletingRegionPos(),
|
|
mWaitingForTracker(FALSE),
|
|
mIsClosing(FALSE),
|
|
mSetToUserPosition(TRUE),
|
|
mTrackedLocation(0,0,0),
|
|
mTrackedStatus(LLTracker::TRACKING_NOTHING),
|
|
mListFriendCombo(NULL),
|
|
mListLandmarkCombo(NULL),
|
|
mListSearchResults(NULL)
|
|
{
|
|
LLCallbackMap::map_t factory_map;
|
|
factory_map["objects_mapview"] = LLCallbackMap(createWorldMapView, NULL);
|
|
LLUICtrlFactory::getInstance()->buildFloater(this, "floater_world_map.xml", &factory_map);
|
|
}
|
|
|
|
// static
|
|
void* LLFloaterWorldMap::createWorldMapView(void* data)
|
|
{
|
|
return new LLWorldMapView(std::string("mapview"), LLRect(0,300,400,0));
|
|
}
|
|
|
|
BOOL LLFloaterWorldMap::postBuild()
|
|
{
|
|
|
|
// The following callback syncs the worlmap tabs with the images.
|
|
// Commented out since it was crashing when LLWorldMap became a singleton.
|
|
// We should be fine without it but override the onOpen method and put it
|
|
// there if it turns out to be needed. -MG
|
|
//
|
|
//onCommitBackground((void*)this, false);
|
|
|
|
mPanel = getChild<LLPanel>("objects_mapview");
|
|
|
|
LLComboBox *avatar_combo = getChild<LLComboBox>("friend combo");
|
|
avatar_combo->selectFirstItem();
|
|
avatar_combo->setPrearrangeCallback( onAvatarComboPrearrange );
|
|
avatar_combo->setTextEntryCallback( onComboTextEntry );
|
|
mListFriendCombo = dynamic_cast<LLCtrlListInterface *>(avatar_combo);
|
|
|
|
LLLineEditor *location_editor = getChild<LLLineEditor>("location");
|
|
location_editor->setFocusChangedCallback(boost::bind(&LLFloaterWorldMap::onLocationFocusChanged, this, _1));
|
|
location_editor->setKeystrokeCallback( onSearchTextEntry );
|
|
|
|
LLScrollListCtrl* search_results = getChild<LLScrollListCtrl>("search_results");
|
|
search_results->setDoubleClickCallback(boost::bind(&LLFloaterWorldMap::onClickTeleportBtn,this));
|
|
mListSearchResults = dynamic_cast<LLCtrlListInterface *>(search_results);
|
|
|
|
LLComboBox *landmark_combo = getChild<LLComboBox>( "landmark combo");
|
|
|
|
landmark_combo->selectFirstItem();
|
|
landmark_combo->setPrearrangeCallback( onLandmarkComboPrearrange );
|
|
landmark_combo->setTextEntryCallback( onComboTextEntry );
|
|
mListLandmarkCombo = dynamic_cast<LLCtrlListInterface *>(landmark_combo);
|
|
|
|
avatar_combo->setCommitCallback( boost::bind(&LLFloaterWorldMap::onAvatarComboCommit,this) );
|
|
|
|
search_results->setCommitCallback( boost::bind(&LLFloaterWorldMap::onCommitSearchResult,this) );
|
|
|
|
landmark_combo->setCommitCallback( boost::bind(&LLFloaterWorldMap::onLandmarkComboCommit,this) );
|
|
|
|
getChild<LLUICtrl>("spin x")->setCommitCallback(boost::bind(&LLFloaterWorldMap::onCoordinatesCommit,this) );
|
|
getChild<LLUICtrl>("spin y")->setCommitCallback(boost::bind(&LLFloaterWorldMap::onCoordinatesCommit,this) );
|
|
getChild<LLUICtrl>("spin z")->setCommitCallback(boost::bind(&LLFloaterWorldMap::onCoordinatesCommit,this) );
|
|
|
|
getChild<LLButton>("DoSearch")->setCommitCallback(boost::bind(&LLFloaterWorldMap::onLocationCommit,this) );
|
|
getChild<LLButton>("Go Home")->setCommitCallback(boost::bind(&LLFloaterWorldMap::onGoHome,this) );
|
|
getChild<LLButton>("Teleport")->setCommitCallback(boost::bind(&LLFloaterWorldMap::onClickTeleportBtn,this) );
|
|
getChild<LLButton>("Show Destination")->setCommitCallback(boost::bind(&LLFloaterWorldMap::onShowTargetBtn,this) );
|
|
getChild<LLButton>("Show My Location")->setCommitCallback(boost::bind(&LLFloaterWorldMap::onShowAgentBtn,this) );
|
|
getChild<LLButton>("Clear")->setCommitCallback(boost::bind(&LLFloaterWorldMap::onClearBtn,this) );
|
|
getChild<LLButton>("copy_slurl")->setCommitCallback(boost::bind(&LLFloaterWorldMap::onCopySLURL,this) );
|
|
|
|
mCurZoomVal = log(LLWorldMapView::sMapScale/256.f)/log(2.f);
|
|
childSetValue("zoom slider", mCurZoomVal);
|
|
|
|
setDefaultBtn(NULL);
|
|
|
|
mZoomTimer.stop();
|
|
|
|
onChangeMaturity();
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
// virtual
|
|
LLFloaterWorldMap::~LLFloaterWorldMap()
|
|
{
|
|
// All cleaned up by LLView destructor
|
|
mPanel = NULL;
|
|
|
|
// Inventory deletes all observers on shutdown
|
|
mInventory = NULL;
|
|
mInventoryObserver = NULL;
|
|
|
|
// avatar tracker will delete this for us.
|
|
mFriendObserver = NULL;
|
|
|
|
gFloaterWorldMap = NULL;
|
|
}
|
|
|
|
|
|
// virtual
|
|
void LLFloaterWorldMap::onClose(bool app_quitting)
|
|
{
|
|
setVisible(FALSE);
|
|
}
|
|
|
|
// static
|
|
void LLFloaterWorldMap::show(bool center_on_target)
|
|
{
|
|
// [RLVa:KB] - Checked: 2009-07-05 (RLVa-1.0.0c)
|
|
if (gRlvHandler.hasBehaviour(RLV_BHVR_SHOWWORLDMAP))
|
|
{
|
|
return;
|
|
}
|
|
// [/RLVa:KB]
|
|
|
|
BOOL was_visible = gFloaterWorldMap->getVisible();
|
|
|
|
gFloaterWorldMap->mIsClosing = FALSE;
|
|
gFloaterWorldMap->open(); /* Flawfinder: ignore */
|
|
|
|
LLWorldMapView* map_panel;
|
|
map_panel = (LLWorldMapView*)gFloaterWorldMap->mPanel;
|
|
map_panel->clearLastClick();
|
|
|
|
if (!was_visible)
|
|
{
|
|
// reset pan on show, so it centers on you again
|
|
if (!center_on_target)
|
|
{
|
|
LLWorldMapView::setPan(0, 0, TRUE);
|
|
}
|
|
map_panel->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.
|
|
gFloaterWorldMap->adjustZoomSliderBounds();
|
|
|
|
// Could be first show
|
|
LLFirstUse::useMap();
|
|
|
|
// Start speculative download of landmarks
|
|
LLUUID landmark_folder_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_LANDMARK);
|
|
LLInventoryModelBackgroundFetch::instance().start(landmark_folder_id);
|
|
|
|
gFloaterWorldMap->childSetFocus("location", TRUE);
|
|
gFocusMgr.triggerFocusFlash();
|
|
|
|
gFloaterWorldMap->buildAvatarIDList();
|
|
gFloaterWorldMap->buildLandmarkIDLists();
|
|
|
|
// If nothing is being tracked, set flag so the user position will be found
|
|
gFloaterWorldMap->mSetToUserPosition = ( LLTracker::getTrackingStatus() == LLTracker::TRACKING_NOTHING );
|
|
}
|
|
|
|
if (center_on_target)
|
|
{
|
|
gFloaterWorldMap->centerOnTarget(FALSE);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// static
|
|
void LLFloaterWorldMap::reloadIcons(void*)
|
|
{
|
|
LLWorldMap::getInstance()->reloadItems();
|
|
}
|
|
|
|
|
|
// static
|
|
void LLFloaterWorldMap::toggle()
|
|
{
|
|
BOOL visible = gFloaterWorldMap->getVisible();
|
|
|
|
if (!visible)
|
|
{
|
|
show(false);
|
|
}
|
|
else
|
|
{
|
|
gFloaterWorldMap->mIsClosing = TRUE;
|
|
gFloaterWorldMap->close();
|
|
}
|
|
}
|
|
|
|
|
|
// static
|
|
void LLFloaterWorldMap::hide()
|
|
{
|
|
gFloaterWorldMap->mIsClosing = TRUE;
|
|
gFloaterWorldMap->close();
|
|
}
|
|
|
|
|
|
// virtual
|
|
void LLFloaterWorldMap::setVisible( BOOL visible )
|
|
{
|
|
LLFloater::setVisible( visible );
|
|
|
|
gSavedSettings.setBOOL( "ShowWorldMap", visible );
|
|
|
|
if( !visible )
|
|
{
|
|
// While we're not visible, discard the overlay images we're using
|
|
LLWorldMap::getInstance()->clearImageRefs();
|
|
}
|
|
}
|
|
|
|
|
|
// 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())
|
|
{
|
|
if(mPanel->pointInView(x, y))
|
|
{
|
|
F32 slider_value = (F32)childGetValue("zoom slider").asReal();
|
|
slider_value += ((F32)clicks * -0.3333f);
|
|
childSetValue("zoom slider", LLSD(slider_value));
|
|
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 );
|
|
|
|
// Might have changed size of world display area
|
|
// JC: Technically, this is correct, but it makes the slider "pop"
|
|
// if you resize the window, then draw the slider. Just leaving it
|
|
// the way it was when you opened the window seems better.
|
|
// adjustZoomSliderBounds();
|
|
}
|
|
|
|
|
|
// virtual
|
|
void LLFloaterWorldMap::draw()
|
|
{
|
|
|
|
// 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;
|
|
childSetEnabled("Go Home", enable_go_home);
|
|
|
|
updateLocation();
|
|
|
|
LLTracker::ETrackingStatus tracking_status = LLTracker::getTrackingStatus();
|
|
if (LLTracker::TRACKING_AVATAR == tracking_status)
|
|
{
|
|
childSetColor("avatar_icon", gTrackColor);
|
|
}
|
|
else
|
|
{
|
|
childSetColor("avatar_icon", gDisabledTrackColor);
|
|
}
|
|
|
|
if (LLTracker::TRACKING_LANDMARK == tracking_status)
|
|
{
|
|
childSetColor("landmark_icon", gTrackColor);
|
|
}
|
|
else
|
|
{
|
|
childSetColor("landmark_icon", gDisabledTrackColor);
|
|
}
|
|
|
|
if (LLTracker::TRACKING_LOCATION == tracking_status)
|
|
{
|
|
childSetColor("location_icon", gTrackColor);
|
|
}
|
|
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);
|
|
childSetColor("location_icon", loading_color);
|
|
}
|
|
else
|
|
{
|
|
childSetColor("location_icon", gDisabledTrackColor);
|
|
}
|
|
}
|
|
|
|
// check for completion of tracking data
|
|
if (mWaitingForTracker)
|
|
{
|
|
centerOnTarget(TRUE);
|
|
}
|
|
|
|
childSetEnabled("Teleport", (BOOL)tracking_status);
|
|
// childSetEnabled("Clear", (BOOL)tracking_status);
|
|
childSetEnabled("Show Destination", (BOOL)tracking_status || LLWorldMap::getInstance()->isTracking());
|
|
childSetEnabled("copy_slurl", (mSLURL.size() > 0) );
|
|
|
|
setMouseOpaque(TRUE);
|
|
getDragHandle()->setMouseOpaque(TRUE);
|
|
|
|
//RN: snaps to zoom value because interpolation caused jitter in the text rendering
|
|
if (!mZoomTimer.getStarted() && mCurZoomVal != (F32)getChild<LLUICtrl>("zoom slider")->getValue().asReal())
|
|
{
|
|
mZoomTimer.start();
|
|
}
|
|
F32 interp = mZoomTimer.getStarted() ? mZoomTimer.getElapsedTimeF32() / MAP_ZOOM_TIME : 1.f;
|
|
if (interp > 1.f)
|
|
{
|
|
interp = 1.f;
|
|
mZoomTimer.stop();
|
|
}
|
|
mCurZoomVal = lerp(mCurZoomVal, (F32)getChild<LLUICtrl>("zoom slider")->getValue().asReal(), interp);
|
|
F32 map_scale = 256.f*pow(2.f, mCurZoomVal);
|
|
LLWorldMapView::setScale( map_scale );
|
|
|
|
onChangeMaturity();
|
|
|
|
|
|
LLFloater::draw();
|
|
}
|
|
|
|
|
|
//-------------------------------------------------------------------------
|
|
// Internal utility functions
|
|
//-------------------------------------------------------------------------
|
|
|
|
|
|
void LLFloaterWorldMap::trackAvatar( const LLUUID& avatar_id, const std::string& name )
|
|
{
|
|
LLCtrlSelectionInterface *iface = childGetSelectionInterface("friend combo");
|
|
if (!iface) return;
|
|
|
|
buildAvatarIDList();
|
|
if(iface->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())
|
|
{
|
|
childSetValue("spin z", 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 || name != mTrackedAvatarName)
|
|
{
|
|
mTrackedStatus = LLTracker::TRACKING_AVATAR;
|
|
mTrackedAvatarName = name;
|
|
LLTracker::trackAvatar(avatar_id, name);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
LLTracker::stopTracking(false);
|
|
}
|
|
setDefaultBtn("Teleport");
|
|
}
|
|
|
|
void LLFloaterWorldMap::trackLandmark( const LLUUID& landmark_item_id )
|
|
{
|
|
LLCtrlSelectionInterface *iface = childGetSelectionInterface("landmark combo");
|
|
if (!iface) return;
|
|
|
|
buildLandmarkIDLists();
|
|
BOOL found = FALSE;
|
|
S32 idx;
|
|
for (idx = 0; idx < mLandmarkItemIDList.count(); idx++)
|
|
{
|
|
if ( mLandmarkItemIDList.get(idx) == landmark_item_id)
|
|
{
|
|
found = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (found && iface->setCurrentByID( landmark_item_id ) )
|
|
{
|
|
LLUUID asset_id = mLandmarkAssetIDList.get( idx );
|
|
std::string name;
|
|
LLComboBox* combo = getChild<LLComboBox>( "landmark combo");
|
|
if (combo) name = combo->getSimple();
|
|
mTrackedStatus = LLTracker::TRACKING_LANDMARK;
|
|
LLTracker::trackLandmark(mLandmarkAssetIDList.get( idx ), // assetID
|
|
mLandmarkItemIDList.get( idx ), // itemID
|
|
name); // name
|
|
|
|
if( asset_id != sHomeID )
|
|
{
|
|
// start the download process
|
|
gLandmarkList.getAsset( asset_id);
|
|
}
|
|
|
|
// We have to download both region info and landmark data, so set busy. JC
|
|
// getWindow()->incBusyCount();
|
|
}
|
|
else
|
|
{
|
|
LLTracker::stopTracking(false);
|
|
}
|
|
setDefaultBtn("Teleport");
|
|
}
|
|
|
|
|
|
void LLFloaterWorldMap::trackEvent(const LLItemInfo &event_info)
|
|
{
|
|
mTrackedStatus = LLTracker::TRACKING_LOCATION;
|
|
LLTracker::trackLocation(event_info.getGlobalPosition(), event_info.getName(), event_info.getToolTip(), LLTracker::LOCATION_EVENT);
|
|
setDefaultBtn("Teleport");
|
|
}
|
|
|
|
void LLFloaterWorldMap::trackGenericItem(const LLItemInfo &item)
|
|
{
|
|
mTrackedStatus = LLTracker::TRACKING_LOCATION;
|
|
LLTracker::trackLocation(item.getGlobalPosition(), item.getName(), item.getToolTip(), LLTracker::LOCATION_ITEM);
|
|
setDefaultBtn("Teleport");
|
|
}
|
|
|
|
void LLFloaterWorldMap::trackLocation(const LLVector3d& pos_global)
|
|
{
|
|
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
|
|
LLWorldMap::getInstance()->setTracking(pos_global);
|
|
LLTracker::stopTracking(false);
|
|
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);
|
|
setDefaultBtn("");
|
|
|
|
// 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
|
|
LLWorldMap::getInstance()->setTracking(pos_global);
|
|
LLWorldMap::getInstance()->setTrackingInvalid();
|
|
LLTracker::stopTracking(false);
|
|
setDefaultBtn("");
|
|
|
|
// clicked on a down region - turn off coord display
|
|
enableTeleportCoordsDisplay( false );
|
|
|
|
return;
|
|
}
|
|
|
|
std::string sim_name = sim_info->getName();
|
|
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 );
|
|
std::string full_name = llformat("%s (%d, %d, %d)",
|
|
// sim_name.c_str(),
|
|
// [RLVa:KB] - Alternate: Snowglobe-1.2.4 | Checked: 2009-07-04 (RLVa-1.0.0a)
|
|
(!gRlvHandler.hasBehaviour(RLV_BHVR_SHOWLOC)) ? sim_name.c_str() : RlvStrings::getString(RLV_STRING_HIDDEN_REGION).c_str(),
|
|
// [/RLVa:KB]
|
|
llround(region_x),
|
|
llround(region_y),
|
|
llround((F32)pos_global.mdV[VZ]));
|
|
|
|
std::string tooltip("");
|
|
mTrackedStatus = LLTracker::TRACKING_LOCATION;
|
|
LLTracker::trackLocation(pos_global, full_name, tooltip);
|
|
LLWorldMap::getInstance()->cancelTracking(); // The floater is taking over the tracking
|
|
|
|
LLVector3d coord_pos = LLTracker::getTrackedPositionGlobal();
|
|
updateTeleportCoordsDisplay( coord_pos );
|
|
|
|
// we have a valid region - turn on coord display
|
|
enableTeleportCoordsDisplay( true );
|
|
|
|
setDefaultBtn("Teleport");
|
|
}
|
|
|
|
|
|
// enable/disable teleport destination coordinates
|
|
void LLFloaterWorldMap::enableTeleportCoordsDisplay( bool enabled )
|
|
{
|
|
childSetEnabled("spin x", enabled );
|
|
childSetEnabled("spin y", enabled );
|
|
childSetEnabled("spin z", enabled );
|
|
}
|
|
|
|
// update display of teleport destination coordinates - pos is in global coordinates
|
|
void LLFloaterWorldMap::updateTeleportCoordsDisplay( const LLVector3d& pos )
|
|
{
|
|
// if we're going to update their value, we should also enable them
|
|
enableTeleportCoordsDisplay( true );
|
|
|
|
// convert global specified position to a local one
|
|
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 );
|
|
F32 region_local_z = (F32)llclamp( pos.mdV[VZ], 0.0, (F64)REGION_HEIGHT_METERS );
|
|
|
|
// write in the values
|
|
childSetValue("spin x", region_local_x );
|
|
childSetValue("spin y", region_local_y );
|
|
childSetValue("spin z", region_local_z );
|
|
}
|
|
|
|
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 );
|
|
if ( gotSimName )
|
|
{
|
|
mSetToUserPosition = FALSE;
|
|
|
|
// Fill out the location field
|
|
childSetValue("location", agent_sim_name);
|
|
|
|
// update the coordinate display with location of avatar in region
|
|
updateTeleportCoordsDisplay( agentPos );
|
|
|
|
S32 x = llround( (F32)fmod( (F32)agentPos[VX], (F32)REGION_WIDTH_METERS ) );
|
|
S32 y = llround( (F32)fmod( (F32)agentPos[VY], (F32)REGION_WIDTH_METERS ) );
|
|
S32 z = llround( (F32)agentPos[VZ] );
|
|
|
|
// Figure out where user is
|
|
// Set the current SLURL
|
|
mSLURL = LLURLDispatcher::buildSLURL(agent_sim_name, x, y, z);
|
|
|
|
// [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]
|
|
}
|
|
}
|
|
|
|
return; // invalid location
|
|
}
|
|
std::string sim_name;
|
|
gotSimName = LLWorldMap::getInstance()->simNameFromPosGlobal( pos_global, sim_name );
|
|
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;
|
|
}
|
|
}
|
|
|
|
childSetValue("location", 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
|
|
if ( gotSimName )
|
|
{
|
|
S32 x = llround( (F32)fmod( (F32)coord_pos[VX], (F32)REGION_WIDTH_METERS ) );
|
|
S32 y = llround( (F32)fmod( (F32)coord_pos[VY], (F32)REGION_WIDTH_METERS ) );
|
|
S32 z = llround( (F32)coord_pos[VZ] );
|
|
mSLURL = LLURLDispatcher::buildSLURL(sim_name, x, y, z);
|
|
}
|
|
else
|
|
{ // Empty SLURL will disable the "Copy SLURL to clipboard" button
|
|
mSLURL = "";
|
|
}
|
|
|
|
// [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);
|
|
z_coord = llclamp(z_coord, 0, 4096);
|
|
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);
|
|
setDefaultBtn("Teleport");
|
|
}
|
|
else
|
|
{
|
|
// fill in UI based on URL
|
|
gFloaterWorldMap->childSetValue("location", region_name);
|
|
|
|
// Save local coords to highlight position after region global
|
|
// position is returned.
|
|
gFloaterWorldMap->mCompletingRegionPos.set(
|
|
(F32)x_coord, (F32)y_coord, (F32)z_coord);
|
|
|
|
// pass sim name to combo box
|
|
gFloaterWorldMap->mCompletingRegionName = region_name;
|
|
LLWorldMapMessage::getInstance()->sendNamedRegionRequest(region_name);
|
|
LLStringUtil::toLower(gFloaterWorldMap->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())
|
|
{
|
|
LLCtrlSelectionInterface *iface = childGetSelectionInterface("friend combo");
|
|
const LLRelationship* buddy_info = t.getBuddyInfo(avatar_id);
|
|
if(!iface ||
|
|
!iface->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()
|
|
{
|
|
LLCtrlListInterface *list = mListFriendCombo;
|
|
if (!list) return;
|
|
|
|
// Delete all but the "None" entry
|
|
S32 list_size = list->getItemCount();
|
|
if (list_size > 1)
|
|
{
|
|
list->selectItemRange(1, -1);
|
|
list->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();
|
|
for( ; it != end; ++it)
|
|
{
|
|
list->addSimpleElement((*it).first, ADD_BOTTOM, (*it).second);
|
|
}
|
|
|
|
list->setCurrentByID( LLAvatarTracker::instance().getAvatarID() );
|
|
list->selectFirstItem();
|
|
}
|
|
|
|
|
|
void LLFloaterWorldMap::buildLandmarkIDLists()
|
|
{
|
|
LLCtrlListInterface *list = mListLandmarkCombo;
|
|
if (!list) return;
|
|
|
|
// Delete all but the "None" entry
|
|
S32 list_size = list->getItemCount();
|
|
if (list_size > 1)
|
|
{
|
|
list->selectItemRange(1, -1);
|
|
list->operateOnSelection(LLCtrlListInterface::OP_DELETE);
|
|
}
|
|
|
|
mLandmarkItemIDList.reset();
|
|
mLandmarkAssetIDList.reset();
|
|
|
|
// Get all of the current landmarks
|
|
mLandmarkAssetIDList.put( LLUUID::null );
|
|
mLandmarkItemIDList.put( LLUUID::null );
|
|
|
|
mLandmarkAssetIDList.put( sHomeID );
|
|
mLandmarkItemIDList.put( 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());
|
|
|
|
S32 count = items.count();
|
|
for(S32 i = 0; i < count; ++i)
|
|
{
|
|
LLInventoryItem* item = items.get(i);
|
|
|
|
list->addSimpleElement(item->getName(), ADD_BOTTOM, item->getUUID());
|
|
|
|
mLandmarkAssetIDList.put( item->getAssetUUID() );
|
|
mLandmarkItemIDList.put( item->getUUID() );
|
|
}
|
|
list->sortByColumn(std::string("landmark name"), TRUE);
|
|
|
|
list->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)
|
|
{
|
|
LLCtrlListInterface *list = mListSearchResults;
|
|
if (list)
|
|
{
|
|
list->operateOnAll(LLCtrlListInterface::OP_DELETE);
|
|
}
|
|
if (!childHasKeyboardFocus("spin x"))
|
|
{
|
|
childSetValue("spin x", SIM_COORD_DEFAULT);
|
|
}
|
|
if (!childHasKeyboardFocus("spin y"))
|
|
{
|
|
childSetValue("spin y", SIM_COORD_DEFAULT);
|
|
}
|
|
if (!childHasKeyboardFocus("spin z"))
|
|
{
|
|
childSetValue("spin z", 0);
|
|
}
|
|
//Singu Note: Don't do this. It basically 'eats' the first click onto void space if the previous tracked target was a valid sim.
|
|
//LLWorldMap::getInstance()->cancelTracking();
|
|
mCompletingRegionName = "";
|
|
}
|
|
|
|
|
|
void LLFloaterWorldMap::clearLandmarkSelection(BOOL clear_ui)
|
|
{
|
|
if (clear_ui || !childHasKeyboardFocus("landmark combo"))
|
|
{
|
|
LLCtrlListInterface *list = mListLandmarkCombo;
|
|
if (list)
|
|
{
|
|
list->selectByValue( "None" );
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void LLFloaterWorldMap::clearAvatarSelection(BOOL clear_ui)
|
|
{
|
|
if (clear_ui || !childHasKeyboardFocus("friend combo"))
|
|
{
|
|
mTrackedStatus = LLTracker::TRACKING_NOTHING;
|
|
LLCtrlListInterface *list = mListFriendCombo;
|
|
if (list)
|
|
{
|
|
list->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
|
|
LLWorldMapView* map_panel;
|
|
map_panel = (LLWorldMapView*)mPanel;
|
|
LLRect view_rect = map_panel->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);
|
|
|
|
childSetMinValue("zoom slider", min_power);
|
|
}
|
|
|
|
|
|
//-------------------------------------------------------------------------
|
|
// User interface widget callbacks
|
|
//-------------------------------------------------------------------------
|
|
|
|
void LLFloaterWorldMap::onGoHome()
|
|
{
|
|
gAgent.teleportHome();
|
|
close();
|
|
}
|
|
|
|
|
|
// static
|
|
void LLFloaterWorldMap::onLandmarkComboPrearrange( LLUICtrl* ctrl, void* userdata )
|
|
{
|
|
LLFloaterWorldMap* self = gFloaterWorldMap;
|
|
if( !self || self->mIsClosing )
|
|
{
|
|
return;
|
|
}
|
|
|
|
LLCtrlListInterface *list = self->childGetListInterface("landmark combo");
|
|
if (!list) return;
|
|
|
|
LLUUID current_choice = list->getCurrentID();
|
|
|
|
gFloaterWorldMap->buildLandmarkIDLists();
|
|
|
|
if( current_choice.isNull() || !list->setCurrentByID( current_choice ) )
|
|
{
|
|
LLTracker::stopTracking(false);
|
|
}
|
|
|
|
}
|
|
|
|
void LLFloaterWorldMap::onComboTextEntry( LLLineEditor* ctrl, void* userdata )
|
|
{
|
|
// 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);
|
|
}
|
|
|
|
// static
|
|
void LLFloaterWorldMap::onSearchTextEntry( LLLineEditor* ctrl, void* userdata )
|
|
{
|
|
onComboTextEntry(ctrl, userdata);
|
|
gFloaterWorldMap->updateSearchEnabled();
|
|
}
|
|
|
|
// static
|
|
void LLFloaterWorldMap::onLandmarkComboCommit()
|
|
{
|
|
if( mIsClosing )
|
|
{
|
|
return;
|
|
}
|
|
|
|
LLCtrlListInterface *list = mListLandmarkCombo;
|
|
if (!list) return;
|
|
|
|
LLUUID asset_id;
|
|
LLUUID item_id = list->getCurrentID();
|
|
|
|
LLTracker::stopTracking(false);
|
|
|
|
//RN: stopTracking() clears current combobox selection, need to reassert it here
|
|
list->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( LLUICtrl* ctrl, void* userdata )
|
|
{
|
|
LLFloaterWorldMap* self = gFloaterWorldMap;
|
|
if( !self || self->mIsClosing )
|
|
{
|
|
return;
|
|
}
|
|
|
|
LLCtrlListInterface *list = self->childGetListInterface("friend combo");
|
|
if (!list) return;
|
|
|
|
LLUUID current_choice;
|
|
|
|
if( LLAvatarTracker::instance().haveTrackingInfo() )
|
|
{
|
|
current_choice = LLAvatarTracker::instance().getAvatarID();
|
|
}
|
|
|
|
self->buildAvatarIDList();
|
|
|
|
if( !list->setCurrentByID( current_choice ) || current_choice.isNull() )
|
|
{
|
|
LLTracker::stopTracking(false);
|
|
}
|
|
}
|
|
|
|
void LLFloaterWorldMap::onAvatarComboCommit()
|
|
{
|
|
if( mIsClosing )
|
|
{
|
|
return;
|
|
}
|
|
|
|
LLCtrlListInterface *list = mListFriendCombo;
|
|
if (!list) return;
|
|
|
|
const LLUUID& new_avatar_id = list->getCurrentID();
|
|
if (new_avatar_id.notNull())
|
|
{
|
|
std::string name;
|
|
LLComboBox* combo = getChild<LLComboBox>("friend combo");
|
|
if (combo) name = combo->getSimple();
|
|
trackAvatar(new_avatar_id, name);
|
|
onShowTargetBtn();
|
|
}
|
|
else
|
|
{ // Reset to user postion if nothing is tracked
|
|
mSetToUserPosition = ( LLTracker::getTrackingStatus() == LLTracker::TRACKING_NOTHING );
|
|
}
|
|
}
|
|
|
|
void LLFloaterWorldMap::onLocationFocusChanged( LLFocusableElement* focus )
|
|
{
|
|
updateSearchEnabled();
|
|
}
|
|
|
|
void LLFloaterWorldMap::updateSearchEnabled()
|
|
{
|
|
if (childHasKeyboardFocus("location") &&
|
|
childGetValue("location").asString().length() > 0)
|
|
{
|
|
setDefaultBtn("DoSearch");
|
|
}
|
|
else
|
|
{
|
|
setDefaultBtn(NULL);
|
|
}
|
|
}
|
|
|
|
void LLFloaterWorldMap::onLocationCommit()
|
|
{
|
|
if( mIsClosing )
|
|
{
|
|
return;
|
|
}
|
|
|
|
clearLocationSelection(FALSE);
|
|
mCompletingRegionName = "";
|
|
mLastRegionName = "";
|
|
|
|
std::string str = childGetValue("location").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
|
|
childSetValue("location", str);
|
|
}
|
|
|
|
// Don't try completing empty name (STORM-1427).
|
|
if (str.empty())
|
|
{
|
|
return;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
S32 x_coord = (S32)childGetValue("spin x").asReal();
|
|
S32 y_coord = (S32)childGetValue("spin y").asReal();
|
|
S32 z_coord = (S32)childGetValue("spin z").asReal();
|
|
|
|
const std::string region_name = childGetValue("location").asString();
|
|
|
|
trackURL( region_name, x_coord, y_coord, z_coord );
|
|
}
|
|
|
|
void LLFloaterWorldMap::onClearBtn()
|
|
{
|
|
mTrackedStatus = LLTracker::TRACKING_NOTHING;
|
|
LLTracker::stopTracking(true);
|
|
LLWorldMap::getInstance()->cancelTracking();
|
|
mSLURL = ""; // Clear the SLURL since it's invalid
|
|
mSetToUserPosition = TRUE; // Revert back to the current user position
|
|
}
|
|
|
|
void LLFloaterWorldMap::onShowTargetBtn()
|
|
{
|
|
centerOnTarget(TRUE);
|
|
}
|
|
|
|
void LLFloaterWorldMap::onShowAgentBtn()
|
|
{
|
|
LLWorldMapView::setPan( 0, 0, FALSE); // FALSE == animate
|
|
// Set flag so user's location will be displayed if not tracking anything else
|
|
mSetToUserPosition = TRUE;
|
|
}
|
|
|
|
void LLFloaterWorldMap::onClickTeleportBtn()
|
|
{
|
|
teleport();
|
|
}
|
|
|
|
void LLFloaterWorldMap::onCopySLURL()
|
|
{
|
|
getWindow()->copyTextToClipboard(utf8str_to_wstring(mSLURL));
|
|
|
|
LLSD args;
|
|
args["SLURL"] = mSLURL;
|
|
|
|
LLNotificationsUtil::add("CopySLURL", args);
|
|
}
|
|
|
|
// 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();
|
|
}
|
|
|
|
LLWorldMapView::setPan( -llfloor((F32)(pos_global.mdV[VX] * (F64)LLWorldMapView::sPixelsPerMeter)),
|
|
-llfloor((F32)(pos_global.mdV[VY] * (F64)LLWorldMapView::sPixelsPerMeter)),
|
|
!animate);
|
|
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 );
|
|
close();
|
|
}
|
|
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] = childGetValue("spin z");
|
|
}
|
|
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 );
|
|
}
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
LLScrollListCtrl *list = getChild<LLScrollListCtrl>("search_results");
|
|
list->operateOnAll(LLCtrlListInterface::OP_DELETE);
|
|
|
|
S32 name_length = mCompletingRegionName.length();
|
|
|
|
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);
|
|
|
|
if (sim_name_lower.substr(0, name_length) == mCompletingRegionName)
|
|
{
|
|
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();
|
|
list->addElement(value);
|
|
num_results++;
|
|
}
|
|
}
|
|
|
|
if (found_null_sim || match.isDefined())
|
|
{
|
|
mCompletingRegionName = "";
|
|
}
|
|
|
|
if (num_results > 0)
|
|
{
|
|
// if match found, highlight it and go
|
|
if (!match.isUndefined())
|
|
{
|
|
list->selectByValue(match);
|
|
}
|
|
// else select first found item
|
|
else
|
|
{
|
|
list->selectFirstItem();
|
|
}
|
|
childSetFocus("search_results");
|
|
onCommitSearchResult();
|
|
}
|
|
else
|
|
{
|
|
// if we found nothing, say "none"
|
|
list->setCommentText(LLTrans::getString("worldmap_results_none_found"));
|
|
list->operateOnAll(LLCtrlListInterface::OP_DESELECT);
|
|
}
|
|
}
|
|
|
|
|
|
void LLFloaterWorldMap::onCommitSearchResult()
|
|
{
|
|
LLCtrlListInterface *list = mListSearchResults;
|
|
if (!list) return;
|
|
|
|
LLSD selected_value = list->getSelectedValue();
|
|
std::string sim_name = selected_value.asString();
|
|
if (sim_name.empty())
|
|
{
|
|
return;
|
|
}
|
|
LLStringUtil::toLower(sim_name);
|
|
|
|
std::map<U64, LLSimInfo*>::const_iterator it;
|
|
for (it = LLWorldMap::getInstance()->getRegionMap().begin(); it != LLWorldMap::getInstance()->getRegionMap().end(); ++it)
|
|
{
|
|
LLSimInfo* info = it->second;
|
|
|
|
if (info->isName(sim_name))
|
|
{
|
|
LLVector3d pos_global = info->getGlobalOrigin();
|
|
|
|
const 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];
|
|
|
|
childSetValue("location", sim_name);
|
|
trackLocation(pos_global);
|
|
setDefaultBtn("Teleport");
|
|
break;
|
|
}
|
|
}
|
|
|
|
onShowTargetBtn();
|
|
}
|
|
|
|
void LLFloaterWorldMap::onChangeMaturity()
|
|
{
|
|
bool can_access_mature = gAgent.canAccessMature();
|
|
bool can_access_adult = gAgent.canAccessAdult();
|
|
|
|
childSetVisible("events_mature_icon", can_access_mature);
|
|
childSetVisible("event_mature_chk", can_access_mature);
|
|
|
|
childSetVisible("events_adult_icon", can_access_adult);
|
|
childSetVisible("event_adult_chk", can_access_adult);
|
|
|
|
// disable mature / adult events.
|
|
if (!can_access_mature)
|
|
{
|
|
static LLCachedControl<bool> show_mature("MapShowMatureEvents",false);
|
|
show_mature = false;
|
|
}
|
|
if (!can_access_adult)
|
|
{
|
|
static LLCachedControl<bool> show_adult("MapShowAdultEvents",false);
|
|
show_adult = false;
|
|
}
|
|
}
|