From 74c8b028d42a8c5b080bb861e427f38cedd4ad7c Mon Sep 17 00:00:00 2001 From: Alexander Gavriliuk Date: Fri, 15 Dec 2023 18:26:14 +0100 Subject: SL-20743 Use LLMutex in LLImageBase for internal data thread-safety --- indra/newview/llnetmap.cpp | 2 ++ 1 file changed, 2 insertions(+) (limited to 'indra/newview/llnetmap.cpp') diff --git a/indra/newview/llnetmap.cpp b/indra/newview/llnetmap.cpp index 0ba3c3d691..e7f9d0e5df 100644 --- a/indra/newview/llnetmap.cpp +++ b/indra/newview/llnetmap.cpp @@ -345,6 +345,7 @@ void LLNetMap::draw() mObjectImageCenterGlobal = viewPosToGlobal(llfloor(new_center.mV[VX]), llfloor(new_center.mV[VY])); // Create the base texture. + LLImageDataLock lock(mObjectRawImagep); U8 *default_texture = mObjectRawImagep->getData(); memset( default_texture, 0, mObjectImagep->getWidth() * mObjectImagep->getHeight() * mObjectImagep->getComponents() ); @@ -915,6 +916,7 @@ void LLNetMap::renderPoint(const LLVector3 &pos_local, const LLColor4U &color, return; } + LLImageDataLock lock(mObjectRawImagep); U8 *datap = mObjectRawImagep->getData(); S32 neg_radius = diameter / 2; -- cgit v1.2.3 From c285f59ce2a05703e3a1232fcaf3ee3aea714b3f Mon Sep 17 00:00:00 2001 From: Ansariel Date: Sun, 18 Feb 2024 12:52:19 +0100 Subject: Replace BOOL with bool in llwindow and dependent classes --- indra/newview/llnetmap.cpp | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) (limited to 'indra/newview/llnetmap.cpp') diff --git a/indra/newview/llnetmap.cpp b/indra/newview/llnetmap.cpp index e7f9d0e5df..1299739857 100644 --- a/indra/newview/llnetmap.cpp +++ b/indra/newview/llnetmap.cpp @@ -688,7 +688,7 @@ LLVector3d LLNetMap::viewPosToGlobal( S32 x, S32 y ) return pos_global; } -BOOL LLNetMap::handleScrollWheel(S32 x, S32 y, S32 clicks) +bool LLNetMap::handleScrollWheel(S32 x, S32 y, S32 clicks) { // note that clicks are reversed from what you'd think: i.e. > 0 means zoom out, < 0 means zoom in F32 new_scale = mScale * pow(MAP_SCALE_ZOOM_FACTOR, -clicks); @@ -709,7 +709,7 @@ BOOL LLNetMap::handleScrollWheel(S32 x, S32 y, S32 clicks) return true; } -BOOL LLNetMap::handleToolTip(S32 x, S32 y, MASK mask) +bool LLNetMap::handleToolTip(S32 x, S32 y, MASK mask) { if (gDisconnected) { @@ -1009,7 +1009,7 @@ void LLNetMap::createObjectImage() mUpdateNow = true; } -BOOL LLNetMap::handleMouseDown(S32 x, S32 y, MASK mask) +bool LLNetMap::handleMouseDown(S32 x, S32 y, MASK mask) { // Start panning gFocusMgr.setMouseCapture(this); @@ -1020,7 +1020,7 @@ BOOL LLNetMap::handleMouseDown(S32 x, S32 y, MASK mask) return true; } -BOOL LLNetMap::handleMouseUp(S32 x, S32 y, MASK mask) +bool LLNetMap::handleMouseUp(S32 x, S32 y, MASK mask) { if (abs(mMouseDown.mX - x) < 3 && abs(mMouseDown.mY - y) < 3) { @@ -1053,7 +1053,7 @@ BOOL LLNetMap::handleMouseUp(S32 x, S32 y, MASK mask) return false; } -BOOL LLNetMap::handleRightMouseDown(S32 x, S32 y, MASK mask) +bool LLNetMap::handleRightMouseDown(S32 x, S32 y, MASK mask) { auto menu = static_cast(mPopupMenuHandle.get()); if (menu) @@ -1064,7 +1064,7 @@ BOOL LLNetMap::handleRightMouseDown(S32 x, S32 y, MASK mask) menu->setItemEnabled("Stop Tracking", LLTracker::isTracking(0)); LLMenuGL::showPopup(this, menu, x, y); } - return TRUE; + return true; } BOOL LLNetMap::handleClick(S32 x, S32 y, MASK mask) @@ -1077,7 +1077,7 @@ BOOL LLNetMap::handleClick(S32 x, S32 y, MASK mask) return TRUE; } -BOOL LLNetMap::handleDoubleClick(S32 x, S32 y, MASK mask) +bool LLNetMap::handleDoubleClick(S32 x, S32 y, MASK mask) { LLVector3d pos_global = viewPosToGlobal(x, y); @@ -1106,7 +1106,7 @@ BOOL LLNetMap::handleDoubleClick(S32 x, S32 y, MASK mask) { LLFloaterReg::showInstance("world_map"); } - return TRUE; + return true; } F32 LLNetMap::getScaleForName(std::string scale_name) @@ -1139,7 +1139,7 @@ bool LLNetMap::outsideSlop( S32 x, S32 y, S32 start_x, S32 start_y, S32 slop ) return (dx <= -slop || slop <= dx || dy <= -slop || slop <= dy); } -BOOL LLNetMap::handleHover( S32 x, S32 y, MASK mask ) +bool LLNetMap::handleHover( S32 x, S32 y, MASK mask ) { if (hasMouseCapture()) { @@ -1173,7 +1173,7 @@ BOOL LLNetMap::handleHover( S32 x, S32 y, MASK mask ) gViewerWindow->setCursor( UI_CURSOR_CROSS ); } - return TRUE; + return true; } bool LLNetMap::isZoomChecked(const LLSD &userdata) -- cgit v1.2.3 From a5261a5fa8fad810ecb5c260d92c3e771822bf58 Mon Sep 17 00:00:00 2001 From: Ansariel Date: Tue, 20 Feb 2024 23:46:23 +0100 Subject: Convert BOOL to bool in llui --- indra/newview/llnetmap.cpp | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) (limited to 'indra/newview/llnetmap.cpp') diff --git a/indra/newview/llnetmap.cpp b/indra/newview/llnetmap.cpp index 1299739857..5cf2d80ee2 100644 --- a/indra/newview/llnetmap.cpp +++ b/indra/newview/llnetmap.cpp @@ -69,20 +69,20 @@ static LLDefaultChildRegistry::Register r1("net_map"); -const F32 LLNetMap::MAP_SCALE_MIN = 32; -const F32 LLNetMap::MAP_SCALE_FAR = 32; -const F32 LLNetMap::MAP_SCALE_MEDIUM = 128; -const F32 LLNetMap::MAP_SCALE_CLOSE = 256; -const F32 LLNetMap::MAP_SCALE_VERY_CLOSE = 1024; -const F32 LLNetMap::MAP_SCALE_MAX = 4096; +constexpr F32 LLNetMap::MAP_SCALE_MIN = 32; +constexpr F32 LLNetMap::MAP_SCALE_FAR = 32; +constexpr F32 LLNetMap::MAP_SCALE_MEDIUM = 128; +constexpr F32 LLNetMap::MAP_SCALE_CLOSE = 256; +constexpr F32 LLNetMap::MAP_SCALE_VERY_CLOSE = 1024; +constexpr F32 LLNetMap::MAP_SCALE_MAX = 4096; -const F32 MAP_SCALE_ZOOM_FACTOR = 1.04f; // Zoom in factor per click of scroll wheel (4%) -const F32 MIN_DOT_RADIUS = 3.5f; -const F32 DOT_SCALE = 0.75f; -const F32 MIN_PICK_SCALE = 2.f; -const S32 MOUSE_DRAG_SLOP = 2; // How far the mouse needs to move before we think it's a drag +constexpr F32 MAP_SCALE_ZOOM_FACTOR = 1.04f; // Zoom in factor per click of scroll wheel (4%) +constexpr F32 MIN_DOT_RADIUS = 3.5f; +constexpr F32 DOT_SCALE = 0.75f; +constexpr F32 MIN_PICK_SCALE = 2.f; +constexpr S32 MOUSE_DRAG_SLOP = 2; // How far the mouse needs to move before we think it's a drag -const F64 COARSEUPDATE_MAX_Z = 1020.0f; +constexpr F64 COARSEUPDATE_MAX_Z = 1020.0f; LLNetMap::LLNetMap (const Params & p) : LLUICtrl (p), @@ -126,7 +126,7 @@ LLNetMap::~LLNetMap() } } -BOOL LLNetMap::postBuild() +bool LLNetMap::postBuild() { LLUICtrl::CommitCallbackRegistry::ScopedRegistrar commitRegistrar; LLUICtrl::EnableCallbackRegistry::ScopedRegistrar enableRegistrar; @@ -142,7 +142,7 @@ BOOL LLNetMap::postBuild() LLMenuGL* menu = LLUICtrlFactory::getInstance()->createFromFile("menu_mini_map.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); mPopupMenuHandle = menu->getHandle(); menu->setItemEnabled("Re-center map", false); - return TRUE; + return true; } void LLNetMap::setScale( F32 scale ) @@ -544,7 +544,7 @@ void LLNetMap::draw() LLUICtrl::draw(); } -void LLNetMap::reshape(S32 width, S32 height, BOOL called_from_parent) +void LLNetMap::reshape(S32 width, S32 height, bool called_from_parent) { LLUICtrl::reshape(width, height, called_from_parent); createObjectImage(); @@ -1067,14 +1067,14 @@ bool LLNetMap::handleRightMouseDown(S32 x, S32 y, MASK mask) return true; } -BOOL LLNetMap::handleClick(S32 x, S32 y, MASK mask) +bool LLNetMap::handleClick(S32 x, S32 y, MASK mask) { // TODO: allow clicking an avatar on minimap to select avatar in the nearby avatar list // if(mClosestAgentToCursor.notNull()) // mNearbyList->selectUser(mClosestAgentToCursor); // Needs a registered observer i guess to accomplish this without using // globals to tell the mNearbyList in llpeoplepanel to select the user - return TRUE; + return true; } bool LLNetMap::handleDoubleClick(S32 x, S32 y, MASK mask) -- cgit v1.2.3 From 60d3dd98a44230c21803c1606552ee098ed9fa7c Mon Sep 17 00:00:00 2001 From: Ansariel Date: Wed, 21 Feb 2024 21:05:14 +0100 Subject: Convert remaining BOOL to bool --- indra/newview/llnetmap.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'indra/newview/llnetmap.cpp') diff --git a/indra/newview/llnetmap.cpp b/indra/newview/llnetmap.cpp index 5cf2d80ee2..671da07016 100644 --- a/indra/newview/llnetmap.cpp +++ b/indra/newview/llnetmap.cpp @@ -577,7 +577,7 @@ LLVector3 LLNetMap::globalPosToView(const LLVector3d& global_pos) } void LLNetMap::drawTracking(const LLVector3d& pos_global, const LLColor4& color, - BOOL draw_arrow ) + bool draw_arrow ) { LLVector3 pos_local = globalPosToView(pos_global); if( (pos_local.mV[VX] < 0) || @@ -838,12 +838,12 @@ bool LLNetMap::handleToolTip(S32 x, S32 y, MASK mask) return true; } -BOOL LLNetMap::handleToolTipAgent(const LLUUID& avatar_id) +bool LLNetMap::handleToolTipAgent(const LLUUID& avatar_id) { LLAvatarName av_name; if (avatar_id.isNull() || !LLAvatarNameCache::get(avatar_id, &av_name)) { - return FALSE; + return false; } // only show tooltip if same inspector not already open @@ -864,7 +864,7 @@ BOOL LLNetMap::handleToolTipAgent(const LLUUID& avatar_id) LLToolTipMgr::instance().show(p); } - return TRUE; + return true; } // static @@ -1003,7 +1003,7 @@ void LLNetMap::createObjectImage() mObjectRawImagep = new LLImageRaw(img_size, img_size, 4); U8* data = mObjectRawImagep->getData(); memset( data, 0, img_size * img_size * 4 ); - mObjectImagep = LLViewerTextureManager::getLocalTexture( mObjectRawImagep.get(), FALSE); + mObjectImagep = LLViewerTextureManager::getLocalTexture( mObjectRawImagep.get(), false); } setScale(mScale); mUpdateNow = true; -- cgit v1.2.3 From e2e37cced861b98de8c1a7c9c0d3a50d2d90e433 Mon Sep 17 00:00:00 2001 From: Ansariel Date: Wed, 22 May 2024 21:25:21 +0200 Subject: Fix line endlings --- indra/newview/llnetmap.cpp | 2492 ++++++++++++++++++++++---------------------- 1 file changed, 1246 insertions(+), 1246 deletions(-) (limited to 'indra/newview/llnetmap.cpp') diff --git a/indra/newview/llnetmap.cpp b/indra/newview/llnetmap.cpp index 6a2299076c..1410232a0f 100644 --- a/indra/newview/llnetmap.cpp +++ b/indra/newview/llnetmap.cpp @@ -1,1246 +1,1246 @@ -/** - * @file llnetmap.cpp - * @author James Cook - * @brief Display of surrounding regions, objects, and agents. - * - * $LicenseInfo:firstyear=2001&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2001-2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#include "llviewerprecompiledheaders.h" - -#include "llnetmap.h" - -// Library includes (should move below) -#include "indra_constants.h" -#include "llavatarnamecache.h" -#include "llmath.h" -#include "llfloaterreg.h" -#include "llfocusmgr.h" -#include "lllocalcliprect.h" -#include "llrender.h" -#include "llresmgr.h" -#include "llui.h" -#include "lltooltip.h" - -#include "llglheaders.h" - -// Viewer includes -#include "llagent.h" -#include "llagentcamera.h" -#include "llappviewer.h" // for gDisconnected -#include "llcallingcard.h" // LLAvatarTracker -#include "llfloaterland.h" -#include "llfloaterworldmap.h" -#include "llparcel.h" -#include "lltracker.h" -#include "llsurface.h" -#include "llurlmatch.h" -#include "llurlregistry.h" -#include "llviewercamera.h" -#include "llviewercontrol.h" -#include "llviewerparcelmgr.h" -#include "llviewertexture.h" -#include "llviewertexturelist.h" -#include "llviewermenu.h" -#include "llviewerobjectlist.h" -#include "llviewerregion.h" -#include "llviewerwindow.h" -#include "llworld.h" -#include "llworldmapview.h" // shared draw code - -static LLDefaultChildRegistry::Register r1("net_map"); - -constexpr F32 LLNetMap::MAP_SCALE_MIN = 32; -constexpr F32 LLNetMap::MAP_SCALE_FAR = 32; -constexpr F32 LLNetMap::MAP_SCALE_MEDIUM = 128; -constexpr F32 LLNetMap::MAP_SCALE_CLOSE = 256; -constexpr F32 LLNetMap::MAP_SCALE_VERY_CLOSE = 1024; -constexpr F32 LLNetMap::MAP_SCALE_MAX = 4096; - -constexpr F32 MAP_SCALE_ZOOM_FACTOR = 1.04f; // Zoom in factor per click of scroll wheel (4%) -constexpr F32 MIN_DOT_RADIUS = 3.5f; -constexpr F32 DOT_SCALE = 0.75f; -constexpr F32 MIN_PICK_SCALE = 2.f; -constexpr S32 MOUSE_DRAG_SLOP = 2; // How far the mouse needs to move before we think it's a drag - -constexpr F64 COARSEUPDATE_MAX_Z = 1020.0f; - -LLNetMap::LLNetMap (const Params & p) -: LLUICtrl (p), - mBackgroundColor (p.bg_color()), - mScale( MAP_SCALE_MEDIUM ), - mPixelsPerMeter( MAP_SCALE_MEDIUM / REGION_WIDTH_METERS ), - mObjectMapTPM(0.f), - mObjectMapPixels(0.f), - mCurPan(0.f, 0.f), - mStartPan(0.f, 0.f), - mPopupWorldPos(0.f, 0.f, 0.f), - mMouseDown(0, 0), - mPanning(false), - mUpdateNow(false), - mObjectImageCenterGlobal( gAgentCamera.getCameraPositionGlobal() ), - mObjectRawImagep(), - mObjectImagep(), - mClosestAgentToCursor(), - mClosestAgentAtLastRightClick(), - mToolTipMsg() -{ - mScale = gSavedSettings.getF32("MiniMapScale"); - if (gAgent.isFirstLogin()) - { - // *HACK: On first run, set this to false for new users, otherwise the - // default is true to maintain consistent experience for existing - // users. - gSavedSettings.setBOOL("MiniMapRotate", false); - } - mPixelsPerMeter = mScale / REGION_WIDTH_METERS; - mDotRadius = llmax(DOT_SCALE * mPixelsPerMeter, MIN_DOT_RADIUS); -} - -LLNetMap::~LLNetMap() -{ - auto menu = static_cast(mPopupMenuHandle.get()); - if (menu) - { - menu->die(); - mPopupMenuHandle.markDead(); - } -} - -bool LLNetMap::postBuild() -{ - LLUICtrl::CommitCallbackRegistry::ScopedRegistrar commitRegistrar; - LLUICtrl::EnableCallbackRegistry::ScopedRegistrar enableRegistrar; - - enableRegistrar.add("Minimap.Zoom.Check", boost::bind(&LLNetMap::isZoomChecked, this, _2)); - commitRegistrar.add("Minimap.Zoom.Set", boost::bind(&LLNetMap::setZoom, this, _2)); - commitRegistrar.add("Minimap.Tracker", boost::bind(&LLNetMap::handleStopTracking, this, _2)); - commitRegistrar.add("Minimap.Center.Activate", boost::bind(&LLNetMap::activateCenterMap, this, _2)); - enableRegistrar.add("Minimap.MapOrientation.Check", boost::bind(&LLNetMap::isMapOrientationChecked, this, _2)); - commitRegistrar.add("Minimap.MapOrientation.Set", boost::bind(&LLNetMap::setMapOrientation, this, _2)); - commitRegistrar.add("Minimap.AboutLand", boost::bind(&LLNetMap::popupShowAboutLand, this, _2)); - - LLMenuGL* menu = LLUICtrlFactory::getInstance()->createFromFile("menu_mini_map.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); - mPopupMenuHandle = menu->getHandle(); - menu->setItemEnabled("Re-center map", false); - return true; -} - -void LLNetMap::setScale( F32 scale ) -{ - scale = llclamp(scale, MAP_SCALE_MIN, MAP_SCALE_MAX); - mCurPan *= scale / mScale; - mScale = scale; - - if (mObjectImagep.notNull()) - { - F32 width = (F32)(getRect().getWidth()); - F32 height = (F32)(getRect().getHeight()); - F32 diameter = sqrt(width * width + height * height); - F32 region_widths = diameter / mScale; - F32 meters = region_widths * LLWorld::getInstance()->getRegionWidthInMeters(); - F32 num_pixels = (F32)mObjectImagep->getWidth(); - mObjectMapTPM = num_pixels / meters; - mObjectMapPixels = diameter; - } - - mPixelsPerMeter = mScale / REGION_WIDTH_METERS; - mDotRadius = llmax(DOT_SCALE * mPixelsPerMeter, MIN_DOT_RADIUS); - - gSavedSettings.setF32("MiniMapScale", mScale); - - mUpdateNow = true; -} - - -/////////////////////////////////////////////////////////////////////////////////// - -void LLNetMap::draw() -{ - if (!LLWorld::instanceExists()) - { - return; - } - LL_PROFILE_ZONE_SCOPED; - static LLFrameTimer map_timer; - static LLUIColor map_avatar_color = LLUIColorTable::instance().getColor("MapAvatarColor", LLColor4::white); - static LLUIColor map_avatar_friend_color = LLUIColorTable::instance().getColor("MapAvatarFriendColor", LLColor4::white); - static LLUIColor map_track_color = LLUIColorTable::instance().getColor("MapTrackColor", LLColor4::white); - //static LLUIColor map_track_disabled_color = LLUIColorTable::instance().getColor("MapTrackDisabledColor", LLColor4::white); - static LLUIColor map_frustum_color = LLUIColorTable::instance().getColor("MapFrustumColor", LLColor4::white); - static LLUIColor map_parcel_outline_color = LLUIColorTable::instance().getColor("MapParcelOutlineColor", LLColor4(LLColor3(LLColor4::yellow), 0.5f)); - - if (mObjectImagep.isNull()) - { - createObjectImage(); - } - - static LLUICachedControl auto_center("MiniMapAutoCenter", true); - bool auto_centering = auto_center && !mPanning; - mCentering = mCentering && !mPanning; - - if (auto_centering || mCentering) - { - mCurPan = lerp(mCurPan, LLVector2(0.0f, 0.0f) , LLSmoothInterpolation::getInterpolant(0.1f)); - } - bool centered = abs(mCurPan.mV[VX]) < 0.5f && abs(mCurPan.mV[VY]) < 0.5f; - if (centered) - { - mCurPan.mV[0] = 0.0f; - mCurPan.mV[1] = 0.0f; - mCentering = false; - } - - auto menu = static_cast(mPopupMenuHandle.get()); - if (menu) - { - bool can_recenter_map = !(centered || mCentering || auto_centering); - menu->setItemEnabled("Re-center map", can_recenter_map); - } - updateAboutLandPopupButton(); - - // Prepare a scissor region - F32 rotation = 0; - - gGL.pushMatrix(); - gGL.pushUIMatrix(); - - LLVector3 offset = gGL.getUITranslation(); - LLVector3 scale = gGL.getUIScale(); - - gGL.loadIdentity(); - gGL.loadUIIdentity(); - - gGL.scalef(scale.mV[0], scale.mV[1], scale.mV[2]); - gGL.translatef(offset.mV[0], offset.mV[1], offset.mV[2]); - - { - LLLocalClipRect clip(getLocalRect()); - { - gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - - gGL.matrixMode(LLRender::MM_MODELVIEW); - - // Draw background rectangle - LLColor4 background_color = mBackgroundColor.get(); - gGL.color4fv( background_color.mV ); - gl_rect_2d(0, getRect().getHeight(), getRect().getWidth(), 0); - } - - // region 0,0 is in the middle - S32 center_sw_left = getRect().getWidth() / 2 + llfloor(mCurPan.mV[VX]); - S32 center_sw_bottom = getRect().getHeight() / 2 + llfloor(mCurPan.mV[VY]); - - gGL.pushMatrix(); - - gGL.translatef( (F32) center_sw_left, (F32) center_sw_bottom, 0.f); - - static LLUICachedControl rotate_map("MiniMapRotate", true); - if( rotate_map ) - { - // rotate subsequent draws to agent rotation - rotation = atan2( LLViewerCamera::getInstance()->getAtAxis().mV[VX], LLViewerCamera::getInstance()->getAtAxis().mV[VY] ); - gGL.rotatef( rotation * RAD_TO_DEG, 0.f, 0.f, 1.f); - } - - // figure out where agent is - const S32 region_width = ll_round(LLWorld::getInstance()->getRegionWidthInMeters()); - const F32 scale_pixels_per_meter = mScale / region_width; - - for (LLWorld::region_list_t::const_iterator iter = LLWorld::getInstance()->getRegionList().begin(); - iter != LLWorld::getInstance()->getRegionList().end(); ++iter) - { - LLViewerRegion* regionp = *iter; - // Find x and y position relative to camera's center. - LLVector3 origin_agent = regionp->getOriginAgent(); - LLVector3 rel_region_pos = origin_agent - gAgentCamera.getCameraPositionAgent(); - F32 relative_x = rel_region_pos.mV[0] * scale_pixels_per_meter; - F32 relative_y = rel_region_pos.mV[1] * scale_pixels_per_meter; - - // background region rectangle - F32 bottom = relative_y; - F32 left = relative_x; - F32 top = bottom + mScale ; - F32 right = left + mScale ; - - if (regionp == gAgent.getRegion()) - { - gGL.color4f(1.f, 1.f, 1.f, 1.f); - } - else - { - gGL.color4f(0.8f, 0.8f, 0.8f, 1.f); - } - - if (!regionp->isAlive()) - { - gGL.color4f(1.f, 0.5f, 0.5f, 1.f); - } - - - - // Draw using texture. - gGL.getTexUnit(0)->bind(regionp->getLand().getSTexture()); - gGL.begin(LLRender::QUADS); - gGL.texCoord2f(0.f, 1.f); - gGL.vertex2f(left, top); - gGL.texCoord2f(0.f, 0.f); - gGL.vertex2f(left, bottom); - gGL.texCoord2f(1.f, 0.f); - gGL.vertex2f(right, bottom); - gGL.texCoord2f(1.f, 1.f); - gGL.vertex2f(right, top); - gGL.end(); - - // Draw water - gGL.flush(); - { - if (regionp->getLand().getWaterTexture()) - { - gGL.getTexUnit(0)->bind(regionp->getLand().getWaterTexture()); - gGL.begin(LLRender::QUADS); - gGL.texCoord2f(0.f, 1.f); - gGL.vertex2f(left, top); - gGL.texCoord2f(0.f, 0.f); - gGL.vertex2f(left, bottom); - gGL.texCoord2f(1.f, 0.f); - gGL.vertex2f(right, bottom); - gGL.texCoord2f(1.f, 1.f); - gGL.vertex2f(right, top); - gGL.end(); - } - } - gGL.flush(); - } - - // Redraw object layer periodically - if (mUpdateNow || (map_timer.getElapsedTimeF32() > 0.5f)) - { - mUpdateNow = false; - - // Locate the centre of the object layer, accounting for panning - LLVector3 new_center = globalPosToView(gAgentCamera.getCameraPositionGlobal()); - new_center.mV[VX] -= mCurPan.mV[VX]; - new_center.mV[VY] -= mCurPan.mV[VY]; - new_center.mV[VZ] = 0.f; - mObjectImageCenterGlobal = viewPosToGlobal(llfloor(new_center.mV[VX]), llfloor(new_center.mV[VY])); - - // Create the base texture. - LLImageDataLock lock(mObjectRawImagep); - U8 *default_texture = mObjectRawImagep->getData(); - memset( default_texture, 0, mObjectImagep->getWidth() * mObjectImagep->getHeight() * mObjectImagep->getComponents() ); - - // Draw objects - gObjectList.renderObjectsForMap(*this); - - mObjectImagep->setSubImage(mObjectRawImagep, 0, 0, mObjectImagep->getWidth(), mObjectImagep->getHeight()); - - map_timer.reset(); - } - - LLVector3 map_center_agent = gAgent.getPosAgentFromGlobal(mObjectImageCenterGlobal); - LLVector3 camera_position = gAgentCamera.getCameraPositionAgent(); - map_center_agent -= camera_position; - map_center_agent.mV[VX] *= scale_pixels_per_meter; - map_center_agent.mV[VY] *= scale_pixels_per_meter; - - gGL.getTexUnit(0)->bind(mObjectImagep); - F32 image_half_width = 0.5f*mObjectMapPixels; - F32 image_half_height = 0.5f*mObjectMapPixels; - - gGL.begin(LLRender::QUADS); - gGL.texCoord2f(0.f, 1.f); - gGL.vertex2f(map_center_agent.mV[VX] - image_half_width, image_half_height + map_center_agent.mV[VY]); - gGL.texCoord2f(0.f, 0.f); - gGL.vertex2f(map_center_agent.mV[VX] - image_half_width, map_center_agent.mV[VY] - image_half_height); - gGL.texCoord2f(1.f, 0.f); - gGL.vertex2f(image_half_width + map_center_agent.mV[VX], map_center_agent.mV[VY] - image_half_height); - gGL.texCoord2f(1.f, 1.f); - gGL.vertex2f(image_half_width + map_center_agent.mV[VX], image_half_height + map_center_agent.mV[VY]); - gGL.end(); - - for (LLWorld::region_list_t::const_iterator iter = LLWorld::getInstance()->getRegionList().begin(); - iter != LLWorld::getInstance()->getRegionList().end(); ++iter) - { - LLViewerRegion* regionp = *iter; - regionp->renderPropertyLinesOnMinimap(scale_pixels_per_meter, map_parcel_outline_color.get().mV); - } - - gGL.popMatrix(); - - // Mouse pointer in local coordinates - S32 local_mouse_x; - S32 local_mouse_y; - //localMouse(&local_mouse_x, &local_mouse_y); - LLUI::getInstance()->getMousePositionLocal(this, &local_mouse_x, &local_mouse_y); - mClosestAgentToCursor.setNull(); - F32 closest_dist_squared = F32_MAX; // value will be overridden in the loop - F32 min_pick_dist_squared = (mDotRadius * MIN_PICK_SCALE) * (mDotRadius * MIN_PICK_SCALE); - - LLVector3 pos_map; - uuid_vec_t avatar_ids; - std::vector positions; - bool unknown_relative_z; - - LLWorld::getInstance()->getAvatars(&avatar_ids, &positions, gAgentCamera.getCameraPositionGlobal()); - - // Draw avatars - for (U32 i = 0; i < avatar_ids.size(); i++) - { - LLUUID uuid = avatar_ids[i]; - // Skip self, we'll draw it later - if (uuid == gAgent.getID()) continue; - - pos_map = globalPosToView(positions[i]); - - bool show_as_friend = (LLAvatarTracker::instance().getBuddyInfo(uuid) != NULL); - - LLColor4 color = show_as_friend ? map_avatar_friend_color : map_avatar_color; - - unknown_relative_z = positions[i].mdV[VZ] >= COARSEUPDATE_MAX_Z && - camera_position.mV[VZ] >= COARSEUPDATE_MAX_Z; - - LLWorldMapView::drawAvatar( - pos_map.mV[VX], pos_map.mV[VY], - color, - pos_map.mV[VZ], mDotRadius, - unknown_relative_z); - - if(uuid.notNull()) - { - bool selected = false; - uuid_vec_t::iterator sel_iter = gmSelected.begin(); - for (; sel_iter != gmSelected.end(); sel_iter++) - { - if(*sel_iter == uuid) - { - selected = true; - break; - } - } - if(selected) - { - if( (pos_map.mV[VX] < 0) || - (pos_map.mV[VY] < 0) || - (pos_map.mV[VX] >= getRect().getWidth()) || - (pos_map.mV[VY] >= getRect().getHeight()) ) - { - S32 x = ll_round( pos_map.mV[VX] ); - S32 y = ll_round( pos_map.mV[VY] ); - LLWorldMapView::drawTrackingCircle( getRect(), x, y, color, 1, 10); - } else - { - LLWorldMapView::drawTrackingDot(pos_map.mV[VX],pos_map.mV[VY],color,0.f); - } - } - } - - F32 dist_to_cursor_squared = dist_vec_squared(LLVector2(pos_map.mV[VX], pos_map.mV[VY]), - LLVector2(local_mouse_x,local_mouse_y)); - if(dist_to_cursor_squared < min_pick_dist_squared && dist_to_cursor_squared < closest_dist_squared) - { - closest_dist_squared = dist_to_cursor_squared; - mClosestAgentToCursor = uuid; - } - } - - // Draw dot for autopilot target - if (gAgent.getAutoPilot()) - { - drawTracking( gAgent.getAutoPilotTargetGlobal(), map_track_color ); - } - else - { - LLTracker::ETrackingStatus tracking_status = LLTracker::getTrackingStatus(); - if ( LLTracker::TRACKING_AVATAR == tracking_status ) - { - drawTracking( LLAvatarTracker::instance().getGlobalPos(), map_track_color ); - } - else if ( LLTracker::TRACKING_LANDMARK == tracking_status - || LLTracker::TRACKING_LOCATION == tracking_status ) - { - drawTracking( LLTracker::getTrackedPositionGlobal(), map_track_color ); - } - } - - // Draw dot for self avatar position - LLVector3d pos_global = gAgent.getPositionGlobal(); - pos_map = globalPosToView(pos_global); - S32 dot_width = ll_round(mDotRadius * 2.f); - LLUIImagePtr you = LLWorldMapView::sAvatarYouLargeImage; - if (you) - { - you->draw(ll_round(pos_map.mV[VX] - mDotRadius), - ll_round(pos_map.mV[VY] - mDotRadius), - dot_width, - dot_width); - - F32 dist_to_cursor_squared = dist_vec_squared(LLVector2(pos_map.mV[VX], pos_map.mV[VY]), - LLVector2(local_mouse_x,local_mouse_y)); - if(dist_to_cursor_squared < min_pick_dist_squared && dist_to_cursor_squared < closest_dist_squared) - { - mClosestAgentToCursor = gAgent.getID(); - } - } - - // Draw frustum - F32 meters_to_pixels = mScale/ LLWorld::getInstance()->getRegionWidthInMeters(); - - F32 horiz_fov = LLViewerCamera::getInstance()->getView() * LLViewerCamera::getInstance()->getAspect(); - F32 far_clip_meters = LLViewerCamera::getInstance()->getFar(); - F32 far_clip_pixels = far_clip_meters * meters_to_pixels; - - F32 ctr_x = (F32)center_sw_left; - F32 ctr_y = (F32)center_sw_bottom; - - const F32 steps_per_circle = 40.0f; - const F32 steps_per_radian = steps_per_circle / F_TWO_PI; - const F32 arc_start = -(horiz_fov / 2.0f) + F_PI_BY_TWO; - const F32 arc_end = (horiz_fov / 2.0f) + F_PI_BY_TWO; - const S32 steps = llmax(1, (S32)((horiz_fov * steps_per_radian) + 0.5f)); - - gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - - if( rotate_map ) - { - gGL.pushMatrix(); - gGL.translatef( ctr_x, ctr_y, 0 ); - gl_washer_segment_2d(far_clip_pixels, 0, arc_start, arc_end, steps, map_frustum_color(), map_frustum_color()); - gGL.popMatrix(); - } - else - { - gGL.pushMatrix(); - gGL.translatef( ctr_x, ctr_y, 0 ); - // If we don't rotate the map, we have to rotate the frustum. - gGL.rotatef( atan2( LLViewerCamera::getInstance()->getAtAxis().mV[VX], LLViewerCamera::getInstance()->getAtAxis().mV[VY] ) * RAD_TO_DEG, 0.f, 0.f, -1.f); - gl_washer_segment_2d(far_clip_pixels, 0, arc_start, arc_end, steps, map_frustum_color(), map_frustum_color()); - gGL.popMatrix(); - } - } - - gGL.popMatrix(); - gGL.popUIMatrix(); - - LLUICtrl::draw(); -} - -void LLNetMap::reshape(S32 width, S32 height, bool called_from_parent) -{ - LLUICtrl::reshape(width, height, called_from_parent); - createObjectImage(); -} - -LLVector3 LLNetMap::globalPosToView(const LLVector3d& global_pos) -{ - LLVector3d camera_position = gAgentCamera.getCameraPositionGlobal(); - - LLVector3d relative_pos_global = global_pos - camera_position; - LLVector3 pos_local; - pos_local.setVec(relative_pos_global); // convert to floats from doubles - - pos_local.mV[VX] *= mPixelsPerMeter; - pos_local.mV[VY] *= mPixelsPerMeter; - // leave Z component in meters - - static LLUICachedControl rotate_map("MiniMapRotate", true); - if( rotate_map ) - { - F32 radians = atan2( LLViewerCamera::getInstance()->getAtAxis().mV[VX], LLViewerCamera::getInstance()->getAtAxis().mV[VY] ); - LLQuaternion rot(radians, LLVector3(0.f, 0.f, 1.f)); - pos_local.rotVec( rot ); - } - - pos_local.mV[VX] += getRect().getWidth() / 2 + mCurPan.mV[VX]; - pos_local.mV[VY] += getRect().getHeight() / 2 + mCurPan.mV[VY]; - - return pos_local; -} - -void LLNetMap::drawTracking(const LLVector3d& pos_global, const LLColor4& color, - bool draw_arrow ) -{ - LLVector3 pos_local = globalPosToView(pos_global); - if( (pos_local.mV[VX] < 0) || - (pos_local.mV[VY] < 0) || - (pos_local.mV[VX] >= getRect().getWidth()) || - (pos_local.mV[VY] >= getRect().getHeight()) ) - { - if (draw_arrow) - { - S32 x = ll_round( pos_local.mV[VX] ); - S32 y = ll_round( pos_local.mV[VY] ); - LLWorldMapView::drawTrackingCircle( getRect(), x, y, color, 1, 10 ); - LLWorldMapView::drawTrackingArrow( getRect(), x, y, color ); - } - } - else - { - LLWorldMapView::drawTrackingDot(pos_local.mV[VX], - pos_local.mV[VY], - color, - pos_local.mV[VZ]); - } -} - -bool LLNetMap::isMouseOnPopupMenu() -{ - auto menu = static_cast(mPopupMenuHandle.get()); - if (!menu || !menu->isOpen()) - { - return false; - } - - S32 popup_x; - S32 popup_y; - LLUI::getInstance()->getMousePositionLocal(menu, &popup_x, &popup_y); - // *NOTE: Tolerance is larger than it needs to be because the context menu is offset from the mouse when the menu is opened from certain - // directions. This may be a quirk of LLMenuGL::showPopup. -Cosmic,2022-03-22 - constexpr S32 tolerance = 10; - // Test tolerance from all four corners, as the popup menu can appear from a different direction if there's not enough space. - // Assume the size of the popup menu is much larger than the provided tolerance. - // In practice, this is a [tolerance]px margin around the popup menu. - for (S32 sign_x = -1; sign_x <= 1; sign_x += 2) - { - for (S32 sign_y = -1; sign_y <= 1; sign_y += 2) - { - if (menu->pointInView(popup_x + (sign_x * tolerance), popup_y + (sign_y * tolerance))) - { - return true; - } - } - } - return false; -} - -void LLNetMap::updateAboutLandPopupButton() -{ - auto menu = static_cast(mPopupMenuHandle.get()); - if (!menu || !menu->isOpen()) - { - return; - } - - LLViewerRegion *region = LLWorld::getInstance()->getRegionFromPosGlobal(mPopupWorldPos); - if (!region) - { - menu->setItemEnabled("About Land", false); - } - else - { - // Check if the mouse is in the bounds of the popup. If so, it's safe to assume no other hover function will be called, so the hover - // parcel can be used to check if location-sensitive tooltip options are available. - if (isMouseOnPopupMenu()) - { - LLViewerParcelMgr::getInstance()->setHoverParcel(mPopupWorldPos); - LLParcel *hover_parcel = LLViewerParcelMgr::getInstance()->getHoverParcel(); - bool valid_parcel = false; - if (hover_parcel) - { - valid_parcel = hover_parcel->getOwnerID().notNull(); - } - menu->setItemEnabled("About Land", valid_parcel); - } - } -} - -LLVector3d LLNetMap::viewPosToGlobal( S32 x, S32 y ) -{ - x -= ll_round(getRect().getWidth() / 2 + mCurPan.mV[VX]); - y -= ll_round(getRect().getHeight() / 2 + mCurPan.mV[VY]); - - LLVector3 pos_local( (F32)x, (F32)y, 0 ); - - F32 radians = - atan2( LLViewerCamera::getInstance()->getAtAxis().mV[VX], LLViewerCamera::getInstance()->getAtAxis().mV[VY] ); - - static LLUICachedControl rotate_map("MiniMapRotate", true); - if( rotate_map ) - { - LLQuaternion rot(radians, LLVector3(0.f, 0.f, 1.f)); - pos_local.rotVec( rot ); - } - - pos_local *= ( LLWorld::getInstance()->getRegionWidthInMeters() / mScale ); - - LLVector3d pos_global; - pos_global.setVec( pos_local ); - pos_global += gAgentCamera.getCameraPositionGlobal(); - - return pos_global; -} - -bool LLNetMap::handleScrollWheel(S32 x, S32 y, S32 clicks) -{ - // note that clicks are reversed from what you'd think: i.e. > 0 means zoom out, < 0 means zoom in - F32 new_scale = mScale * pow(MAP_SCALE_ZOOM_FACTOR, -clicks); - F32 old_scale = mScale; - - setScale(new_scale); - - static LLUICachedControl auto_center("MiniMapAutoCenter", true); - if (!auto_center) - { - // Adjust pan to center the zoom on the mouse pointer - LLVector2 zoom_offset; - zoom_offset.mV[VX] = x - getRect().getWidth() / 2; - zoom_offset.mV[VY] = y - getRect().getHeight() / 2; - mCurPan -= zoom_offset * mScale / old_scale - zoom_offset; - } - - return true; -} - -bool LLNetMap::handleToolTip(S32 x, S32 y, MASK mask) -{ - if (gDisconnected) - { - return false; - } - - // If the cursor is near an avatar on the minimap, a mini-inspector will be - // shown for the avatar, instead of the normal map tooltip. - if (handleToolTipAgent(mClosestAgentToCursor)) - { - return true; - } - - // The popup menu uses the hover parcel when it is open and the mouse is on - // top of it, with some additional tolerance. Returning early here prevents - // fighting over that hover parcel when getting tooltip info in the - // tolerance region. - if (isMouseOnPopupMenu()) - { - return false; - } - - LLRect sticky_rect; - S32 SLOP = 4; - localPointToScreen(x - SLOP, y - SLOP, &(sticky_rect.mLeft), &(sticky_rect.mBottom)); - sticky_rect.mRight = sticky_rect.mLeft + 2 * SLOP; - sticky_rect.mTop = sticky_rect.mBottom + 2 * SLOP; - - std::string parcel_name_msg; - std::string parcel_sale_price_msg; - std::string parcel_sale_area_msg; - std::string parcel_owner_msg; - std::string region_name_msg; - - LLVector3d posGlobal = viewPosToGlobal(x, y); - LLViewerRegion *region = LLWorld::getInstance()->getRegionFromPosGlobal(posGlobal); - if (region) - { - std::string region_name = region->getName(); - if (!region_name.empty()) - { - region_name_msg = mRegionNameMsg; - LLStringUtil::format(region_name_msg, {{"[REGION_NAME]", region_name}}); - } - - // Only show parcel information in the tooltip if property lines are visible. Otherwise, the parcel the tooltip is referring to is - // ambiguous. - if (gSavedSettings.getBOOL("MiniMapShowPropertyLines")) - { - LLViewerParcelMgr::getInstance()->setHoverParcel(posGlobal); - LLParcel *hover_parcel = LLViewerParcelMgr::getInstance()->getHoverParcel(); - if (hover_parcel) - { - std::string parcel_name = hover_parcel->getName(); - if (!parcel_name.empty()) - { - parcel_name_msg = mParcelNameMsg; - LLStringUtil::format(parcel_name_msg, {{"[PARCEL_NAME]", parcel_name}}); - } - - const LLUUID parcel_owner = hover_parcel->getOwnerID(); - std::string parcel_owner_name_url = LLSLURL("agent", parcel_owner, "inspect").getSLURLString(); - static LLUrlMatch parcel_owner_name_url_match; - LLUrlRegistry::getInstance()->findUrl(parcel_owner_name_url, parcel_owner_name_url_match); - if (!parcel_owner_name_url_match.empty()) - { - parcel_owner_msg = mParcelOwnerMsg; - std::string parcel_owner_name = parcel_owner_name_url_match.getLabel(); - LLStringUtil::format(parcel_owner_msg, {{"[PARCEL_OWNER]", parcel_owner_name}}); - } - - if (hover_parcel->getForSale()) - { - const LLUUID auth_buyer_id = hover_parcel->getAuthorizedBuyerID(); - const LLUUID agent_id = gAgent.getID(); - bool show_for_sale = auth_buyer_id.isNull() || auth_buyer_id == agent_id || parcel_owner == agent_id; - if (show_for_sale) - { - S32 price = hover_parcel->getSalePrice(); - S32 area = hover_parcel->getArea(); - F32 cost_per_sqm = 0.0f; - if (area > 0) - { - cost_per_sqm = F32(price) / area; - } - std::string formatted_price = LLResMgr::getInstance()->getMonetaryString(price); - std::string formatted_cost_per_meter = llformat("%.1f", cost_per_sqm); - parcel_sale_price_msg = mParcelSalePriceMsg; - LLStringUtil::format(parcel_sale_price_msg, - {{"[PRICE]", formatted_price}, {"[PRICE_PER_SQM]", formatted_cost_per_meter}}); - std::string formatted_area = llformat("%d", area); - parcel_sale_area_msg = mParcelSaleAreaMsg; - LLStringUtil::format(parcel_sale_area_msg, {{"[AREA]", formatted_area}}); - } - } - } - } - } - - std::string tool_tip_hint_msg; - if (gSavedSettings.getBOOL("DoubleClickTeleport")) - { - tool_tip_hint_msg = mAltToolTipHintMsg; - } - else if (gSavedSettings.getBOOL("DoubleClickShowWorldMap")) - { - tool_tip_hint_msg = mToolTipHintMsg; - } - - LLStringUtil::format_map_t args; - args["[PARCEL_NAME_MSG]"] = parcel_name_msg.empty() ? "" : parcel_name_msg + '\n'; - args["[PARCEL_SALE_PRICE_MSG]"] = parcel_sale_price_msg.empty() ? "" : parcel_sale_price_msg + '\n'; - args["[PARCEL_SALE_AREA_MSG]"] = parcel_sale_area_msg.empty() ? "" : parcel_sale_area_msg + '\n'; - args["[PARCEL_OWNER_MSG]"] = parcel_owner_msg.empty() ? "" : parcel_owner_msg + '\n'; - args["[REGION_NAME_MSG]"] = region_name_msg.empty() ? "" : region_name_msg + '\n'; - args["[TOOL_TIP_HINT_MSG]"] = tool_tip_hint_msg.empty() ? "" : tool_tip_hint_msg + '\n'; - - std::string msg = mToolTipMsg; - LLStringUtil::format(msg, args); - if (msg.back() == '\n') - { - msg.resize(msg.size() - 1); - } - LLToolTipMgr::instance().show(LLToolTip::Params().message(msg).sticky_rect(sticky_rect)); - - return true; -} - -bool LLNetMap::handleToolTipAgent(const LLUUID& avatar_id) -{ - LLAvatarName av_name; - if (avatar_id.isNull() || !LLAvatarNameCache::get(avatar_id, &av_name)) - { - return false; - } - - // only show tooltip if same inspector not already open - LLFloater* existing_inspector = LLFloaterReg::findInstance("inspect_avatar"); - if (!existing_inspector - || !existing_inspector->getVisible() - || existing_inspector->getKey()["avatar_id"].asUUID() != avatar_id) - { - LLInspector::Params p; - p.fillFrom(LLUICtrlFactory::instance().getDefaultParams()); - p.message(av_name.getCompleteName()); - p.image.name("Inspector_I"); - p.click_callback(boost::bind(showAvatarInspector, avatar_id)); - p.visible_time_near(6.f); - p.visible_time_far(3.f); - p.delay_time(0.35f); - p.wrap(false); - - LLToolTipMgr::instance().show(p); - } - return true; -} - -// static -void LLNetMap::showAvatarInspector(const LLUUID& avatar_id) -{ - LLSD params; - params["avatar_id"] = avatar_id; - - if (LLToolTipMgr::instance().toolTipVisible()) - { - LLRect rect = LLToolTipMgr::instance().getToolTipRect(); - params["pos"]["x"] = rect.mLeft; - params["pos"]["y"] = rect.mTop; - } - - LLFloaterReg::showInstance("inspect_avatar", params); -} - -void LLNetMap::renderScaledPointGlobal( const LLVector3d& pos, const LLColor4U &color, F32 radius_meters ) -{ - LLVector3 local_pos; - local_pos.setVec( pos - mObjectImageCenterGlobal ); - - S32 diameter_pixels = ll_round(2 * radius_meters * mObjectMapTPM); - renderPoint( local_pos, color, diameter_pixels ); -} - - -void LLNetMap::renderPoint(const LLVector3 &pos_local, const LLColor4U &color, - S32 diameter, S32 relative_height) -{ - if (diameter <= 0) - { - return; - } - - const S32 image_width = (S32)mObjectImagep->getWidth(); - const S32 image_height = (S32)mObjectImagep->getHeight(); - - S32 x_offset = ll_round(pos_local.mV[VX] * mObjectMapTPM + image_width / 2); - S32 y_offset = ll_round(pos_local.mV[VY] * mObjectMapTPM + image_height / 2); - - if ((x_offset < 0) || (x_offset >= image_width)) - { - return; - } - if ((y_offset < 0) || (y_offset >= image_height)) - { - return; - } - - LLImageDataLock lock(mObjectRawImagep); - U8 *datap = mObjectRawImagep->getData(); - - S32 neg_radius = diameter / 2; - S32 pos_radius = diameter - neg_radius; - S32 x, y; - - if (relative_height > 0) - { - // ...point above agent - S32 px, py; - - // vertical line - px = x_offset; - for (y = -neg_radius; y < pos_radius; y++) - { - py = y_offset + y; - if ((py < 0) || (py >= image_height)) - { - continue; - } - S32 offset = px + py * image_width; - ((U32*)datap)[offset] = color.asRGBA(); - } - - // top line - py = y_offset + pos_radius - 1; - for (x = -neg_radius; x < pos_radius; x++) - { - px = x_offset + x; - if ((px < 0) || (px >= image_width)) - { - continue; - } - S32 offset = px + py * image_width; - ((U32*)datap)[offset] = color.asRGBA(); - } - } - else - { - // ...point level with agent - for (x = -neg_radius; x < pos_radius; x++) - { - S32 p_x = x_offset + x; - if ((p_x < 0) || (p_x >= image_width)) - { - continue; - } - - for (y = -neg_radius; y < pos_radius; y++) - { - S32 p_y = y_offset + y; - if ((p_y < 0) || (p_y >= image_height)) - { - continue; - } - S32 offset = p_x + p_y * image_width; - ((U32*)datap)[offset] = color.asRGBA(); - } - } - } -} - -void LLNetMap::createObjectImage() -{ - // Find the size of the side of a square that surrounds the circle that surrounds getRect(). - // ... which is, the diagonal of the rect. - F32 width = (F32)getRect().getWidth(); - F32 height = (F32)getRect().getHeight(); - S32 square_size = ll_round( sqrt(width*width + height*height) ); - - // Find the least power of two >= the minimum size. - const S32 MIN_SIZE = 64; - const S32 MAX_SIZE = 256; - S32 img_size = MIN_SIZE; - while( (img_size*2 < square_size ) && (img_size < MAX_SIZE) ) - { - img_size <<= 1; - } - - if( mObjectImagep.isNull() || - (mObjectImagep->getWidth() != img_size) || - (mObjectImagep->getHeight() != img_size) ) - { - mObjectRawImagep = new LLImageRaw(img_size, img_size, 4); - U8* data = mObjectRawImagep->getData(); - memset( data, 0, img_size * img_size * 4 ); - mObjectImagep = LLViewerTextureManager::getLocalTexture( mObjectRawImagep.get(), false); - } - setScale(mScale); - mUpdateNow = true; -} - -bool LLNetMap::handleMouseDown(S32 x, S32 y, MASK mask) -{ - // Start panning - gFocusMgr.setMouseCapture(this); - - mStartPan = mCurPan; - mMouseDown.mX = x; - mMouseDown.mY = y; - return true; -} - -bool LLNetMap::handleMouseUp(S32 x, S32 y, MASK mask) -{ - if (abs(mMouseDown.mX - x) < 3 && abs(mMouseDown.mY - y) < 3) - { - handleClick(x, y, mask); - } - - if (hasMouseCapture()) - { - if (mPanning) - { - // restore mouse cursor - S32 local_x, local_y; - local_x = mMouseDown.mX + llfloor(mCurPan.mV[VX] - mStartPan.mV[VX]); - local_y = mMouseDown.mY + llfloor(mCurPan.mV[VY] - mStartPan.mV[VY]); - LLRect clip_rect = getRect(); - clip_rect.stretch(-8); - clip_rect.clipPointToRect(mMouseDown.mX, mMouseDown.mY, local_x, local_y); - LLUI::getInstance()->setMousePositionLocal(this, local_x, local_y); - - // finish the pan - mPanning = false; - - mMouseDown.set(0, 0); - } - gViewerWindow->showCursor(); - gFocusMgr.setMouseCapture(NULL); - return true; - } - - return false; -} - -bool LLNetMap::handleRightMouseDown(S32 x, S32 y, MASK mask) -{ - auto menu = static_cast(mPopupMenuHandle.get()); - if (menu) - { - mPopupWorldPos = viewPosToGlobal(x, y); - menu->buildDrawLabels(); - menu->updateParent(LLMenuGL::sMenuContainer); - menu->setItemEnabled("Stop Tracking", LLTracker::isTracking(0)); - LLMenuGL::showPopup(this, menu, x, y); - } - return true; -} - -bool LLNetMap::handleClick(S32 x, S32 y, MASK mask) -{ - // TODO: allow clicking an avatar on minimap to select avatar in the nearby avatar list - // if(mClosestAgentToCursor.notNull()) - // mNearbyList->selectUser(mClosestAgentToCursor); - // Needs a registered observer i guess to accomplish this without using - // globals to tell the mNearbyList in llpeoplepanel to select the user - return true; -} - -bool LLNetMap::handleDoubleClick(S32 x, S32 y, MASK mask) -{ - LLVector3d pos_global = viewPosToGlobal(x, y); - - bool double_click_teleport = gSavedSettings.getBOOL("DoubleClickTeleport"); - bool double_click_show_world_map = gSavedSettings.getBOOL("DoubleClickShowWorldMap"); - - if (double_click_teleport || double_click_show_world_map) - { - // If we're not tracking a beacon already, double-click will set one - if (!LLTracker::isTracking(NULL)) - { - LLFloaterWorldMap* world_map = LLFloaterWorldMap::getInstance(); - if (world_map) - { - world_map->trackLocation(pos_global); - } - } - } - - if (double_click_teleport) - { - // If DoubleClickTeleport is on, double clicking the minimap will teleport there - gAgent.teleportViaLocationLookAt(pos_global); - } - else if (double_click_show_world_map) - { - LLFloaterReg::showInstance("world_map"); - } - return true; -} - -F32 LLNetMap::getScaleForName(std::string scale_name) -{ - if (scale_name == "very close") - { - return LLNetMap::MAP_SCALE_VERY_CLOSE; - } - else if (scale_name == "close") - { - return LLNetMap::MAP_SCALE_CLOSE; - } - else if (scale_name == "medium") - { - return LLNetMap::MAP_SCALE_MEDIUM; - } - else if (scale_name == "far") - { - return LLNetMap::MAP_SCALE_FAR; - } - return 0.0f; -} - -// static -bool LLNetMap::outsideSlop( S32 x, S32 y, S32 start_x, S32 start_y, S32 slop ) -{ - S32 dx = x - start_x; - S32 dy = y - start_y; - - return (dx <= -slop || slop <= dx || dy <= -slop || slop <= dy); -} - -bool LLNetMap::handleHover( S32 x, S32 y, MASK mask ) -{ - if (hasMouseCapture()) - { - if (mPanning || outsideSlop(x, y, mMouseDown.mX, mMouseDown.mY, MOUSE_DRAG_SLOP)) - { - if (!mPanning) - { - // Just started panning. Hide cursor. - mPanning = true; - gViewerWindow->hideCursor(); - } - - LLVector2 delta(static_cast(gViewerWindow->getCurrentMouseDX()), - static_cast(gViewerWindow->getCurrentMouseDY())); - - // Set pan to value at start of drag + offset - mCurPan += delta; - - gViewerWindow->moveCursorToCenter(); - } - } - - if (mask & MASK_SHIFT) - { - // If shift is held, change the cursor to hint that the map can be - // dragged. However, holding shift is not required to drag the map. - gViewerWindow->setCursor( UI_CURSOR_TOOLPAN ); - } - else - { - gViewerWindow->setCursor( UI_CURSOR_CROSS ); - } - - return true; -} - -bool LLNetMap::isZoomChecked(const LLSD &userdata) -{ - std::string level = userdata.asString(); - F32 scale = getScaleForName(level); - return scale == mScale; -} - -void LLNetMap::setZoom(const LLSD &userdata) -{ - std::string level = userdata.asString(); - F32 scale = getScaleForName(level); - if (scale != 0.0f) - { - setScale(scale); - } -} - -void LLNetMap::handleStopTracking (const LLSD& userdata) -{ - auto menu = static_cast(mPopupMenuHandle.get()); - if (menu) - { - menu->setItemEnabled ("Stop Tracking", false); - LLTracker::stopTracking (LLTracker::isTracking(NULL)); - } -} - -void LLNetMap::activateCenterMap(const LLSD &userdata) { mCentering = true; } - -bool LLNetMap::isMapOrientationChecked(const LLSD &userdata) -{ - const std::string command_name = userdata.asString(); - const bool rotate_map = gSavedSettings.getBOOL("MiniMapRotate"); - if (command_name == "north_at_top") - { - return !rotate_map; - } - - if (command_name == "camera_at_top") - { - return rotate_map; - } - - return false; -} - -void LLNetMap::setMapOrientation(const LLSD &userdata) -{ - const std::string command_name = userdata.asString(); - if (command_name == "north_at_top") - { - gSavedSettings.setBOOL("MiniMapRotate", false); - } - else if (command_name == "camera_at_top") - { - gSavedSettings.setBOOL("MiniMapRotate", true); - } -} - -void LLNetMap::popupShowAboutLand(const LLSD &userdata) -{ - // Update parcel selection. It's important to deselect land first so the "About Land" floater doesn't refresh with the old selection. - LLViewerParcelMgr::getInstance()->deselectLand(); - LLParcelSelectionHandle selection = LLViewerParcelMgr::getInstance()->selectParcelAt(mPopupWorldPos); - gMenuHolder->setParcelSelection(selection); - - LLFloaterReg::showInstance("about_land", LLSD(), false); -} +/** + * @file llnetmap.cpp + * @author James Cook + * @brief Display of surrounding regions, objects, and agents. + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2001-2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "llnetmap.h" + +// Library includes (should move below) +#include "indra_constants.h" +#include "llavatarnamecache.h" +#include "llmath.h" +#include "llfloaterreg.h" +#include "llfocusmgr.h" +#include "lllocalcliprect.h" +#include "llrender.h" +#include "llresmgr.h" +#include "llui.h" +#include "lltooltip.h" + +#include "llglheaders.h" + +// Viewer includes +#include "llagent.h" +#include "llagentcamera.h" +#include "llappviewer.h" // for gDisconnected +#include "llcallingcard.h" // LLAvatarTracker +#include "llfloaterland.h" +#include "llfloaterworldmap.h" +#include "llparcel.h" +#include "lltracker.h" +#include "llsurface.h" +#include "llurlmatch.h" +#include "llurlregistry.h" +#include "llviewercamera.h" +#include "llviewercontrol.h" +#include "llviewerparcelmgr.h" +#include "llviewertexture.h" +#include "llviewertexturelist.h" +#include "llviewermenu.h" +#include "llviewerobjectlist.h" +#include "llviewerregion.h" +#include "llviewerwindow.h" +#include "llworld.h" +#include "llworldmapview.h" // shared draw code + +static LLDefaultChildRegistry::Register r1("net_map"); + +constexpr F32 LLNetMap::MAP_SCALE_MIN = 32; +constexpr F32 LLNetMap::MAP_SCALE_FAR = 32; +constexpr F32 LLNetMap::MAP_SCALE_MEDIUM = 128; +constexpr F32 LLNetMap::MAP_SCALE_CLOSE = 256; +constexpr F32 LLNetMap::MAP_SCALE_VERY_CLOSE = 1024; +constexpr F32 LLNetMap::MAP_SCALE_MAX = 4096; + +constexpr F32 MAP_SCALE_ZOOM_FACTOR = 1.04f; // Zoom in factor per click of scroll wheel (4%) +constexpr F32 MIN_DOT_RADIUS = 3.5f; +constexpr F32 DOT_SCALE = 0.75f; +constexpr F32 MIN_PICK_SCALE = 2.f; +constexpr S32 MOUSE_DRAG_SLOP = 2; // How far the mouse needs to move before we think it's a drag + +constexpr F64 COARSEUPDATE_MAX_Z = 1020.0f; + +LLNetMap::LLNetMap (const Params & p) +: LLUICtrl (p), + mBackgroundColor (p.bg_color()), + mScale( MAP_SCALE_MEDIUM ), + mPixelsPerMeter( MAP_SCALE_MEDIUM / REGION_WIDTH_METERS ), + mObjectMapTPM(0.f), + mObjectMapPixels(0.f), + mCurPan(0.f, 0.f), + mStartPan(0.f, 0.f), + mPopupWorldPos(0.f, 0.f, 0.f), + mMouseDown(0, 0), + mPanning(false), + mUpdateNow(false), + mObjectImageCenterGlobal( gAgentCamera.getCameraPositionGlobal() ), + mObjectRawImagep(), + mObjectImagep(), + mClosestAgentToCursor(), + mClosestAgentAtLastRightClick(), + mToolTipMsg() +{ + mScale = gSavedSettings.getF32("MiniMapScale"); + if (gAgent.isFirstLogin()) + { + // *HACK: On first run, set this to false for new users, otherwise the + // default is true to maintain consistent experience for existing + // users. + gSavedSettings.setBOOL("MiniMapRotate", false); + } + mPixelsPerMeter = mScale / REGION_WIDTH_METERS; + mDotRadius = llmax(DOT_SCALE * mPixelsPerMeter, MIN_DOT_RADIUS); +} + +LLNetMap::~LLNetMap() +{ + auto menu = static_cast(mPopupMenuHandle.get()); + if (menu) + { + menu->die(); + mPopupMenuHandle.markDead(); + } +} + +bool LLNetMap::postBuild() +{ + LLUICtrl::CommitCallbackRegistry::ScopedRegistrar commitRegistrar; + LLUICtrl::EnableCallbackRegistry::ScopedRegistrar enableRegistrar; + + enableRegistrar.add("Minimap.Zoom.Check", boost::bind(&LLNetMap::isZoomChecked, this, _2)); + commitRegistrar.add("Minimap.Zoom.Set", boost::bind(&LLNetMap::setZoom, this, _2)); + commitRegistrar.add("Minimap.Tracker", boost::bind(&LLNetMap::handleStopTracking, this, _2)); + commitRegistrar.add("Minimap.Center.Activate", boost::bind(&LLNetMap::activateCenterMap, this, _2)); + enableRegistrar.add("Minimap.MapOrientation.Check", boost::bind(&LLNetMap::isMapOrientationChecked, this, _2)); + commitRegistrar.add("Minimap.MapOrientation.Set", boost::bind(&LLNetMap::setMapOrientation, this, _2)); + commitRegistrar.add("Minimap.AboutLand", boost::bind(&LLNetMap::popupShowAboutLand, this, _2)); + + LLMenuGL* menu = LLUICtrlFactory::getInstance()->createFromFile("menu_mini_map.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); + mPopupMenuHandle = menu->getHandle(); + menu->setItemEnabled("Re-center map", false); + return true; +} + +void LLNetMap::setScale( F32 scale ) +{ + scale = llclamp(scale, MAP_SCALE_MIN, MAP_SCALE_MAX); + mCurPan *= scale / mScale; + mScale = scale; + + if (mObjectImagep.notNull()) + { + F32 width = (F32)(getRect().getWidth()); + F32 height = (F32)(getRect().getHeight()); + F32 diameter = sqrt(width * width + height * height); + F32 region_widths = diameter / mScale; + F32 meters = region_widths * LLWorld::getInstance()->getRegionWidthInMeters(); + F32 num_pixels = (F32)mObjectImagep->getWidth(); + mObjectMapTPM = num_pixels / meters; + mObjectMapPixels = diameter; + } + + mPixelsPerMeter = mScale / REGION_WIDTH_METERS; + mDotRadius = llmax(DOT_SCALE * mPixelsPerMeter, MIN_DOT_RADIUS); + + gSavedSettings.setF32("MiniMapScale", mScale); + + mUpdateNow = true; +} + + +/////////////////////////////////////////////////////////////////////////////////// + +void LLNetMap::draw() +{ + if (!LLWorld::instanceExists()) + { + return; + } + LL_PROFILE_ZONE_SCOPED; + static LLFrameTimer map_timer; + static LLUIColor map_avatar_color = LLUIColorTable::instance().getColor("MapAvatarColor", LLColor4::white); + static LLUIColor map_avatar_friend_color = LLUIColorTable::instance().getColor("MapAvatarFriendColor", LLColor4::white); + static LLUIColor map_track_color = LLUIColorTable::instance().getColor("MapTrackColor", LLColor4::white); + //static LLUIColor map_track_disabled_color = LLUIColorTable::instance().getColor("MapTrackDisabledColor", LLColor4::white); + static LLUIColor map_frustum_color = LLUIColorTable::instance().getColor("MapFrustumColor", LLColor4::white); + static LLUIColor map_parcel_outline_color = LLUIColorTable::instance().getColor("MapParcelOutlineColor", LLColor4(LLColor3(LLColor4::yellow), 0.5f)); + + if (mObjectImagep.isNull()) + { + createObjectImage(); + } + + static LLUICachedControl auto_center("MiniMapAutoCenter", true); + bool auto_centering = auto_center && !mPanning; + mCentering = mCentering && !mPanning; + + if (auto_centering || mCentering) + { + mCurPan = lerp(mCurPan, LLVector2(0.0f, 0.0f) , LLSmoothInterpolation::getInterpolant(0.1f)); + } + bool centered = abs(mCurPan.mV[VX]) < 0.5f && abs(mCurPan.mV[VY]) < 0.5f; + if (centered) + { + mCurPan.mV[0] = 0.0f; + mCurPan.mV[1] = 0.0f; + mCentering = false; + } + + auto menu = static_cast(mPopupMenuHandle.get()); + if (menu) + { + bool can_recenter_map = !(centered || mCentering || auto_centering); + menu->setItemEnabled("Re-center map", can_recenter_map); + } + updateAboutLandPopupButton(); + + // Prepare a scissor region + F32 rotation = 0; + + gGL.pushMatrix(); + gGL.pushUIMatrix(); + + LLVector3 offset = gGL.getUITranslation(); + LLVector3 scale = gGL.getUIScale(); + + gGL.loadIdentity(); + gGL.loadUIIdentity(); + + gGL.scalef(scale.mV[0], scale.mV[1], scale.mV[2]); + gGL.translatef(offset.mV[0], offset.mV[1], offset.mV[2]); + + { + LLLocalClipRect clip(getLocalRect()); + { + gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); + + gGL.matrixMode(LLRender::MM_MODELVIEW); + + // Draw background rectangle + LLColor4 background_color = mBackgroundColor.get(); + gGL.color4fv( background_color.mV ); + gl_rect_2d(0, getRect().getHeight(), getRect().getWidth(), 0); + } + + // region 0,0 is in the middle + S32 center_sw_left = getRect().getWidth() / 2 + llfloor(mCurPan.mV[VX]); + S32 center_sw_bottom = getRect().getHeight() / 2 + llfloor(mCurPan.mV[VY]); + + gGL.pushMatrix(); + + gGL.translatef( (F32) center_sw_left, (F32) center_sw_bottom, 0.f); + + static LLUICachedControl rotate_map("MiniMapRotate", true); + if( rotate_map ) + { + // rotate subsequent draws to agent rotation + rotation = atan2( LLViewerCamera::getInstance()->getAtAxis().mV[VX], LLViewerCamera::getInstance()->getAtAxis().mV[VY] ); + gGL.rotatef( rotation * RAD_TO_DEG, 0.f, 0.f, 1.f); + } + + // figure out where agent is + const S32 region_width = ll_round(LLWorld::getInstance()->getRegionWidthInMeters()); + const F32 scale_pixels_per_meter = mScale / region_width; + + for (LLWorld::region_list_t::const_iterator iter = LLWorld::getInstance()->getRegionList().begin(); + iter != LLWorld::getInstance()->getRegionList().end(); ++iter) + { + LLViewerRegion* regionp = *iter; + // Find x and y position relative to camera's center. + LLVector3 origin_agent = regionp->getOriginAgent(); + LLVector3 rel_region_pos = origin_agent - gAgentCamera.getCameraPositionAgent(); + F32 relative_x = rel_region_pos.mV[0] * scale_pixels_per_meter; + F32 relative_y = rel_region_pos.mV[1] * scale_pixels_per_meter; + + // background region rectangle + F32 bottom = relative_y; + F32 left = relative_x; + F32 top = bottom + mScale ; + F32 right = left + mScale ; + + if (regionp == gAgent.getRegion()) + { + gGL.color4f(1.f, 1.f, 1.f, 1.f); + } + else + { + gGL.color4f(0.8f, 0.8f, 0.8f, 1.f); + } + + if (!regionp->isAlive()) + { + gGL.color4f(1.f, 0.5f, 0.5f, 1.f); + } + + + + // Draw using texture. + gGL.getTexUnit(0)->bind(regionp->getLand().getSTexture()); + gGL.begin(LLRender::QUADS); + gGL.texCoord2f(0.f, 1.f); + gGL.vertex2f(left, top); + gGL.texCoord2f(0.f, 0.f); + gGL.vertex2f(left, bottom); + gGL.texCoord2f(1.f, 0.f); + gGL.vertex2f(right, bottom); + gGL.texCoord2f(1.f, 1.f); + gGL.vertex2f(right, top); + gGL.end(); + + // Draw water + gGL.flush(); + { + if (regionp->getLand().getWaterTexture()) + { + gGL.getTexUnit(0)->bind(regionp->getLand().getWaterTexture()); + gGL.begin(LLRender::QUADS); + gGL.texCoord2f(0.f, 1.f); + gGL.vertex2f(left, top); + gGL.texCoord2f(0.f, 0.f); + gGL.vertex2f(left, bottom); + gGL.texCoord2f(1.f, 0.f); + gGL.vertex2f(right, bottom); + gGL.texCoord2f(1.f, 1.f); + gGL.vertex2f(right, top); + gGL.end(); + } + } + gGL.flush(); + } + + // Redraw object layer periodically + if (mUpdateNow || (map_timer.getElapsedTimeF32() > 0.5f)) + { + mUpdateNow = false; + + // Locate the centre of the object layer, accounting for panning + LLVector3 new_center = globalPosToView(gAgentCamera.getCameraPositionGlobal()); + new_center.mV[VX] -= mCurPan.mV[VX]; + new_center.mV[VY] -= mCurPan.mV[VY]; + new_center.mV[VZ] = 0.f; + mObjectImageCenterGlobal = viewPosToGlobal(llfloor(new_center.mV[VX]), llfloor(new_center.mV[VY])); + + // Create the base texture. + LLImageDataLock lock(mObjectRawImagep); + U8 *default_texture = mObjectRawImagep->getData(); + memset( default_texture, 0, mObjectImagep->getWidth() * mObjectImagep->getHeight() * mObjectImagep->getComponents() ); + + // Draw objects + gObjectList.renderObjectsForMap(*this); + + mObjectImagep->setSubImage(mObjectRawImagep, 0, 0, mObjectImagep->getWidth(), mObjectImagep->getHeight()); + + map_timer.reset(); + } + + LLVector3 map_center_agent = gAgent.getPosAgentFromGlobal(mObjectImageCenterGlobal); + LLVector3 camera_position = gAgentCamera.getCameraPositionAgent(); + map_center_agent -= camera_position; + map_center_agent.mV[VX] *= scale_pixels_per_meter; + map_center_agent.mV[VY] *= scale_pixels_per_meter; + + gGL.getTexUnit(0)->bind(mObjectImagep); + F32 image_half_width = 0.5f*mObjectMapPixels; + F32 image_half_height = 0.5f*mObjectMapPixels; + + gGL.begin(LLRender::QUADS); + gGL.texCoord2f(0.f, 1.f); + gGL.vertex2f(map_center_agent.mV[VX] - image_half_width, image_half_height + map_center_agent.mV[VY]); + gGL.texCoord2f(0.f, 0.f); + gGL.vertex2f(map_center_agent.mV[VX] - image_half_width, map_center_agent.mV[VY] - image_half_height); + gGL.texCoord2f(1.f, 0.f); + gGL.vertex2f(image_half_width + map_center_agent.mV[VX], map_center_agent.mV[VY] - image_half_height); + gGL.texCoord2f(1.f, 1.f); + gGL.vertex2f(image_half_width + map_center_agent.mV[VX], image_half_height + map_center_agent.mV[VY]); + gGL.end(); + + for (LLWorld::region_list_t::const_iterator iter = LLWorld::getInstance()->getRegionList().begin(); + iter != LLWorld::getInstance()->getRegionList().end(); ++iter) + { + LLViewerRegion* regionp = *iter; + regionp->renderPropertyLinesOnMinimap(scale_pixels_per_meter, map_parcel_outline_color.get().mV); + } + + gGL.popMatrix(); + + // Mouse pointer in local coordinates + S32 local_mouse_x; + S32 local_mouse_y; + //localMouse(&local_mouse_x, &local_mouse_y); + LLUI::getInstance()->getMousePositionLocal(this, &local_mouse_x, &local_mouse_y); + mClosestAgentToCursor.setNull(); + F32 closest_dist_squared = F32_MAX; // value will be overridden in the loop + F32 min_pick_dist_squared = (mDotRadius * MIN_PICK_SCALE) * (mDotRadius * MIN_PICK_SCALE); + + LLVector3 pos_map; + uuid_vec_t avatar_ids; + std::vector positions; + bool unknown_relative_z; + + LLWorld::getInstance()->getAvatars(&avatar_ids, &positions, gAgentCamera.getCameraPositionGlobal()); + + // Draw avatars + for (U32 i = 0; i < avatar_ids.size(); i++) + { + LLUUID uuid = avatar_ids[i]; + // Skip self, we'll draw it later + if (uuid == gAgent.getID()) continue; + + pos_map = globalPosToView(positions[i]); + + bool show_as_friend = (LLAvatarTracker::instance().getBuddyInfo(uuid) != NULL); + + LLColor4 color = show_as_friend ? map_avatar_friend_color : map_avatar_color; + + unknown_relative_z = positions[i].mdV[VZ] >= COARSEUPDATE_MAX_Z && + camera_position.mV[VZ] >= COARSEUPDATE_MAX_Z; + + LLWorldMapView::drawAvatar( + pos_map.mV[VX], pos_map.mV[VY], + color, + pos_map.mV[VZ], mDotRadius, + unknown_relative_z); + + if(uuid.notNull()) + { + bool selected = false; + uuid_vec_t::iterator sel_iter = gmSelected.begin(); + for (; sel_iter != gmSelected.end(); sel_iter++) + { + if(*sel_iter == uuid) + { + selected = true; + break; + } + } + if(selected) + { + if( (pos_map.mV[VX] < 0) || + (pos_map.mV[VY] < 0) || + (pos_map.mV[VX] >= getRect().getWidth()) || + (pos_map.mV[VY] >= getRect().getHeight()) ) + { + S32 x = ll_round( pos_map.mV[VX] ); + S32 y = ll_round( pos_map.mV[VY] ); + LLWorldMapView::drawTrackingCircle( getRect(), x, y, color, 1, 10); + } else + { + LLWorldMapView::drawTrackingDot(pos_map.mV[VX],pos_map.mV[VY],color,0.f); + } + } + } + + F32 dist_to_cursor_squared = dist_vec_squared(LLVector2(pos_map.mV[VX], pos_map.mV[VY]), + LLVector2(local_mouse_x,local_mouse_y)); + if(dist_to_cursor_squared < min_pick_dist_squared && dist_to_cursor_squared < closest_dist_squared) + { + closest_dist_squared = dist_to_cursor_squared; + mClosestAgentToCursor = uuid; + } + } + + // Draw dot for autopilot target + if (gAgent.getAutoPilot()) + { + drawTracking( gAgent.getAutoPilotTargetGlobal(), map_track_color ); + } + else + { + LLTracker::ETrackingStatus tracking_status = LLTracker::getTrackingStatus(); + if ( LLTracker::TRACKING_AVATAR == tracking_status ) + { + drawTracking( LLAvatarTracker::instance().getGlobalPos(), map_track_color ); + } + else if ( LLTracker::TRACKING_LANDMARK == tracking_status + || LLTracker::TRACKING_LOCATION == tracking_status ) + { + drawTracking( LLTracker::getTrackedPositionGlobal(), map_track_color ); + } + } + + // Draw dot for self avatar position + LLVector3d pos_global = gAgent.getPositionGlobal(); + pos_map = globalPosToView(pos_global); + S32 dot_width = ll_round(mDotRadius * 2.f); + LLUIImagePtr you = LLWorldMapView::sAvatarYouLargeImage; + if (you) + { + you->draw(ll_round(pos_map.mV[VX] - mDotRadius), + ll_round(pos_map.mV[VY] - mDotRadius), + dot_width, + dot_width); + + F32 dist_to_cursor_squared = dist_vec_squared(LLVector2(pos_map.mV[VX], pos_map.mV[VY]), + LLVector2(local_mouse_x,local_mouse_y)); + if(dist_to_cursor_squared < min_pick_dist_squared && dist_to_cursor_squared < closest_dist_squared) + { + mClosestAgentToCursor = gAgent.getID(); + } + } + + // Draw frustum + F32 meters_to_pixels = mScale/ LLWorld::getInstance()->getRegionWidthInMeters(); + + F32 horiz_fov = LLViewerCamera::getInstance()->getView() * LLViewerCamera::getInstance()->getAspect(); + F32 far_clip_meters = LLViewerCamera::getInstance()->getFar(); + F32 far_clip_pixels = far_clip_meters * meters_to_pixels; + + F32 ctr_x = (F32)center_sw_left; + F32 ctr_y = (F32)center_sw_bottom; + + const F32 steps_per_circle = 40.0f; + const F32 steps_per_radian = steps_per_circle / F_TWO_PI; + const F32 arc_start = -(horiz_fov / 2.0f) + F_PI_BY_TWO; + const F32 arc_end = (horiz_fov / 2.0f) + F_PI_BY_TWO; + const S32 steps = llmax(1, (S32)((horiz_fov * steps_per_radian) + 0.5f)); + + gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); + + if( rotate_map ) + { + gGL.pushMatrix(); + gGL.translatef( ctr_x, ctr_y, 0 ); + gl_washer_segment_2d(far_clip_pixels, 0, arc_start, arc_end, steps, map_frustum_color(), map_frustum_color()); + gGL.popMatrix(); + } + else + { + gGL.pushMatrix(); + gGL.translatef( ctr_x, ctr_y, 0 ); + // If we don't rotate the map, we have to rotate the frustum. + gGL.rotatef( atan2( LLViewerCamera::getInstance()->getAtAxis().mV[VX], LLViewerCamera::getInstance()->getAtAxis().mV[VY] ) * RAD_TO_DEG, 0.f, 0.f, -1.f); + gl_washer_segment_2d(far_clip_pixels, 0, arc_start, arc_end, steps, map_frustum_color(), map_frustum_color()); + gGL.popMatrix(); + } + } + + gGL.popMatrix(); + gGL.popUIMatrix(); + + LLUICtrl::draw(); +} + +void LLNetMap::reshape(S32 width, S32 height, bool called_from_parent) +{ + LLUICtrl::reshape(width, height, called_from_parent); + createObjectImage(); +} + +LLVector3 LLNetMap::globalPosToView(const LLVector3d& global_pos) +{ + LLVector3d camera_position = gAgentCamera.getCameraPositionGlobal(); + + LLVector3d relative_pos_global = global_pos - camera_position; + LLVector3 pos_local; + pos_local.setVec(relative_pos_global); // convert to floats from doubles + + pos_local.mV[VX] *= mPixelsPerMeter; + pos_local.mV[VY] *= mPixelsPerMeter; + // leave Z component in meters + + static LLUICachedControl rotate_map("MiniMapRotate", true); + if( rotate_map ) + { + F32 radians = atan2( LLViewerCamera::getInstance()->getAtAxis().mV[VX], LLViewerCamera::getInstance()->getAtAxis().mV[VY] ); + LLQuaternion rot(radians, LLVector3(0.f, 0.f, 1.f)); + pos_local.rotVec( rot ); + } + + pos_local.mV[VX] += getRect().getWidth() / 2 + mCurPan.mV[VX]; + pos_local.mV[VY] += getRect().getHeight() / 2 + mCurPan.mV[VY]; + + return pos_local; +} + +void LLNetMap::drawTracking(const LLVector3d& pos_global, const LLColor4& color, + bool draw_arrow ) +{ + LLVector3 pos_local = globalPosToView(pos_global); + if( (pos_local.mV[VX] < 0) || + (pos_local.mV[VY] < 0) || + (pos_local.mV[VX] >= getRect().getWidth()) || + (pos_local.mV[VY] >= getRect().getHeight()) ) + { + if (draw_arrow) + { + S32 x = ll_round( pos_local.mV[VX] ); + S32 y = ll_round( pos_local.mV[VY] ); + LLWorldMapView::drawTrackingCircle( getRect(), x, y, color, 1, 10 ); + LLWorldMapView::drawTrackingArrow( getRect(), x, y, color ); + } + } + else + { + LLWorldMapView::drawTrackingDot(pos_local.mV[VX], + pos_local.mV[VY], + color, + pos_local.mV[VZ]); + } +} + +bool LLNetMap::isMouseOnPopupMenu() +{ + auto menu = static_cast(mPopupMenuHandle.get()); + if (!menu || !menu->isOpen()) + { + return false; + } + + S32 popup_x; + S32 popup_y; + LLUI::getInstance()->getMousePositionLocal(menu, &popup_x, &popup_y); + // *NOTE: Tolerance is larger than it needs to be because the context menu is offset from the mouse when the menu is opened from certain + // directions. This may be a quirk of LLMenuGL::showPopup. -Cosmic,2022-03-22 + constexpr S32 tolerance = 10; + // Test tolerance from all four corners, as the popup menu can appear from a different direction if there's not enough space. + // Assume the size of the popup menu is much larger than the provided tolerance. + // In practice, this is a [tolerance]px margin around the popup menu. + for (S32 sign_x = -1; sign_x <= 1; sign_x += 2) + { + for (S32 sign_y = -1; sign_y <= 1; sign_y += 2) + { + if (menu->pointInView(popup_x + (sign_x * tolerance), popup_y + (sign_y * tolerance))) + { + return true; + } + } + } + return false; +} + +void LLNetMap::updateAboutLandPopupButton() +{ + auto menu = static_cast(mPopupMenuHandle.get()); + if (!menu || !menu->isOpen()) + { + return; + } + + LLViewerRegion *region = LLWorld::getInstance()->getRegionFromPosGlobal(mPopupWorldPos); + if (!region) + { + menu->setItemEnabled("About Land", false); + } + else + { + // Check if the mouse is in the bounds of the popup. If so, it's safe to assume no other hover function will be called, so the hover + // parcel can be used to check if location-sensitive tooltip options are available. + if (isMouseOnPopupMenu()) + { + LLViewerParcelMgr::getInstance()->setHoverParcel(mPopupWorldPos); + LLParcel *hover_parcel = LLViewerParcelMgr::getInstance()->getHoverParcel(); + bool valid_parcel = false; + if (hover_parcel) + { + valid_parcel = hover_parcel->getOwnerID().notNull(); + } + menu->setItemEnabled("About Land", valid_parcel); + } + } +} + +LLVector3d LLNetMap::viewPosToGlobal( S32 x, S32 y ) +{ + x -= ll_round(getRect().getWidth() / 2 + mCurPan.mV[VX]); + y -= ll_round(getRect().getHeight() / 2 + mCurPan.mV[VY]); + + LLVector3 pos_local( (F32)x, (F32)y, 0 ); + + F32 radians = - atan2( LLViewerCamera::getInstance()->getAtAxis().mV[VX], LLViewerCamera::getInstance()->getAtAxis().mV[VY] ); + + static LLUICachedControl rotate_map("MiniMapRotate", true); + if( rotate_map ) + { + LLQuaternion rot(radians, LLVector3(0.f, 0.f, 1.f)); + pos_local.rotVec( rot ); + } + + pos_local *= ( LLWorld::getInstance()->getRegionWidthInMeters() / mScale ); + + LLVector3d pos_global; + pos_global.setVec( pos_local ); + pos_global += gAgentCamera.getCameraPositionGlobal(); + + return pos_global; +} + +bool LLNetMap::handleScrollWheel(S32 x, S32 y, S32 clicks) +{ + // note that clicks are reversed from what you'd think: i.e. > 0 means zoom out, < 0 means zoom in + F32 new_scale = mScale * pow(MAP_SCALE_ZOOM_FACTOR, -clicks); + F32 old_scale = mScale; + + setScale(new_scale); + + static LLUICachedControl auto_center("MiniMapAutoCenter", true); + if (!auto_center) + { + // Adjust pan to center the zoom on the mouse pointer + LLVector2 zoom_offset; + zoom_offset.mV[VX] = x - getRect().getWidth() / 2; + zoom_offset.mV[VY] = y - getRect().getHeight() / 2; + mCurPan -= zoom_offset * mScale / old_scale - zoom_offset; + } + + return true; +} + +bool LLNetMap::handleToolTip(S32 x, S32 y, MASK mask) +{ + if (gDisconnected) + { + return false; + } + + // If the cursor is near an avatar on the minimap, a mini-inspector will be + // shown for the avatar, instead of the normal map tooltip. + if (handleToolTipAgent(mClosestAgentToCursor)) + { + return true; + } + + // The popup menu uses the hover parcel when it is open and the mouse is on + // top of it, with some additional tolerance. Returning early here prevents + // fighting over that hover parcel when getting tooltip info in the + // tolerance region. + if (isMouseOnPopupMenu()) + { + return false; + } + + LLRect sticky_rect; + S32 SLOP = 4; + localPointToScreen(x - SLOP, y - SLOP, &(sticky_rect.mLeft), &(sticky_rect.mBottom)); + sticky_rect.mRight = sticky_rect.mLeft + 2 * SLOP; + sticky_rect.mTop = sticky_rect.mBottom + 2 * SLOP; + + std::string parcel_name_msg; + std::string parcel_sale_price_msg; + std::string parcel_sale_area_msg; + std::string parcel_owner_msg; + std::string region_name_msg; + + LLVector3d posGlobal = viewPosToGlobal(x, y); + LLViewerRegion *region = LLWorld::getInstance()->getRegionFromPosGlobal(posGlobal); + if (region) + { + std::string region_name = region->getName(); + if (!region_name.empty()) + { + region_name_msg = mRegionNameMsg; + LLStringUtil::format(region_name_msg, {{"[REGION_NAME]", region_name}}); + } + + // Only show parcel information in the tooltip if property lines are visible. Otherwise, the parcel the tooltip is referring to is + // ambiguous. + if (gSavedSettings.getBOOL("MiniMapShowPropertyLines")) + { + LLViewerParcelMgr::getInstance()->setHoverParcel(posGlobal); + LLParcel *hover_parcel = LLViewerParcelMgr::getInstance()->getHoverParcel(); + if (hover_parcel) + { + std::string parcel_name = hover_parcel->getName(); + if (!parcel_name.empty()) + { + parcel_name_msg = mParcelNameMsg; + LLStringUtil::format(parcel_name_msg, {{"[PARCEL_NAME]", parcel_name}}); + } + + const LLUUID parcel_owner = hover_parcel->getOwnerID(); + std::string parcel_owner_name_url = LLSLURL("agent", parcel_owner, "inspect").getSLURLString(); + static LLUrlMatch parcel_owner_name_url_match; + LLUrlRegistry::getInstance()->findUrl(parcel_owner_name_url, parcel_owner_name_url_match); + if (!parcel_owner_name_url_match.empty()) + { + parcel_owner_msg = mParcelOwnerMsg; + std::string parcel_owner_name = parcel_owner_name_url_match.getLabel(); + LLStringUtil::format(parcel_owner_msg, {{"[PARCEL_OWNER]", parcel_owner_name}}); + } + + if (hover_parcel->getForSale()) + { + const LLUUID auth_buyer_id = hover_parcel->getAuthorizedBuyerID(); + const LLUUID agent_id = gAgent.getID(); + bool show_for_sale = auth_buyer_id.isNull() || auth_buyer_id == agent_id || parcel_owner == agent_id; + if (show_for_sale) + { + S32 price = hover_parcel->getSalePrice(); + S32 area = hover_parcel->getArea(); + F32 cost_per_sqm = 0.0f; + if (area > 0) + { + cost_per_sqm = F32(price) / area; + } + std::string formatted_price = LLResMgr::getInstance()->getMonetaryString(price); + std::string formatted_cost_per_meter = llformat("%.1f", cost_per_sqm); + parcel_sale_price_msg = mParcelSalePriceMsg; + LLStringUtil::format(parcel_sale_price_msg, + {{"[PRICE]", formatted_price}, {"[PRICE_PER_SQM]", formatted_cost_per_meter}}); + std::string formatted_area = llformat("%d", area); + parcel_sale_area_msg = mParcelSaleAreaMsg; + LLStringUtil::format(parcel_sale_area_msg, {{"[AREA]", formatted_area}}); + } + } + } + } + } + + std::string tool_tip_hint_msg; + if (gSavedSettings.getBOOL("DoubleClickTeleport")) + { + tool_tip_hint_msg = mAltToolTipHintMsg; + } + else if (gSavedSettings.getBOOL("DoubleClickShowWorldMap")) + { + tool_tip_hint_msg = mToolTipHintMsg; + } + + LLStringUtil::format_map_t args; + args["[PARCEL_NAME_MSG]"] = parcel_name_msg.empty() ? "" : parcel_name_msg + '\n'; + args["[PARCEL_SALE_PRICE_MSG]"] = parcel_sale_price_msg.empty() ? "" : parcel_sale_price_msg + '\n'; + args["[PARCEL_SALE_AREA_MSG]"] = parcel_sale_area_msg.empty() ? "" : parcel_sale_area_msg + '\n'; + args["[PARCEL_OWNER_MSG]"] = parcel_owner_msg.empty() ? "" : parcel_owner_msg + '\n'; + args["[REGION_NAME_MSG]"] = region_name_msg.empty() ? "" : region_name_msg + '\n'; + args["[TOOL_TIP_HINT_MSG]"] = tool_tip_hint_msg.empty() ? "" : tool_tip_hint_msg + '\n'; + + std::string msg = mToolTipMsg; + LLStringUtil::format(msg, args); + if (msg.back() == '\n') + { + msg.resize(msg.size() - 1); + } + LLToolTipMgr::instance().show(LLToolTip::Params().message(msg).sticky_rect(sticky_rect)); + + return true; +} + +bool LLNetMap::handleToolTipAgent(const LLUUID& avatar_id) +{ + LLAvatarName av_name; + if (avatar_id.isNull() || !LLAvatarNameCache::get(avatar_id, &av_name)) + { + return false; + } + + // only show tooltip if same inspector not already open + LLFloater* existing_inspector = LLFloaterReg::findInstance("inspect_avatar"); + if (!existing_inspector + || !existing_inspector->getVisible() + || existing_inspector->getKey()["avatar_id"].asUUID() != avatar_id) + { + LLInspector::Params p; + p.fillFrom(LLUICtrlFactory::instance().getDefaultParams()); + p.message(av_name.getCompleteName()); + p.image.name("Inspector_I"); + p.click_callback(boost::bind(showAvatarInspector, avatar_id)); + p.visible_time_near(6.f); + p.visible_time_far(3.f); + p.delay_time(0.35f); + p.wrap(false); + + LLToolTipMgr::instance().show(p); + } + return true; +} + +// static +void LLNetMap::showAvatarInspector(const LLUUID& avatar_id) +{ + LLSD params; + params["avatar_id"] = avatar_id; + + if (LLToolTipMgr::instance().toolTipVisible()) + { + LLRect rect = LLToolTipMgr::instance().getToolTipRect(); + params["pos"]["x"] = rect.mLeft; + params["pos"]["y"] = rect.mTop; + } + + LLFloaterReg::showInstance("inspect_avatar", params); +} + +void LLNetMap::renderScaledPointGlobal( const LLVector3d& pos, const LLColor4U &color, F32 radius_meters ) +{ + LLVector3 local_pos; + local_pos.setVec( pos - mObjectImageCenterGlobal ); + + S32 diameter_pixels = ll_round(2 * radius_meters * mObjectMapTPM); + renderPoint( local_pos, color, diameter_pixels ); +} + + +void LLNetMap::renderPoint(const LLVector3 &pos_local, const LLColor4U &color, + S32 diameter, S32 relative_height) +{ + if (diameter <= 0) + { + return; + } + + const S32 image_width = (S32)mObjectImagep->getWidth(); + const S32 image_height = (S32)mObjectImagep->getHeight(); + + S32 x_offset = ll_round(pos_local.mV[VX] * mObjectMapTPM + image_width / 2); + S32 y_offset = ll_round(pos_local.mV[VY] * mObjectMapTPM + image_height / 2); + + if ((x_offset < 0) || (x_offset >= image_width)) + { + return; + } + if ((y_offset < 0) || (y_offset >= image_height)) + { + return; + } + + LLImageDataLock lock(mObjectRawImagep); + U8 *datap = mObjectRawImagep->getData(); + + S32 neg_radius = diameter / 2; + S32 pos_radius = diameter - neg_radius; + S32 x, y; + + if (relative_height > 0) + { + // ...point above agent + S32 px, py; + + // vertical line + px = x_offset; + for (y = -neg_radius; y < pos_radius; y++) + { + py = y_offset + y; + if ((py < 0) || (py >= image_height)) + { + continue; + } + S32 offset = px + py * image_width; + ((U32*)datap)[offset] = color.asRGBA(); + } + + // top line + py = y_offset + pos_radius - 1; + for (x = -neg_radius; x < pos_radius; x++) + { + px = x_offset + x; + if ((px < 0) || (px >= image_width)) + { + continue; + } + S32 offset = px + py * image_width; + ((U32*)datap)[offset] = color.asRGBA(); + } + } + else + { + // ...point level with agent + for (x = -neg_radius; x < pos_radius; x++) + { + S32 p_x = x_offset + x; + if ((p_x < 0) || (p_x >= image_width)) + { + continue; + } + + for (y = -neg_radius; y < pos_radius; y++) + { + S32 p_y = y_offset + y; + if ((p_y < 0) || (p_y >= image_height)) + { + continue; + } + S32 offset = p_x + p_y * image_width; + ((U32*)datap)[offset] = color.asRGBA(); + } + } + } +} + +void LLNetMap::createObjectImage() +{ + // Find the size of the side of a square that surrounds the circle that surrounds getRect(). + // ... which is, the diagonal of the rect. + F32 width = (F32)getRect().getWidth(); + F32 height = (F32)getRect().getHeight(); + S32 square_size = ll_round( sqrt(width*width + height*height) ); + + // Find the least power of two >= the minimum size. + const S32 MIN_SIZE = 64; + const S32 MAX_SIZE = 256; + S32 img_size = MIN_SIZE; + while( (img_size*2 < square_size ) && (img_size < MAX_SIZE) ) + { + img_size <<= 1; + } + + if( mObjectImagep.isNull() || + (mObjectImagep->getWidth() != img_size) || + (mObjectImagep->getHeight() != img_size) ) + { + mObjectRawImagep = new LLImageRaw(img_size, img_size, 4); + U8* data = mObjectRawImagep->getData(); + memset( data, 0, img_size * img_size * 4 ); + mObjectImagep = LLViewerTextureManager::getLocalTexture( mObjectRawImagep.get(), false); + } + setScale(mScale); + mUpdateNow = true; +} + +bool LLNetMap::handleMouseDown(S32 x, S32 y, MASK mask) +{ + // Start panning + gFocusMgr.setMouseCapture(this); + + mStartPan = mCurPan; + mMouseDown.mX = x; + mMouseDown.mY = y; + return true; +} + +bool LLNetMap::handleMouseUp(S32 x, S32 y, MASK mask) +{ + if (abs(mMouseDown.mX - x) < 3 && abs(mMouseDown.mY - y) < 3) + { + handleClick(x, y, mask); + } + + if (hasMouseCapture()) + { + if (mPanning) + { + // restore mouse cursor + S32 local_x, local_y; + local_x = mMouseDown.mX + llfloor(mCurPan.mV[VX] - mStartPan.mV[VX]); + local_y = mMouseDown.mY + llfloor(mCurPan.mV[VY] - mStartPan.mV[VY]); + LLRect clip_rect = getRect(); + clip_rect.stretch(-8); + clip_rect.clipPointToRect(mMouseDown.mX, mMouseDown.mY, local_x, local_y); + LLUI::getInstance()->setMousePositionLocal(this, local_x, local_y); + + // finish the pan + mPanning = false; + + mMouseDown.set(0, 0); + } + gViewerWindow->showCursor(); + gFocusMgr.setMouseCapture(NULL); + return true; + } + + return false; +} + +bool LLNetMap::handleRightMouseDown(S32 x, S32 y, MASK mask) +{ + auto menu = static_cast(mPopupMenuHandle.get()); + if (menu) + { + mPopupWorldPos = viewPosToGlobal(x, y); + menu->buildDrawLabels(); + menu->updateParent(LLMenuGL::sMenuContainer); + menu->setItemEnabled("Stop Tracking", LLTracker::isTracking(0)); + LLMenuGL::showPopup(this, menu, x, y); + } + return true; +} + +bool LLNetMap::handleClick(S32 x, S32 y, MASK mask) +{ + // TODO: allow clicking an avatar on minimap to select avatar in the nearby avatar list + // if(mClosestAgentToCursor.notNull()) + // mNearbyList->selectUser(mClosestAgentToCursor); + // Needs a registered observer i guess to accomplish this without using + // globals to tell the mNearbyList in llpeoplepanel to select the user + return true; +} + +bool LLNetMap::handleDoubleClick(S32 x, S32 y, MASK mask) +{ + LLVector3d pos_global = viewPosToGlobal(x, y); + + bool double_click_teleport = gSavedSettings.getBOOL("DoubleClickTeleport"); + bool double_click_show_world_map = gSavedSettings.getBOOL("DoubleClickShowWorldMap"); + + if (double_click_teleport || double_click_show_world_map) + { + // If we're not tracking a beacon already, double-click will set one + if (!LLTracker::isTracking(NULL)) + { + LLFloaterWorldMap* world_map = LLFloaterWorldMap::getInstance(); + if (world_map) + { + world_map->trackLocation(pos_global); + } + } + } + + if (double_click_teleport) + { + // If DoubleClickTeleport is on, double clicking the minimap will teleport there + gAgent.teleportViaLocationLookAt(pos_global); + } + else if (double_click_show_world_map) + { + LLFloaterReg::showInstance("world_map"); + } + return true; +} + +F32 LLNetMap::getScaleForName(std::string scale_name) +{ + if (scale_name == "very close") + { + return LLNetMap::MAP_SCALE_VERY_CLOSE; + } + else if (scale_name == "close") + { + return LLNetMap::MAP_SCALE_CLOSE; + } + else if (scale_name == "medium") + { + return LLNetMap::MAP_SCALE_MEDIUM; + } + else if (scale_name == "far") + { + return LLNetMap::MAP_SCALE_FAR; + } + return 0.0f; +} + +// static +bool LLNetMap::outsideSlop( S32 x, S32 y, S32 start_x, S32 start_y, S32 slop ) +{ + S32 dx = x - start_x; + S32 dy = y - start_y; + + return (dx <= -slop || slop <= dx || dy <= -slop || slop <= dy); +} + +bool LLNetMap::handleHover( S32 x, S32 y, MASK mask ) +{ + if (hasMouseCapture()) + { + if (mPanning || outsideSlop(x, y, mMouseDown.mX, mMouseDown.mY, MOUSE_DRAG_SLOP)) + { + if (!mPanning) + { + // Just started panning. Hide cursor. + mPanning = true; + gViewerWindow->hideCursor(); + } + + LLVector2 delta(static_cast(gViewerWindow->getCurrentMouseDX()), + static_cast(gViewerWindow->getCurrentMouseDY())); + + // Set pan to value at start of drag + offset + mCurPan += delta; + + gViewerWindow->moveCursorToCenter(); + } + } + + if (mask & MASK_SHIFT) + { + // If shift is held, change the cursor to hint that the map can be + // dragged. However, holding shift is not required to drag the map. + gViewerWindow->setCursor( UI_CURSOR_TOOLPAN ); + } + else + { + gViewerWindow->setCursor( UI_CURSOR_CROSS ); + } + + return true; +} + +bool LLNetMap::isZoomChecked(const LLSD &userdata) +{ + std::string level = userdata.asString(); + F32 scale = getScaleForName(level); + return scale == mScale; +} + +void LLNetMap::setZoom(const LLSD &userdata) +{ + std::string level = userdata.asString(); + F32 scale = getScaleForName(level); + if (scale != 0.0f) + { + setScale(scale); + } +} + +void LLNetMap::handleStopTracking (const LLSD& userdata) +{ + auto menu = static_cast(mPopupMenuHandle.get()); + if (menu) + { + menu->setItemEnabled ("Stop Tracking", false); + LLTracker::stopTracking (LLTracker::isTracking(NULL)); + } +} + +void LLNetMap::activateCenterMap(const LLSD &userdata) { mCentering = true; } + +bool LLNetMap::isMapOrientationChecked(const LLSD &userdata) +{ + const std::string command_name = userdata.asString(); + const bool rotate_map = gSavedSettings.getBOOL("MiniMapRotate"); + if (command_name == "north_at_top") + { + return !rotate_map; + } + + if (command_name == "camera_at_top") + { + return rotate_map; + } + + return false; +} + +void LLNetMap::setMapOrientation(const LLSD &userdata) +{ + const std::string command_name = userdata.asString(); + if (command_name == "north_at_top") + { + gSavedSettings.setBOOL("MiniMapRotate", false); + } + else if (command_name == "camera_at_top") + { + gSavedSettings.setBOOL("MiniMapRotate", true); + } +} + +void LLNetMap::popupShowAboutLand(const LLSD &userdata) +{ + // Update parcel selection. It's important to deselect land first so the "About Land" floater doesn't refresh with the old selection. + LLViewerParcelMgr::getInstance()->deselectLand(); + LLParcelSelectionHandle selection = LLViewerParcelMgr::getInstance()->selectParcelAt(mPopupWorldPos); + gMenuHolder->setParcelSelection(selection); + + LLFloaterReg::showInstance("about_land", LLSD(), false); +} -- cgit v1.2.3