/** * @file llnetmap.cpp * @author James Cook * @brief Display of surrounding regions, objects, and agents. View contained by LLFloaterMap. * * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc. * $License$ */ #include "llviewerprecompiledheaders.h" #include "llnetmap.h" #include "indra_constants.h" #include "llui.h" #include "linked_lists.h" #include "llmath.h" // clampf() #include "llfocusmgr.h" #include "llagent.h" #include "llcallingcard.h" #include "llcolorscheme.h" #include "llviewercontrol.h" #include "llfloaterworldmap.h" #include "llfloatermap.h" #include "llframetimer.h" #include "lltracker.h" #include "llmenugl.h" #include "llstatgraph.h" #include "llsurface.h" #include "lltextbox.h" #include "llviewercamera.h" #include "llviewerimage.h" #include "llviewerimagelist.h" #include "llviewermenu.h" #include "llviewerobjectlist.h" #include "llviewerparceloverlay.h" #include "llviewerregion.h" #include "llviewerwindow.h" #include "llvoavatar.h" #include "llworld.h" #include "llworldmapview.h" // shared draw code #include "viewer.h" // Only for constants! #include "llglheaders.h" const F32 MAP_SCALE_MIN = 64; const F32 MAP_SCALE_MID = 172; const F32 MAP_SCALE_MAX = 512; const F32 MAP_SCALE_INCREMENT = 16; const S32 TRACKING_RADIUS = 3; //static BOOL LLNetMap::sRotateMap = FALSE; LLNetMap* LLNetMap::sInstance = NULL; LLNetMap::LLNetMap( const std::string& name, const LLRect& rect, const LLColor4& bg_color ) : LLUICtrl(name, rect, FALSE, NULL, NULL), mBackgroundColor( bg_color ), mObjectMapTPM(1.f), mObjectMapPixels(255.f), mTargetPanX( 0.f ), mTargetPanY( 0.f ), mCurPanX( 0.f ), mCurPanY( 0.f ), mUpdateNow( FALSE ) { mPixelsPerMeter = gMiniMapScale / REGION_WIDTH_METERS; LLNetMap::sRotateMap = gSavedSettings.getBOOL( "MiniMapRotate" ); // Surface texture is dynamically generated/updated. // createObjectImage(); mObjectImageCenterGlobal = gAgent.getCameraPositionGlobal(); const S32 DIR_WIDTH = 10; const S32 DIR_HEIGHT = 10; LLRect major_dir_rect( 0, DIR_HEIGHT, DIR_WIDTH, 0 ); mTextBoxNorth = new LLTextBox( "N", major_dir_rect ); mTextBoxNorth->setFontStyle(LLFontGL::DROP_SHADOW_SOFT); addChild( mTextBoxNorth ); LLColor4 minor_color( 1.f, 1.f, 1.f, .7f ); mTextBoxEast = new LLTextBox( "E", major_dir_rect ); mTextBoxEast->setColor( minor_color ); addChild( mTextBoxEast ); mTextBoxWest = new LLTextBox( "W", major_dir_rect ); mTextBoxWest->setColor( minor_color ); addChild( mTextBoxWest ); mTextBoxSouth = new LLTextBox( "S", major_dir_rect ); mTextBoxSouth->setColor( minor_color ); addChild( mTextBoxSouth ); LLRect minor_dir_rect( 0, DIR_HEIGHT, DIR_WIDTH * 2, 0 ); mTextBoxSouthEast = new LLTextBox( "SE", minor_dir_rect ); mTextBoxSouthEast->setColor( minor_color ); addChild( mTextBoxSouthEast ); mTextBoxNorthEast = new LLTextBox( "NE", minor_dir_rect ); mTextBoxNorthEast->setColor( minor_color ); addChild( mTextBoxNorthEast ); mTextBoxSouthWest = new LLTextBox( "SW", minor_dir_rect ); mTextBoxSouthWest->setColor( minor_color ); addChild( mTextBoxSouthWest ); mTextBoxNorthWest = new LLTextBox( "NW", minor_dir_rect ); mTextBoxNorthWest->setColor( minor_color ); addChild( mTextBoxNorthWest ); // Right-click menu LLMenuGL* menu; menu = new LLMenuGL("popup"); menu->setCanTearOff(FALSE); menu->append(new LLMenuItemCallGL("Zoom Close", handleZoomLevel, NULL, (void*)2) ); menu->append(new LLMenuItemCallGL("Zoom Medium", handleZoomLevel, NULL, (void*)1) ); menu->append(new LLMenuItemCallGL("Zoom Far", handleZoomLevel, NULL, (void*)0) ); menu->appendSeparator(); menu->append(new LLMenuItemCallGL("Stop Tracking", &LLTracker::stopTracking, &LLTracker::isTracking, NULL) ); menu->setVisible(FALSE); addChild(menu); mPopupMenuHandle = menu->mViewHandle; sInstance = this; gSavedSettings.getControl("MiniMapRotate")->addListener(&mNetMapListener); } LLNetMap::~LLNetMap() { sInstance = NULL; } EWidgetType LLNetMap::getWidgetType() const { return WIDGET_TYPE_NET_MAP; } LLString LLNetMap::getWidgetTag() const { return LL_NET_MAP_TAG; } void LLNetMap::setScale( F32 scale ) { gMiniMapScale = scale; if (gMiniMapScale == 0.f) { gMiniMapScale = 0.1f; } if (mObjectImagep.notNull()) { F32 half_width = (F32)(mRect.getWidth() / 2); F32 half_height = (F32)(mRect.getHeight() / 2); F32 radius = sqrt( half_width * half_width + half_height * half_height ); F32 region_widths = (2.f*radius)/gMiniMapScale; F32 meters; if (!gWorldPointer) { // Hack! Sometimes world hasn't been initialized at this point. meters = 256.f*region_widths; } else { meters = region_widths * gWorldPointer->getRegionWidthInMeters(); } F32 num_pixels = (F32)mObjectImagep->getWidth(); mObjectMapTPM = num_pixels/meters; mObjectMapPixels = 2.f*radius; } mPixelsPerMeter = gMiniMapScale / REGION_WIDTH_METERS; mUpdateNow = TRUE; } void LLNetMap::translatePan( F32 delta_x, F32 delta_y ) { mTargetPanX += delta_x; mTargetPanY += delta_y; } /////////////////////////////////////////////////////////////////////////////////// void LLNetMap::draw() { static LLFrameTimer map_timer; if (!getVisible() || !gWorldPointer) { return; } if (mObjectImagep.isNull()) { createObjectImage(); } mCurPanX = lerp(mCurPanX, mTargetPanX, LLCriticalDamp::getInterpolant(0.1f)); mCurPanY = lerp(mCurPanY, mTargetPanY, LLCriticalDamp::getInterpolant(0.1f)); // Prepare a scissor region F32 rotation = 0; { LLGLEnable scissor(GL_SCISSOR_TEST); { LLGLSNoTexture no_texture; LLUI::setScissorRegionLocal(LLRect(0, mRect.getHeight(), mRect.getWidth(), 0)); glMatrixMode(GL_MODELVIEW); // Draw background rectangle glColor4fv( mBackgroundColor.mV ); gl_rect_2d(0, mRect.getHeight(), mRect.getWidth(), 0); } // region 0,0 is in the middle S32 center_sw_left = mRect.getWidth() / 2 + llfloor(mCurPanX); S32 center_sw_bottom = mRect.getHeight() / 2 + llfloor(mCurPanY); glPushMatrix(); glTranslatef( (F32) center_sw_left, (F32) center_sw_bottom, 0.f); if( LLNetMap::sRotateMap ) { // rotate subsequent draws to agent rotation rotation = atan2( gCamera->getAtAxis().mV[VX], gCamera->getAtAxis().mV[VY] ); glRotatef( rotation * RAD_TO_DEG, 0.f, 0.f, 1.f); } // figure out where agent is S32 region_width = llround(gWorldPointer->getRegionWidthInMeters()); for (LLWorld::region_list_t::iterator iter = gWorldp->mActiveRegionList.begin(); iter != gWorldp->mActiveRegionList.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 - gAgent.getCameraPositionAgent(); F32 relative_x = (rel_region_pos.mV[0] / region_width) * gMiniMapScale; F32 relative_y = (rel_region_pos.mV[1] / region_width) * gMiniMapScale; // background region rectangle F32 bottom = relative_y; F32 left = relative_x; F32 top = bottom + gMiniMapScale ; F32 right = left + gMiniMapScale ; if (regionp == gAgent.getRegion()) { glColor4f(1.f, 1.f, 1.f, 1.f); } else { glColor4f(0.8f, 0.8f, 0.8f, 1.f); } if (!regionp->mAlive) { glColor4f(1.f, 0.5f, 0.5f, 1.f); } // Draw using texture. LLViewerImage::bindTexture(regionp->getLand().getSTexture()); glBegin(GL_QUADS); glTexCoord2f(0.f, 1.f); glVertex2f(left, top); glTexCoord2f(0.f, 0.f); glVertex2f(left, bottom); glTexCoord2f(1.f, 0.f); glVertex2f(right, bottom); glTexCoord2f(1.f, 1.f); glVertex2f(right, top); glEnd(); // Draw water glAlphaFunc(GL_GREATER, ABOVE_WATERLINE_ALPHA / 255.f ); { if (regionp->getLand().getWaterTexture()) { LLViewerImage::bindTexture(regionp->getLand().getWaterTexture()); glBegin(GL_QUADS); glTexCoord2f(0.f, 1.f); glVertex2f(left, top); glTexCoord2f(0.f, 0.f); glVertex2f(left, bottom); glTexCoord2f(1.f, 0.f); glVertex2f(right, bottom); glTexCoord2f(1.f, 1.f); glVertex2f(right, top); glEnd(); } } glAlphaFunc(GL_GREATER,0.01f); } LLVector3d old_center = mObjectImageCenterGlobal; LLVector3d new_center = gAgent.getCameraPositionGlobal(); new_center.mdV[0] = (5.f/mObjectMapTPM)*floor(0.2f*mObjectMapTPM*new_center.mdV[0]); new_center.mdV[1] = (5.f/mObjectMapTPM)*floor(0.2f*mObjectMapTPM*new_center.mdV[1]); new_center.mdV[2] = 0.f; if (mUpdateNow || (map_timer.getElapsedTimeF32() > 0.5f)) { mUpdateNow = FALSE; mObjectImageCenterGlobal = new_center; // Center moved enough. // Create the base texture. U8 *default_texture = mObjectRawImagep->getData(); memset( default_texture, 0, mObjectImagep->getWidth() * mObjectImagep->getHeight() * mObjectImagep->getComponents() ); // Draw buildings gObjectList.renderObjectsForMap(*this); mObjectImagep->setSubImage(mObjectRawImagep, 0, 0, mObjectImagep->getWidth(), mObjectImagep->getHeight()); map_timer.reset(); } LLVector3 map_center_agent = gAgent.getPosAgentFromGlobal(mObjectImageCenterGlobal); map_center_agent -= gAgent.getCameraPositionAgent(); map_center_agent.mV[VX] *= gMiniMapScale/region_width; map_center_agent.mV[VY] *= gMiniMapScale/region_width; LLViewerImage::bindTexture(mObjectImagep); F32 image_half_width = 0.5f*mObjectMapPixels; F32 image_half_height = 0.5f*mObjectMapPixels; glBegin(GL_QUADS); glTexCoord2f(0.f, 1.f); glVertex2f(map_center_agent.mV[VX] - image_half_width, image_half_height + map_center_agent.mV[VY]); glTexCoord2f(0.f, 0.f); glVertex2f(map_center_agent.mV[VX] - image_half_width, map_center_agent.mV[VY] - image_half_height); glTexCoord2f(1.f, 0.f); glVertex2f(image_half_width + map_center_agent.mV[VX], map_center_agent.mV[VY] - image_half_height); glTexCoord2f(1.f, 1.f); glVertex2f(image_half_width + map_center_agent.mV[VX], image_half_height + map_center_agent.mV[VY]); glEnd(); glPopMatrix(); LLVector3d pos_global; LLVector3 pos_map; // Draw avatars for (LLWorld::region_list_t::iterator iter = gWorldp->mActiveRegionList.begin(); iter != gWorldp->mActiveRegionList.end(); ++iter) { LLViewerRegion* regionp = *iter; const LLVector3d& origin_global = regionp->getOriginGlobal(); S32 count = regionp->mMapAvatars.count(); S32 i; LLVector3 pos_local; U32 compact_local; U8 bits; for (i = 0; i < count; i++) { compact_local = regionp->mMapAvatars.get(i); bits = compact_local & 0xFF; pos_local.mV[VZ] = F32(bits) * 4.f; compact_local >>= 8; bits = compact_local & 0xFF; pos_local.mV[VY] = (F32)bits; compact_local >>= 8; bits = compact_local & 0xFF; pos_local.mV[VX] = (F32)bits; pos_global.setVec( pos_local ); pos_global += origin_global; pos_map = globalPosToView(pos_global); LLWorldMapView::drawAvatar(pos_map.mV[VX], pos_map.mV[VY], gAvatarMapColor, pos_map.mV[VZ]); } } // Draw dot for autopilot target if (gAgent.getAutoPilot()) { drawTracking( gAgent.getAutoPilotTargetGlobal(), gTrackColor ); } else { LLTracker::ETrackingStatus tracking_status = LLTracker::getTrackingStatus(); if ( LLTracker::TRACKING_AVATAR == tracking_status ) { drawTracking( LLAvatarTracker::instance().getGlobalPos(), gTrackColor ); } else if ( LLTracker::TRACKING_LANDMARK == tracking_status || LLTracker::TRACKING_LOCATION == tracking_status ) { drawTracking( LLTracker::getTrackedPositionGlobal(), gTrackColor ); } } // Draw dot for self avatar position //drawTracking( gAgent.getPosGlobalFromAgent(gAgent.getFrameAgent().getCenter()), gSelfMapColor ); pos_global = gAgent.getPositionGlobal(); pos_map = globalPosToView(pos_global); gl_draw_image(llround(pos_map.mV[VX]) - 4, llround(pos_map.mV[VY]) - 4, LLWorldMapView::sAvatarYouSmallImage, LLColor4::white); // Draw frustum F32 meters_to_pixels = gMiniMapScale/ gWorldPointer->getRegionWidthInMeters(); F32 horiz_fov = gCamera->getView() * gCamera->getAspect(); F32 far_clip_meters = gCamera->getFar(); F32 far_clip_pixels = far_clip_meters * meters_to_pixels; F32 half_width_meters = far_clip_meters * tan( horiz_fov / 2 ); F32 half_width_pixels = half_width_meters * meters_to_pixels; F32 ctr_x = (F32)center_sw_left; F32 ctr_y = (F32)center_sw_bottom; LLGLSNoTexture no_texture; if( LLNetMap::sRotateMap ) { glColor4fv(gFrustumMapColor.mV); glBegin( GL_TRIANGLES ); glVertex2f( ctr_x, ctr_y ); glVertex2f( ctr_x - half_width_pixels, ctr_y + far_clip_pixels ); glVertex2f( ctr_x + half_width_pixels, ctr_y + far_clip_pixels ); glEnd(); } else { glColor4fv(gRotatingFrustumMapColor.mV); // If we don't rotate the map, we have to rotate the frustum. glPushMatrix(); glTranslatef( ctr_x, ctr_y, 0 ); glRotatef( atan2( gCamera->getAtAxis().mV[VX], gCamera->getAtAxis().mV[VY] ) * RAD_TO_DEG, 0.f, 0.f, -1.f); glBegin( GL_TRIANGLES ); glVertex2f( 0, 0 ); glVertex2f( -half_width_pixels, far_clip_pixels ); glVertex2f( half_width_pixels, far_clip_pixels ); glEnd(); glPopMatrix(); } } // Rotation of 0 means that North is up setDirectionPos( mTextBoxEast, rotation ); setDirectionPos( mTextBoxNorth, rotation + F_PI_BY_TWO ); setDirectionPos( mTextBoxWest, rotation + F_PI ); setDirectionPos( mTextBoxSouth, rotation + F_PI + F_PI_BY_TWO ); setDirectionPos( mTextBoxNorthEast, rotation + F_PI_BY_TWO / 2); setDirectionPos( mTextBoxNorthWest, rotation + F_PI_BY_TWO + F_PI_BY_TWO / 2); setDirectionPos( mTextBoxSouthWest, rotation + F_PI + F_PI_BY_TWO / 2); setDirectionPos( mTextBoxSouthEast, rotation + F_PI + F_PI_BY_TWO + F_PI_BY_TWO / 2); LLUICtrl::draw(); } LLVector3 LLNetMap::globalPosToView( const LLVector3d& global_pos ) { LLVector3d relative_pos_global = global_pos - gAgent.getCameraPositionGlobal(); 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 if( LLNetMap::sRotateMap ) { F32 radians = atan2( gCamera->getAtAxis().mV[VX], gCamera->getAtAxis().mV[VY] ); LLQuaternion rot(radians, LLVector3(0.f, 0.f, 1.f)); pos_local.rotVec( rot ); } pos_local.mV[VX] += mRect.getWidth() / 2 + mCurPanX; pos_local.mV[VY] += mRect.getHeight() / 2 + mCurPanY; 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] >= mRect.getWidth()) || (pos_local.mV[VY] >= mRect.getHeight()) ) { if (draw_arrow) { S32 x = llround( pos_local.mV[VX] ); S32 y = llround( pos_local.mV[VY] ); LLWorldMapView::drawTrackingCircle( mRect, x, y, color, 1, 10 ); LLWorldMapView::drawTrackingArrow( mRect, x, y, color ); } } else { LLWorldMapView::drawTrackingDot(pos_local.mV[VX], pos_local.mV[VY], color, pos_local.mV[VZ]); } } LLVector3d LLNetMap::viewPosToGlobal( S32 x, S32 y ) { x -= llround(mRect.getWidth() / 2 + mCurPanX); y -= llround(mRect.getHeight() / 2 + mCurPanY); LLVector3 pos_local( (F32)x, (F32)y, 0 ); F32 radians = - atan2( gCamera->getAtAxis().mV[VX], gCamera->getAtAxis().mV[VY] ); if( LLNetMap::sRotateMap ) { LLQuaternion rot(radians, LLVector3(0.f, 0.f, 1.f)); pos_local.rotVec( rot ); } pos_local *= ( gWorldPointer->getRegionWidthInMeters() / gMiniMapScale ); LLVector3d pos_global; pos_global.setVec( pos_local ); pos_global += gAgent.getCameraPositionGlobal(); return pos_global; } BOOL LLNetMap::handleScrollWheel(S32 x, S32 y, S32 clicks) { // note that clicks are reversed from what you'd think setScale(llclamp(gMiniMapScale - clicks*MAP_SCALE_INCREMENT, MAP_SCALE_MIN, MAP_SCALE_MAX)); return TRUE; } BOOL LLNetMap::handleToolTip( S32 x, S32 y, LLString& msg, LLRect* sticky_rect_screen ) { BOOL handled = FALSE; if (gDisconnected) { return FALSE; } if( getVisible() && pointInView( x, y ) ) { LLViewerRegion* region = gWorldPointer->getRegionFromPosGlobal( viewPosToGlobal( x, y ) ); if( region ) { msg.assign( region->getName() ); #ifndef LL_RELEASE_FOR_DOWNLOAD char buffer[MAX_STRING]; /*Flawfinder: ignore*/ msg.append("\n"); region->getHost().getHostName(buffer, MAX_STRING); msg.append(buffer); msg.append("\n"); region->getHost().getString(buffer, MAX_STRING); msg.append(buffer); #endif // *TODO: put this under the control of XUI so it can be // translated. msg.append("\n(Double-click to open Map)"); S32 SLOP = 4; localPointToScreen( x - SLOP, y - SLOP, &(sticky_rect_screen->mLeft), &(sticky_rect_screen->mBottom) ); sticky_rect_screen->mRight = sticky_rect_screen->mLeft + 2 * SLOP; sticky_rect_screen->mTop = sticky_rect_screen->mBottom + 2 * SLOP; } handled = TRUE; } return handled; } void LLNetMap::setDirectionPos( LLTextBox* text_box, F32 rotation ) { // Rotation is in radians. // Rotation of 0 means x = 1, y = 0 on the unit circle. F32 map_half_height = (F32)(mRect.getHeight() / 2); F32 map_half_width = (F32)(mRect.getWidth() / 2); F32 text_half_height = (F32)(text_box->getRect().getHeight() / 2); F32 text_half_width = (F32)(text_box->getRect().getWidth() / 2); F32 radius = llmin( map_half_height - text_half_height, map_half_width - text_half_width ); // Inset by a little to account for position display. radius -= 8.f; text_box->setOrigin( llround(map_half_width - text_half_width + radius * cos( rotation )), llround(map_half_height - text_half_height + radius * sin( rotation )) ); } void LLNetMap::renderScaledPointGlobal( const LLVector3d& pos, const LLColor4U &color, F32 radius_meters ) { LLVector3 local_pos; local_pos.setVec( pos - mObjectImageCenterGlobal ); S32 diameter_pixels = llround(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 = llround(pos_local.mV[VX] * mObjectMapTPM + image_width / 2); S32 y_offset = llround(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; } 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.mAll; } // 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.mAll; } } 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.mAll; } } } } void LLNetMap::createObjectImage() { // Find the size of the side of a square that surrounds the circle that surrounds mRect. F32 half_width = (F32)(mRect.getWidth() / 2); F32 half_height = (F32)(mRect.getHeight() / 2); F32 radius = sqrt( half_width * half_width + half_height * half_height ); S32 square_size = S32( 2 * radius ); // Find the least power of two >= the minimum size. const S32 MIN_SIZE = 32; 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 = new LLImageGL( mObjectRawImagep, FALSE); setScale(gMiniMapScale); } mUpdateNow = TRUE; } BOOL LLNetMap::handleDoubleClick( S32 x, S32 y, MASK mask ) { LLFloaterWorldMap::show(NULL, FALSE); return TRUE; } BOOL LLNetMap::handleRightMouseDown(S32 x, S32 y, MASK mask) { LLMenuGL* menu = (LLMenuGL*)LLView::getViewByHandle(mPopupMenuHandle); if (menu) { menu->buildDrawLabels(); menu->updateParent(LLMenuGL::sMenuContainer); LLMenuGL::showPopup(this, menu, x, y); } return TRUE; } // static void LLNetMap::handleZoomLevel(void* which) { intptr_t level = (intptr_t)which; switch(level) { case 0: LLNetMap::sInstance->setScale(MAP_SCALE_MIN); break; case 1: LLNetMap::sInstance->setScale(MAP_SCALE_MID); break; case 2: LLNetMap::sInstance->setScale(MAP_SCALE_MAX); break; default: break; } } bool LLRotateNetMapListener::handleEvent(LLPointer event, const LLSD& user_data) { LLNetMap::setRotateMap(event->getValue().asBoolean()); return true; }