diff options
Diffstat (limited to 'indra/newview')
185 files changed, 19440 insertions, 11894 deletions
diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index cba9b09dc0..08e43da9e4 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -268,7 +268,6 @@ set(viewer_SOURCE_FILES llmaniprotate.cpp llmanipscale.cpp llmaniptranslate.cpp - llmapresponders.cpp llmediactrl.cpp llmediadataclient.cpp llmediaremotectrl.cpp @@ -370,7 +369,9 @@ set(viewer_SOURCE_FILES llsearchhistory.cpp llselectmgr.cpp llsidepanelinventory.cpp - llsidepanelobjectinfo.cpp + llsidepanelinventorysubpanel.cpp + llsidepaneliteminfo.cpp + llsidepaneltaskinfo.cpp llsidetray.cpp llsidetraypanelcontainer.cpp llsky.cpp @@ -397,6 +398,10 @@ set(viewer_SOURCE_FILES lltexturecache.cpp lltexturectrl.cpp lltexturefetch.cpp + lltextureinfo.cpp + lltextureinfodetails.cpp + lltexturestats.cpp + lltexturestatsuploader.cpp lltextureview.cpp lltoast.cpp lltoastalertpanel.cpp @@ -522,6 +527,8 @@ set(viewer_SOURCE_FILES llwlparamset.cpp llworld.cpp llworldmap.cpp + llworldmapmessage.cpp + llworldmipmap.cpp llworldmapview.cpp llxmlrpclistener.cpp llxmlrpctransaction.cpp @@ -757,7 +764,6 @@ set(viewer_HEADER_FILES llmaniprotate.h llmanipscale.h llmaniptranslate.h - llmapresponders.h llmediactrl.h llmediadataclient.h llmediaremotectrl.h @@ -857,7 +863,9 @@ set(viewer_HEADER_FILES llsearchhistory.h llselectmgr.h llsidepanelinventory.h - llsidepanelobjectinfo.h + llsidepanelinventorysubpanel.h + llsidepaneliteminfo.h + llsidepaneltaskinfo.h llsidetray.h llsidetraypanelcontainer.h llsky.h @@ -885,6 +893,10 @@ set(viewer_HEADER_FILES lltexturecache.h lltexturectrl.h lltexturefetch.h + lltextureinfo.h + lltextureinfodetails.h + lltexturestats.h + lltexturestatsuploader.h lltextureview.h lltoast.h lltoastalertpanel.h @@ -1011,6 +1023,8 @@ set(viewer_HEADER_FILES llwlparamset.h llworld.h llworldmap.h + llworldmapmessage.h + llworldmipmap.h llworldmapview.h llxmlrpclistener.h llxmlrpctransaction.h @@ -1663,6 +1677,12 @@ LL_ADD_INTEGRATION_TEST(llcapabilitylistener ) #ADD_VIEWER_BUILD_TEST(llmemoryview viewer) +#ADD_VIEWER_BUILD_TEST(llagentaccess viewer) +#ADD_VIEWER_BUILD_TEST(llworldmap viewer) +#ADD_VIEWER_BUILD_TEST(llworldmipmap viewer) +#ADD_VIEWER_BUILD_TEST(lltextureinfo viewer) +#ADD_VIEWER_BUILD_TEST(lltextureinfodetails viewer) +#ADD_VIEWER_BUILD_TEST(lltexturestatsuploader viewer) # Don't do these for DARWIN or LINUX here -- they're taken care of by viewer_manifest.py diff --git a/indra/newview/English.lproj/InfoPlist.strings b/indra/newview/English.lproj/InfoPlist.strings index dceaba9a43..879408d6e4 100644 --- a/indra/newview/English.lproj/InfoPlist.strings +++ b/indra/newview/English.lproj/InfoPlist.strings @@ -2,6 +2,6 @@ CFBundleName = "Second Life"; -CFBundleShortVersionString = "Second Life version 2.0.0.3256"; -CFBundleGetInfoString = "Second Life version 2.0.0.3256, Copyright 2004-2009 Linden Research, Inc."; +CFBundleShortVersionString = "Second Life version 2.0.0.200030"; +CFBundleGetInfoString = "Second Life version 2.0.0.200030, Copyright 2004-2009 Linden Research, Inc."; diff --git a/indra/newview/Info-SecondLife.plist b/indra/newview/Info-SecondLife.plist index 7aec8a343d..38ebb22b84 100644 --- a/indra/newview/Info-SecondLife.plist +++ b/indra/newview/Info-SecondLife.plist @@ -32,7 +32,7 @@ </dict> </array> <key>CFBundleVersion</key> - <string>2.0.0.3256</string> + <string>2.0.0.200030</string> <key>CSResourcesFileMapped</key> <true/> </dict> diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index e8af26c743..6f6d5fb979 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -309,7 +309,18 @@ <key>Value</key> <integer>0</integer> </map> - <key>AutoAcceptNewInventory</key> + <key>AuditTexture</key> + <map> + <key>Comment</key> + <string>Enable texture auditting.</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>Boolean</string> + <key>Value</key> + <integer>0</integer> + </map> + <key>AutoAcceptNewInventory</key> <map> <key>Comment</key> <string>Automatically accept new notecards/textures/landmarks</string> @@ -3574,7 +3585,7 @@ <key>Type</key> <string>String</string> <key>Value</key> - <string>http://www.google.com/search?q=site%3Awiki.secondlife.com+[TOPIC]&ignore_channel=[CHANNEL]&ignore_version=[VERSION]&ignore_os=[OS]&ignore_language=[LANGUAGE]&ignore_version_major=[VERSION_MAJOR]&ignore_version_minor=[VERSION_MINOR]&ignore_version_patch=[VERSION_PATCH]&ignore_version_build=[VERSION_BUILD]</string> + <string>http://docs.lindenlab.com/help/helpfloater.php?topic=[TOPIC]&channel=[CHANNEL]&version=[VERSION]&os=[OS]&language=[LANGUAGE]&version_major=[VERSION_MAJOR]&version_minor=[VERSION_MINOR]&version_patch=[VERSION_PATCH]&version_build=[VERSION_BUILD]</string> </map> <key>HighResSnapshot</key> <map> @@ -4567,6 +4578,17 @@ <key>Value</key> <integer>1</integer> </map> + <key>MiniMapPrimMaxRadius</key> + <map> + <key>Comment</key> + <string>Radius of the largest prim to show on the MiniMap. Increasing beyond 256 may cause client lag.</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>F32</string> + <key>Value</key> + <real>256.0</real> + </map> <key>MiniMapRotate</key> <map> <key>Comment</key> @@ -4581,7 +4603,7 @@ <key>MiniMapScale</key> <map> <key>Comment</key> - <string>Miniature world map zoom levle (pixels per region)</string> + <string>Miniature world map zoom level (pixels per region)</string> <key>Persist</key> <integer>1</integer> <key>Type</key> @@ -6247,7 +6269,7 @@ <key>Type</key> <string>Boolean</string> <key>Value</key> - <integer>0</integer> + <integer>1</integer> </map> <key>RenderHighlightFadeTime</key> @@ -8611,6 +8633,28 @@ <key>Value</key> <real>20.0</real> </map> + <key>TextureDisable</key> + <map> + <key>Comment</key> + <string>If TRUE, do not load textures for in-world content</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>Boolean</string> + <key>Value</key> + <integer>0</integer> + </map> + <key>TextureLoadFullRes</key> + <map> + <key>Comment</key> + <string>If TRUE, always load textures at full resolution (discard = 0)</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>Boolean</string> + <key>Value</key> + <integer>0</integer> + </map> <key>TextureMemory</key> <map> <key>Comment</key> @@ -10669,5 +10713,38 @@ <key>Value</key> <integer>0</integer> </map> - </map> + <key>LogTextureDownloadsToViewerLog</key> + <map> + <key>Comment</key> + <string>Send texture download details to the viewer log</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>Boolean</string> + <key>Value</key> + <integer>0</integer> + </map> + <key>LogTextureDownloadsToSimulator</key> + <map> + <key>Comment</key> + <string>Send a digest of texture info to the sim</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>Boolean</string> + <key>Value</key> + <integer>0</integer> + </map> + <key>TextureLoggingThreshold</key> + <map> + <key>Comment</key> + <string>Specifies the byte threshold at which texture download data should be sent to the sim.</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>U32</string> + <key>Value</key> + <integer>1</integer> + </map> +</map> </llsd> diff --git a/indra/newview/llagent.cpp b/indra/newview/llagent.cpp index 75a72e5b17..24bdf66c2a 100644 --- a/indra/newview/llagent.cpp +++ b/indra/newview/llagent.cpp @@ -75,6 +75,8 @@ #include "llstatusbar.h" #include "llteleportflags.h" #include "llteleporthistory.h" +#include "lltexturestats.h" +#include "lltexturestats.h" #include "lltool.h" #include "lltoolcomp.h" #include "lltoolmgr.h" @@ -6085,17 +6087,16 @@ void LLAgent::teleportCancel() void LLAgent::teleportViaLocation(const LLVector3d& pos_global) { LLViewerRegion* regionp = getRegion(); - LLSimInfo* info = LLWorldMap::getInstance()->simInfoFromPosGlobal(pos_global); + U64 handle = to_region_handle(pos_global); + LLSimInfo* info = LLWorldMap::getInstance()->simInfoFromHandle(handle); if(regionp && info) { - U32 x_pos; - U32 y_pos; - from_region_handle(info->mHandle, &x_pos, &y_pos); + LLVector3d region_origin = info->getGlobalOrigin(); LLVector3 pos_local( - (F32)(pos_global.mdV[VX] - x_pos), - (F32)(pos_global.mdV[VY] - y_pos), + (F32)(pos_global.mdV[VX] - region_origin.mdV[VX]), + (F32)(pos_global.mdV[VY] - region_origin.mdV[VY]), (F32)(pos_global.mdV[VZ])); - teleportRequest(info->mHandle, pos_local); + teleportRequest(handle, pos_local); } else if(regionp && teleportCore(regionp->getHandle() == to_region_handle_global((F32)pos_global.mdV[VX], (F32)pos_global.mdV[VY]))) @@ -6512,3 +6513,4 @@ LLAgentQueryManager::~LLAgentQueryManager() } // EOF + diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index e95eec4741..8e6d6b885d 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -57,6 +57,8 @@ #include "llallocator.h" #include "llares.h" #include "llcurl.h" +#include "lltexturestats.h" +#include "lltexturestats.h" #include "llviewerwindow.h" #include "llviewerdisplay.h" #include "llviewermedia.h" @@ -255,9 +257,6 @@ F32 gLogoutMaxTime = LOGOUT_REQUEST_TIME; BOOL gDisconnected = FALSE; -// Map scale in pixels per region -F32 gMapScale = 128.f; - // used to restore texture state after a mode switch LLFrameTimer gRestoreGLTimer; BOOL gRestoreGL = FALSE; @@ -419,7 +418,7 @@ static void settings_to_globals() gDebugWindowProc = gSavedSettings.getBOOL("DebugWindowProc"); gShowObjectUpdates = gSavedSettings.getBOOL("ShowObjectUpdates"); - gMapScale = gSavedSettings.getF32("MapScale"); + LLWorldMapView::sMapScale = gSavedSettings.getF32("MapScale"); LLCubeMap::sUseCubeMaps = LLFeatureManager::getInstance()->isFeatureAvailable("RenderCubeMap"); } @@ -432,7 +431,7 @@ static void settings_modify() LLVOSurfacePatch::sLODFactor *= LLVOSurfacePatch::sLODFactor; //square lod factor to get exponential range of [1,4] gDebugGL = gSavedSettings.getBOOL("RenderDebugGL") || gDebugSession; gDebugPipeline = gSavedSettings.getBOOL("RenderDebugPipeline"); - + gAuditTexture = gSavedSettings.getBOOL("AuditTexture"); #if LL_VECTORIZE if (gSysCPU.hasAltivec()) { @@ -553,7 +552,7 @@ LLAppViewer* LLAppViewer::sInstance = NULL; const std::string LLAppViewer::sGlobalSettingsName = "Global"; LLTextureCache* LLAppViewer::sTextureCache = NULL; -LLWorkerThread* LLAppViewer::sImageDecodeThread = NULL; +LLImageDecodeThread* LLAppViewer::sImageDecodeThread = NULL; LLTextureFetch* LLAppViewer::sTextureFetch = NULL; LLAppViewer::LLAppViewer() : @@ -657,6 +656,9 @@ bool LLAppViewer::init() ////////////////////////////////////////////////////////////////////////////// // *FIX: The following code isn't grouped into functions yet. + // Statistics / debug timer initialization + init_statistics(); + // // Various introspection concerning the libs we're using - particularly // the libs involved in getting to a full login screen. @@ -1637,14 +1639,14 @@ bool LLAppViewer::initThreads() LLWatchdog::getInstance()->init(watchdog_killer_callback); } - LLVFSThread::initClass(enable_threads && true); - LLLFSThread::initClass(enable_threads && true); + LLVFSThread::initClass(enable_threads && false); + LLLFSThread::initClass(enable_threads && false); // Image decoding - LLAppViewer::sImageDecodeThread = new LLWorkerThread("ImageDecode", enable_threads && true); + LLAppViewer::sImageDecodeThread = new LLImageDecodeThread(enable_threads && true); LLAppViewer::sTextureCache = new LLTextureCache(enable_threads && true); - LLAppViewer::sTextureFetch = new LLTextureFetch(LLAppViewer::getTextureCache(), enable_threads && false); - LLImage::initClass(LLAppViewer::getImageDecodeThread()); + LLAppViewer::sTextureFetch = new LLTextureFetch(LLAppViewer::getTextureCache(), sImageDecodeThread, enable_threads && true); + LLImage::initClass(); if (LLFastTimer::sLog || LLFastTimer::sMetricLog) { @@ -2437,7 +2439,7 @@ void LLAppViewer::cleanupSavedSettings() } } - gSavedSettings.setF32("MapScale", gMapScale ); + gSavedSettings.setF32("MapScale", LLWorldMapView::sMapScale ); // Some things are cached in LLAgent. if (gAgent.mInitialized) @@ -2763,7 +2765,7 @@ void LLAppViewer::initMarkerFile() // Create the marker file for this execution & lock it apr_status_t s; - s = mMarkerFile.open(mMarkerFileName, LL_APR_W, gAPRPoolp); + s = mMarkerFile.open(mMarkerFileName, LL_APR_W, TRUE); if (s == APR_SUCCESS && mMarkerFile.getFileHandle()) { diff --git a/indra/newview/llappviewer.h b/indra/newview/llappviewer.h index 73256a8fe6..c1bfbca868 100644 --- a/indra/newview/llappviewer.h +++ b/indra/newview/llappviewer.h @@ -41,15 +41,13 @@ class LLCommandLineParser; class LLFrameTimer; class LLPumpIO; class LLTextureCache; +class LLImageDecodeThread; class LLTextureFetch; -class LLTimer; -class LLVFS; class LLWatchdogTimeout; -class LLWorkerThread; +class LLCommandLineParser; struct apr_dso_handle_t; - class LLAppViewer : public LLApp { public: @@ -100,7 +98,7 @@ public: // Thread accessors static LLTextureCache* getTextureCache() { return sTextureCache; } - static LLWorkerThread* getImageDecodeThread() { return sImageDecodeThread; } + static LLImageDecodeThread* getImageDecodeThread() { return sImageDecodeThread; } static LLTextureFetch* getTextureFetch() { return sTextureFetch; } const std::string& getSerialNumber() { return mSerialNumber; } @@ -232,7 +230,7 @@ private: // Thread objects. static LLTextureCache* sTextureCache; - static LLWorkerThread* sImageDecodeThread; + static LLImageDecodeThread* sImageDecodeThread; static LLTextureFetch* sTextureFetch; S32 mNumSessions; @@ -320,9 +318,6 @@ extern F32 gSimFrames; extern BOOL gDisconnected; -// Map scale in pixels per region -extern F32 gMapScale; - extern LLFrameTimer gRestoreGLTimer; extern BOOL gRestoreGL; extern BOOL gUseWireframe; diff --git a/indra/newview/llassetuploadresponders.cpp b/indra/newview/llassetuploadresponders.cpp index d4df6dfbe7..4d85ecb97c 100644 --- a/indra/newview/llassetuploadresponders.cpp +++ b/indra/newview/llassetuploadresponders.cpp @@ -369,7 +369,7 @@ void LLSendTexLayerResponder::uploadComplete(const LLSD& content) std::string result = content["state"]; LLUUID new_id = content["new_asset"]; - llinfos << "LLSendTexLayerResponder::result from capabilities: " << result << llendl; + llinfos << "result: " << result << "new_id:" << new_id << llendl; if (result == "complete" && mBakedUploadData != NULL) { // Invoke @@ -383,6 +383,14 @@ void LLSendTexLayerResponder::uploadComplete(const LLSD& content) } } +void LLSendTexLayerResponder::error(U32 statusNum, const std::string& reason) +{ + llinfos << "status: " << statusNum << " reason: " << reason << llendl; + + // Invoke the original callback with an error result + LLTexLayerSetBuffer::onTextureUploadComplete(LLUUID(), (void*) mBakedUploadData, -1, LL_EXSTAT_NONE); + mBakedUploadData = NULL; // deleted in onTextureUploadComplete() +} LLUpdateAgentInventoryResponder::LLUpdateAgentInventoryResponder(const LLSD& post_data, const LLUUID& vfile_id, diff --git a/indra/newview/llassetuploadresponders.h b/indra/newview/llassetuploadresponders.h index a08d70213c..e656351305 100644 --- a/indra/newview/llassetuploadresponders.h +++ b/indra/newview/llassetuploadresponders.h @@ -84,6 +84,7 @@ public: ~LLSendTexLayerResponder(); virtual void uploadComplete(const LLSD& content); + virtual void error(U32 statusNum, const std::string& reason); LLBakedUploadData * mBakedUploadData; }; diff --git a/indra/newview/llcolorswatch.cpp b/indra/newview/llcolorswatch.cpp index ed304bdd34..442e9ab27b 100644 --- a/indra/newview/llcolorswatch.cpp +++ b/indra/newview/llcolorswatch.cpp @@ -228,7 +228,7 @@ void LLColorSwatchCtrl::draw() { if (!mFallbackImageName.empty()) { - LLPointer<LLViewerTexture> fallback_image = LLViewerTextureManager::getFetchedTextureFromFile(mFallbackImageName, TRUE, + LLPointer<LLViewerFetchedTexture> fallback_image = LLViewerTextureManager::getFetchedTextureFromFile(mFallbackImageName, TRUE, LLViewerTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE); if( fallback_image->getComponents() == 4 ) { diff --git a/indra/newview/llcurrencyuimanager.cpp b/indra/newview/llcurrencyuimanager.cpp index c4bfd71999..319cbf8209 100644 --- a/indra/newview/llcurrencyuimanager.cpp +++ b/indra/newview/llcurrencyuimanager.cpp @@ -76,8 +76,15 @@ public: bool mUserEnteredCurrencyBuy; // from website - bool mSiteCurrencyEstimated; - S32 mSiteCurrencyEstimatedCost; + + // pre-viewer 2.0, the server returned estimates as an + // integer US cents value, e.g., "1000" for $10.00 + // post-viewer 2.0, the server may also return estimates + // as a string with currency embedded, e.g., "10.00 Euros" + bool mUSDCurrencyEstimated; + S32 mUSDCurrencyEstimatedCost; + bool mLocalCurrencyEstimated; + std::string mLocalCurrencyEstimatedCost; std::string mSiteConfirm; bool mBought; @@ -101,6 +108,10 @@ public: void startCurrencyBuy(const std::string& password); void finishCurrencyBuy(); + + void clearEstimate(); + bool hasEstimate() const; + std::string getLocalEstimate() const; void startTransaction(TransactionType type, const char* method, LLXMLRPCValue params); @@ -126,12 +137,11 @@ LLCurrencyUIManager::Impl::Impl(LLPanel& dialog) mError(false), mUserCurrencyBuy(2000), // note, this is a default, real value set in llfloaterbuycurrency.cpp mUserEnteredCurrencyBuy(false), - mSiteCurrencyEstimated(false), - mSiteCurrencyEstimatedCost(0), mBought(false), mTransactionType(TransactionNone), mTransaction(0), mCurrencyChanged(false) { + clearEstimate(); } LLCurrencyUIManager::Impl::~Impl() @@ -141,14 +151,13 @@ LLCurrencyUIManager::Impl::~Impl() void LLCurrencyUIManager::Impl::updateCurrencyInfo() { - mSiteCurrencyEstimated = false; - mSiteCurrencyEstimatedCost = 0; + clearEstimate(); mBought = false; mCurrencyChanged = false; if (mUserCurrencyBuy == 0) { - mSiteCurrencyEstimated = true; + mLocalCurrencyEstimated = true; return; } @@ -185,9 +194,21 @@ void LLCurrencyUIManager::Impl::finishCurrencyInfo() } LLXMLRPCValue currency = result["currency"]; - mSiteCurrencyEstimated = true; - mSiteCurrencyEstimatedCost = currency["estimatedCost"].asInt(); - + + // old XML-RPC server: estimatedCost = value in US cents + mUSDCurrencyEstimated = currency["estimatedCost"].isValid(); + if (mUSDCurrencyEstimated) + { + mUSDCurrencyEstimatedCost = currency["estimatedCost"].asInt(); + } + + // newer XML-RPC server: estimatedLocalCost = local currency string + mLocalCurrencyEstimated = currency["estimatedLocalCost"].isValid(); + if (mLocalCurrencyEstimated) + { + mLocalCurrencyEstimatedCost = currency["estimatedLocalCost"].asString(); + } + S32 newCurrencyBuy = currency["currencyBuy"].asInt(); if (newCurrencyBuy != mUserCurrencyBuy) { @@ -200,17 +221,20 @@ void LLCurrencyUIManager::Impl::finishCurrencyInfo() void LLCurrencyUIManager::Impl::startCurrencyBuy(const std::string& password) { - mSiteCurrencyEstimated = false; - mSiteCurrencyEstimatedCost = 0; - mCurrencyChanged = false; - LLXMLRPCValue keywordArgs = LLXMLRPCValue::createStruct(); keywordArgs.appendString("agentId", gAgent.getID().asString()); keywordArgs.appendString( "secureSessionId", gAgent.getSecureSessionID().asString()); keywordArgs.appendInt("currencyBuy", mUserCurrencyBuy); - keywordArgs.appendInt("estimatedCost", mSiteCurrencyEstimatedCost); + if (mUSDCurrencyEstimated) + { + keywordArgs.appendInt("estimatedCost", mUSDCurrencyEstimatedCost); + } + if (mLocalCurrencyEstimated) + { + keywordArgs.appendString("estimatedLocalCost", mLocalCurrencyEstimatedCost); + } keywordArgs.appendString("confirm", mSiteConfirm); if (!password.empty()) { @@ -226,6 +250,9 @@ void LLCurrencyUIManager::Impl::startCurrencyBuy(const std::string& password) params.append(keywordArgs); startTransaction(TransactionBuy, "buyCurrency", params); + + clearEstimate(); + mCurrencyChanged = false; } void LLCurrencyUIManager::Impl::finishCurrencyBuy() @@ -270,6 +297,34 @@ void LLCurrencyUIManager::Impl::startTransaction(TransactionType type, clearError(); } +void LLCurrencyUIManager::Impl::clearEstimate() +{ + mUSDCurrencyEstimated = false; + mUSDCurrencyEstimatedCost = 0; + mLocalCurrencyEstimated = false; + mLocalCurrencyEstimatedCost = "0"; +} + +bool LLCurrencyUIManager::Impl::hasEstimate() const +{ + return (mUSDCurrencyEstimated || mLocalCurrencyEstimated); +} + +std::string LLCurrencyUIManager::Impl::getLocalEstimate() const +{ + if (mLocalCurrencyEstimated) + { + // we have the new-style local currency string + return mLocalCurrencyEstimatedCost; + } + if (mUSDCurrencyEstimated) + { + // we have the old-style USD-specific value + return "US$ " + llformat("%#.2f", mUSDCurrencyEstimatedCost / 100.0); + } + return ""; +} + bool LLCurrencyUIManager::Impl::checkTransaction() { if (!mTransaction) @@ -342,8 +397,8 @@ void LLCurrencyUIManager::Impl::currencyKey(S32 value) mUserCurrencyBuy = value; - if (mSiteCurrencyEstimated) { - mSiteCurrencyEstimated = false; + if (hasEstimate()) { + clearEstimate(); //cannot just simply refresh the whole UI, as the edit field will // get reset and the cursor will change... @@ -406,8 +461,8 @@ void LLCurrencyUIManager::Impl::updateUI() } } - mPanel.childSetTextArg("currency_est", "[LOCALAMOUNT]", "US$ " + llformat("%#.2f", mSiteCurrencyEstimatedCost / 100.0)); - mPanel.childSetVisible("currency_est", mSiteCurrencyEstimated && mUserCurrencyBuy > 0); + mPanel.childSetTextArg("currency_est", "[LOCALAMOUNT]", getLocalEstimate()); + mPanel.childSetVisible("currency_est", hasEstimate() && mUserCurrencyBuy > 0); if (mPanel.childIsEnabled("buy_btn") ||mPanel.childIsVisible("currency_est") @@ -448,18 +503,32 @@ void LLCurrencyUIManager::setZeroMessage(const std::string& message) impl.mZeroMessage = message; } -void LLCurrencyUIManager::setEstimate(int amount) +void LLCurrencyUIManager::setUSDEstimate(int amount) +{ + impl.mUSDCurrencyEstimatedCost = amount; + impl.mUSDCurrencyEstimated = true; + impl.updateUI(); + + impl.mCurrencyChanged = false; +} + +int LLCurrencyUIManager::getUSDEstimate() +{ + return impl.mUSDCurrencyEstimated ? impl.mUSDCurrencyEstimatedCost : 0; +} + +void LLCurrencyUIManager::setLocalEstimate(const std::string &amount) { - impl.mSiteCurrencyEstimatedCost = amount; - impl.mSiteCurrencyEstimated = true; + impl.mLocalCurrencyEstimatedCost = amount; + impl.mLocalCurrencyEstimated = true; impl.updateUI(); impl.mCurrencyChanged = false; } -int LLCurrencyUIManager::getEstimate() +std::string LLCurrencyUIManager::getLocalEstimate() const { - return impl.mSiteCurrencyEstimated ? impl.mSiteCurrencyEstimatedCost : 0; + return impl.getLocalEstimate(); } void LLCurrencyUIManager::prepare() @@ -490,7 +559,7 @@ void LLCurrencyUIManager::buy(const std::string& buy_msg) LLUIString msg = buy_msg; msg.setArg("[LINDENS]", llformat("%d", impl.mUserCurrencyBuy)); - msg.setArg("[LOCALAMOUNT]", "US$ " + llformat("%#.2f", impl.mSiteCurrencyEstimatedCost / 100.0)); + msg.setArg("[LOCALAMOUNT]", getLocalEstimate()); LLConfirmationManager::confirm(impl.mSiteConfirm, msg, impl, @@ -511,7 +580,7 @@ bool LLCurrencyUIManager::canCancel() bool LLCurrencyUIManager::canBuy() { return impl.mTransactionType == Impl::TransactionNone - && impl.mSiteCurrencyEstimated + && impl.hasEstimate() && impl.mUserCurrencyBuy > 0; } diff --git a/indra/newview/llcurrencyuimanager.h b/indra/newview/llcurrencyuimanager.h index 93427aed7f..dfe027098d 100644 --- a/indra/newview/llcurrencyuimanager.h +++ b/indra/newview/llcurrencyuimanager.h @@ -57,11 +57,16 @@ public: void setZeroMessage(const std::string& message); // sets the gray message to show when zero - void setEstimate(int); - int getEstimate(); + void setUSDEstimate(int); // deprecated in 2.0 + int getUSDEstimate(); // deprecated in 2.0 // the amount in US$ * 100 (in otherwords, in cents) // use set when you get this information from elsewhere + void setLocalEstimate(const std::string &local_est); + std::string getLocalEstimate() const; + // the estimated cost in the user's local currency + // for example, "US$ 10.00" or "10.00 Euros" + void prepare(); // call once after dialog is built, from postBuild() void updateUI(bool show = true); diff --git a/indra/newview/lldebugview.cpp b/indra/newview/lldebugview.cpp index 24a57cb0c1..9057d84f63 100644 --- a/indra/newview/lldebugview.cpp +++ b/indra/newview/lldebugview.cpp @@ -45,7 +45,7 @@ #include "llviewerwindow.h" #include "llappviewer.h" #include "llmemoryview.h" - +#include "llviewertexture.h" // // Globals // @@ -102,17 +102,29 @@ LLDebugView::LLDebugView(const LLDebugView::Params& p) gTextureView = LLUICtrlFactory::create<LLTextureView>(tvp); addChild(gTextureView); //gTextureView->reshape(r.getWidth(), r.getHeight(), TRUE); -#if !LL_RELEASE_FOR_DOWNLOAD - r.set(150, rect.getHeight() - 50, 900 + LLImageGL::sTextureLoadedCounter.size() * 30, 100); - LLTextureSizeView::Params tsvp; - tsvp.name("gTextureSizeView"); - tsvp.rect(r); - tsvp.follows.flags(FOLLOWS_BOTTOM|FOLLOWS_LEFT); - tsvp.visible(false); - gTextureSizeView = LLUICtrlFactory::create<LLTextureSizeView>(tsvp); - addChild(gTextureSizeView); -#endif + if(gAuditTexture) + { + r.set(150, rect.getHeight() - 50, 900 + LLImageGL::sTextureLoadedCounter.size() * 30, 100); + LLTextureSizeView::Params tsv ; + tsv.name("gTextureSizeView"); + tsv.rect(r); + tsv.follows.flags(FOLLOWS_BOTTOM|FOLLOWS_LEFT); + tsv.visible(false); + gTextureSizeView = LLUICtrlFactory::create<LLTextureSizeView>(tsv); + addChild(gTextureSizeView); + gTextureSizeView->setType(LLTextureSizeView::TEXTURE_MEM_OVER_SIZE) ; + + r.set(150, rect.getHeight() - 50, 900 + LLViewerTexture::getTotalNumOfCategories() * 30, 100); + LLTextureSizeView::Params tcv ; + tcv.name("gTextureCategoryView"); + tcv.rect(r); + tcv.follows.flags(FOLLOWS_BOTTOM|FOLLOWS_LEFT); + tcv.visible(false); + gTextureCategoryView = LLUICtrlFactory::create<LLTextureSizeView>(tcv); + gTextureCategoryView->setType(LLTextureSizeView::TEXTURE_MEM_OVER_CATEGORY); + addChild(gTextureCategoryView); + } } @@ -122,5 +134,6 @@ LLDebugView::~LLDebugView() gDebugView = NULL; gTextureView = NULL; gTextureSizeView = NULL; + gTextureCategoryView = NULL; } diff --git a/indra/newview/lldrawable.cpp b/indra/newview/lldrawable.cpp index 069155c255..03a3f2b43d 100644 --- a/indra/newview/lldrawable.cpp +++ b/indra/newview/lldrawable.cpp @@ -105,7 +105,7 @@ void LLDrawable::init() mVObjp = NULL; // mFaces mSpatialGroupp = NULL; - mVisible = 0; + mVisible = sCurVisible - 2;//invisible for the current frame and the last frame. mRadius = 0.f; mGeneration = -1; diff --git a/indra/newview/lldrawpool.cpp b/indra/newview/lldrawpool.cpp index d8c34581d5..95ddacb722 100644 --- a/indra/newview/lldrawpool.cpp +++ b/indra/newview/lldrawpool.cpp @@ -279,7 +279,7 @@ S32 LLFacePool::drawLoopSetTex(face_array_t& face_list, S32 stage) iter != face_list.end(); iter++) { LLFace *facep = *iter; - gGL.getTexUnit(stage)->bind(facep->getTexture()); + gGL.getTexUnit(stage)->bind(facep->getTexture(), TRUE) ; gGL.getTexUnit(0)->activate(); res += facep->renderIndexed(); } @@ -475,17 +475,13 @@ void LLRenderPass::pushBatch(LLDrawInfo& params, U32 mask, BOOL texture) { if (params.mTexture.notNull()) { - gGL.getTexUnit(0)->bind(params.mTexture.get()); + gGL.getTexUnit(0)->bind(params.mTexture, TRUE) ; if (params.mTextureMatrix) { glMatrixMode(GL_TEXTURE); glLoadMatrixf((GLfloat*) params.mTextureMatrix->mMatrix); gPipeline.mTextureMatrixOps++; } - if(params.mTexture.notNull())//will be removed. - { - params.mTexture->addTextureStats(params.mVSize); - } } else { diff --git a/indra/newview/lldrawpoolalpha.cpp b/indra/newview/lldrawpoolalpha.cpp index a5a29dea7b..6d77361414 100644 --- a/indra/newview/lldrawpoolalpha.cpp +++ b/indra/newview/lldrawpoolalpha.cpp @@ -219,7 +219,7 @@ void LLDrawPoolAlpha::render(S32 pass) gPipeline.enableLightsFullbright(LLColor4(1,1,1,1)); glColor4f(1,0,0,1); LLViewerFetchedTexture::sSmokeImagep->addTextureStats(1024.f*1024.f); - gGL.getTexUnit(0)->bind(LLViewerFetchedTexture::sSmokeImagep) ; + gGL.getTexUnit(0)->bind(LLViewerFetchedTexture::sSmokeImagep, TRUE) ; renderAlphaHighlight(LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_TEXCOORD0); } diff --git a/indra/newview/lldrawpooltree.cpp b/indra/newview/lldrawpooltree.cpp index 8d2cbc583c..5521fb05a8 100644 --- a/indra/newview/lldrawpooltree.cpp +++ b/indra/newview/lldrawpooltree.cpp @@ -253,7 +253,7 @@ void LLDrawPoolTree::renderTree(BOOL selecting) LLGLState normalize(GL_NORMALIZE, TRUE); // Bind the texture for this tree. - gGL.getTexUnit(sDiffTex)->bind(mTexturep.get()); + gGL.getTexUnit(sDiffTex)->bind(mTexturep.get(), TRUE); U32 indices_drawn = 0; diff --git a/indra/newview/lldynamictexture.cpp b/indra/newview/lldynamictexture.cpp index 0bb5edf3f9..e41c4104eb 100644 --- a/indra/newview/lldynamictexture.cpp +++ b/indra/newview/lldynamictexture.cpp @@ -104,7 +104,7 @@ void LLViewerDynamicTexture::generateGLTexture(LLGLint internal_format, LLGLenum { setExplicitFormat(internal_format, primary_format, type_format, swap_bytes); } - createGLTexture(0, raw_image); + createGLTexture(0, raw_image, 0, TRUE, LLViewerTexture::DYNAMIC_TEX); setAddressMode((mClamp) ? LLTexUnit::TAM_CLAMP : LLTexUnit::TAM_WRAP); mGLTexturep->setGLTextureCreated(false); } diff --git a/indra/newview/llface.cpp b/indra/newview/llface.cpp index 8ec448e281..c54b83d865 100644 --- a/indra/newview/llface.cpp +++ b/indra/newview/llface.cpp @@ -52,6 +52,7 @@ #include "llvovolume.h" #include "pipeline.h" #include "llviewerregion.h" +#include "llviewerwindow.h" #define LL_MAX_INDICES_COUNT 1000000 @@ -175,6 +176,9 @@ void LLFace::init(LLDrawable* drawablep, LLViewerObject* objp) mLastIndicesCount = mIndicesCount; mLastIndicesIndex = mIndicesIndex; + mImportanceToCamera = 0.f ; + mBoundingSphereRadius = 0.0f ; + mAtlasInfop = NULL ; mUsingAtlas = FALSE ; } @@ -186,6 +190,7 @@ void LLFace::destroy() { mTexture->removeFace(this) ; } + if (mDrawPoolp) { mDrawPoolp->removeFace(this); @@ -207,7 +212,7 @@ void LLFace::destroy() } } } - + setDrawInfo(NULL); removeAtlas(); @@ -256,6 +261,7 @@ void LLFace::setPool(LLFacePool* new_pool, LLViewerTexture *texturep) } mDrawPoolp = new_pool; } + setTexture(texturep) ; } @@ -750,7 +756,9 @@ BOOL LLFace::genVolumeBBoxes(const LLVolume &volume, S32 f, } mCenterLocal = (newMin+newMax)*0.5f; - + LLVector3 tmp = (newMin - newMax) ; + mBoundingSphereRadius = tmp.length() * 0.5f ; + updateCenterAgent(); } @@ -1314,6 +1322,151 @@ BOOL LLFace::getGeometryVolume(const LLVolume& volume, return TRUE; } +const F32 LEAST_IMPORTANCE = 0.05f ; +const F32 LEAST_IMPORTANCE_FOR_LARGE_IMAGE = 0.3f ; + +F32 LLFace::getTextureVirtualSize() +{ + F32 radius; + F32 cos_angle_to_view_dir; + mPixelArea = calcPixelArea(cos_angle_to_view_dir, radius); + + if (mPixelArea <= 0) + { + return 0.f; + } + + //get area of circle in texture space + LLVector2 tdim = mTexExtents[1] - mTexExtents[0]; + F32 texel_area = (tdim * 0.5f).lengthSquared()*3.14159f; + if (texel_area <= 0) + { + // Probably animated, use default + texel_area = 1.f; + } + + //apply texel area to face area to get accurate ratio + //face_area /= llclamp(texel_area, 1.f/64.f, 16.f); + F32 face_area = mPixelArea / llclamp(texel_area, 0.015625f, 128.f); + + if(face_area > LLViewerTexture::sMaxSmallImageSize) + { + if(mImportanceToCamera < LEAST_IMPORTANCE) //if the face is not important, do not load hi-res. + { + static const F32 MAX_LEAST_IMPORTANCE_IMAGE_SIZE = 128.0f * 128.0f ; + face_area = llmin(face_area * 0.5f, MAX_LEAST_IMPORTANCE_IMAGE_SIZE) ; + } + else if(face_area > LLViewerTexture::sMinLargeImageSize) //if is large image, shrink face_area by considering the partial overlapping. + { + if(mImportanceToCamera < LEAST_IMPORTANCE_FOR_LARGE_IMAGE)//if the face is not important, do not load hi-res. + { + face_area = LLViewerTexture::sMinLargeImageSize ; + } + else if(mTexture.notNull() && mTexture->isLargeImage()) + { + face_area *= adjustPartialOverlapPixelArea(cos_angle_to_view_dir, radius ); + } + } + } + + return face_area; +} + +F32 LLFace::calcPixelArea(F32& cos_angle_to_view_dir, F32& radius) +{ + //get area of circle around face + LLVector3 center = getPositionAgent(); + LLVector3 size = (mExtents[1] - mExtents[0]) * 0.5f; + + LLVector3 lookAt = center - LLViewerCamera::getInstance()->getOrigin(); + F32 dist = lookAt.normVec() ; + + //get area of circle around node + F32 app_angle = atanf(size.length()/dist); + radius = app_angle*LLDrawable::sCurPixelAngle; + F32 face_area = radius*radius * 3.14159f; + + if(dist < mBoundingSphereRadius) //camera is very close + { + cos_angle_to_view_dir = 1.0f ; + mImportanceToCamera = 1.0f ; + } + else + { + cos_angle_to_view_dir = lookAt * LLViewerCamera::getInstance()->getXAxis() ; + mImportanceToCamera = LLFace::calcImportanceToCamera(cos_angle_to_view_dir, dist) ; + } + + return face_area ; +} + +//the projection of the face partially overlaps with the screen +F32 LLFace::adjustPartialOverlapPixelArea(F32 cos_angle_to_view_dir, F32 radius ) +{ + F32 screen_radius = (F32)llmax(gViewerWindow->getWindowDisplayWidth(), gViewerWindow->getWindowDisplayHeight()) ; + F32 center_angle = acosf(cos_angle_to_view_dir) ; + F32 d = center_angle * LLDrawable::sCurPixelAngle ; + + if(d + radius > screen_radius + 5.f) + { + //---------------------------------------------- + //calculate the intersection area of two circles + //F32 radius_square = radius * radius ; + //F32 d_square = d * d ; + //F32 screen_radius_square = screen_radius * screen_radius ; + //face_area = + // radius_square * acosf((d_square + radius_square - screen_radius_square)/(2 * d * radius)) + + // screen_radius_square * acosf((d_square + screen_radius_square - radius_square)/(2 * d * screen_radius)) - + // 0.5f * sqrtf((-d + radius + screen_radius) * (d + radius - screen_radius) * (d - radius + screen_radius) * (d + radius + screen_radius)) ; + //---------------------------------------------- + + //the above calculation is too expensive + //the below is a good estimation: bounding box of the bounding sphere: + F32 alpha = 0.5f * (radius + screen_radius - d) / radius ; + alpha = llclamp(alpha, 0.f, 1.f) ; + return alpha * alpha ; + } + return 1.0f ; +} + +const S8 FACE_IMPORTANCE_LEVEL = 4 ; +const F32 FACE_IMPORTANCE_TO_CAMERA_OVER_DISTANCE[FACE_IMPORTANCE_LEVEL][2] = //{distance, importance_weight} + {{16.1f, 1.0f}, {32.1f, 0.5f}, {48.1f, 0.2f}, {96.1f, 0.05f} } ; +const F32 FACE_IMPORTANCE_TO_CAMERA_OVER_ANGLE[FACE_IMPORTANCE_LEVEL][2] = //{cos(angle), importance_weight} + {{0.985f /*cos(10 degrees)*/, 1.0f}, {0.94f /*cos(20 degrees)*/, 0.8f}, {0.866f /*cos(30 degrees)*/, 0.64f}, {0.0f, 0.36f}} ; + +//static +F32 LLFace::calcImportanceToCamera(F32 cos_angle_to_view_dir, F32 dist) +{ + F32 importance = 0.f ; + + if(cos_angle_to_view_dir > LLViewerCamera::getInstance()->getCosHalfFov() && + dist < FACE_IMPORTANCE_TO_CAMERA_OVER_DISTANCE[FACE_IMPORTANCE_LEVEL - 1][0]) + { + F32 camera_moving_speed = LLViewerCamera::getInstance()->getAverageSpeed() ; + F32 camera_angular_speed = LLViewerCamera::getInstance()->getAverageAngularSpeed(); + + if(camera_moving_speed > 10.0f || camera_angular_speed > 1.0f) + { + //if camera moves or rotates too fast, ignore the importance factor + return 0.f ; + } + + //F32 camera_relative_speed = camera_moving_speed * (lookAt * LLViewerCamera::getInstance()->getVelocityDir()) ; + + S32 i = 0 ; + for(i = 0; i < FACE_IMPORTANCE_LEVEL && dist > FACE_IMPORTANCE_TO_CAMERA_OVER_DISTANCE[i][0]; ++i); + i = llmin(i, FACE_IMPORTANCE_LEVEL - 1) ; + F32 dist_factor = FACE_IMPORTANCE_TO_CAMERA_OVER_DISTANCE[i][1] ; + + for(i = 0; i < FACE_IMPORTANCE_LEVEL && cos_angle_to_view_dir < FACE_IMPORTANCE_TO_CAMERA_OVER_ANGLE[i][0] ; ++i) ; + i = llmin(i, FACE_IMPORTANCE_LEVEL - 1) ; + importance = dist_factor * FACE_IMPORTANCE_TO_CAMERA_OVER_ANGLE[i][1] ; + } + + return importance ; +} + BOOL LLFace::verify(const U32* indices_array) const { BOOL ok = TRUE; diff --git a/indra/newview/llface.h b/indra/newview/llface.h index 2b134c8c31..68eee061b8 100644 --- a/indra/newview/llface.h +++ b/indra/newview/llface.h @@ -188,6 +188,9 @@ public: void setIndicesIndex(S32 idx) { mIndicesIndex = idx; } void setDrawInfo(LLDrawInfo* draw_info); + F32 getTextureVirtualSize() ; + F32 getImportanceToCamera()const {return mImportanceToCamera ;} + //for atlas LLTextureAtlasSlot* getAtlasInfo() ; void setAtlasInUse(BOOL flag); @@ -200,6 +203,12 @@ public: void removeAtlas() ; BOOL switchTexture() ; +private: + F32 adjustPartialOverlapPixelArea(F32 cos_angle_to_view_dir, F32 radius ); + F32 calcPixelArea(F32& cos_angle_to_view_dir, F32& radius) ; +public: + static F32 calcImportanceToCamera(F32 to_view_dir, F32 dist); + public: LLVector3 mCenterLocal; @@ -214,7 +223,7 @@ public: LLMatrix4* mTextureMatrix; LLDrawInfo* mDrawInfo; -protected: +private: friend class LLGeometryManager; friend class LLVolumeGeometryManager; @@ -244,6 +253,13 @@ protected: F32 mVSize; F32 mPixelArea; + //importance factor, in the range [0, 1.0]. + //1.0: the most important. + //based on the distance from the face to the view point and the angle from the face center to the view direction. + F32 mImportanceToCamera ; + F32 mBoundingSphereRadius ; + + //atlas LLPointer<LLTextureAtlasSlot> mAtlasInfop ; BOOL mUsingAtlas ; diff --git a/indra/newview/llfloaterbuyland.cpp b/indra/newview/llfloaterbuyland.cpp index 36f0315790..467796b4a3 100644 --- a/indra/newview/llfloaterbuyland.cpp +++ b/indra/newview/llfloaterbuyland.cpp @@ -685,7 +685,14 @@ void LLFloaterBuyLandUI::finishWebSiteInfo() mSiteLandUseAction = landUse["action"].asString(); LLXMLRPCValue currency = result["currency"]; - mCurrency.setEstimate(currency["estimatedCost"].asInt()); + if (currency["estimatedCost"].isValid()) + { + mCurrency.setUSDEstimate(currency["estimatedCost"].asInt()); + } + if (currency["estimatedLocalCost"].isValid()) + { + mCurrency.setLocalEstimate(currency["estimatedLocalCost"].asString()); + } mSiteConfirm = result["confirm"].asString(); } @@ -733,7 +740,8 @@ void LLFloaterBuyLandUI::runWebSitePrep(const std::string& password) keywordArgs.appendInt("billableArea", mIsForGroup ? 0 : mParcelBillableArea); keywordArgs.appendInt("currencyBuy", mCurrency.getAmount()); - keywordArgs.appendInt("estimatedCost", mCurrency.getEstimate()); + keywordArgs.appendInt("estimatedCost", mCurrency.getUSDEstimate()); + keywordArgs.appendString("estimatedLocalCost", mCurrency.getLocalEstimate()); keywordArgs.appendString("confirm", mSiteConfirm); if (!password.empty()) { @@ -1217,7 +1225,7 @@ void LLFloaterBuyLandUI::refreshUI() childSetText("currency_reason", getString("not_enough_lindens", string_args)); - childSetTextArg("currency_est", "[AMOUNT2]", llformat("%#.2f", mCurrency.getEstimate() / 100.0)); + childSetTextArg("currency_est", "[LOCAL_AMOUNT]", mCurrency.getLocalEstimate()); } if (willHaveEnough) @@ -1297,7 +1305,7 @@ void LLFloaterBuyLandUI::startBuyPreConfirm() { LLStringUtil::format_map_t string_args; string_args["[AMOUNT]"] = llformat("%d", mCurrency.getAmount()); - string_args["[AMOUNT2]"] = llformat("%#.2f", mCurrency.getEstimate() / 100.0); + string_args["[LOCAL_AMOUNT]"] = mCurrency.getLocalEstimate(); action += getString("buy_for_US", string_args); } diff --git a/indra/newview/llfloaterinspect.cpp b/indra/newview/llfloaterinspect.cpp index e26937e93f..13ca7638c5 100644 --- a/indra/newview/llfloaterinspect.cpp +++ b/indra/newview/llfloaterinspect.cpp @@ -88,32 +88,7 @@ LLFloaterInspect::~LLFloaterInspect(void) } //sInstance = NULL; } -/* -BOOL LLFloaterInspect::isVisible() -{ - return (!!sInstance); -}*/ -/* -void LLFloaterInspect::show(void* ignored) -{ - // setForceSelection ensures that the pie menu does not deselect things when it - // looses the focus (this can happen with "select own objects only" enabled - // VWR-1471 - BOOL forcesel = LLSelectMgr::getInstance()->setForceSelection(TRUE); - - if (!sInstance) // first use - { - sInstance = new LLFloaterInspect; - } - sInstance->openFloater(); - LLToolMgr::getInstance()->setTransientTool(LLToolCompInspect::getInstance()); - LLSelectMgr::getInstance()->setForceSelection(forcesel); // restore previouis value - - sInstance->mObjectSelection = LLSelectMgr::getInstance()->getSelection(); - sInstance->refresh(); -} -*/ void LLFloaterInspect::onOpen(const LLSD& key) { BOOL forcesel = LLSelectMgr::getInstance()->setForceSelection(TRUE); diff --git a/indra/newview/llfloatermap.cpp b/indra/newview/llfloatermap.cpp index 0c9a759f32..3fe711a166 100644 --- a/indra/newview/llfloatermap.cpp +++ b/indra/newview/llfloatermap.cpp @@ -51,11 +51,26 @@ #include "llviewermenu.h" // +// Constants +// +const F32 MAP_MINOR_DIR_THRESHOLD = 0.08f; + +// // Member functions // LLFloaterMap::LLFloaterMap(const LLSD& key) - : LLFloater(key) + : LLFloater(key), + mPopupMenu(NULL), + mTextBoxEast(NULL), + mTextBoxNorth(NULL), + mTextBoxWest(NULL), + mTextBoxSouth(NULL), + mTextBoxSouthEast(NULL), + mTextBoxNorthEast(NULL), + mTextBoxNorthWest(NULL), + mTextBoxSouthWest(NULL), + mMap(NULL) { //Called from floater reg: LLUICtrlFactory::getInstance()->buildFloater(this, "floater_map.xml", FALSE); } @@ -92,6 +107,8 @@ BOOL LLFloaterMap::postBuild() mPopupMenu->setItemEnabled ("Stop Tracking", false); } + updateMinorDirections(); + // Get the drag handle all the way in back sendChildToBack(getDragHandle()); @@ -139,6 +156,23 @@ void LLFloaterMap::setDirectionPos( LLTextBox* text_box, F32 rotation ) llround(map_half_height - text_half_height + radius * sin( rotation )) ); } +void LLFloaterMap::updateMinorDirections() +{ + if (mTextBoxNorthEast == NULL) + { + return; + } + + // Hide minor directions if they cover too much of the map + bool show_minors = mTextBoxNorthEast->getRect().getHeight() < MAP_MINOR_DIR_THRESHOLD * + llmin(getRect().getWidth(), getRect().getHeight()); + + mTextBoxNorthEast->setVisible(show_minors); + mTextBoxNorthWest->setVisible(show_minors); + mTextBoxSouthWest->setVisible(show_minors); + mTextBoxSouthEast->setVisible(show_minors); +} + // virtual void LLFloaterMap::draw() { @@ -180,17 +214,23 @@ void LLFloaterMap::draw() LLFloater::draw(); } +void LLFloaterMap::reshape(S32 width, S32 height, BOOL called_from_parent) +{ + LLFloater::reshape(width, height, called_from_parent); + updateMinorDirections(); +} + void LLFloaterMap::handleZoom(const LLSD& userdata) { std::string level = userdata.asString(); F32 scale = 0.0f; if (level == std::string("close")) - scale = MAP_SCALE_MAX; + scale = LLNetMap::MAP_SCALE_MAX; else if (level == std::string("medium")) - scale = MAP_SCALE_MID; + scale = LLNetMap::MAP_SCALE_MID; else if (level == std::string("far")) - scale = MAP_SCALE_MIN; + scale = LLNetMap::MAP_SCALE_MIN; if (scale != 0.0f) { gSavedSettings.setF32("MiniMapScale", scale ); diff --git a/indra/newview/llfloatermap.h b/indra/newview/llfloatermap.h index 501777ed07..6c9138c6a7 100644 --- a/indra/newview/llfloatermap.h +++ b/indra/newview/llfloatermap.h @@ -51,12 +51,14 @@ public: /*virtual*/ BOOL postBuild(); /*virtual*/ BOOL handleDoubleClick( S32 x, S32 y, MASK mask ); /*virtual*/ BOOL handleRightMouseDown( S32 x, S32 y, MASK mask ); + /*virtual*/ void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE); /*virtual*/ void draw(); private: void handleZoom(const LLSD& userdata); void handleStopTracking (const LLSD& userdata); void setDirectionPos( LLTextBox* text_box, F32 rotation ); + void updateMinorDirections(); LLMenuGL* mPopupMenu; diff --git a/indra/newview/llfloaterreporter.cpp b/indra/newview/llfloaterreporter.cpp index 0f4d6e33a3..1002697fe5 100644 --- a/indra/newview/llfloaterreporter.cpp +++ b/indra/newview/llfloaterreporter.cpp @@ -790,7 +790,7 @@ void LLFloaterReporter::takeScreenshot() // store in the image list so it doesn't try to fetch from the server LLPointer<LLViewerFetchedTexture> image_in_list = LLViewerTextureManager::getFetchedTexture(mResourceDatap->mAssetInfo.mUuid, TRUE, LLViewerTexture::BOOST_NONE, LLViewerTexture::FETCHED_TEXTURE); - image_in_list->createGLTexture(0, raw); + image_in_list->createGLTexture(0, raw, 0, TRUE, LLViewerTexture::OTHER); // the texture picker then uses that texture LLTexturePicker* texture = getChild<LLTextureCtrl>("screenshot"); diff --git a/indra/newview/llfloateruipreview.cpp b/indra/newview/llfloateruipreview.cpp index 2fe21f28de..663bd232f7 100644 --- a/indra/newview/llfloateruipreview.cpp +++ b/indra/newview/llfloateruipreview.cpp @@ -670,6 +670,15 @@ void LLFloaterUIPreview::refreshList() } } + found = TRUE; + while(found) // for every sidepanel file that matches the pattern + { + if((found = gDirUtilp->getNextFileInDir(getLocalizedDirectory(), "sidepanel_*.xml", name, FALSE))) // get next file matching pattern + { + addFloaterEntry(name.c_str()); // and add it to the list (file name only; localization code takes care of rest of path) + } + } + if(!mFileList->isEmpty()) // if there were any matching files, just select the first one (so we don't have to worry about disabling buttons when no entry is selected) { mFileList->selectFirstItem(); diff --git a/indra/newview/llfloatervoicedevicesettings.cpp b/indra/newview/llfloatervoicedevicesettings.cpp index bbeb287171..43024a4bd0 100644 --- a/indra/newview/llfloatervoicedevicesettings.cpp +++ b/indra/newview/llfloatervoicedevicesettings.cpp @@ -95,6 +95,9 @@ void LLPanelVoiceDeviceSettings::handleVisibilityChange ( BOOL new_visibility ) else { cleanup(); + // when closing this window, turn of visiblity control so that + // next time preferences is opened we don't suspend voice + gSavedSettings.setBOOL("ShowDeviceSettings", FALSE); } } void LLPanelVoiceDeviceSettings::draw() diff --git a/indra/newview/llfloaterworldmap.cpp b/indra/newview/llfloaterworldmap.cpp index 823c8e7911..7d2eb98111 100644 --- a/indra/newview/llfloaterworldmap.cpp +++ b/indra/newview/llfloaterworldmap.cpp @@ -56,7 +56,6 @@ #include "llregionhandle.h" #include "llscrolllistctrl.h" #include "llslurl.h" -#include "lltabcontainer.h" #include "lltextbox.h" #include "lltracker.h" #include "lltrans.h" @@ -64,7 +63,9 @@ #include "llviewermenu.h" #include "llviewerregion.h" #include "llviewerstats.h" +#include "llviewertexture.h" #include "llworldmap.h" +#include "llworldmapmessage.h" #include "llworldmapview.h" #include "lluictrlfactory.h" #include "llappviewer.h" @@ -80,6 +81,12 @@ //--------------------------------------------------------------------------- static const F32 MAP_ZOOM_TIME = 0.2f; +// Merov: we switched from using the "world size" (which varies depending where the user went) to a fixed +// width of 512 regions max visible at a time. This makes the zoom slider works in a consistent way across +// sessions and doesn't prevent the user to pan the world if it was to grow a lot beyond that limit. +// Currently (01/26/09), this value allows the whole grid to be visible in a 1024x1024 window. +static const S32 MAX_VISIBLE_REGIONS = 512; + enum EPanDirection { PAN_UP, @@ -190,11 +197,11 @@ LLFloaterWorldMap::LLFloaterWorldMap(const LLSD& key) gFloaterWorldMap = this; mFactoryMap["objects_mapview"] = LLCallbackMap(createWorldMapView, NULL); - mFactoryMap["terrain_mapview"] = LLCallbackMap(createWorldMapView, NULL); //Called from floater reg: LLUICtrlFactory::getInstance()->buildFloater(this, "floater_world_map.xml", FALSE); mCommitCallbackRegistrar.add("WMap.Location", boost::bind(&LLFloaterWorldMap::onLocationCommit, this)); mCommitCallbackRegistrar.add("WMap.AvatarCombo", boost::bind(&LLFloaterWorldMap::onAvatarComboCommit, this)); + mCommitCallbackRegistrar.add("WMap.Landmark", boost::bind(&LLFloaterWorldMap::onLandmarkComboCommit, this)); mCommitCallbackRegistrar.add("WMap.SearchResult", boost::bind(&LLFloaterWorldMap::onCommitSearchResult, this)); mCommitCallbackRegistrar.add("WMap.CommitLocation", boost::bind(&LLFloaterWorldMap::onCommitLocation, this)); mCommitCallbackRegistrar.add("WMap.GoHome", boost::bind(&LLFloaterWorldMap::onGoHome, this)); @@ -213,17 +220,7 @@ void* LLFloaterWorldMap::createWorldMapView(void* data) BOOL LLFloaterWorldMap::postBuild() { - mTabs = getChild<LLTabContainer>("maptab"); - if (!mTabs) return FALSE; - - mTabs->setCommitCallback(boost::bind(&LLFloaterWorldMap::onCommitBackground, this)); - - // The following callback syncs the worlmap tabs with the images. - // Commented out since it was crashing when LLWorldMap became a singleton. - // We should be fine without it but override the onOpen method and put it - // there if it turns out to be needed. -MG - // - //onCommitBackground(); + mPanel = getChild<LLPanel>("objects_mapview"); LLComboBox *avatar_combo = getChild<LLComboBox>("friend combo"); if (avatar_combo) @@ -251,8 +248,8 @@ BOOL LLFloaterWorldMap::postBuild() landmark_combo->setTextEntryCallback( boost::bind(&LLFloaterWorldMap::onComboTextEntry, this) ); } - mCurZoomVal = log(gMapScale)/log(2.f); - childSetValue("zoom slider", gMapScale); + mCurZoomVal = log(LLWorldMapView::sMapScale)/log(2.f); + childSetValue("zoom slider", LLWorldMapView::sMapScale); setDefaultBtn(NULL); @@ -265,7 +262,7 @@ BOOL LLFloaterWorldMap::postBuild() LLFloaterWorldMap::~LLFloaterWorldMap() { // All cleaned up by LLView destructor - mTabs = NULL; + mPanel = NULL; // Inventory deletes all observers on shutdown mInventory = NULL; @@ -298,7 +295,7 @@ void LLFloaterWorldMap::onOpen(const LLSD& key) mIsClosing = FALSE; LLWorldMapView* map_panel; - map_panel = (LLWorldMapView*)mTabs->getCurrentPanel(); + map_panel = (LLWorldMapView*)gFloaterWorldMap->mPanel; map_panel->clearLastClick(); { @@ -309,15 +306,8 @@ void LLFloaterWorldMap::onOpen(const LLSD& key) } map_panel->updateVisibleBlocks(); - // Reload the agent positions when we show the window - LLWorldMap::getInstance()->eraseItems(); - - // Reload any maps that may have changed - LLWorldMap::getInstance()->clearSimFlags(); - - const S32 panel_num = mTabs->getCurrentPanelIndex(); - const bool request_from_sim = true; - LLWorldMap::getInstance()->setCurrentLayer(panel_num, request_from_sim); + // Reload items as they may have changed + LLWorldMap::getInstance()->reloadItems(); // We may already have a bounding box for the regions of the world, // so use that to adjust the view. @@ -351,12 +341,9 @@ void LLFloaterWorldMap::onOpen(const LLSD& key) // static void LLFloaterWorldMap::reloadIcons(void*) { - LLWorldMap::getInstance()->eraseItems(); - - LLWorldMap::getInstance()->sendMapLayerRequest(); + LLWorldMap::getInstance()->reloadItems(); } - // virtual BOOL LLFloaterWorldMap::handleHover(S32 x, S32 y, MASK mask) { @@ -388,12 +375,6 @@ BOOL LLFloaterWorldMap::handleScrollWheel(S32 x, S32 y, S32 clicks) void LLFloaterWorldMap::reshape( S32 width, S32 height, BOOL called_from_parent ) { LLFloater::reshape( width, height, called_from_parent ); - - // Might have changed size of world display area - // JC: Technically, this is correct, but it makes the slider "pop" - // if you resize the window, then draw the slider. Just leaving it - // the way it was when you opened the window seems better. - // adjustZoomSliderBounds(); } @@ -457,7 +438,7 @@ void LLFloaterWorldMap::draw() { F64 seconds = LLTimer::getElapsedSeconds(); double value = fmod(seconds, 2); - value = 0.5 + 0.5*cos(value * M_PI); + value = 0.5 + 0.5*cos(value * F_PI); LLColor4 loading_color(0.0, F32(value/2), F32(value), 1.0); childSetColor("location_icon", loading_color); } @@ -475,7 +456,7 @@ void LLFloaterWorldMap::draw() childSetEnabled("Teleport", (BOOL)tracking_status); // childSetEnabled("Clear", (BOOL)tracking_status); - childSetEnabled("Show Destination", (BOOL)tracking_status || LLWorldMap::getInstance()->mIsTrackingUnknownLocation); + childSetEnabled("Show Destination", (BOOL)tracking_status || LLWorldMap::getInstance()->isTracking()); childSetEnabled("copy_slurl", (mSLURL.size() > 0) ); setMouseOpaque(TRUE); @@ -495,6 +476,18 @@ void LLFloaterWorldMap::draw() mCurZoomVal = lerp(mCurZoomVal, (F32)childGetValue("zoom slider").asReal(), interp); F32 map_scale = 256.f*pow(2.f, mCurZoomVal); LLWorldMapView::setScale( map_scale ); + + // Enable/disable checkboxes depending on the zoom level + // If above threshold level (i.e. low res) -> Disable all checkboxes + // If under threshold level (i.e. high res) -> Enable all checkboxes + bool enable = LLWorldMapView::showRegionInfo(); + childSetEnabled("people_chk", enable); + childSetEnabled("infohub_chk", enable); + childSetEnabled("telehub_chk", enable); + childSetEnabled("land_for_sale_chk", enable); + childSetEnabled("event_chk", enable); + childSetEnabled("event_mature_chk", enable); + childSetEnabled("event_adult_chk", enable); LLFloater::draw(); } @@ -583,14 +576,14 @@ void LLFloaterWorldMap::trackLandmark( const LLUUID& landmark_item_id ) void LLFloaterWorldMap::trackEvent(const LLItemInfo &event_info) { mTrackedStatus = LLTracker::TRACKING_LOCATION; - LLTracker::trackLocation(event_info.mPosGlobal, event_info.mName, event_info.mToolTip, LLTracker::LOCATION_EVENT); + LLTracker::trackLocation(event_info.getGlobalPosition(), event_info.getName(), event_info.getToolTip(), LLTracker::LOCATION_EVENT); setDefaultBtn("Teleport"); } void LLFloaterWorldMap::trackGenericItem(const LLItemInfo &item) { mTrackedStatus = LLTracker::TRACKING_LOCATION; - LLTracker::trackLocation(item.mPosGlobal, item.mName, item.mToolTip, LLTracker::LOCATION_ITEM); + LLTracker::trackLocation(item.getGlobalPosition(), item.getName(), item.getToolTip(), LLTracker::LOCATION_ITEM); setDefaultBtn("Teleport"); } @@ -599,29 +592,27 @@ void LLFloaterWorldMap::trackLocation(const LLVector3d& pos_global) LLSimInfo* sim_info = LLWorldMap::getInstance()->simInfoFromPosGlobal(pos_global); if (!sim_info) { - LLWorldMap::getInstance()->mIsTrackingUnknownLocation = TRUE; - LLWorldMap::getInstance()->mInvalidLocation = FALSE; - LLWorldMap::getInstance()->mUnknownLocation = pos_global; + // We haven't found a region for that point yet, leave the tracking to the world map + LLWorldMap::getInstance()->setTracking(pos_global); LLTracker::stopTracking(NULL); S32 world_x = S32(pos_global.mdV[0] / 256); S32 world_y = S32(pos_global.mdV[1] / 256); - LLWorldMap::getInstance()->sendMapBlockRequest(world_x, world_y, world_x, world_y, true); + LLWorldMapMessage::getInstance()->sendMapBlockRequest(world_x, world_y, world_x, world_y, true); setDefaultBtn(""); return; } - if (sim_info->mAccess == SIM_ACCESS_DOWN) + if (sim_info->isDown()) { - // Down sim. Show the blue circle of death! - LLWorldMap::getInstance()->mIsTrackingUnknownLocation = TRUE; - LLWorldMap::getInstance()->mUnknownLocation = pos_global; - LLWorldMap::getInstance()->mInvalidLocation = TRUE; + // Down region. Show the blue circle of death! + // i.e. let the world map that this and tell it it's invalid + LLWorldMap::getInstance()->setTracking(pos_global); + LLWorldMap::getInstance()->setTrackingInvalid(); LLTracker::stopTracking(NULL); setDefaultBtn(""); return; } - std::string sim_name; - LLWorldMap::getInstance()->simNameFromPosGlobal( pos_global, sim_name ); + std::string sim_name = sim_info->getName(); F32 region_x = (F32)fmod( pos_global.mdV[VX], (F64)REGION_WIDTH_METERS ); F32 region_y = (F32)fmod( pos_global.mdV[VY], (F64)REGION_WIDTH_METERS ); std::string full_name = llformat("%s (%d, %d, %d)", @@ -633,9 +624,7 @@ void LLFloaterWorldMap::trackLocation(const LLVector3d& pos_global) std::string tooltip(""); mTrackedStatus = LLTracker::TRACKING_LOCATION; LLTracker::trackLocation(pos_global, full_name, tooltip); - LLWorldMap::getInstance()->mIsTrackingUnknownLocation = FALSE; - LLWorldMap::getInstance()->mIsTrackingDoubleClick = FALSE; - LLWorldMap::getInstance()->mIsTrackingCommit = FALSE; + LLWorldMap::getInstance()->cancelTracking(); // The floater is taking over the tracking setDefaultBtn("Teleport"); } @@ -748,9 +737,9 @@ void LLFloaterWorldMap::trackURL(const std::string& region_name, S32 x_coord, S3 // pass sim name to combo box gFloaterWorldMap->mCompletingRegionName = region_name; - LLWorldMap::getInstance()->sendNamedRegionRequest(region_name); + LLWorldMapMessage::getInstance()->sendNamedRegionRequest(region_name); LLStringUtil::toLower(gFloaterWorldMap->mCompletingRegionName); - LLWorldMap::getInstance()->mIsTrackingCommit = TRUE; + LLWorldMap::getInstance()->setTrackingCommit(); } } @@ -817,19 +806,12 @@ void LLFloaterWorldMap::buildAvatarIDList() // Delete all but the "None" entry S32 list_size = list->getItemCount(); - while (list_size > 1) + if (list_size > 1) { - list->selectNthItem(1); + list->selectItemRange(1, -1); list->operateOnSelection(LLCtrlListInterface::OP_DELETE); - --list_size; } - LLSD default_column; - default_column["name"] = "friend name"; - default_column["label"] = "Friend Name"; - default_column["width"] = 500; - list->addColumn(default_column); - // Get all of the calling cards for avatar that are currently online LLCollectMappableBuddies collector; LLAvatarTracker::instance().applyFunctor(collector); @@ -850,10 +832,7 @@ void LLFloaterWorldMap::buildAvatarIDList() void LLFloaterWorldMap::buildLandmarkIDLists() { LLCtrlListInterface *list = childGetListInterface("landmark combo"); - if (!list) - { - return; - } + if (!list) return; // Delete all but the "None" entry S32 list_size = list->getItemCount(); @@ -894,7 +873,6 @@ void LLFloaterWorldMap::buildLandmarkIDLists() mLandmarkAssetIDList.put( item->getAssetUUID() ); mLandmarkItemIDList.put( item->getUUID() ); } - list->sortByColumn(std::string("landmark name"), TRUE); list->selectFirstItem(); } @@ -931,7 +909,7 @@ void LLFloaterWorldMap::clearLocationSelection(BOOL clear_ui) { childSetValue("spin z", 0); } - LLWorldMap::getInstance()->mIsTrackingCommit = FALSE; + LLWorldMap::getInstance()->cancelTracking(); mCompletingRegionName = ""; } @@ -967,18 +945,16 @@ void LLFloaterWorldMap::clearAvatarSelection(BOOL clear_ui) // can see the whole world, plus a little. void LLFloaterWorldMap::adjustZoomSliderBounds() { - // World size in regions - S32 world_width_regions = LLWorldMap::getInstance()->getWorldWidth() / REGION_WIDTH_UNITS; - S32 world_height_regions = LLWorldMap::getInstance()->getWorldHeight() / REGION_WIDTH_UNITS; - - // Pad the world size a little bit, so we have a nice border on - // the edge - world_width_regions++; - world_height_regions++; + // Merov: we switched from using the "world size" (which varies depending where the user went) to a fixed + // width of 512 regions max visible at a time. This makes the zoom slider works in a consistent way across + // sessions and doesn't prevent the user to pan the world if it was to grow a lot beyond that limit. + // Currently (01/26/09), this value allows the whole grid to be visible in a 1024x1024 window. + S32 world_width_regions = MAX_VISIBLE_REGIONS; + S32 world_height_regions = MAX_VISIBLE_REGIONS; // Find how much space we have to display the world LLWorldMapView* map_panel; - map_panel = (LLWorldMapView*)mTabs->getCurrentPanel(); + map_panel = (LLWorldMapView*)mPanel; LLRect view_rect = map_panel->getRect(); // View size in pixels @@ -1191,15 +1167,15 @@ void LLFloaterWorldMap::onLocationCommit() LLStringUtil::toLower(str); mCompletingRegionName = str; - LLWorldMap::getInstance()->mIsTrackingCommit = TRUE; + LLWorldMap::getInstance()->setTrackingCommit(); if (str.length() >= 3) { - LLWorldMap::getInstance()->sendNamedRegionRequest(str); + LLWorldMapMessage::getInstance()->sendNamedRegionRequest(str); } else { str += "#"; - LLWorldMap::getInstance()->sendNamedRegionRequest(str); + LLWorldMapMessage::getInstance()->sendNamedRegionRequest(str); } } @@ -1207,8 +1183,8 @@ void LLFloaterWorldMap::onClearBtn() { mTrackedStatus = LLTracker::TRACKING_NOTHING; LLTracker::stopTracking((void *)(intptr_t)TRUE); - LLWorldMap::getInstance()->mIsTrackingUnknownLocation = FALSE; - mSLURL = ""; // Clear the SLURL since it's invalid + LLWorldMap::getInstance()->cancelTracking(); + mSLURL = ""; // Clear the SLURL since it's invalid mSetToUserPosition = TRUE; // Revert back to the current user position } @@ -1260,9 +1236,9 @@ void LLFloaterWorldMap::centerOnTarget(BOOL animate) pos_global = LLTracker::getTrackedPositionGlobal() - gAgent.getCameraPositionGlobal(); } } - else if(LLWorldMap::getInstance()->mIsTrackingUnknownLocation) + else if(LLWorldMap::getInstance()->isTracking()) { - pos_global = LLWorldMap::getInstance()->mUnknownLocation - gAgent.getCameraPositionGlobal();; + pos_global = LLWorldMap::getInstance()->getTrackedPositionGlobal() - gAgent.getCameraPositionGlobal();; } else { @@ -1270,8 +1246,8 @@ void LLFloaterWorldMap::centerOnTarget(BOOL animate) pos_global.clearVec(); } - LLWorldMapView::setPan( -llfloor((F32)(pos_global.mdV[VX] * (F64)LLWorldMapView::sPixelsPerMeter)), - -llfloor((F32)(pos_global.mdV[VY] * (F64)LLWorldMapView::sPixelsPerMeter)), + LLWorldMapView::setPan( -llfloor((F32)(pos_global.mdV[VX] * (F64)LLWorldMapView::sMapScale / REGION_WIDTH_METERS)), + -llfloor((F32)(pos_global.mdV[VY] * (F64)LLWorldMapView::sMapScale / REGION_WIDTH_METERS)), !animate); mWaitingForTracker = FALSE; } @@ -1429,13 +1405,6 @@ void LLFloaterWorldMap::flyToAvatar() } } -void LLFloaterWorldMap::onCommitBackground() -{ - // Find my index - S32 index = mTabs->getCurrentPanelIndex(); - LLWorldMap::getInstance()->setCurrentLayer(index); -} - void LLFloaterWorldMap::updateSims(bool found_null_sim) { if (mCompletingRegionName == "") @@ -1452,24 +1421,23 @@ void LLFloaterWorldMap::updateSims(bool found_null_sim) S32 num_results = 0; std::map<U64, LLSimInfo*>::const_iterator it; - for (it = LLWorldMap::getInstance()->mSimInfoMap.begin(); it != LLWorldMap::getInstance()->mSimInfoMap.end(); ++it) + for (it = LLWorldMap::getInstance()->getRegionMap().begin(); it != LLWorldMap::getInstance()->getRegionMap().end(); ++it) { - LLSimInfo* info = (*it).second; - std::string sim_name = info->mName; - std::string sim_name_lower = sim_name; + LLSimInfo* info = it->second; + std::string sim_name_lower = info->getName(); LLStringUtil::toLower(sim_name_lower); if (sim_name_lower.substr(0, name_length) == mCompletingRegionName) { if (sim_name_lower == mCompletingRegionName) { - match = sim_name; + match = info->getName(); } LLSD value; - value["id"] = sim_name; + value["id"] = info->getName(); value["columns"][0]["column"] = "sim_name"; - value["columns"][0]["value"] = sim_name; + value["columns"][0]["value"] = info->getName(); list->addElement(value); num_results++; } @@ -1526,15 +1494,13 @@ void LLFloaterWorldMap::onCommitSearchResult() LLStringUtil::toLower(sim_name); std::map<U64, LLSimInfo*>::const_iterator it; - for (it = LLWorldMap::getInstance()->mSimInfoMap.begin(); it != LLWorldMap::getInstance()->mSimInfoMap.end(); ++it) + for (it = LLWorldMap::getInstance()->getRegionMap().begin(); it != LLWorldMap::getInstance()->getRegionMap().end(); ++it) { - LLSimInfo* info = (*it).second; - std::string info_sim_name = info->mName; - LLStringUtil::toLower(info_sim_name); + LLSimInfo* info = it->second; - if (sim_name == info_sim_name) + if (info->isName(sim_name)) { - LLVector3d pos_global = from_region_handle( info->mHandle ); + LLVector3d pos_global = info->getGlobalOrigin(); F64 local_x = childGetValue("spin x"); F64 local_y = childGetValue("spin y"); F64 local_z = childGetValue("spin z"); diff --git a/indra/newview/llfloaterworldmap.h b/indra/newview/llfloaterworldmap.h index 20a8e6d321..7feebb583d 100644 --- a/indra/newview/llfloaterworldmap.h +++ b/indra/newview/llfloaterworldmap.h @@ -96,7 +96,7 @@ public: static const LLUUID& getHomeID() { return sHomeID; } // A z_attenuation of 0.0f collapses the distance into the X-Y plane - F32 getDistanceToDestination(const LLVector3d& pos_global, F32 z_attenuation = 0.5f) const; + F32 getDistanceToDestination(const LLVector3d& pos_global, F32 z_attenuation = 0.5f) const; void clearLocationSelection(BOOL clear_ui = FALSE); void clearAvatarSelection(BOOL clear_ui = FALSE); @@ -121,8 +121,6 @@ protected: void onAvatarComboPrearrange(); void onAvatarComboCommit(); - void onCommitBackground(); - void onComboTextEntry( ); void onSearchTextEntry( LLLineEditor* ctrl ); @@ -155,10 +153,10 @@ protected: void cacheLandmarkPosition(); -protected: - LLTabContainer* mTabs; +private: + LLPanel* mPanel; // Panel displaying the map - // Sets gMapScale, in pixels per region + // Ties to LLWorldMapView::sMapScale, in pixels per region F32 mCurZoomVal; LLFrameTimer mZoomTimer; diff --git a/indra/newview/llinspectavatar.cpp b/indra/newview/llinspectavatar.cpp index 9989a3b473..8dc7833f6a 100644 --- a/indra/newview/llinspectavatar.cpp +++ b/indra/newview/llinspectavatar.cpp @@ -47,6 +47,7 @@ #include "llstartup.h" #include "llviewermenu.h" #include "llvoiceclient.h" +#include "llviewerobjectlist.h" // Linden libraries #include "llfloater.h" @@ -113,6 +114,7 @@ private: void onClickFindOnMap(); bool onVisibleFindOnMap(); bool onVisibleFreezeEject(); + bool onVisibleZoomIn(); void onClickMuteVolume(); void onVolumeChange(const LLSD& data); @@ -204,6 +206,8 @@ LLInspectAvatar::LLInspectAvatar(const LLSD& sd) mVisibleCallbackRegistrar.add("InspectAvatar.VisibleFindOnMap", boost::bind(&LLInspectAvatar::onVisibleFindOnMap, this)); mVisibleCallbackRegistrar.add("InspectAvatar.VisibleFreezeEject", boost::bind(&LLInspectAvatar::onVisibleFreezeEject, this)); + mVisibleCallbackRegistrar.add("InspectAvatar.VisibleZoomIn", + boost::bind(&LLInspectAvatar::onVisibleZoomIn, this)); // can't make the properties request until the widgets are constructed // as it might return immediately, so do it in postBuild. @@ -479,6 +483,11 @@ bool LLInspectAvatar::onVisibleFreezeEject() return enable_freeze_eject( LLSD(mAvatarID) ); } +bool LLInspectAvatar::onVisibleZoomIn() +{ + return gObjectList.findObject(mAvatarID); +} + void LLInspectAvatar::onClickIM() { LLAvatarActions::startIM(mAvatarID); diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp index ef61c45d4d..90e48d22ec 100644 --- a/indra/newview/llinventorybridge.cpp +++ b/indra/newview/llinventorybridge.cpp @@ -1,5161 +1,5204 @@ -/** - * @file llinventorybridge.cpp - * @brief Implementation of the Inventory-Folder-View-Bridge classes. - * - * $LicenseInfo:firstyear=2001&license=viewergpl$ - * - * Copyright (c) 2001-2009, Linden Research, Inc. - * - * Second Life Viewer Source Code - * The source code in this file ("Source Code") is provided by Linden Lab - * to you under the terms of the GNU General Public License, version 2.0 - * ("GPL"), unless you have obtained a separate licensing agreement - * ("Other License"), formally executed by you and Linden Lab. Terms of - * the GPL can be found in doc/GPL-license.txt in this distribution, or - * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 - * - * There are special exceptions to the terms and conditions of the GPL as - * it is applied to this Source Code. View the full text of the exception - * in the file doc/FLOSS-exception.txt in this software distribution, or - * online at - * http://secondlifegrid.net/programs/open_source/licensing/flossexception - * - * By copying, modifying or distributing this software, you acknowledge - * that you have read and understood your obligations described above, - * and agree to abide by those obligations. - * - * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO - * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, - * COMPLETENESS OR PERFORMANCE. - * $/LicenseInfo$ - */ - -#include "llviewerprecompiledheaders.h" - -#include <utility> // for std::pair<> - -#include "llfloaterinventory.h" -#include "llinventorybridge.h" - -#include "message.h" - -#include "llagent.h" -#include "llagentwearables.h" -#include "llcallingcard.h" -#include "llcheckboxctrl.h" // for radio buttons -#include "llfloaterreg.h" -#include "llradiogroup.h" -#include "llspinctrl.h" -#include "lltextbox.h" -#include "llui.h" - -#include "llviewercontrol.h" -#include "llfirstuse.h" -#include "llfoldertype.h" -#include "llfloaterchat.h" -#include "llfloatercustomize.h" -#include "llfloaterproperties.h" -#include "llfloaterworldmap.h" -#include "llfocusmgr.h" -#include "llfolderview.h" -#include "llfriendcard.h" -#include "llavataractions.h" -#include "llgesturemgr.h" -#include "lliconctrl.h" -#include "llinventoryfunctions.h" -#include "llinventorymodel.h" -#include "llinventorypanel.h" -#include "llinventoryclipboard.h" -#include "lllineeditor.h" -#include "llmenugl.h" -#include "llpreviewanim.h" -#include "llpreviewgesture.h" -#include "llpreviewnotecard.h" -#include "llpreviewscript.h" -#include "llpreviewsound.h" -#include "llpreviewtexture.h" -#include "llresmgr.h" -#include "llscrollcontainer.h" -#include "llimview.h" -#include "lltooldraganddrop.h" -#include "llviewerfoldertype.h" -#include "llviewertexturelist.h" -#include "llviewerinventory.h" -#include "llviewerobjectlist.h" -#include "llviewerwindow.h" -#include "llvoavatar.h" -#include "llwearable.h" -#include "llwearablelist.h" -#include "llviewerassettype.h" -#include "llviewermessage.h" -#include "llviewerregion.h" -#include "llvoavatarself.h" -#include "lltabcontainer.h" -#include "lluictrlfactory.h" -#include "llselectmgr.h" -#include "llsidetray.h" -#include "llfloateropenobject.h" -#include "lltrans.h" -#include "llappearancemgr.h" -#include "llimfloater.h" - -using namespace LLOldEvents; - -// Helpers -// bug in busy count inc/dec right now, logic is complex... do we really need it? -void inc_busy_count() -{ -// gViewerWindow->getWindow()->incBusyCount(); -// check balance of these calls if this code is changed to ever actually -// *do* something! -} -void dec_busy_count() -{ -// gViewerWindow->getWindow()->decBusyCount(); -// check balance of these calls if this code is changed to ever actually -// *do* something! -} - -// Function declarations -void wear_add_inventory_item_on_avatar(LLInventoryItem* item); -void remove_inventory_category_from_avatar(LLInventoryCategory* category); -void remove_inventory_category_from_avatar_step2( BOOL proceed, LLUUID category_id); -bool move_task_inventory_callback(const LLSD& notification, const LLSD& response, LLMoveInv*); -bool confirm_replace_attachment_rez(const LLSD& notification, const LLSD& response); - -std::string ICON_NAME[ICON_NAME_COUNT] = -{ - "Inv_Texture", - "Inv_Sound", - "Inv_CallingCard", - "Inv_CallingCard", - "Inv_Landmark", - "Inv_Landmark", - "Inv_Script", - "Inv_Clothing", - "Inv_Object", - "Inv_Object", - "Inv_Notecard", - "Inv_Skin", - "Inv_Snapshot", - - "Inv_BodyShape", - "Inv_Skin", - "Inv_Hair", - "Inv_Eye", - "Inv_Shirt", - "Inv_Pants", - "Inv_Shoe", - "Inv_Socks", - "Inv_Jacket", - "Inv_Gloves", - "Inv_Undershirt", - "Inv_Underpants", - "Inv_Skirt", - "Inv_Alpha", - "Inv_Tattoo", - - "Inv_Animation", - "Inv_Gesture", - - "inv_item_linkitem.tga", - "inv_item_linkfolder.tga" -}; - - -// +=================================================+ -// | LLInventoryPanelObserver | -// +=================================================+ -void LLInventoryPanelObserver::changed(U32 mask) -{ - mIP->modelChanged(mask); -} - - -// +=================================================+ -// | LLInvFVBridge | -// +=================================================+ - -LLInvFVBridge::LLInvFVBridge(LLInventoryPanel* inventory, const LLUUID& uuid) : -mUUID(uuid), mInvType(LLInventoryType::IT_NONE) -{ - mInventoryPanel = inventory->getHandle(); -} - -const std::string& LLInvFVBridge::getName() const -{ - LLInventoryObject* obj = getInventoryObject(); - if(obj) - { - return obj->getName(); - } - return LLStringUtil::null; -} - -const std::string& LLInvFVBridge::getDisplayName() const -{ - return getName(); -} - -// Folders have full perms -PermissionMask LLInvFVBridge::getPermissionMask() const -{ - - return PERM_ALL; -} - -// virtual -LLFolderType::EType LLInvFVBridge::getPreferredType() const -{ - return LLFolderType::FT_NONE; -} - - -// Folders don't have creation dates. -time_t LLInvFVBridge::getCreationDate() const -{ - return 0; -} - -// Can be destoryed (or moved to trash) -BOOL LLInvFVBridge::isItemRemovable() -{ - LLInventoryModel* model = getInventoryModel(); - if(!model) return FALSE; - if(model->isObjectDescendentOf(mUUID, gInventory.getRootFolderID())) - { - return TRUE; - } - return FALSE; -} - -// Sends an update to all link items that point to the base item. -void LLInvFVBridge::renameLinkedItems(const LLUUID &item_id, const std::string& new_name) -{ - LLInventoryModel* model = getInventoryModel(); - if(!model) return; - - LLInventoryItem* itemp = model->getItem(mUUID); - if (!itemp) return; - - if (itemp->getIsLinkType()) - { - return; - } - - LLInventoryModel::item_array_t item_array = model->collectLinkedItems(item_id); - for (LLInventoryModel::item_array_t::iterator iter = item_array.begin(); - iter != item_array.end(); - iter++) - { - LLViewerInventoryItem *linked_item = (*iter); - if (linked_item->getUUID() == item_id) continue; - - LLPointer<LLViewerInventoryItem> new_item = new LLViewerInventoryItem(linked_item); - new_item->rename(new_name); - new_item->updateServer(FALSE); - model->updateItem(new_item); - // model->addChangedMask(LLInventoryObserver::LABEL, linked_item->getUUID()); - } - model->notifyObservers(); -} - -// Can be moved to another folder -BOOL LLInvFVBridge::isItemMovable() const -{ - return TRUE; -} - -/*virtual*/ -/** - * @brief Adds this item into clipboard storage - */ -void LLInvFVBridge::cutToClipboard() -{ - if(isItemMovable()) - { - LLInventoryClipboard::instance().cut(mUUID); - } -} -// *TODO: make sure this does the right thing -void LLInvFVBridge::showProperties() -{ - LLSD key; - key["id"] = mUUID; - LLSideTray::getInstance()->showPanel("sidepanel_inventory", key); - - // LLFloaterReg::showInstance("properties", mUUID); -} - -void LLInvFVBridge::removeBatch(LLDynamicArray<LLFolderViewEventListener*>& batch) -{ - // Deactivate gestures when moving them into Trash - LLInvFVBridge* bridge; - LLInventoryModel* model = getInventoryModel(); - LLViewerInventoryItem* item = NULL; - LLViewerInventoryCategory* cat = NULL; - LLInventoryModel::cat_array_t descendent_categories; - LLInventoryModel::item_array_t descendent_items; - S32 count = batch.count(); - S32 i,j; - for(i = 0; i < count; ++i) - { - bridge = (LLInvFVBridge*)(batch.get(i)); - if(!bridge || !bridge->isItemRemovable()) continue; - item = (LLViewerInventoryItem*)model->getItem(bridge->getUUID()); - if (item) - { - if(LLAssetType::AT_GESTURE == item->getType()) - { - LLGestureManager::instance().deactivateGesture(item->getUUID()); - } - } - } - for(i = 0; i < count; ++i) - { - bridge = (LLInvFVBridge*)(batch.get(i)); - if(!bridge || !bridge->isItemRemovable()) continue; - cat = (LLViewerInventoryCategory*)model->getCategory(bridge->getUUID()); - if (cat) - { - gInventory.collectDescendents( cat->getUUID(), descendent_categories, descendent_items, FALSE ); - for (j=0; j<descendent_items.count(); j++) - { - if(LLAssetType::AT_GESTURE == descendent_items[j]->getType()) - { - LLGestureManager::instance().deactivateGesture(descendent_items[j]->getUUID()); - } - } - } - } - removeBatchNoCheck(batch); -} - -void LLInvFVBridge::removeBatchNoCheck(LLDynamicArray<LLFolderViewEventListener*>& batch) -{ - // this method moves a bunch of items and folders to the trash. As - // per design guidelines for the inventory model, the message is - // built and the accounting is performed first. After all of that, - // we call LLInventoryModel::moveObject() to move everything - // around. - LLInvFVBridge* bridge; - LLInventoryModel* model = getInventoryModel(); - if(!model) return; - LLMessageSystem* msg = gMessageSystem; - const LLUUID trash_id = model->findCategoryUUIDForType(LLFolderType::FT_TRASH); - LLViewerInventoryItem* item = NULL; - LLViewerInventoryCategory* cat = NULL; - std::vector<LLUUID> move_ids; - LLInventoryModel::update_map_t update; - bool start_new_message = true; - S32 count = batch.count(); - S32 i; - for(i = 0; i < count; ++i) - { - bridge = (LLInvFVBridge*)(batch.get(i)); - if(!bridge || !bridge->isItemRemovable()) continue; - item = (LLViewerInventoryItem*)model->getItem(bridge->getUUID()); - if(item) - { - if(item->getParentUUID() == trash_id) continue; - move_ids.push_back(item->getUUID()); - LLPreview::hide(item->getUUID()); - --update[item->getParentUUID()]; - ++update[trash_id]; - if(start_new_message) - { - start_new_message = false; - msg->newMessageFast(_PREHASH_MoveInventoryItem); - msg->nextBlockFast(_PREHASH_AgentData); - msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); - msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); - msg->addBOOLFast(_PREHASH_Stamp, TRUE); - } - msg->nextBlockFast(_PREHASH_InventoryData); - msg->addUUIDFast(_PREHASH_ItemID, item->getUUID()); - msg->addUUIDFast(_PREHASH_FolderID, trash_id); - msg->addString("NewName", NULL); - if(msg->isSendFullFast(_PREHASH_InventoryData)) - { - start_new_message = true; - gAgent.sendReliableMessage(); - gInventory.accountForUpdate(update); - update.clear(); - } - } - } - if(!start_new_message) - { - start_new_message = true; - gAgent.sendReliableMessage(); - gInventory.accountForUpdate(update); - update.clear(); - } - for(i = 0; i < count; ++i) - { - bridge = (LLInvFVBridge*)(batch.get(i)); - if(!bridge || !bridge->isItemRemovable()) continue; - cat = (LLViewerInventoryCategory*)model->getCategory(bridge->getUUID()); - if(cat) - { - if(cat->getParentUUID() == trash_id) continue; - move_ids.push_back(cat->getUUID()); - --update[cat->getParentUUID()]; - ++update[trash_id]; - if(start_new_message) - { - start_new_message = false; - msg->newMessageFast(_PREHASH_MoveInventoryFolder); - msg->nextBlockFast(_PREHASH_AgentData); - msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); - msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); - msg->addBOOL("Stamp", TRUE); - } - msg->nextBlockFast(_PREHASH_InventoryData); - msg->addUUIDFast(_PREHASH_FolderID, cat->getUUID()); - msg->addUUIDFast(_PREHASH_ParentID, trash_id); - if(msg->isSendFullFast(_PREHASH_InventoryData)) - { - start_new_message = true; - gAgent.sendReliableMessage(); - gInventory.accountForUpdate(update); - update.clear(); - } - } - } - if(!start_new_message) - { - gAgent.sendReliableMessage(); - gInventory.accountForUpdate(update); - } - - // move everything. - std::vector<LLUUID>::iterator it = move_ids.begin(); - std::vector<LLUUID>::iterator end = move_ids.end(); - for(; it != end; ++it) - { - gInventory.moveObject((*it), trash_id); - } - - // notify inventory observers. - model->notifyObservers(); -} - -BOOL LLInvFVBridge::isClipboardPasteable() const -{ - if (!LLInventoryClipboard::instance().hasContents() || !isAgentInventory()) - { - return FALSE; - } - LLInventoryModel* model = getInventoryModel(); - if (!model) - { - return FALSE; - } - - const LLUUID &agent_id = gAgent.getID(); - - LLDynamicArray<LLUUID> objects; - LLInventoryClipboard::instance().retrieve(objects); - S32 count = objects.count(); - for(S32 i = 0; i < count; i++) - { - const LLUUID &item_id = objects.get(i); - - // Can't paste folders - const LLInventoryCategory *cat = model->getCategory(item_id); - if (cat) - { - return FALSE; - } - - const LLInventoryItem *item = model->getItem(item_id); - if (item) - { - if (!item->getPermissions().allowCopyBy(agent_id)) - { - return FALSE; - } - } - } - return TRUE; -} - -BOOL LLInvFVBridge::isClipboardPasteableAsLink() const -{ - if (!LLInventoryClipboard::instance().hasContents() || !isAgentInventory()) - { - return FALSE; - } - const LLInventoryModel* model = getInventoryModel(); - if (!model) - { - return FALSE; - } - - LLDynamicArray<LLUUID> objects; - LLInventoryClipboard::instance().retrieve(objects); - S32 count = objects.count(); - for(S32 i = 0; i < count; i++) - { - const LLInventoryItem *item = model->getItem(objects.get(i)); - if (item) - { - if (!LLAssetType::lookupCanLink(item->getActualType())) - { - return FALSE; - } - } - const LLViewerInventoryCategory *cat = model->getCategory(objects.get(i)); - if (cat && !LLFolderType::lookupIsProtectedType(cat->getPreferredType())) - { - return FALSE; - } - } - return TRUE; -} - -void hide_context_entries(LLMenuGL& menu, - const std::vector<std::string> &entries_to_show, - const std::vector<std::string> &disabled_entries) -{ - const LLView::child_list_t *list = menu.getChildList(); - - LLView::child_list_t::const_iterator itor; - for (itor = list->begin(); itor != list->end(); ++itor) - { - std::string name = (*itor)->getName(); - - // descend into split menus: - LLMenuItemBranchGL* branchp = dynamic_cast<LLMenuItemBranchGL*>(*itor); - if ((name == "More") && branchp) - { - hide_context_entries(*branchp->getBranch(), entries_to_show, disabled_entries); - } - - - bool found = false; - std::vector<std::string>::const_iterator itor2; - for (itor2 = entries_to_show.begin(); itor2 != entries_to_show.end(); ++itor2) - { - if (*itor2 == name) - { - found = true; - } - } - if (!found) - { - (*itor)->setVisible(FALSE); - } - else - { - for (itor2 = disabled_entries.begin(); itor2 != disabled_entries.end(); ++itor2) - { - if (*itor2 == name) - { - (*itor)->setEnabled(FALSE); - } - } - } - } -} - -// Helper for commonly-used entries -void LLInvFVBridge::getClipboardEntries(bool show_asset_id, - std::vector<std::string> &items, - std::vector<std::string> &disabled_items, U32 flags) -{ - items.push_back(std::string("Rename")); - if (!isItemRenameable() || (flags & FIRST_SELECTED_ITEM) == 0) - { - disabled_items.push_back(std::string("Rename")); - } - - if (show_asset_id) - { - items.push_back(std::string("Copy Asset UUID")); - if ( (! ( isItemPermissive() || gAgent.isGodlike() ) ) - || (flags & FIRST_SELECTED_ITEM) == 0) - { - disabled_items.push_back(std::string("Copy Asset UUID")); - } - } - - items.push_back(std::string("Copy Separator")); - - items.push_back(std::string("Copy")); - if (!isItemCopyable()) - { - disabled_items.push_back(std::string("Copy")); - } - - items.push_back(std::string("Paste")); - if (!isClipboardPasteable() || (flags & FIRST_SELECTED_ITEM) == 0) - { - disabled_items.push_back(std::string("Paste")); - } - - items.push_back(std::string("Paste As Link")); - if (!isClipboardPasteableAsLink() || (flags & FIRST_SELECTED_ITEM) == 0) - { - disabled_items.push_back(std::string("Paste As Link")); - } - items.push_back(std::string("Paste Separator")); - - items.push_back(std::string("Delete")); - if (!isItemRemovable()) - { - disabled_items.push_back(std::string("Delete")); - } - - // If multiple items are selected, disable properties (if it exists). - if ((flags & FIRST_SELECTED_ITEM) == 0) - { - disabled_items.push_back(std::string("Properties")); - } -} - -void LLInvFVBridge::buildContextMenu(LLMenuGL& menu, U32 flags) -{ - lldebugs << "LLInvFVBridge::buildContextMenu()" << llendl; - std::vector<std::string> items; - std::vector<std::string> disabled_items; - if(isInTrash()) - { - items.push_back(std::string("PurgeItem")); - if (!isItemRemovable()) - { - disabled_items.push_back(std::string("PurgeItem")); - } - items.push_back(std::string("RestoreItem")); - } - else - { - items.push_back(std::string("Open")); - items.push_back(std::string("Properties")); - - getClipboardEntries(true, items, disabled_items, flags); - } - hide_context_entries(menu, items, disabled_items); -} - -// *TODO: remove this -BOOL LLInvFVBridge::startDrag(EDragAndDropType* type, LLUUID* id) const -{ - BOOL rv = FALSE; - - const LLInventoryObject* obj = getInventoryObject(); - - if(obj) - { - *type = LLViewerAssetType::lookupDragAndDropType(obj->getActualType()); - if(*type == DAD_NONE) - { - return FALSE; - } - - *id = obj->getUUID(); - //object_ids.put(obj->getUUID()); - - if (*type == DAD_CATEGORY) - { - gInventory.startBackgroundFetch(obj->getUUID()); - } - - rv = TRUE; - } - - return rv; -} - -LLInventoryObject* LLInvFVBridge::getInventoryObject() const -{ - LLInventoryObject* obj = NULL; - LLInventoryModel* model = getInventoryModel(); - if(model) - { - obj = (LLInventoryObject*)model->getObject(mUUID); - } - return obj; -} - -LLInventoryModel* LLInvFVBridge::getInventoryModel() const -{ - LLInventoryPanel* panel = dynamic_cast<LLInventoryPanel*>(mInventoryPanel.get()); - return panel ? panel->getModel() : NULL; -} - -BOOL LLInvFVBridge::isInTrash() const -{ - LLInventoryModel* model = getInventoryModel(); - if(!model) return FALSE; - const LLUUID trash_id = model->findCategoryUUIDForType(LLFolderType::FT_TRASH); - return model->isObjectDescendentOf(mUUID, trash_id); -} - -BOOL LLInvFVBridge::isLinkedObjectInTrash() const -{ - if (isInTrash()) return TRUE; - - const LLInventoryObject *obj = getInventoryObject(); - if (obj && obj->getIsLinkType()) - { - LLInventoryModel* model = getInventoryModel(); - if(!model) return FALSE; - const LLUUID trash_id = model->findCategoryUUIDForType(LLFolderType::FT_TRASH); - return model->isObjectDescendentOf(obj->getLinkedUUID(), trash_id); - } - return FALSE; -} - -BOOL LLInvFVBridge::isAgentInventory() const -{ - const LLInventoryModel* model = getInventoryModel(); - if(!model) return FALSE; - if(gInventory.getRootFolderID() == mUUID) return TRUE; - return model->isObjectDescendentOf(mUUID, gInventory.getRootFolderID()); -} - -BOOL LLInvFVBridge::isCOFFolder() const -{ - const LLInventoryModel* model = getInventoryModel(); - if(!model) return TRUE; - const LLUUID cof_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT); - if (mUUID == cof_id || model->isObjectDescendentOf(mUUID, cof_id)) - { - return TRUE; - } - return FALSE; -} - -BOOL LLInvFVBridge::isItemPermissive() const -{ - return FALSE; -} - -// static -void LLInvFVBridge::changeItemParent(LLInventoryModel* model, - LLViewerInventoryItem* item, - const LLUUID& new_parent, - BOOL restamp) -{ - if(item->getParentUUID() != new_parent) - { - LLInventoryModel::update_list_t update; - LLInventoryModel::LLCategoryUpdate old_folder(item->getParentUUID(),-1); - update.push_back(old_folder); - LLInventoryModel::LLCategoryUpdate new_folder(new_parent, 1); - update.push_back(new_folder); - gInventory.accountForUpdate(update); - - LLPointer<LLViewerInventoryItem> new_item = new LLViewerInventoryItem(item); - new_item->setParent(new_parent); - new_item->updateParentOnServer(restamp); - model->updateItem(new_item); - model->notifyObservers(); - } -} - -// static -void LLInvFVBridge::changeCategoryParent(LLInventoryModel* model, - LLViewerInventoryCategory* cat, - const LLUUID& new_parent, - BOOL restamp) -{ - if(cat->getParentUUID() != new_parent) - { - LLInventoryModel::update_list_t update; - LLInventoryModel::LLCategoryUpdate old_folder(cat->getParentUUID(), -1); - update.push_back(old_folder); - LLInventoryModel::LLCategoryUpdate new_folder(new_parent, 1); - update.push_back(new_folder); - gInventory.accountForUpdate(update); - - LLPointer<LLViewerInventoryCategory> new_cat = new LLViewerInventoryCategory(cat); - new_cat->setParent(new_parent); - new_cat->updateParentOnServer(restamp); - model->updateCategory(new_cat); - model->notifyObservers(); - } -} - - -const std::string safe_inv_type_lookup(LLInventoryType::EType inv_type) -{ - const std::string rv= LLInventoryType::lookup(inv_type); - if(rv.empty()) - { - return std::string("<invalid>"); - } - return rv; -} - -LLInvFVBridge* LLInvFVBridge::createBridge(LLAssetType::EType asset_type, - LLAssetType::EType actual_asset_type, - LLInventoryType::EType inv_type, - LLInventoryPanel* inventory, - const LLUUID& uuid, - U32 flags) -{ - LLInvFVBridge* new_listener = NULL; - switch(asset_type) - { - case LLAssetType::AT_TEXTURE: - if(!(inv_type == LLInventoryType::IT_TEXTURE || inv_type == LLInventoryType::IT_SNAPSHOT)) - { - llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << safe_inv_type_lookup(inv_type) << " on uuid " << uuid << llendl; - } - new_listener = new LLTextureBridge(inventory, uuid, inv_type); - break; - - case LLAssetType::AT_SOUND: - if(!(inv_type == LLInventoryType::IT_SOUND)) - { - llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << safe_inv_type_lookup(inv_type) << " on uuid " << uuid << llendl; - } - new_listener = new LLSoundBridge(inventory, uuid); - break; - - case LLAssetType::AT_LANDMARK: - if(!(inv_type == LLInventoryType::IT_LANDMARK)) - { - llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << safe_inv_type_lookup(inv_type) << " on uuid " << uuid << llendl; - } - new_listener = new LLLandmarkBridge(inventory, uuid, flags); - break; - - case LLAssetType::AT_CALLINGCARD: - if(!(inv_type == LLInventoryType::IT_CALLINGCARD)) - { - llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << safe_inv_type_lookup(inv_type) << " on uuid " << uuid << llendl; - } - new_listener = new LLCallingCardBridge(inventory, uuid); - break; - - case LLAssetType::AT_SCRIPT: - if(!(inv_type == LLInventoryType::IT_LSL)) - { - llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << safe_inv_type_lookup(inv_type) << " on uuid " << uuid << llendl; - } - new_listener = new LLScriptBridge(inventory, uuid); - break; - - case LLAssetType::AT_OBJECT: - if(!(inv_type == LLInventoryType::IT_OBJECT || inv_type == LLInventoryType::IT_ATTACHMENT)) - { - llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << safe_inv_type_lookup(inv_type) << " on uuid " << uuid << llendl; - } - new_listener = new LLObjectBridge(inventory, uuid, inv_type, flags); - break; - - case LLAssetType::AT_NOTECARD: - if(!(inv_type == LLInventoryType::IT_NOTECARD)) - { - llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << safe_inv_type_lookup(inv_type) << " on uuid " << uuid << llendl; - } - new_listener = new LLNotecardBridge(inventory, uuid); - break; - - case LLAssetType::AT_ANIMATION: - if(!(inv_type == LLInventoryType::IT_ANIMATION)) - { - llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << safe_inv_type_lookup(inv_type) << " on uuid " << uuid << llendl; - } - new_listener = new LLAnimationBridge(inventory, uuid); - break; - - case LLAssetType::AT_GESTURE: - if(!(inv_type == LLInventoryType::IT_GESTURE)) - { - llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << safe_inv_type_lookup(inv_type) << " on uuid " << uuid << llendl; - } - new_listener = new LLGestureBridge(inventory, uuid); - break; - - case LLAssetType::AT_LSL_TEXT: - if(!(inv_type == LLInventoryType::IT_LSL)) - { - llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << safe_inv_type_lookup(inv_type) << " on uuid " << uuid << llendl; - } - new_listener = new LLLSLTextBridge(inventory, uuid); - break; - - case LLAssetType::AT_CLOTHING: - case LLAssetType::AT_BODYPART: - if(!(inv_type == LLInventoryType::IT_WEARABLE)) - { - llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << safe_inv_type_lookup(inv_type) << " on uuid " << uuid << llendl; - } - new_listener = new LLWearableBridge(inventory, uuid, asset_type, inv_type, (EWearableType)flags); - break; - case LLAssetType::AT_CATEGORY: - case LLAssetType::AT_ROOT_CATEGORY: - if (actual_asset_type == LLAssetType::AT_LINK_FOLDER) - { - // Create a link folder handler instead. - new_listener = new LLLinkFolderBridge(inventory, uuid); - break; - } - new_listener = new LLFolderBridge(inventory, uuid); - break; - case LLAssetType::AT_LINK: - // Only should happen for broken links. - new_listener = new LLLinkItemBridge(inventory, uuid); - break; - case LLAssetType::AT_LINK_FOLDER: - // Only should happen for broken links. - new_listener = new LLLinkItemBridge(inventory, uuid); - break; - default: - llinfos << "Unhandled asset type (llassetstorage.h): " - << (S32)asset_type << llendl; - break; - } - - if (new_listener) - { - new_listener->mInvType = inv_type; - } - - return new_listener; -} - -void LLInvFVBridge::purgeItem(LLInventoryModel *model, const LLUUID &uuid) -{ - LLInventoryCategory* cat = model->getCategory(uuid); - if (cat) - { - model->purgeDescendentsOf(uuid); - model->notifyObservers(); - } - LLInventoryObject* obj = model->getObject(uuid); - if (obj) - { - model->purgeObject(uuid); - model->notifyObservers(); - } -} - -// +=================================================+ -// | InventoryFVBridgeBuilder | -// +=================================================+ -LLInvFVBridge* LLInventoryFVBridgeBuilder::createBridge(LLAssetType::EType asset_type, - LLAssetType::EType actual_asset_type, - LLInventoryType::EType inv_type, - LLInventoryPanel* inventory, - const LLUUID& uuid, - U32 flags /* = 0x00 */) const -{ - return LLInvFVBridge::createBridge(asset_type, - actual_asset_type, - inv_type, - inventory, - uuid, - flags); -} - -// +=================================================+ -// | LLItemBridge | -// +=================================================+ - -void LLItemBridge::performAction(LLFolderView* folder, LLInventoryModel* model, std::string action) -{ - if ("goto" == action) - { - gotoItem(folder); - } - if ("open" == action) - { - openItem(); - return; - } - else if ("properties" == action) - { - showProperties(); - return; - } - else if ("purge" == action) - { - purgeItem(model, mUUID); - return; - } - else if ("restoreToWorld" == action) - { - restoreToWorld(); - return; - } - else if ("restore" == action) - { - restoreItem(); - return; - } - else if ("copy_uuid" == action) - { - // Single item only - LLInventoryItem* item = model->getItem(mUUID); - if(!item) return; - LLUUID asset_id = item->getAssetUUID(); - std::string buffer; - asset_id.toString(buffer); - - gViewerWindow->mWindow->copyTextToClipboard(utf8str_to_wstring(buffer)); - return; - } - else if ("copy" == action) - { - copyToClipboard(); - return; - } - else if ("paste" == action) - { - // Single item only - LLInventoryItem* itemp = model->getItem(mUUID); - if (!itemp) return; - - LLFolderViewItem* folder_view_itemp = folder->getItemByID(itemp->getParentUUID()); - if (!folder_view_itemp) return; - - folder_view_itemp->getListener()->pasteFromClipboard(); - return; - } - else if ("paste_link" == action) - { - // Single item only - LLInventoryItem* itemp = model->getItem(mUUID); - if (!itemp) return; - - LLFolderViewItem* folder_view_itemp = folder->getItemByID(itemp->getParentUUID()); - if (!folder_view_itemp) return; - - folder_view_itemp->getListener()->pasteLinkFromClipboard(); - return; - } -} - -void LLItemBridge::selectItem() -{ - LLViewerInventoryItem* item = (LLViewerInventoryItem*)getItem(); - if(item && !item->isComplete()) - { - item->fetchFromServer(); - } -} - -void LLItemBridge::restoreItem() -{ - LLViewerInventoryItem* item = (LLViewerInventoryItem*)getItem(); - if(item) - { - LLInventoryModel* model = getInventoryModel(); - const LLUUID new_parent = model->findCategoryUUIDForType(LLFolderType::assetTypeToFolderType(item->getType())); - // do not restamp on restore. - LLInvFVBridge::changeItemParent(model, item, new_parent, FALSE); - } -} - -void LLItemBridge::restoreToWorld() -{ - LLViewerInventoryItem* itemp = (LLViewerInventoryItem*)getItem(); - if (itemp) - { - LLMessageSystem* msg = gMessageSystem; - msg->newMessage("RezRestoreToWorld"); - msg->nextBlockFast(_PREHASH_AgentData); - msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); - msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); - - msg->nextBlockFast(_PREHASH_InventoryData); - itemp->packMessage(msg); - msg->sendReliable(gAgent.getRegion()->getHost()); - } - - //Similar functionality to the drag and drop rez logic - BOOL remove_from_inventory = FALSE; - - //remove local inventory copy, sim will deal with permissions and removing the item - //from the actual inventory if its a no-copy etc - if(!itemp->getPermissions().allowCopyBy(gAgent.getID())) - { - remove_from_inventory = TRUE; - } - - // Check if it's in the trash. (again similar to the normal rez logic) - const LLUUID trash_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_TRASH); - if(gInventory.isObjectDescendentOf(itemp->getUUID(), trash_id)) - { - remove_from_inventory = TRUE; - } - - if(remove_from_inventory) - { - gInventory.deleteObject(itemp->getUUID()); - gInventory.notifyObservers(); - } -} - -void LLItemBridge::gotoItem(LLFolderView *folder) -{ - LLInventoryObject *obj = getInventoryObject(); - if (obj && obj->getIsLinkType()) - { - LLInventoryPanel* active_panel = LLFloaterInventory::getActiveInventory()->getPanel(); - if (active_panel) - { - active_panel->setSelection(obj->getLinkedUUID(), TAKE_FOCUS_NO); - } - } -} - -LLUIImagePtr LLItemBridge::getIcon() const -{ - return LLUI::getUIImage(ICON_NAME[OBJECT_ICON_NAME]); -} - -PermissionMask LLItemBridge::getPermissionMask() const -{ - LLViewerInventoryItem* item = getItem(); - PermissionMask perm_mask = 0; - if(item) - { - BOOL copy = item->getPermissions().allowCopyBy(gAgent.getID()); - BOOL mod = item->getPermissions().allowModifyBy(gAgent.getID()); - BOOL xfer = item->getPermissions().allowOperationBy(PERM_TRANSFER, - gAgent.getID()); - - if (copy) perm_mask |= PERM_COPY; - if (mod) perm_mask |= PERM_MODIFY; - if (xfer) perm_mask |= PERM_TRANSFER; - - } - return perm_mask; -} - -const std::string& LLItemBridge::getDisplayName() const -{ - if(mDisplayName.empty()) - { - buildDisplayName(getItem(), mDisplayName); - } - return mDisplayName; -} - -void LLItemBridge::buildDisplayName(LLInventoryItem* item, std::string& name) -{ - if(item) - { - name.assign(item->getName()); - } - else - { - name.assign(LLStringUtil::null); - } -} - -LLFontGL::StyleFlags LLItemBridge::getLabelStyle() const -{ - U8 font = LLFontGL::NORMAL; - - if( gAgentWearables.isWearingItem( mUUID ) ) - { - // llinfos << "BOLD" << llendl; - font |= LLFontGL::BOLD; - } - - const LLViewerInventoryItem* item = getItem(); - if (item && item->getIsLinkType()) - { - font |= LLFontGL::ITALIC; - } - return (LLFontGL::StyleFlags)font; -} - -std::string LLItemBridge::getLabelSuffix() const -{ - // String table is loaded before login screen and inventory items are - // loaded after login, so LLTrans should be ready. - static std::string NO_COPY =LLTrans::getString("no_copy"); - static std::string NO_MOD = LLTrans::getString("no_modify"); - static std::string NO_XFER = LLTrans::getString("no_transfer"); - static std::string LINK = LLTrans::getString("link"); - static std::string BROKEN_LINK = LLTrans::getString("broken_link"); - std::string suffix; - LLInventoryItem* item = getItem(); - if(item) - { - // it's a bit confusing to put nocopy/nomod/etc on calling cards. - if(LLAssetType::AT_CALLINGCARD != item->getType() - && item->getPermissions().getOwner() == gAgent.getID()) - { - BOOL broken_link = LLAssetType::lookupIsLinkType(item->getType()); - if (broken_link) return BROKEN_LINK; - - BOOL link = item->getIsLinkType(); - if (link) return LINK; - - BOOL copy = item->getPermissions().allowCopyBy(gAgent.getID()); - if (!copy) - { - suffix += NO_COPY; - } - BOOL mod = item->getPermissions().allowModifyBy(gAgent.getID()); - if (!mod) - { - suffix += NO_MOD; - } - BOOL xfer = item->getPermissions().allowOperationBy(PERM_TRANSFER, - gAgent.getID()); - if (!xfer) - { - suffix += NO_XFER; - } - } - } - return suffix; -} - -time_t LLItemBridge::getCreationDate() const -{ - LLViewerInventoryItem* item = getItem(); - if (item) - { - return item->getCreationDate(); - } - return 0; -} - - -BOOL LLItemBridge::isItemRenameable() const -{ - LLViewerInventoryItem* item = getItem(); - if(item) - { - // (For now) Don't allow calling card rename since that may confuse users as to - // what the calling card points to. - if (item->getInventoryType() == LLInventoryType::IT_CALLINGCARD) - { - return FALSE; - } - return (item->getPermissions().allowModifyBy(gAgent.getID())); - } - return FALSE; -} - -BOOL LLItemBridge::renameItem(const std::string& new_name) -{ - if(!isItemRenameable()) - return FALSE; - LLPreview::dirty(mUUID); - LLInventoryModel* model = getInventoryModel(); - if(!model) - return FALSE; - LLViewerInventoryItem* item = getItem(); - if(item && (item->getName() != new_name)) - { - LLPointer<LLViewerInventoryItem> new_item = new LLViewerInventoryItem(item); - new_item->rename(new_name); - buildDisplayName(new_item, mDisplayName); - new_item->updateServer(FALSE); - model->updateItem(new_item); - - model->notifyObservers(); - } - // return FALSE because we either notified observers (& therefore - // rebuilt) or we didn't update. - return FALSE; -} - - -BOOL LLItemBridge::removeItem() -{ - if(!isItemRemovable()) - { - return FALSE; - } - // move it to the trash - LLPreview::hide(mUUID, TRUE); - LLInventoryModel* model = getInventoryModel(); - if(!model) return FALSE; - const LLUUID trash_id = model->findCategoryUUIDForType(LLFolderType::FT_TRASH); - LLViewerInventoryItem* item = getItem(); - - // if item is not already in trash - if(item && !model->isObjectDescendentOf(mUUID, trash_id)) - { - // move to trash, and restamp - LLInvFVBridge::changeItemParent(model, item, trash_id, TRUE); - // delete was successful - return TRUE; - } - else - { - // tried to delete already item in trash (should purge?) - return FALSE; - } -} - -BOOL LLItemBridge::isItemCopyable() const -{ - LLViewerInventoryItem* item = getItem(); - if (item) - { - // can't copy worn objects. DEV-15183 - LLVOAvatarSelf *avatarp = gAgent.getAvatarObject(); - if( !avatarp ) - { - return FALSE; - } - - if(avatarp->isWearingAttachment(mUUID)) - { - return FALSE; - } - - // All items can be copied, not all can be pasted. - // The only time an item can't be copied is if it's a link - // return (item->getPermissions().allowCopyBy(gAgent.getID())); - if (item->getIsLinkType()) - { - return FALSE; - } - return TRUE; - } - return FALSE; -} -BOOL LLItemBridge::copyToClipboard() const -{ - if(isItemCopyable()) - { - LLInventoryClipboard::instance().add(mUUID); - return TRUE; - } - return FALSE; -} - -LLViewerInventoryItem* LLItemBridge::getItem() const -{ - LLViewerInventoryItem* item = NULL; - LLInventoryModel* model = getInventoryModel(); - if(model) - { - item = (LLViewerInventoryItem*)model->getItem(mUUID); - } - return item; -} - -BOOL LLItemBridge::isItemPermissive() const -{ - LLViewerInventoryItem* item = getItem(); - if(item) - { - U32 mask = item->getPermissions().getMaskBase(); - if((mask & PERM_ITEM_UNRESTRICTED) == PERM_ITEM_UNRESTRICTED) - { - return TRUE; - } - } - return FALSE; -} - -// +=================================================+ -// | LLFolderBridge | -// +=================================================+ - -LLFolderBridge* LLFolderBridge::sSelf=NULL; - -// Can be moved to another folder -BOOL LLFolderBridge::isItemMovable() const -{ - LLInventoryObject* obj = getInventoryObject(); - if(obj) - { - return (!LLFolderType::lookupIsProtectedType(((LLInventoryCategory*)obj)->getPreferredType())); - } - return FALSE; -} - -void LLFolderBridge::selectItem() -{ -} - - -// Can be destroyed (or moved to trash) -BOOL LLFolderBridge::isItemRemovable() -{ - LLInventoryModel* model = getInventoryModel(); - if(!model) - { - return FALSE; - } - - if(!model->isObjectDescendentOf(mUUID, gInventory.getRootFolderID())) - { - return FALSE; - } - - LLVOAvatarSelf* avatar = gAgent.getAvatarObject(); - if( !avatar ) - { - return FALSE; - } - - LLInventoryCategory* category = model->getCategory(mUUID); - if( !category ) - { - return FALSE; - } - - if(LLFolderType::lookupIsProtectedType(category->getPreferredType())) - { - return FALSE; - } - - LLInventoryModel::cat_array_t descendent_categories; - LLInventoryModel::item_array_t descendent_items; - gInventory.collectDescendents( mUUID, descendent_categories, descendent_items, FALSE ); - - S32 i; - for( i = 0; i < descendent_categories.count(); i++ ) - { - LLInventoryCategory* category = descendent_categories[i]; - if(LLFolderType::lookupIsProtectedType(category->getPreferredType())) - { - return FALSE; - } - } - - for( i = 0; i < descendent_items.count(); i++ ) - { - LLInventoryItem* item = descendent_items[i]; - if( (item->getType() == LLAssetType::AT_CLOTHING) || - (item->getType() == LLAssetType::AT_BODYPART) ) - { - if(gAgentWearables.isWearingItem(item->getUUID())) - { - return FALSE; - } - } - else - if( item->getType() == LLAssetType::AT_OBJECT ) - { - if(avatar->isWearingAttachment(item->getUUID())) - { - return FALSE; - } - } - } - - return TRUE; -} - -BOOL LLFolderBridge::isUpToDate() const -{ - LLInventoryModel* model = getInventoryModel(); - if(!model) return FALSE; - LLViewerInventoryCategory* category = (LLViewerInventoryCategory*)model->getCategory(mUUID); - if( !category ) - { - return FALSE; - } - - return category->getVersion() != LLViewerInventoryCategory::VERSION_UNKNOWN; -} - -BOOL LLFolderBridge::isItemCopyable() const -{ - return TRUE; -} - -BOOL LLFolderBridge::copyToClipboard() const -{ - if(isItemCopyable()) - { - LLInventoryClipboard::instance().add(mUUID); - return TRUE; - } - return FALSE; -} - -BOOL LLFolderBridge::isClipboardPasteable() const -{ - if ( ! LLInvFVBridge::isClipboardPasteable() ) - return FALSE; - - // Don't allow pasting duplicates to the Calling Card/Friends subfolders, see bug EXT-1599 - if ( LLFriendCardsManager::instance().isCategoryInFriendFolder( getCategory() ) ) - { - LLInventoryModel* model = getInventoryModel(); - if ( !model ) - { - return FALSE; - } - - LLDynamicArray<LLUUID> objects; - LLInventoryClipboard::instance().retrieve(objects); - const LLViewerInventoryCategory *current_cat = getCategory(); - - // Search for the direct descendent of current Friends subfolder among all pasted items, - // and return false if is found. - for(S32 i = objects.count() - 1; i >= 0; --i) - { - const LLUUID &obj_id = objects.get(i); - if ( LLFriendCardsManager::instance().isObjDirectDescendentOfCategory(model->getObject(obj_id), current_cat) ) - { - return FALSE; - } - } - - } - return TRUE; -} - -BOOL LLFolderBridge::isClipboardPasteableAsLink() const -{ - // Check normal paste-as-link permissions - if (!LLInvFVBridge::isClipboardPasteableAsLink()) - { - return FALSE; - } - - const LLInventoryModel* model = getInventoryModel(); - if (!model) - { - return FALSE; - } - - const LLViewerInventoryCategory *current_cat = getCategory(); - if (current_cat) - { - const BOOL is_in_friend_folder = LLFriendCardsManager::instance().isCategoryInFriendFolder( current_cat ); - const LLUUID ¤t_cat_id = current_cat->getUUID(); - LLDynamicArray<LLUUID> objects; - LLInventoryClipboard::instance().retrieve(objects); - S32 count = objects.count(); - for(S32 i = 0; i < count; i++) - { - const LLUUID &obj_id = objects.get(i); - const LLInventoryCategory *cat = model->getCategory(obj_id); - if (cat) - { - const LLUUID &cat_id = cat->getUUID(); - // Don't allow recursive pasting - if ((cat_id == current_cat_id) || - model->isObjectDescendentOf(current_cat_id, cat_id)) - { - return FALSE; - } - } - // Don't allow pasting duplicates to the Calling Card/Friends subfolders, see bug EXT-1599 - if ( is_in_friend_folder ) - { - // If object is direct descendent of current Friends subfolder than return false. - // Note: We can't use 'const LLInventoryCategory *cat', because it may be null - // in case type of obj_id is LLInventoryItem. - if ( LLFriendCardsManager::instance().isObjDirectDescendentOfCategory(model->getObject(obj_id), current_cat) ) - { - return FALSE; - } - } - } - } - return TRUE; - -} - -BOOL LLFolderBridge::dragCategoryIntoFolder(LLInventoryCategory* inv_cat, - BOOL drop) -{ - // This should never happen, but if an inventory item is incorrectly parented, - // the UI will get confused and pass in a NULL. - if(!inv_cat) return FALSE; - - LLInventoryModel* model = getInventoryModel(); - if(!model) return FALSE; - - LLVOAvatarSelf* avatar = gAgent.getAvatarObject(); - if(!avatar) return FALSE; - - // cannot drag categories into library - if(!isAgentInventory()) - { - return FALSE; - } - - // check to make sure source is agent inventory, and is represented there. - LLToolDragAndDrop::ESource source = LLToolDragAndDrop::getInstance()->getSource(); - BOOL is_agent_inventory = (model->getCategory(inv_cat->getUUID()) != NULL) - && (LLToolDragAndDrop::SOURCE_AGENT == source); - - BOOL accept = FALSE; - S32 i; - LLInventoryModel::cat_array_t descendent_categories; - LLInventoryModel::item_array_t descendent_items; - if(is_agent_inventory) - { - const LLUUID& cat_id = inv_cat->getUUID(); - - // Is the destination the trash? - const LLUUID trash_id = model->findCategoryUUIDForType(LLFolderType::FT_TRASH); - BOOL move_is_into_trash = (mUUID == trash_id) - || model->isObjectDescendentOf(mUUID, trash_id); - BOOL is_movable = (!LLFolderType::lookupIsProtectedType(inv_cat->getPreferredType())); - const LLUUID current_outfit_id = model->findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT); - BOOL move_is_into_current_outfit = (mUUID == current_outfit_id); - BOOL move_is_into_outfit = (getCategory() && getCategory()->getPreferredType()==LLFolderType::FT_OUTFIT); - if (move_is_into_current_outfit || move_is_into_outfit) - { - // BAP - restrictions? - is_movable = true; - } - - if (mUUID == gInventory.findCategoryUUIDForType(LLFolderType::FT_FAVORITE)) - { - is_movable = FALSE; // It's generally movable but not into Favorites folder. EXT-1604 - } - - if( is_movable ) - { - gInventory.collectDescendents( cat_id, descendent_categories, descendent_items, FALSE ); - - for( i = 0; i < descendent_categories.count(); i++ ) - { - LLInventoryCategory* category = descendent_categories[i]; - if(LLFolderType::lookupIsProtectedType(category->getPreferredType())) - { - // ...can't move "special folders" like Textures - is_movable = FALSE; - break; - } - } - - if( is_movable ) - { - if( move_is_into_trash ) - { - for( i = 0; i < descendent_items.count(); i++ ) - { - LLInventoryItem* item = descendent_items[i]; - if( (item->getType() == LLAssetType::AT_CLOTHING) || - (item->getType() == LLAssetType::AT_BODYPART) ) - { - if( gAgentWearables.isWearingItem( item->getUUID() ) ) - { - is_movable = FALSE; // It's generally movable, but not into the trash! - break; - } - } - else - if( item->getType() == LLAssetType::AT_OBJECT ) - { - if( avatar->isWearingAttachment( item->getUUID() ) ) - { - is_movable = FALSE; // It's generally movable, but not into the trash! - break; - } - } - } - } - } - } - - - accept = is_movable - && (mUUID != cat_id) // Can't move a folder into itself - && (mUUID != inv_cat->getParentUUID()) // Avoid moves that would change nothing - && !(model->isObjectDescendentOf(mUUID, cat_id)); // Avoid circularity - if(accept && drop) - { - // Look for any gestures and deactivate them - if (move_is_into_trash) - { - for (i = 0; i < descendent_items.count(); i++) - { - LLInventoryItem* item = descendent_items[i]; - if (item->getType() == LLAssetType::AT_GESTURE - && LLGestureManager::instance().isGestureActive(item->getUUID())) - { - LLGestureManager::instance().deactivateGesture(item->getUUID()); - } - } - } - // if target is an outfit or current outfit folder we use link - if (move_is_into_current_outfit || move_is_into_outfit) - { -#if SUPPORT_ENSEMBLES - // BAP - should skip if dup. - if (move_is_into_current_outfit) - { - LLAppearanceManager::wearEnsemble(inv_cat); - } - else - { - LLPointer<LLInventoryCallback> cb = NULL; - link_inventory_item( - gAgent.getID(), - inv_cat->getUUID(), - mUUID, - inv_cat->getName(), - LLAssetType::AT_LINK_FOLDER, - cb); - } -#endif - } - else - { - - // Reparent the folder and restamp children if it's moving - // into trash. - LLInvFVBridge::changeCategoryParent( - model, - (LLViewerInventoryCategory*)inv_cat, - mUUID, - move_is_into_trash); - } - } - } - else if(LLToolDragAndDrop::SOURCE_WORLD == source) - { - // content category has same ID as object itself - LLUUID object_id = inv_cat->getUUID(); - LLUUID category_id = mUUID; - accept = move_inv_category_world_to_agent(object_id, category_id, drop); - } - return accept; -} - -void warn_move_inventory(LLViewerObject* object, LLMoveInv* move_inv) -{ - const char* dialog = NULL; - if (object->flagScripted()) - { - dialog = "MoveInventoryFromScriptedObject"; - } - else - { - dialog = "MoveInventoryFromObject"; - } - LLNotifications::instance().add(dialog, LLSD(), LLSD(), boost::bind(move_task_inventory_callback, _1, _2, move_inv)); -} - -// Move/copy all inventory items from the Contents folder of an in-world -// object to the agent's inventory, inside a given category. -BOOL move_inv_category_world_to_agent(const LLUUID& object_id, - const LLUUID& category_id, - BOOL drop, - void (*callback)(S32, void*), - void* user_data) -{ - // Make sure the object exists. If we allowed dragging from - // anonymous objects, it would be possible to bypass - // permissions. - // content category has same ID as object itself - LLViewerObject* object = gObjectList.findObject(object_id); - if(!object) - { - llinfos << "Object not found for drop." << llendl; - return FALSE; - } - - // this folder is coming from an object, as there is only one folder in an object, the root, - // we need to collect the entire contents and handle them as a group - InventoryObjectList inventory_objects; - object->getInventoryContents(inventory_objects); - - if (inventory_objects.empty()) - { - llinfos << "Object contents not found for drop." << llendl; - return FALSE; - } - - BOOL accept = TRUE; - BOOL is_move = FALSE; - - // coming from a task. Need to figure out if the person can - // move/copy this item. - InventoryObjectList::iterator it = inventory_objects.begin(); - InventoryObjectList::iterator end = inventory_objects.end(); - for ( ; it != end; ++it) - { - // coming from a task. Need to figure out if the person can - // move/copy this item. - LLPermissions perm(((LLInventoryItem*)((LLInventoryObject*)(*it)))->getPermissions()); - if((perm.allowCopyBy(gAgent.getID(), gAgent.getGroupID()) - && perm.allowTransferTo(gAgent.getID()))) -// || gAgent.isGodlike()) - { - accept = TRUE; - } - else if(object->permYouOwner()) - { - // If the object cannot be copied, but the object the - // inventory is owned by the agent, then the item can be - // moved from the task to agent inventory. - is_move = TRUE; - accept = TRUE; - } - else - { - accept = FALSE; - break; - } - } - - if(drop && accept) - { - it = inventory_objects.begin(); - InventoryObjectList::iterator first_it = inventory_objects.begin(); - LLMoveInv* move_inv = new LLMoveInv; - move_inv->mObjectID = object_id; - move_inv->mCategoryID = category_id; - move_inv->mCallback = callback; - move_inv->mUserData = user_data; - - for ( ; it != end; ++it) - { - two_uuids_t two(category_id, (*it)->getUUID()); - move_inv->mMoveList.push_back(two); - } - - if(is_move) - { - // Callback called from within here. - warn_move_inventory(object, move_inv); - } - else - { - LLNotification::Params params("MoveInventoryFromObject"); - params.functor.function(boost::bind(move_task_inventory_callback, _1, _2, move_inv)); - LLNotifications::instance().forceResponse(params, 0); - } - } - return accept; -} - -bool LLFindCOFValidItems::operator()(LLInventoryCategory* cat, - LLInventoryItem* item) -{ - // Valid COF items are: - // - links to wearables (body parts or clothing) - // - links to attachments - // - links to gestures - // - links to ensemble folders - LLViewerInventoryItem *linked_item = ((LLViewerInventoryItem*)item)->getLinkedItem(); // BAP - safe? - if (linked_item) - { - LLAssetType::EType type = linked_item->getType(); - return (type == LLAssetType::AT_CLOTHING || - type == LLAssetType::AT_BODYPART || - type == LLAssetType::AT_GESTURE || - type == LLAssetType::AT_OBJECT); - } - else - { - LLViewerInventoryCategory *linked_category = ((LLViewerInventoryItem*)item)->getLinkedCategory(); // BAP - safe? - // BAP remove AT_NONE support after ensembles are fully working? - return (linked_category && - ((linked_category->getPreferredType() == LLFolderType::FT_NONE) || - (LLFolderType::lookupIsEnsembleType(linked_category->getPreferredType())))); - } -} - - -bool LLFindWearables::operator()(LLInventoryCategory* cat, - LLInventoryItem* item) -{ - if(item) - { - if((item->getType() == LLAssetType::AT_CLOTHING) - || (item->getType() == LLAssetType::AT_BODYPART)) - { - return TRUE; - } - } - return FALSE; -} - - - -//Used by LLFolderBridge as callback for directory recursion. -class LLRightClickInventoryFetchObserver : public LLInventoryFetchObserver -{ -public: - LLRightClickInventoryFetchObserver() : - mCopyItems(false) - { }; - LLRightClickInventoryFetchObserver(const LLUUID& cat_id, bool copy_items) : - mCatID(cat_id), - mCopyItems(copy_items) - { }; - virtual void done() - { - // we've downloaded all the items, so repaint the dialog - LLFolderBridge::staticFolderOptionsMenu(); - - gInventory.removeObserver(this); - delete this; - } - - -protected: - LLUUID mCatID; - bool mCopyItems; - -}; - -//Used by LLFolderBridge as callback for directory recursion. -class LLRightClickInventoryFetchDescendentsObserver : public LLInventoryFetchDescendentsObserver -{ -public: - LLRightClickInventoryFetchDescendentsObserver(bool copy_items) : mCopyItems(copy_items) {} - ~LLRightClickInventoryFetchDescendentsObserver() {} - virtual void done(); -protected: - bool mCopyItems; -}; - -void LLRightClickInventoryFetchDescendentsObserver::done() -{ - // Avoid passing a NULL-ref as mCompleteFolders.front() down to - // gInventory.collectDescendents() - if( mCompleteFolders.empty() ) - { - llwarns << "LLRightClickInventoryFetchDescendentsObserver::done with empty mCompleteFolders" << llendl; - dec_busy_count(); - gInventory.removeObserver(this); - delete this; - return; - } - - // What we do here is get the complete information on the items in - // the library, and set up an observer that will wait for that to - // happen. - LLInventoryModel::cat_array_t cat_array; - LLInventoryModel::item_array_t item_array; - gInventory.collectDescendents(mCompleteFolders.front(), - cat_array, - item_array, - LLInventoryModel::EXCLUDE_TRASH); - S32 count = item_array.count(); -#if 0 // HACK/TODO: Why? - // This early causes a giant menu to get produced, and doesn't seem to be needed. - if(!count) - { - llwarns << "Nothing fetched in category " << mCompleteFolders.front() - << llendl; - dec_busy_count(); - gInventory.removeObserver(this); - delete this; - return; - } -#endif - - LLRightClickInventoryFetchObserver* outfit; - outfit = new LLRightClickInventoryFetchObserver(mCompleteFolders.front(), mCopyItems); - LLInventoryFetchObserver::item_ref_t ids; - for(S32 i = 0; i < count; ++i) - { - ids.push_back(item_array.get(i)->getUUID()); - } - - // clean up, and remove this as an observer since the call to the - // outfit could notify observers and throw us into an infinite - // loop. - dec_busy_count(); - gInventory.removeObserver(this); - delete this; - - // increment busy count and either tell the inventory to check & - // call done, or add this object to the inventory for observation. - inc_busy_count(); - - // do the fetch - outfit->fetchItems(ids); - outfit->done(); //Not interested in waiting and this will be right 99% of the time. -//Uncomment the following code for laggy Inventory UI. -/* if(outfit->isEverythingComplete()) - { - // everything is already here - call done. - outfit->done(); - } - else - { - // it's all on it's way - add an observer, and the inventory - // will call done for us when everything is here. - gInventory.addObserver(outfit); - }*/ -} - - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// Class LLInventoryWearObserver -// -// Observer for "copy and wear" operation to support knowing -// when the all of the contents have been added to inventory. -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -class LLInventoryCopyAndWearObserver : public LLInventoryObserver -{ -public: - LLInventoryCopyAndWearObserver(const LLUUID& cat_id, int count) :mCatID(cat_id), mContentsCount(count), mFolderAdded(FALSE) {} - virtual ~LLInventoryCopyAndWearObserver() {} - virtual void changed(U32 mask); - -protected: - LLUUID mCatID; - int mContentsCount; - BOOL mFolderAdded; -}; - - - -void LLInventoryCopyAndWearObserver::changed(U32 mask) -{ - if((mask & (LLInventoryObserver::ADD)) != 0) - { - if (!mFolderAdded) - { - const std::set<LLUUID>& changed_items = gInventory.getChangedIDs(); - - std::set<LLUUID>::const_iterator id_it = changed_items.begin(); - std::set<LLUUID>::const_iterator id_end = changed_items.end(); - for (;id_it != id_end; ++id_it) - { - if ((*id_it) == mCatID) - { - mFolderAdded = TRUE; - break; - } - } - } - - if (mFolderAdded) - { - LLViewerInventoryCategory* category = gInventory.getCategory(mCatID); - - if (NULL == category) - { - llwarns << "gInventory.getCategory(" << mCatID - << ") was NULL" << llendl; - } - else - { - if (category->getDescendentCount() == - mContentsCount) - { - gInventory.removeObserver(this); - LLAppearanceManager::wearInventoryCategory(category, FALSE, TRUE); - delete this; - } - } - } - - } -} - - - -void LLFolderBridge::performAction(LLFolderView* folder, LLInventoryModel* model, std::string action) -{ - if ("open" == action) - { - openItem(); - return; - } - else if ("paste" == action) - { - pasteFromClipboard(); - return; - } - else if ("paste_link" == action) - { - pasteLinkFromClipboard(); - return; - } - else if ("properties" == action) - { - showProperties(); - return; - } - else if ("replaceoutfit" == action) - { - modifyOutfit(FALSE); - return; - } -#if SUPPORT_ENSEMBLES - else if ("wearasensemble" == action) - { - LLInventoryModel* model = getInventoryModel(); - if(!model) return; - LLViewerInventoryCategory* cat = getCategory(); - if(!cat) return; - LLAppearanceManager::wearEnsemble(cat,true); - return; - } -#endif - else if ("addtooutfit" == action) - { - modifyOutfit(TRUE); - return; - } - else if ("copy" == action) - { - copyToClipboard(); - return; - } - else if ("removefromoutfit" == action) - { - LLInventoryModel* model = getInventoryModel(); - if(!model) return; - LLViewerInventoryCategory* cat = getCategory(); - if(!cat) return; - - remove_inventory_category_from_avatar ( cat ); - return; - } - else if ("purge" == action) - { - purgeItem(model, mUUID); - return; - } - else if ("restore" == action) - { - restoreItem(); - return; - } -} - -void LLFolderBridge::openItem() -{ - lldebugs << "LLFolderBridge::openItem()" << llendl; - LLInventoryModel* model = getInventoryModel(); - if(!model) return; - bool fetching_inventory = model->fetchDescendentsOf(mUUID); - // Only change folder type if we have the folder contents. - if (!fetching_inventory) - { - // Disabling this for now, it's causing crash when new items are added to folders - // since folder type may change before new item item has finished processing. - // determineFolderType(); - } -} - -void LLFolderBridge::closeItem() -{ - determineFolderType(); -} - -void LLFolderBridge::determineFolderType() -{ - if (isUpToDate()) - { - LLInventoryModel* model = getInventoryModel(); - LLViewerInventoryCategory* category = model->getCategory(mUUID); - category->determineFolderType(); - } -} - -BOOL LLFolderBridge::isItemRenameable() const -{ - LLViewerInventoryCategory* cat = (LLViewerInventoryCategory*)getCategory(); - if(cat && !LLFolderType::lookupIsProtectedType(cat->getPreferredType()) - && (cat->getOwnerID() == gAgent.getID())) - { - return TRUE; - } - return FALSE; -} - -void LLFolderBridge::restoreItem() -{ - LLViewerInventoryCategory* cat; - cat = (LLViewerInventoryCategory*)getCategory(); - if(cat) - { - LLInventoryModel* model = getInventoryModel(); - const LLUUID new_parent = model->findCategoryUUIDForType(LLFolderType::assetTypeToFolderType(cat->getType())); - // do not restamp children on restore - LLInvFVBridge::changeCategoryParent(model, cat, new_parent, FALSE); - } -} - -LLFolderType::EType LLFolderBridge::getPreferredType() const -{ - LLFolderType::EType preferred_type = LLFolderType::FT_NONE; - LLViewerInventoryCategory* cat = getCategory(); - if(cat) - { - preferred_type = cat->getPreferredType(); - } - - return preferred_type; -} - -// Icons for folders are based on the preferred type -LLUIImagePtr LLFolderBridge::getIcon() const -{ - LLFolderType::EType preferred_type = LLFolderType::FT_NONE; - LLViewerInventoryCategory* cat = getCategory(); - if(cat) - { - preferred_type = cat->getPreferredType(); - } - return getIcon(preferred_type); -} - -LLUIImagePtr LLFolderBridge::getIcon(LLFolderType::EType preferred_type) -{ - // we only have one folder image now - return LLUI::getUIImage("Inv_FolderClosed"); -} - -BOOL LLFolderBridge::renameItem(const std::string& new_name) -{ - if(!isItemRenameable()) - return FALSE; - LLInventoryModel* model = getInventoryModel(); - if(!model) - return FALSE; - LLViewerInventoryCategory* cat = getCategory(); - if(cat && (cat->getName() != new_name)) - { - LLPointer<LLViewerInventoryCategory> new_cat = new LLViewerInventoryCategory(cat); - new_cat->rename(new_name); - new_cat->updateServer(FALSE); - model->updateCategory(new_cat); - - model->notifyObservers(); - } - // return FALSE because we either notified observers (& therefore - // rebuilt) or we didn't update. - return FALSE; -} - -BOOL LLFolderBridge::removeItem() -{ - if(!isItemRemovable()) - { - return FALSE; - } - // move it to the trash - LLPreview::hide(mUUID); - LLInventoryModel* model = getInventoryModel(); - if(!model) return FALSE; - - const LLUUID trash_id = model->findCategoryUUIDForType(LLFolderType::FT_TRASH); - - // Look for any gestures and deactivate them - LLInventoryModel::cat_array_t descendent_categories; - LLInventoryModel::item_array_t descendent_items; - gInventory.collectDescendents( mUUID, descendent_categories, descendent_items, FALSE ); - - S32 i; - for (i = 0; i < descendent_items.count(); i++) - { - LLInventoryItem* item = descendent_items[i]; - if (item->getType() == LLAssetType::AT_GESTURE - && LLGestureManager::instance().isGestureActive(item->getUUID())) - { - LLGestureManager::instance().deactivateGesture(item->getUUID()); - } - } - - // go ahead and do the normal remove if no 'last calling - // cards' are being removed. - LLViewerInventoryCategory* cat = getCategory(); - if(cat) - { - LLInvFVBridge::changeCategoryParent(model, cat, trash_id, TRUE); - } - - return TRUE; -} - -void LLFolderBridge::pasteFromClipboard() -{ - LLInventoryModel* model = getInventoryModel(); - if(model && isClipboardPasteable()) - { - LLInventoryItem* item = NULL; - LLDynamicArray<LLUUID> objects; - LLInventoryClipboard::instance().retrieve(objects); - S32 count = objects.count(); - const LLUUID parent_id(mUUID); - for(S32 i = 0; i < count; i++) - { - item = model->getItem(objects.get(i)); - if (item) - { - if(LLInventoryClipboard::instance().isCutMode()) - { - // move_inventory_item() is not enough, - //we have to update inventory locally too - changeItemParent(model, dynamic_cast<LLViewerInventoryItem*>(item), parent_id, FALSE); - } - else - { - copy_inventory_item( - gAgent.getID(), - item->getPermissions().getOwner(), - item->getUUID(), - parent_id, - std::string(), - LLPointer<LLInventoryCallback>(NULL)); - } - } - } - } -} - -void LLFolderBridge::pasteLinkFromClipboard() -{ - const LLInventoryModel* model = getInventoryModel(); - if(model) - { - LLDynamicArray<LLUUID> objects; - LLInventoryClipboard::instance().retrieve(objects); - S32 count = objects.count(); - LLUUID parent_id(mUUID); - for(S32 i = 0; i < count; i++) - { - const LLUUID &object_id = objects.get(i); -#if SUPPORT_ENSEMBLES - if (LLInventoryCategory *cat = model->getCategory(object_id)) - { - link_inventory_item( - gAgent.getID(), - cat->getUUID(), - parent_id, - cat->getName(), - LLAssetType::AT_LINK_FOLDER, - LLPointer<LLInventoryCallback>(NULL)); - } - else -#endif - if (LLInventoryItem *item = model->getItem(object_id)) - { - link_inventory_item( - gAgent.getID(), - item->getUUID(), - parent_id, - item->getName(), - LLAssetType::AT_LINK, - LLPointer<LLInventoryCallback>(NULL)); - } - } - } -} - -void LLFolderBridge::staticFolderOptionsMenu() -{ - if (!sSelf) return; - sSelf->folderOptionsMenu(); -} - -void LLFolderBridge::folderOptionsMenu() -{ - std::vector<std::string> disabled_items; - - LLInventoryModel* model = getInventoryModel(); - if(!model) return; - - const LLInventoryCategory* category = model->getCategory(mUUID); - LLFolderType::EType type = category->getPreferredType(); - const bool is_default_folder = category && LLFolderType::lookupIsProtectedType(type); - // BAP change once we're no longer treating regular categories as ensembles. - const bool is_ensemble = category && (type == LLFolderType::FT_NONE || - LLFolderType::lookupIsEnsembleType(type)); - - // calling card related functionality for folders. - - // Only enable calling-card related options for non-default folders. - if (!is_default_folder) - { - LLIsType is_callingcard(LLAssetType::AT_CALLINGCARD); - if (mCallingCards || checkFolderForContentsOfType(model, is_callingcard)) - { - mItems.push_back(std::string("Calling Card Separator")); - mItems.push_back(std::string("Conference Chat Folder")); - mItems.push_back(std::string("IM All Contacts In Folder")); - } - } - - // wearables related functionality for folders. - //is_wearable - LLFindWearables is_wearable; - LLIsType is_object( LLAssetType::AT_OBJECT ); - LLIsType is_gesture( LLAssetType::AT_GESTURE ); - - if (mWearables || - checkFolderForContentsOfType(model, is_wearable) || - checkFolderForContentsOfType(model, is_object) || - checkFolderForContentsOfType(model, is_gesture) ) - { - mItems.push_back(std::string("Folder Wearables Separator")); - - // Only enable add/replace outfit for non-default folders. - if (!is_default_folder) - { - mItems.push_back(std::string("Add To Outfit")); - mItems.push_back(std::string("Replace Outfit")); - } - if (is_ensemble) - { - mItems.push_back(std::string("Wear As Ensemble")); - } - mItems.push_back(std::string("Take Off Items")); - } - hide_context_entries(*mMenu, mItems, disabled_items); -} - -BOOL LLFolderBridge::checkFolderForContentsOfType(LLInventoryModel* model, LLInventoryCollectFunctor& is_type) -{ - LLInventoryModel::cat_array_t cat_array; - LLInventoryModel::item_array_t item_array; - model->collectDescendentsIf(mUUID, - cat_array, - item_array, - LLInventoryModel::EXCLUDE_TRASH, - is_type); - return ((item_array.count() > 0) ? TRUE : FALSE ); -} - -// Flags unused -void LLFolderBridge::buildContextMenu(LLMenuGL& menu, U32 flags) -{ - mItems.clear(); - mDisabledItems.clear(); - - lldebugs << "LLFolderBridge::buildContextMenu()" << llendl; -// std::vector<std::string> disabled_items; - LLInventoryModel* model = getInventoryModel(); - if(!model) return; - const LLUUID trash_id = model->findCategoryUUIDForType(LLFolderType::FT_TRASH); - const LLUUID lost_and_found_id = model->findCategoryUUIDForType(LLFolderType::FT_LOST_AND_FOUND); - - mItems.clear(); //adding code to clear out member Items (which means Items should not have other data here at this point) - mDisabledItems.clear(); //adding code to clear out disabled members from previous - if (lost_and_found_id == mUUID) - { - // This is the lost+found folder. - mItems.push_back(std::string("Empty Lost And Found")); - } - - if(trash_id == mUUID) - { - // This is the trash. - mItems.push_back(std::string("Empty Trash")); - } - else if(model->isObjectDescendentOf(mUUID, trash_id)) - { - // This is a folder in the trash. - mItems.clear(); // clear any items that used to exist - mItems.push_back(std::string("Purge Item")); - if (!isItemRemovable()) - { - mDisabledItems.push_back(std::string("Purge Item")); - } - - mItems.push_back(std::string("Restore Item")); - } - else if(isAgentInventory()) // do not allow creating in library - { - LLViewerInventoryCategory *cat = getCategory(); - // BAP removed protected check to re-enable standard ops in untyped folders. - // Not sure what the right thing is to do here. - if (!isCOFFolder() && cat /*&& - LLAssetType::lookupIsProtectedCategoryType(cat->getPreferredType())*/) - { - // Do not allow to create 2-level subfolder in the Calling Card/Friends folder. EXT-694. - if (!LLFriendCardsManager::instance().isCategoryInFriendFolder(cat)) - mItems.push_back(std::string("New Folder")); - mItems.push_back(std::string("New Script")); - mItems.push_back(std::string("New Note")); - mItems.push_back(std::string("New Gesture")); - mItems.push_back(std::string("New Clothes")); - mItems.push_back(std::string("New Body Parts")); - mItems.push_back(std::string("Change Type")); - - LLViewerInventoryCategory *cat = getCategory(); - if (cat && LLFolderType::lookupIsProtectedType(cat->getPreferredType())) - { - mDisabledItems.push_back(std::string("Change Type")); - } - - getClipboardEntries(false, mItems, mDisabledItems, flags); - } - else - { - // Want some but not all of the items from getClipboardEntries for outfits. - if (cat && cat->getPreferredType()==LLFolderType::FT_OUTFIT) - { - mItems.push_back(std::string("Rename")); - mItems.push_back(std::string("Delete")); - } - } - - //Added by spatters to force inventory pull on right-click to display folder options correctly. 07-17-06 - mCallingCards = mWearables = FALSE; - - LLIsType is_callingcard(LLAssetType::AT_CALLINGCARD); - if (checkFolderForContentsOfType(model, is_callingcard)) - { - mCallingCards=TRUE; - } - - LLFindWearables is_wearable; - LLIsType is_object( LLAssetType::AT_OBJECT ); - LLIsType is_gesture( LLAssetType::AT_GESTURE ); - - if (checkFolderForContentsOfType(model, is_wearable) || - checkFolderForContentsOfType(model, is_object) || - checkFolderForContentsOfType(model, is_gesture) ) - { - mWearables=TRUE; - } - - mMenu = &menu; - sSelf = this; - LLRightClickInventoryFetchDescendentsObserver* fetch = new LLRightClickInventoryFetchDescendentsObserver(FALSE); - - LLInventoryFetchDescendentsObserver::folder_ref_t folders; - LLViewerInventoryCategory* category = (LLViewerInventoryCategory*)model->getCategory(mUUID); - if (category) - { - folders.push_back(category->getUUID()); - } - fetch->fetchDescendents(folders); - inc_busy_count(); - if(fetch->isEverythingComplete()) - { - // everything is already here - call done. - fetch->done(); - } - else - { - // it's all on it's way - add an observer, and the inventory - // will call done for us when everything is here. - gInventory.addObserver(fetch); - } - } - else - { - mItems.push_back(std::string("--no options--")); - mDisabledItems.push_back(std::string("--no options--")); - } - hide_context_entries(menu, mItems, mDisabledItems); -} - -BOOL LLFolderBridge::hasChildren() const -{ - LLInventoryModel* model = getInventoryModel(); - if(!model) return FALSE; - LLInventoryModel::EHasChildren has_children; - has_children = gInventory.categoryHasChildren(mUUID); - return has_children != LLInventoryModel::CHILDREN_NO; -} - -BOOL LLFolderBridge::dragOrDrop(MASK mask, BOOL drop, - EDragAndDropType cargo_type, - void* cargo_data) -{ - //llinfos << "LLFolderBridge::dragOrDrop()" << llendl; - BOOL accept = FALSE; - switch(cargo_type) - { - case DAD_TEXTURE: - case DAD_SOUND: - case DAD_CALLINGCARD: - case DAD_LANDMARK: - case DAD_SCRIPT: - case DAD_OBJECT: - case DAD_NOTECARD: - case DAD_CLOTHING: - case DAD_BODYPART: - case DAD_ANIMATION: - case DAD_GESTURE: - case DAD_LINK: - accept = dragItemIntoFolder((LLInventoryItem*)cargo_data, - drop); - break; - case DAD_CATEGORY: - if (LLFriendCardsManager::instance().isAnyFriendCategory(mUUID)) - { - accept = FALSE; - } - else - { - accept = dragCategoryIntoFolder((LLInventoryCategory*)cargo_data, drop); - } - break; - default: - break; - } - return accept; -} - -LLViewerInventoryCategory* LLFolderBridge::getCategory() const -{ - LLViewerInventoryCategory* cat = NULL; - LLInventoryModel* model = getInventoryModel(); - if(model) - { - cat = (LLViewerInventoryCategory*)model->getCategory(mUUID); - } - return cat; -} - - -// static -void LLFolderBridge::pasteClipboard(void* user_data) -{ - LLFolderBridge* self = (LLFolderBridge*)user_data; - if(self) self->pasteFromClipboard(); -} - -void LLFolderBridge::createNewCategory(void* user_data) -{ - LLFolderBridge* bridge = (LLFolderBridge*)user_data; - if(!bridge) return; - LLInventoryPanel* panel = dynamic_cast<LLInventoryPanel*>(bridge->mInventoryPanel.get()); - if (!panel) return; - LLInventoryModel* model = panel->getModel(); - if(!model) return; - LLUUID id; - id = model->createNewCategory(bridge->getUUID(), - LLFolderType::FT_NONE, - LLStringUtil::null); - model->notifyObservers(); - - // At this point, the bridge has probably been deleted, but the - // view is still there. - panel->setSelection(id, TAKE_FOCUS_YES); -} - -void LLFolderBridge::createNewShirt(void* user_data) -{ - LLFolderBridge::createWearable((LLFolderBridge*)user_data, WT_SHIRT); -} - -void LLFolderBridge::createNewPants(void* user_data) -{ - LLFolderBridge::createWearable((LLFolderBridge*)user_data, WT_PANTS); -} - -void LLFolderBridge::createNewShoes(void* user_data) -{ - LLFolderBridge::createWearable((LLFolderBridge*)user_data, WT_SHOES); -} - -void LLFolderBridge::createNewSocks(void* user_data) -{ - LLFolderBridge::createWearable((LLFolderBridge*)user_data, WT_SOCKS); -} - -void LLFolderBridge::createNewJacket(void* user_data) -{ - LLFolderBridge::createWearable((LLFolderBridge*)user_data, WT_JACKET); -} - -void LLFolderBridge::createNewSkirt(void* user_data) -{ - LLFolderBridge::createWearable((LLFolderBridge*)user_data, WT_SKIRT); -} - -void LLFolderBridge::createNewGloves(void* user_data) -{ - LLFolderBridge::createWearable((LLFolderBridge*)user_data, WT_GLOVES); -} - -void LLFolderBridge::createNewUndershirt(void* user_data) -{ - LLFolderBridge::createWearable((LLFolderBridge*)user_data, WT_UNDERSHIRT); -} - -void LLFolderBridge::createNewUnderpants(void* user_data) -{ - LLFolderBridge::createWearable((LLFolderBridge*)user_data, WT_UNDERPANTS); -} - -void LLFolderBridge::createNewShape(void* user_data) -{ - LLFolderBridge::createWearable((LLFolderBridge*)user_data, WT_SHAPE); -} - -void LLFolderBridge::createNewSkin(void* user_data) -{ - LLFolderBridge::createWearable((LLFolderBridge*)user_data, WT_SKIN); -} - -void LLFolderBridge::createNewHair(void* user_data) -{ - LLFolderBridge::createWearable((LLFolderBridge*)user_data, WT_HAIR); -} - -void LLFolderBridge::createNewEyes(void* user_data) -{ - LLFolderBridge::createWearable((LLFolderBridge*)user_data, WT_EYES); -} - -// static -void LLFolderBridge::createWearable(LLFolderBridge* bridge, EWearableType type) -{ - if(!bridge) return; - LLUUID parent_id = bridge->getUUID(); - createWearable(parent_id, type); -} - -// Separate function so can be called by global menu as well as right-click -// menu. -// static -void LLFolderBridge::createWearable(const LLUUID &parent_id, EWearableType type) -{ - LLWearable* wearable = LLWearableList::instance().createNewWearable(type); - LLAssetType::EType asset_type = wearable->getAssetType(); - LLInventoryType::EType inv_type = LLInventoryType::IT_WEARABLE; - create_inventory_item(gAgent.getID(), gAgent.getSessionID(), - parent_id, wearable->getTransactionID(), wearable->getName(), - wearable->getDescription(), asset_type, inv_type, wearable->getType(), - wearable->getPermissions().getMaskNextOwner(), - LLPointer<LLInventoryCallback>(NULL)); -} - -void LLFolderBridge::modifyOutfit(BOOL append) -{ - LLInventoryModel* model = getInventoryModel(); - if(!model) return; - LLViewerInventoryCategory* cat = getCategory(); - if(!cat) return; - - // BAP - was: - // wear_inventory_category_on_avatar( cat, append ); - LLAppearanceManager::wearInventoryCategory( cat, FALSE, append ); -} - -// helper stuff -bool move_task_inventory_callback(const LLSD& notification, const LLSD& response, LLMoveInv* move_inv) -{ - LLFloaterOpenObject::LLCatAndWear* cat_and_wear = (LLFloaterOpenObject::LLCatAndWear* )move_inv->mUserData; - LLViewerObject* object = gObjectList.findObject(move_inv->mObjectID); - S32 option = LLNotification::getSelectedOption(notification, response); - - if(option == 0 && object) - { - if (cat_and_wear && cat_and_wear->mWear) - { - InventoryObjectList inventory_objects; - object->getInventoryContents(inventory_objects); - int contents_count = inventory_objects.size()-1; //subtract one for containing folder - - LLInventoryCopyAndWearObserver* inventoryObserver = new LLInventoryCopyAndWearObserver(cat_and_wear->mCatID, contents_count); - gInventory.addObserver(inventoryObserver); - } - - two_uuids_list_t::iterator move_it; - for (move_it = move_inv->mMoveList.begin(); - move_it != move_inv->mMoveList.end(); - ++move_it) - { - object->moveInventory(move_it->first, move_it->second); - } - - // update the UI. - dialog_refresh_all(); - } - - if (move_inv->mCallback) - { - move_inv->mCallback(option, move_inv->mUserData); - } - - delete move_inv; - return false; -} - -/* -Next functions intended to reorder items in the inventory folder and save order on server -Is now used for Favorites folder. - -*TODO: refactoring is needed with Favorites Bar functionality. Probably should be moved in LLInventoryModel -*/ -void saveItemsOrder(LLInventoryModel::item_array_t& items) -{ - int sortField = 0; - - // current order is saved by setting incremental values (1, 2, 3, ...) for the sort field - for (LLInventoryModel::item_array_t::iterator i = items.begin(); i != items.end(); ++i) - { - LLViewerInventoryItem* item = *i; - - item->setSortField(++sortField); - item->setComplete(TRUE); - item->updateServer(FALSE); - - gInventory.updateItem(item); - } - - gInventory.notifyObservers(); -} - -LLInventoryModel::item_array_t::iterator findItemByUUID(LLInventoryModel::item_array_t& items, const LLUUID& id) -{ - LLInventoryModel::item_array_t::iterator result = items.end(); - - for (LLInventoryModel::item_array_t::iterator i = items.begin(); i != items.end(); ++i) - { - if ((*i)->getUUID() == id) - { - result = i; - break; - } - } - - return result; -} - -void updateItemsOrder(LLInventoryModel::item_array_t& items, const LLUUID& srcItemId, const LLUUID& destItemId) -{ - LLViewerInventoryItem* srcItem = gInventory.getItem(srcItemId); - LLViewerInventoryItem* destItem = gInventory.getItem(destItemId); - - items.erase(findItemByUUID(items, srcItem->getUUID())); - items.insert(findItemByUUID(items, destItem->getUUID()), srcItem); -} - -BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item, - BOOL drop) -{ - LLInventoryModel* model = getInventoryModel(); - if(!model) return FALSE; - - // cannot drag into library - if(!isAgentInventory()) - { - return FALSE; - } - - LLVOAvatarSelf* avatar = gAgent.getAvatarObject(); - if(!avatar) return FALSE; - - LLToolDragAndDrop::ESource source = LLToolDragAndDrop::getInstance()->getSource(); - BOOL accept = FALSE; - LLViewerObject* object = NULL; - if(LLToolDragAndDrop::SOURCE_AGENT == source) - { - - BOOL is_movable = TRUE; - switch( inv_item->getActualType() ) - { - case LLAssetType::AT_ROOT_CATEGORY: - is_movable = FALSE; - break; - - case LLAssetType::AT_CATEGORY: - is_movable = !LLFolderType::lookupIsProtectedType(((LLInventoryCategory*)inv_item)->getPreferredType()); - break; - default: - break; - } - - const LLUUID trash_id = model->findCategoryUUIDForType(LLFolderType::FT_TRASH); - BOOL move_is_into_trash = (mUUID == trash_id) || model->isObjectDescendentOf(mUUID, trash_id); - const LLUUID current_outfit_id = model->findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT); - BOOL move_is_into_current_outfit = (mUUID == current_outfit_id); - BOOL move_is_into_outfit = (getCategory() && getCategory()->getPreferredType()==LLFolderType::FT_OUTFIT); - - if(is_movable && move_is_into_trash) - { - switch(inv_item->getType()) - { - case LLAssetType::AT_CLOTHING: - case LLAssetType::AT_BODYPART: - is_movable = !gAgentWearables.isWearingItem(inv_item->getUUID()); - break; - - case LLAssetType::AT_OBJECT: - is_movable = !avatar->isWearingAttachment(inv_item->getUUID()); - break; - default: - break; - } - } - - if ( is_movable ) - { - // Don't allow creating duplicates in the Calling Card/Friends - // subfolders, see bug EXT-1599. Check is item direct descendent - // of target folder and forbid item's movement if it so. - // Note: isItemDirectDescendentOfCategory checks if - // passed category is in the Calling Card/Friends folder - is_movable = ! LLFriendCardsManager::instance() - .isObjDirectDescendentOfCategory (inv_item, getCategory()); - } - - const LLUUID& favorites_id = model->findCategoryUUIDForType(LLFolderType::FT_FAVORITE); - - // we can move item inside a folder only if this folder is Favorites. See EXT-719 - accept = is_movable && ((mUUID != inv_item->getParentUUID()) || (mUUID == favorites_id)); - if(accept && drop) - { - if (inv_item->getType() == LLAssetType::AT_GESTURE - && LLGestureManager::instance().isGestureActive(inv_item->getUUID()) && move_is_into_trash) - { - LLGestureManager::instance().deactivateGesture(inv_item->getUUID()); - } - // If an item is being dragged between windows, unselect - // everything in the active window so that we don't follow - // the selection to its new location (which is very - // annoying). - if (LLFloaterInventory::getActiveInventory()) - { - LLInventoryPanel* active_panel = LLFloaterInventory::getActiveInventory()->getPanel(); - LLInventoryPanel* panel = dynamic_cast<LLInventoryPanel*>(mInventoryPanel.get()); - if (active_panel && (panel != active_panel)) - { - active_panel->unSelectAll(); - } - } - - // if dragging from/into favorites folder only reorder items - if ((mUUID == inv_item->getParentUUID()) && (favorites_id == mUUID)) - { - LLInventoryModel::cat_array_t cats; - LLInventoryModel::item_array_t items; - LLIsType is_type(LLAssetType::AT_LANDMARK); - model->collectDescendentsIf(favorites_id, cats, items, LLInventoryModel::EXCLUDE_TRASH, is_type); - - LLInventoryPanel* panel = dynamic_cast<LLInventoryPanel*>(mInventoryPanel.get()); - LLFolderViewItem* itemp = panel ? panel->getRootFolder()->getDraggingOverItem() : NULL; - if (itemp) - { - LLUUID srcItemId = inv_item->getUUID(); - LLUUID destItemId = itemp->getListener()->getUUID(); - - // update order - updateItemsOrder(items, srcItemId, destItemId); - - saveItemsOrder(items); - } - } - else if (favorites_id == mUUID) // if target is the favorites folder we use copy - { - copy_inventory_item( - gAgent.getID(), - inv_item->getPermissions().getOwner(), - inv_item->getUUID(), - mUUID, - std::string(), - LLPointer<LLInventoryCallback>(NULL)); - } - else if (move_is_into_current_outfit || move_is_into_outfit) - { - // BAP - should skip if dup. - if (move_is_into_current_outfit) - { - LLAppearanceManager::wearItem(inv_item); - } - else - { - LLPointer<LLInventoryCallback> cb = NULL; - link_inventory_item( - gAgent.getID(), - inv_item->getUUID(), - mUUID, - std::string(), - LLAssetType::AT_LINK, - cb); - } - } - else - { - // restamp if the move is into the trash. - LLInvFVBridge::changeItemParent( - model, - (LLViewerInventoryItem*)inv_item, - mUUID, - move_is_into_trash); - } - } - } - else if(LLToolDragAndDrop::SOURCE_WORLD == source) - { - // Make sure the object exists. If we allowed dragging from - // anonymous objects, it would be possible to bypass - // permissions. - object = gObjectList.findObject(inv_item->getParentUUID()); - if(!object) - { - llinfos << "Object not found for drop." << llendl; - return FALSE; - } - - // coming from a task. Need to figure out if the person can - // move/copy this item. - LLPermissions perm(inv_item->getPermissions()); - BOOL is_move = FALSE; - if((perm.allowCopyBy(gAgent.getID(), gAgent.getGroupID()) - && perm.allowTransferTo(gAgent.getID()))) -// || gAgent.isGodlike()) - - { - accept = TRUE; - } - else if(object->permYouOwner()) - { - // If the object cannot be copied, but the object the - // inventory is owned by the agent, then the item can be - // moved from the task to agent inventory. - is_move = TRUE; - accept = TRUE; - } - if(drop && accept) - { - LLMoveInv* move_inv = new LLMoveInv; - move_inv->mObjectID = inv_item->getParentUUID(); - two_uuids_t item_pair(mUUID, inv_item->getUUID()); - move_inv->mMoveList.push_back(item_pair); - move_inv->mCallback = NULL; - move_inv->mUserData = NULL; - if(is_move) - { - warn_move_inventory(object, move_inv); - } - else - { - LLNotification::Params params("MoveInventoryFromObject"); - params.functor.function(boost::bind(move_task_inventory_callback, _1, _2, move_inv)); - LLNotifications::instance().forceResponse(params, 0); - } - } - - } - else if(LLToolDragAndDrop::SOURCE_NOTECARD == source) - { - accept = TRUE; - if(drop) - { - copy_inventory_from_notecard(LLToolDragAndDrop::getInstance()->getObjectID(), - LLToolDragAndDrop::getInstance()->getSourceID(), inv_item); - } - } - else if(LLToolDragAndDrop::SOURCE_LIBRARY == source) - { - LLViewerInventoryItem* item = (LLViewerInventoryItem*)inv_item; - if(item && item->isComplete()) - { - accept = TRUE; - if(drop) - { - copy_inventory_item( - gAgent.getID(), - inv_item->getPermissions().getOwner(), - inv_item->getUUID(), - mUUID, - std::string(), - LLPointer<LLInventoryCallback>(NULL)); - } - } - } - else - { - llwarns << "unhandled drag source" << llendl; - } - return accept; -} - -// +=================================================+ -// | LLScriptBridge (DEPRECTED) | -// +=================================================+ - -LLUIImagePtr LLScriptBridge::getIcon() const -{ - return get_item_icon(LLAssetType::AT_SCRIPT, LLInventoryType::IT_LSL, 0, FALSE); -} - -// +=================================================+ -// | LLTextureBridge | -// +=================================================+ - -LLUIImagePtr LLTextureBridge::getIcon() const -{ - return get_item_icon(LLAssetType::AT_TEXTURE, mInvType, 0, FALSE); -} - -void LLTextureBridge::openItem() -{ - LLViewerInventoryItem* item = getItem(); - - if (item) - { - LLInvFVBridgeAction::doAction(item->getType(),mUUID,getInventoryModel()); - } -} - -// +=================================================+ -// | LLSoundBridge | -// +=================================================+ - -LLUIImagePtr LLSoundBridge::getIcon() const -{ - return get_item_icon(LLAssetType::AT_SOUND, LLInventoryType::IT_SOUND, 0, FALSE); -} - -void LLSoundBridge::openItem() -{ - LLViewerInventoryItem* item = getItem(); - - if (item) - { - LLInvFVBridgeAction::doAction(item->getType(),mUUID,getInventoryModel()); - } -/* -// Changed this back to the way it USED to work: -// only open the preview dialog through the contextual right-click menu -// double-click just plays the sound - - LLViewerInventoryItem* item = getItem(); - if(item) - { - openSoundPreview((void*)this); - //send_uuid_sound_trigger(item->getAssetUUID(), 1.0); - } -*/ -} - -void LLSoundBridge::previewItem() -{ - LLViewerInventoryItem* item = getItem(); - if(item) - { - send_sound_trigger(item->getAssetUUID(), 1.0); - } -} - -void LLSoundBridge::openSoundPreview(void* which) -{ - LLSoundBridge *me = (LLSoundBridge *)which; - LLFloaterReg::showInstance("preview_sound", LLSD(me->mUUID), TAKE_FOCUS_YES); -} - -void LLSoundBridge::buildContextMenu(LLMenuGL& menu, U32 flags) -{ - lldebugs << "LLTextureBridge::buildContextMenu()" << llendl; - std::vector<std::string> items; - std::vector<std::string> disabled_items; - - if(isInTrash()) - { - items.push_back(std::string("Purge Item")); - if (!isItemRemovable()) - { - disabled_items.push_back(std::string("Purge Item")); - } - - items.push_back(std::string("Restore Item")); - } - else - { - items.push_back(std::string("Sound Open")); - items.push_back(std::string("Properties")); - - getClipboardEntries(true, items, disabled_items, flags); - } - - items.push_back(std::string("Sound Separator")); - items.push_back(std::string("Sound Play")); - - hide_context_entries(menu, items, disabled_items); -} - -// +=================================================+ -// | LLLandmarkBridge | -// +=================================================+ - -LLLandmarkBridge::LLLandmarkBridge(LLInventoryPanel* inventory, const LLUUID& uuid, U32 flags/* = 0x00*/) : -LLItemBridge(inventory, uuid) -{ - mVisited = FALSE; - if (flags & LLInventoryItem::II_FLAGS_LANDMARK_VISITED) - { - mVisited = TRUE; - } -} - -LLUIImagePtr LLLandmarkBridge::getIcon() const -{ - return get_item_icon(LLAssetType::AT_LANDMARK, LLInventoryType::IT_LANDMARK, mVisited, FALSE); -} - -void LLLandmarkBridge::buildContextMenu(LLMenuGL& menu, U32 flags) -{ - std::vector<std::string> items; - std::vector<std::string> disabled_items; - - lldebugs << "LLLandmarkBridge::buildContextMenu()" << llendl; - if(isInTrash()) - { - items.push_back(std::string("Purge Item")); - if (!isItemRemovable()) - { - disabled_items.push_back(std::string("Purge Item")); - } - - items.push_back(std::string("Restore Item")); - } - else - { - items.push_back(std::string("Landmark Open")); - items.push_back(std::string("Properties")); - - getClipboardEntries(true, items, disabled_items, flags); - } - - items.push_back(std::string("Landmark Separator")); - items.push_back(std::string("About Landmark")); - - // Disable "About Landmark" menu item for - // multiple landmarks selected. Only one landmark - // info panel can be shown at a time. - if ((flags & FIRST_SELECTED_ITEM) == 0) - { - disabled_items.push_back(std::string("About Landmark")); - } - - hide_context_entries(menu, items, disabled_items); -} - -// Convenience function for the two functions below. -void teleport_via_landmark(const LLUUID& asset_id) -{ - gAgent.teleportViaLandmark( asset_id ); - - // we now automatically track the landmark you're teleporting to - // because you'll probably arrive at a telehub instead - LLFloaterWorldMap* floater_world_map = LLFloaterWorldMap::getInstance(); - if( floater_world_map ) - { - floater_world_map->trackLandmark( asset_id ); - } -} - -// virtual -void LLLandmarkBridge::performAction(LLFolderView* folder, LLInventoryModel* model, std::string action) -{ - if ("teleport" == action) - { - LLViewerInventoryItem* item = getItem(); - if(item) - { - teleport_via_landmark(item->getAssetUUID()); - } - } - else if ("about" == action) - { - LLViewerInventoryItem* item = getItem(); - if(item) - { - LLSD key; - key["type"] = "landmark"; - key["id"] = item->getUUID(); - - LLSideTray::getInstance()->showPanel("panel_places", key); - } - } - else - { - LLItemBridge::performAction(folder, model, action); - } -} - -static bool open_landmark_callback(const LLSD& notification, const LLSD& response) -{ - S32 option = LLNotification::getSelectedOption(notification, response); - - LLUUID asset_id = notification["payload"]["asset_id"].asUUID(); - if (option == 0) - { - teleport_via_landmark(asset_id); - } - - return false; -} -static LLNotificationFunctorRegistration open_landmark_callback_reg("TeleportFromLandmark", open_landmark_callback); - - -void LLLandmarkBridge::openItem() -{ - LLViewerInventoryItem* item = getItem(); - - if (item) - { - LLInvFVBridgeAction::doAction(item->getType(),mUUID,getInventoryModel()); - } -/* - LLViewerInventoryItem* item = getItem(); - if( item ) - { - // Opening (double-clicking) a landmark immediately teleports, - // but warns you the first time. - // open_landmark(item); - LLSD payload; - payload["asset_id"] = item->getAssetUUID(); - LLNotifications::instance().add("TeleportFromLandmark", LLSD(), payload); - } -*/ -} - - -// +=================================================+ -// | LLCallingCardObserver | -// +=================================================+ -void LLCallingCardObserver::changed(U32 mask) -{ - mBridgep->refreshFolderViewItem(); -} - -// +=================================================+ -// | LLCallingCardBridge | -// +=================================================+ - -LLCallingCardBridge::LLCallingCardBridge( LLInventoryPanel* inventory, const LLUUID& uuid ) : - LLItemBridge(inventory, uuid) -{ - mObserver = new LLCallingCardObserver(this); - LLAvatarTracker::instance().addObserver(mObserver); -} - -LLCallingCardBridge::~LLCallingCardBridge() -{ - LLAvatarTracker::instance().removeObserver(mObserver); - delete mObserver; -} - -void LLCallingCardBridge::refreshFolderViewItem() -{ - LLInventoryPanel* panel = dynamic_cast<LLInventoryPanel*>(mInventoryPanel.get()); - LLFolderViewItem* itemp = panel ? panel->getRootFolder()->getItemByID(mUUID) : NULL; - if (itemp) - { - itemp->refresh(); - } -} - -// virtual -void LLCallingCardBridge::performAction(LLFolderView* folder, LLInventoryModel* model, std::string action) -{ - if ("begin_im" == action) - { - LLViewerInventoryItem *item = getItem(); - if (item && (item->getCreatorUUID() != gAgent.getID()) && - (!item->getCreatorUUID().isNull())) - { - std::string callingcard_name; - gCacheName->getFullName(item->getCreatorUUID(), callingcard_name); - LLUUID session_id = gIMMgr->addSession(callingcard_name, IM_NOTHING_SPECIAL, item->getCreatorUUID()); - if (session_id != LLUUID::null) - { - LLIMFloater::show(session_id); - } - } - } - else if ("lure" == action) - { - LLViewerInventoryItem *item = getItem(); - if (item && (item->getCreatorUUID() != gAgent.getID()) && - (!item->getCreatorUUID().isNull())) - { - LLAvatarActions::offerTeleport(item->getCreatorUUID()); - } - } - else LLItemBridge::performAction(folder, model, action); -} - -LLUIImagePtr LLCallingCardBridge::getIcon() const -{ - BOOL online = FALSE; - LLViewerInventoryItem* item = getItem(); - if(item) - { - online = LLAvatarTracker::instance().isBuddyOnline(item->getCreatorUUID()); - } - return get_item_icon(LLAssetType::AT_CALLINGCARD, LLInventoryType::IT_CALLINGCARD, online, FALSE); -} - -std::string LLCallingCardBridge::getLabelSuffix() const -{ - LLViewerInventoryItem* item = getItem(); - if( item && LLAvatarTracker::instance().isBuddyOnline(item->getCreatorUUID()) ) - { - return LLItemBridge::getLabelSuffix() + " (online)"; - } - else - { - return LLItemBridge::getLabelSuffix(); - } -} - -void LLCallingCardBridge::openItem() -{ - LLViewerInventoryItem* item = getItem(); - - if (item) - { - LLInvFVBridgeAction::doAction(item->getType(),mUUID,getInventoryModel()); - } -/* - LLViewerInventoryItem* item = getItem(); - if(item && !item->getCreatorUUID().isNull()) - { - LLAvatarActions::showProfile(item->getCreatorUUID()); - } -*/ -} - -void LLCallingCardBridge::buildContextMenu(LLMenuGL& menu, U32 flags) -{ - lldebugs << "LLCallingCardBridge::buildContextMenu()" << llendl; - std::vector<std::string> items; - std::vector<std::string> disabled_items; - - if(isInTrash()) - { - items.push_back(std::string("Purge Item")); - if (!isItemRemovable()) - { - disabled_items.push_back(std::string("Purge Item")); - } - - items.push_back(std::string("Restore Item")); - } - else - { - items.push_back(std::string("Open")); - items.push_back(std::string("Properties")); - - getClipboardEntries(true, items, disabled_items, flags); - - LLInventoryItem* item = getItem(); - BOOL good_card = (item - && (LLUUID::null != item->getCreatorUUID()) - && (item->getCreatorUUID() != gAgent.getID())); - BOOL user_online = (LLAvatarTracker::instance().isBuddyOnline(item->getCreatorUUID())); - items.push_back(std::string("Send Instant Message Separator")); - items.push_back(std::string("Send Instant Message")); - items.push_back(std::string("Offer Teleport...")); - items.push_back(std::string("Conference Chat")); - - if (!good_card) - { - disabled_items.push_back(std::string("Send Instant Message")); - } - if (!good_card || !user_online) - { - disabled_items.push_back(std::string("Offer Teleport...")); - disabled_items.push_back(std::string("Conference Chat")); - } - } - hide_context_entries(menu, items, disabled_items); -} - -BOOL LLCallingCardBridge::dragOrDrop(MASK mask, BOOL drop, - EDragAndDropType cargo_type, - void* cargo_data) -{ - LLViewerInventoryItem* item = getItem(); - BOOL rv = FALSE; - if(item) - { - // check the type - switch(cargo_type) - { - case DAD_TEXTURE: - case DAD_SOUND: - case DAD_LANDMARK: - case DAD_SCRIPT: - case DAD_CLOTHING: - case DAD_OBJECT: - case DAD_NOTECARD: - case DAD_BODYPART: - case DAD_ANIMATION: - case DAD_GESTURE: - { - LLInventoryItem* inv_item = (LLInventoryItem*)cargo_data; - const LLPermissions& perm = inv_item->getPermissions(); - if(gInventory.getItem(inv_item->getUUID()) - && perm.allowOperationBy(PERM_TRANSFER, gAgent.getID())) - { - rv = TRUE; - if(drop) - { - LLToolDragAndDrop::giveInventory(item->getCreatorUUID(), - (LLInventoryItem*)cargo_data); - } - } - else - { - // It's not in the user's inventory (it's probably in - // an object's contents), so disallow dragging it here. - // You can't give something you don't yet have. - rv = FALSE; - } - break; - } - case DAD_CATEGORY: - { - LLInventoryCategory* inv_cat = (LLInventoryCategory*)cargo_data; - if( gInventory.getCategory( inv_cat->getUUID() ) ) - { - rv = TRUE; - if(drop) - { - LLToolDragAndDrop::giveInventoryCategory( - item->getCreatorUUID(), - inv_cat); - } - } - else - { - // It's not in the user's inventory (it's probably in - // an object's contents), so disallow dragging it here. - // You can't give something you don't yet have. - rv = FALSE; - } - break; - } - default: - break; - } - } - return rv; -} - -BOOL LLCallingCardBridge::removeItem() -{ - if (LLFriendCardsManager::instance().isItemInAnyFriendsList(getItem())) - { - LLAvatarActions::removeFriendDialog(getItem()->getCreatorUUID()); - return FALSE; - } - else - { - return LLItemBridge::removeItem(); - } -} -// +=================================================+ -// | LLNotecardBridge | -// +=================================================+ - -LLUIImagePtr LLNotecardBridge::getIcon() const -{ - return get_item_icon(LLAssetType::AT_NOTECARD, LLInventoryType::IT_NOTECARD, 0, FALSE); -} - -void LLNotecardBridge::openItem() -{ - LLViewerInventoryItem* item = getItem(); - - if (item) - { - LLInvFVBridgeAction::doAction(item->getType(),mUUID,getInventoryModel()); - } - -/* - LLViewerInventoryItem* item = getItem(); - if (item) - { - LLFloaterReg::showInstance("preview_notecard", LLSD(item->getUUID()), TAKE_FOCUS_YES); - } -*/ -} - - -// +=================================================+ -// | LLGestureBridge | -// +=================================================+ - -LLUIImagePtr LLGestureBridge::getIcon() const -{ - return get_item_icon(LLAssetType::AT_GESTURE, LLInventoryType::IT_GESTURE, 0, FALSE); -} - -LLFontGL::StyleFlags LLGestureBridge::getLabelStyle() const -{ - if( LLGestureManager::instance().isGestureActive(mUUID) ) - { - return LLFontGL::BOLD; - } - else - { - return LLFontGL::NORMAL; - } -} - -std::string LLGestureBridge::getLabelSuffix() const -{ - if( LLGestureManager::instance().isGestureActive(mUUID) ) - { - return LLItemBridge::getLabelSuffix() + " (active)"; - } - else - { - return LLItemBridge::getLabelSuffix(); - } -} - -// virtual -void LLGestureBridge::performAction(LLFolderView* folder, LLInventoryModel* model, std::string action) -{ - if ("activate" == action) - { - LLGestureManager::instance().activateGesture(mUUID); - - LLViewerInventoryItem* item = gInventory.getItem(mUUID); - if (!item) return; - - // Since we just changed the suffix to indicate (active) - // the server doesn't need to know, just the viewer. - gInventory.updateItem(item); - gInventory.notifyObservers(); - } - else if ("deactivate" == action) - { - LLGestureManager::instance().deactivateGesture(mUUID); - - LLViewerInventoryItem* item = gInventory.getItem(mUUID); - if (!item) return; - - // Since we just changed the suffix to indicate (active) - // the server doesn't need to know, just the viewer. - gInventory.updateItem(item); - gInventory.notifyObservers(); - } - else LLItemBridge::performAction(folder, model, action); -} - -void LLGestureBridge::openItem() -{ - LLViewerInventoryItem* item = getItem(); - - if (item) - { - LLInvFVBridgeAction::doAction(item->getType(),mUUID,getInventoryModel()); - } -/* - LLViewerInventoryItem* item = getItem(); - if (item) - { - LLPreviewGesture* preview = LLPreviewGesture::show(mUUID, LLUUID::null); - preview->setFocus(TRUE); - } -*/ -} - -BOOL LLGestureBridge::removeItem() -{ - // Force close the preview window, if it exists - LLGestureManager::instance().deactivateGesture(mUUID); - return LLItemBridge::removeItem(); -} - -void LLGestureBridge::buildContextMenu(LLMenuGL& menu, U32 flags) -{ - lldebugs << "LLGestureBridge::buildContextMenu()" << llendl; - std::vector<std::string> items; - std::vector<std::string> disabled_items; - if(isInTrash()) - { - items.push_back(std::string("Purge Item")); - if (!isItemRemovable()) - { - disabled_items.push_back(std::string("Purge Item")); - } - - items.push_back(std::string("Restore Item")); - } - else - { - items.push_back(std::string("Open")); - items.push_back(std::string("Properties")); - - getClipboardEntries(true, items, disabled_items, flags); - - items.push_back(std::string("Gesture Separator")); - items.push_back(std::string("Activate")); - items.push_back(std::string("Deactivate")); - } - hide_context_entries(menu, items, disabled_items); -} - -// +=================================================+ -// | LLAnimationBridge | -// +=================================================+ - -LLUIImagePtr LLAnimationBridge::getIcon() const -{ - return get_item_icon(LLAssetType::AT_ANIMATION, LLInventoryType::IT_ANIMATION, 0, FALSE); -} - -void LLAnimationBridge::buildContextMenu(LLMenuGL& menu, U32 flags) -{ - std::vector<std::string> items; - std::vector<std::string> disabled_items; - - lldebugs << "LLAnimationBridge::buildContextMenu()" << llendl; - if(isInTrash()) - { - items.push_back(std::string("Purge Item")); - if (!isItemRemovable()) - { - disabled_items.push_back(std::string("Purge Item")); - } - - items.push_back(std::string("Restore Item")); - } - else - { - items.push_back(std::string("Animation Open")); - items.push_back(std::string("Properties")); - - getClipboardEntries(true, items, disabled_items, flags); - } - - items.push_back(std::string("Animation Separator")); - items.push_back(std::string("Animation Play")); - items.push_back(std::string("Animation Audition")); - - hide_context_entries(menu, items, disabled_items); - -} - -// virtual -void LLAnimationBridge::performAction(LLFolderView* folder, LLInventoryModel* model, std::string action) -{ - if ((action == "playworld") || (action == "playlocal")) - { - if (getItem()) - { - LLPreviewAnim::e_activation_type activate = LLPreviewAnim::NONE; - if ("playworld" == action) activate = LLPreviewAnim::PLAY; - if ("playlocal" == action) activate = LLPreviewAnim::AUDITION; - - LLPreviewAnim* preview = LLFloaterReg::showTypedInstance<LLPreviewAnim>("preview_anim", LLSD(mUUID)); - if (preview) - { - preview->activate(activate); - } - } - } - else - { - LLItemBridge::performAction(folder, model, action); - } -} - -void LLAnimationBridge::openItem() -{ - LLViewerInventoryItem* item = getItem(); - - if (item) - { - LLInvFVBridgeAction::doAction(item->getType(),mUUID,getInventoryModel()); - } -/* - LLViewerInventoryItem* item = getItem(); - if (item) - { - LLFloaterReg::showInstance("preview_anim", LLSD(mUUID), TAKE_FOCUS_YES); - } -*/ -} - -// +=================================================+ -// | LLObjectBridge | -// +=================================================+ - -// static -LLUUID LLObjectBridge::sContextMenuItemID; - -LLObjectBridge::LLObjectBridge(LLInventoryPanel* inventory, const LLUUID& uuid, LLInventoryType::EType type, U32 flags) : -LLItemBridge(inventory, uuid), mInvType(type) -{ - mAttachPt = (flags & 0xff); // low bye of inventory flags - - mIsMultiObject = ( flags & LLInventoryItem::II_FLAGS_OBJECT_HAS_MULTIPLE_ITEMS ) ? TRUE: FALSE; -} - -BOOL LLObjectBridge::isItemRemovable() -{ - LLVOAvatarSelf* avatar = gAgent.getAvatarObject(); - if(!avatar) return FALSE; - if(avatar->isWearingAttachment(mUUID)) return FALSE; - return LLInvFVBridge::isItemRemovable(); -} - -LLUIImagePtr LLObjectBridge::getIcon() const -{ - return get_item_icon(LLAssetType::AT_OBJECT, mInvType, mAttachPt, mIsMultiObject ); -} - -LLInventoryObject* LLObjectBridge::getObject() const -{ - LLInventoryObject* object = NULL; - LLInventoryModel* model = getInventoryModel(); - if(model) - { - object = (LLInventoryObject*)model->getObject(mUUID); - } - return object; -} - -// virtual -void LLObjectBridge::performAction(LLFolderView* folder, LLInventoryModel* model, std::string action) -{ - if ("attach" == action) - { - LLUUID object_id = mUUID; - LLViewerInventoryItem* item; - item = (LLViewerInventoryItem*)gInventory.getItem(object_id); - if(item && gInventory.isObjectDescendentOf(object_id, gInventory.getRootFolderID())) - { - rez_attachment(item, NULL); - } - else if(item && item->isComplete()) - { - // must be in library. copy it to our inventory and put it on. - LLPointer<LLInventoryCallback> cb = new RezAttachmentCallback(0); - copy_inventory_item( - gAgent.getID(), - item->getPermissions().getOwner(), - item->getUUID(), - LLUUID::null, - std::string(), - cb); - } - gFocusMgr.setKeyboardFocus(NULL); - } - else if ("detach" == action) - { - LLInventoryItem* item = gInventory.getItem(mUUID); - if(item) - { - gMessageSystem->newMessageFast(_PREHASH_DetachAttachmentIntoInv); - gMessageSystem->nextBlockFast(_PREHASH_ObjectData); - gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); - gMessageSystem->addUUIDFast(_PREHASH_ItemID, item->getLinkedUUID()); - gMessageSystem->sendReliable( gAgent.getRegion()->getHost()); - } - // this object might have been selected, so let the selection manager know it's gone now - LLViewerObject *found_obj = - gObjectList.findObject(item->getUUID()); - if (found_obj) - { - LLSelectMgr::getInstance()->remove(found_obj); - } - else - { - llwarns << "object not found - ignoring" << llendl; - } - } - else LLItemBridge::performAction(folder, model, action); -} - -void LLObjectBridge::openItem() -{ - LLViewerInventoryItem* item = getItem(); - - if (item) - { - LLInvFVBridgeAction::doAction(item->getType(),mUUID,getInventoryModel()); - } - - LLSD key; - key["id"] = mUUID; - LLSideTray::getInstance()->showPanel("sidepanel_inventory", key); - - /* - LLFloaterReg::showInstance("properties", mUUID); - */ -} - -LLFontGL::StyleFlags LLObjectBridge::getLabelStyle() const -{ - U8 font = LLFontGL::NORMAL; - - LLVOAvatarSelf* avatar = gAgent.getAvatarObject(); - if( avatar && avatar->isWearingAttachment( mUUID ) ) - { - font |= LLFontGL::BOLD; - } - - LLInventoryItem* item = getItem(); - if (item && item->getIsLinkType()) - { - font |= LLFontGL::ITALIC; - } - - return (LLFontGL::StyleFlags)font; -} - -std::string LLObjectBridge::getLabelSuffix() const -{ - LLVOAvatarSelf* avatar = gAgent.getAvatarObject(); - if( avatar && avatar->isWearingAttachment( mUUID ) ) - { - std::string attachment_point_name = avatar->getAttachedPointName(mUUID); - LLStringUtil::toLower(attachment_point_name); - - LLStringUtil::format_map_t args; - args["[ATTACHMENT_POINT]"] = attachment_point_name.c_str(); - return LLItemBridge::getLabelSuffix() + LLTrans::getString("WornOnAttachmentPoint", args); - } - else - { - return LLItemBridge::getLabelSuffix(); - } -} - -void rez_attachment(LLViewerInventoryItem* item, LLViewerJointAttachment* attachment) -{ - LLSD payload; - payload["item_id"] = item->getLinkedUUID(); // Wear the base object in case this is a link. - - S32 attach_pt = 0; - if (gAgent.getAvatarObject() && attachment) - { - for (LLVOAvatar::attachment_map_t::iterator iter = gAgent.getAvatarObject()->mAttachmentPoints.begin(); - iter != gAgent.getAvatarObject()->mAttachmentPoints.end(); ++iter) - { - if (iter->second == attachment) - { - attach_pt = iter->first; - break; - } - } - } - - payload["attachment_point"] = attach_pt; - -#if !ENABLE_MULTIATTACHMENTS - if (attachment && attachment->getNumObjects() > 0) - { - LLNotifications::instance().add("ReplaceAttachment", LLSD(), payload, confirm_replace_attachment_rez); - } - else -#endif - { - LLNotifications::instance().forceResponse(LLNotification::Params("ReplaceAttachment").payload(payload), 0/*YES*/); - } -} - -bool confirm_replace_attachment_rez(const LLSD& notification, const LLSD& response) -{ - LLVOAvatar *avatarp = gAgent.getAvatarObject(); - - if (!avatarp->canAttachMoreObjects()) - { - LLSD args; - args["MAX_ATTACHMENTS"] = llformat("%d", MAX_AGENT_ATTACHMENTS); - LLNotifications::instance().add("MaxAttachmentsOnOutfit", args); - return false; - } - - S32 option = LLNotification::getSelectedOption(notification, response); - if (option == 0/*YES*/) - { - LLViewerInventoryItem* itemp = gInventory.getItem(notification["payload"]["item_id"].asUUID()); - - if (itemp) - { - LLMessageSystem* msg = gMessageSystem; - msg->newMessageFast(_PREHASH_RezSingleAttachmentFromInv); - msg->nextBlockFast(_PREHASH_AgentData); - msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); - msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); - msg->nextBlockFast(_PREHASH_ObjectData); - msg->addUUIDFast(_PREHASH_ItemID, itemp->getUUID()); - msg->addUUIDFast(_PREHASH_OwnerID, itemp->getPermissions().getOwner()); - U8 attachment_pt = notification["payload"]["attachment_point"].asInteger(); -#if ENABLE_MULTIATTACHMENTS - attachment_pt |= ATTACHMENT_ADD; -#endif - msg->addU8Fast(_PREHASH_AttachmentPt, attachment_pt); - pack_permissions_slam(msg, itemp->getFlags(), itemp->getPermissions()); - msg->addStringFast(_PREHASH_Name, itemp->getName()); - msg->addStringFast(_PREHASH_Description, itemp->getDescription()); - msg->sendReliable(gAgent.getRegion()->getHost()); - } - } - return false; -} -static LLNotificationFunctorRegistration confirm_replace_attachment_rez_reg("ReplaceAttachment", confirm_replace_attachment_rez); - -void LLObjectBridge::buildContextMenu(LLMenuGL& menu, U32 flags) -{ - std::vector<std::string> items; - std::vector<std::string> disabled_items; - if(isInTrash()) - { - items.push_back(std::string("Purge Item")); - if (!isItemRemovable()) - { - disabled_items.push_back(std::string("Purge Item")); - } - - items.push_back(std::string("Restore Item")); - } - else - { - LLInventoryItem* item = getItem(); - if (item && item->getIsLinkType()) - { - items.push_back(std::string("Goto Link")); - } - - items.push_back(std::string("Properties")); - - getClipboardEntries(true, items, disabled_items, flags); - - LLObjectBridge::sContextMenuItemID = mUUID; - - if(item) - { - LLVOAvatarSelf* avatarp = gAgent.getAvatarObject(); - if( !avatarp ) - { - return; - } - - if( avatarp->isWearingAttachment( mUUID ) ) - { - items.push_back(std::string("Detach From Yourself")); - } - else - if( !isInTrash() && !isLinkedObjectInTrash() ) - { - items.push_back(std::string("Attach Separator")); - items.push_back(std::string("Object Wear")); - items.push_back(std::string("Attach To")); - items.push_back(std::string("Attach To HUD")); - // commented out for DEV-32347 - //items.push_back(std::string("Restore to Last Position")); - - if (!avatarp->canAttachMoreObjects()) - { - disabled_items.push_back(std::string("Object Wear")); - disabled_items.push_back(std::string("Attach To")); - disabled_items.push_back(std::string("Attach To HUD")); - } - LLMenuGL* attach_menu = menu.findChildMenuByName("Attach To", TRUE); - LLMenuGL* attach_hud_menu = menu.findChildMenuByName("Attach To HUD", TRUE); - LLVOAvatar *avatarp = gAgent.getAvatarObject(); - if (attach_menu - && (attach_menu->getChildCount() == 0) - && attach_hud_menu - && (attach_hud_menu->getChildCount() == 0) - && avatarp) - { - for (LLVOAvatar::attachment_map_t::iterator iter = avatarp->mAttachmentPoints.begin(); - iter != avatarp->mAttachmentPoints.end(); ) - { - LLVOAvatar::attachment_map_t::iterator curiter = iter++; - LLViewerJointAttachment* attachment = curiter->second; - LLMenuItemCallGL::Params p; - std::string submenu_name = attachment->getName(); - if (LLTrans::getString(submenu_name) != "") - { - p.name = (" ")+LLTrans::getString(submenu_name)+" "; - } - else - { - p.name = submenu_name; - } - LLSD cbparams; - cbparams["index"] = curiter->first; - cbparams["label"] = attachment->getName(); - p.on_click.function_name = "Inventory.AttachObject"; - p.on_click.parameter = LLSD(attachment->getName()); - p.on_enable.function_name = "Attachment.Label"; - p.on_enable.parameter = cbparams; - LLView* parent = attachment->getIsHUDAttachment() ? attach_hud_menu : attach_menu; - LLUICtrlFactory::create<LLMenuItemCallGL>(p, parent); - } - } - } - } - } - hide_context_entries(menu, items, disabled_items); -} - -BOOL LLObjectBridge::renameItem(const std::string& new_name) -{ - if(!isItemRenameable()) - return FALSE; - LLPreview::dirty(mUUID); - LLInventoryModel* model = getInventoryModel(); - if(!model) - return FALSE; - LLViewerInventoryItem* item = getItem(); - if(item && (item->getName() != new_name)) - { - LLPointer<LLViewerInventoryItem> new_item = new LLViewerInventoryItem(item); - new_item->rename(new_name); - buildDisplayName(new_item, mDisplayName); - new_item->updateServer(FALSE); - model->updateItem(new_item); - - model->notifyObservers(); - - LLVOAvatarSelf* avatar = gAgent.getAvatarObject(); - if( avatar ) - { - LLViewerObject* obj = avatar->getWornAttachment( item->getUUID() ); - if( obj ) - { - LLSelectMgr::getInstance()->deselectAll(); - LLSelectMgr::getInstance()->addAsIndividual( obj, SELECT_ALL_TES, FALSE ); - LLSelectMgr::getInstance()->selectionSetObjectName( new_name ); - LLSelectMgr::getInstance()->deselectAll(); - } - } - } - // return FALSE because we either notified observers (& therefore - // rebuilt) or we didn't update. - return FALSE; -} - -// +=================================================+ -// | LLLSLTextBridge | -// +=================================================+ - -LLUIImagePtr LLLSLTextBridge::getIcon() const -{ - return get_item_icon(LLAssetType::AT_SCRIPT, LLInventoryType::IT_LSL, 0, FALSE); -} - -void LLLSLTextBridge::openItem() -{ - LLViewerInventoryItem* item = getItem(); - - if (item) - { - LLInvFVBridgeAction::doAction(item->getType(),mUUID,getInventoryModel()); - } - /* - LLViewerInventoryItem* item = getItem(); - if (item) - { - LLFloaterReg::showInstance("preview_script", LLSD(mUUID), TAKE_FOCUS_YES); - } - */ -} - -// +=================================================+ -// | LLWearableBridge | -// +=================================================+ - -// *NOTE: hack to get from avatar inventory to avatar -void wear_inventory_item_on_avatar( LLInventoryItem* item ) -{ - if(item) - { - lldebugs << "wear_inventory_item_on_avatar( " << item->getName() - << " )" << llendl; - - LLAppearanceManager::wearItem(item); - } -} - -void wear_add_inventory_item_on_avatar( LLInventoryItem* item ) -{ - if(item) - { - lldebugs << "wear_add_inventory_item_on_avatar( " << item->getName() - << " )" << llendl; - - LLWearableList::instance().getAsset(item->getAssetUUID(), - item->getName(), - item->getType(), - LLWearableBridge::onWearAddOnAvatarArrived, - new LLUUID(item->getUUID())); - } -} - -void remove_inventory_category_from_avatar( LLInventoryCategory* category ) -{ - if(!category) return; - lldebugs << "remove_inventory_category_from_avatar( " << category->getName() - << " )" << llendl; - - - if( gFloaterCustomize ) - { - gFloaterCustomize->askToSaveIfDirty( - boost::bind(remove_inventory_category_from_avatar_step2, _1, category->getUUID())); - } - else - { - remove_inventory_category_from_avatar_step2(TRUE, category->getUUID() ); - } -} - -struct OnRemoveStruct -{ - LLUUID mUUID; - OnRemoveStruct(const LLUUID& uuid): - mUUID(uuid) - { - } -}; - -void remove_inventory_category_from_avatar_step2( BOOL proceed, LLUUID category_id) -{ - - // Find all the wearables that are in the category's subtree. - lldebugs << "remove_inventory_category_from_avatar_step2()" << llendl; - if(proceed) - { - LLInventoryModel::cat_array_t cat_array; - LLInventoryModel::item_array_t item_array; - LLFindWearables is_wearable; - gInventory.collectDescendentsIf(category_id, - cat_array, - item_array, - LLInventoryModel::EXCLUDE_TRASH, - is_wearable); - S32 i; - S32 wearable_count = item_array.count(); - - LLInventoryModel::cat_array_t obj_cat_array; - LLInventoryModel::item_array_t obj_item_array; - LLIsType is_object( LLAssetType::AT_OBJECT ); - gInventory.collectDescendentsIf(category_id, - obj_cat_array, - obj_item_array, - LLInventoryModel::EXCLUDE_TRASH, - is_object); - S32 obj_count = obj_item_array.count(); - - // Find all gestures in this folder - LLInventoryModel::cat_array_t gest_cat_array; - LLInventoryModel::item_array_t gest_item_array; - LLIsType is_gesture( LLAssetType::AT_GESTURE ); - gInventory.collectDescendentsIf(category_id, - gest_cat_array, - gest_item_array, - LLInventoryModel::EXCLUDE_TRASH, - is_gesture); - S32 gest_count = gest_item_array.count(); - - if (wearable_count > 0) //Loop through wearables. If worn, remove. - { - for(i = 0; i < wearable_count; ++i) - { - if( gAgentWearables.isWearingItem (item_array.get(i)->getUUID()) ) - { - LLWearableList::instance().getAsset(item_array.get(i)->getAssetUUID(), - item_array.get(i)->getName(), - item_array.get(i)->getType(), - LLWearableBridge::onRemoveFromAvatarArrived, - new OnRemoveStruct(item_array.get(i)->getUUID())); - - } - } - } - - - if (obj_count > 0) - { - for(i = 0; i < obj_count; ++i) - { - gMessageSystem->newMessageFast(_PREHASH_DetachAttachmentIntoInv); - gMessageSystem->nextBlockFast(_PREHASH_ObjectData ); - gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID() ); - gMessageSystem->addUUIDFast(_PREHASH_ItemID, obj_item_array.get(i)->getUUID() ); - - gMessageSystem->sendReliable( gAgent.getRegion()->getHost() ); - - // this object might have been selected, so let the selection manager know it's gone now - LLViewerObject *found_obj = gObjectList.findObject( obj_item_array.get(i)->getUUID()); - if (found_obj) - { - LLSelectMgr::getInstance()->remove(found_obj); - } - else - { - llwarns << "object not found, ignoring" << llendl; - } - } - } - - if (gest_count > 0) - { - for(i = 0; i < gest_count; ++i) - { - if ( LLGestureManager::instance().isGestureActive( gest_item_array.get(i)->getUUID()) ) - { - LLGestureManager::instance().deactivateGesture( gest_item_array.get(i)->getUUID() ); - gInventory.updateItem( gest_item_array.get(i) ); - gInventory.notifyObservers(); - } - - } - } - } -} - -BOOL LLWearableBridge::renameItem(const std::string& new_name) -{ - if( gAgentWearables.isWearingItem( mUUID ) ) - { - gAgentWearables.setWearableName( mUUID, new_name ); - } - return LLItemBridge::renameItem(new_name); -} - -BOOL LLWearableBridge::isItemRemovable() -{ - if (gAgentWearables.isWearingItem(mUUID)) return FALSE; - return LLInvFVBridge::isItemRemovable(); -} - -std::string LLWearableBridge::getLabelSuffix() const -{ - if( gAgentWearables.isWearingItem( mUUID ) ) - { - return LLItemBridge::getLabelSuffix() + LLTrans::getString("worn"); - } - else - { - return LLItemBridge::getLabelSuffix(); - } -} - -LLUIImagePtr LLWearableBridge::getIcon() const -{ - return get_item_icon(mAssetType, mInvType, mWearableType, FALSE); -} - -// virtual -void LLWearableBridge::performAction(LLFolderView* folder, LLInventoryModel* model, std::string action) -{ - if ("wear" == action) - { - wearOnAvatar(); - } - else if ("wear_add" == action) - { - wearAddOnAvatar(); - } - else if ("edit" == action) - { - editOnAvatar(); - return; - } - else if ("take_off" == action) - { - if(gAgentWearables.isWearingItem(mUUID)) - { - LLViewerInventoryItem* item = getItem(); - if (item) - { - LLWearableList::instance().getAsset(item->getAssetUUID(), - item->getName(), - item->getType(), - LLWearableBridge::onRemoveFromAvatarArrived, - new OnRemoveStruct(mUUID)); - } - } - } - else LLItemBridge::performAction(folder, model, action); -} - -void LLWearableBridge::openItem() -{ - LLViewerInventoryItem* item = getItem(); - - if (item) - { - LLInvFVBridgeAction::doAction(item->getType(),mUUID,getInventoryModel()); - } - /* - if( isInTrash() ) - { - LLNotifications::instance().add("CannotWearTrash"); - } - else if(isAgentInventory()) - { - if( !gAgentWearables.isWearingItem( mUUID ) ) - { - wearOnAvatar(); - } - } - else - { - // must be in the inventory library. copy it to our inventory - // and put it on right away. - LLViewerInventoryItem* item = getItem(); - if(item && item->isComplete()) - { - LLPointer<LLInventoryCallback> cb = new WearOnAvatarCallback(); - copy_inventory_item( - gAgent.getID(), - item->getPermissions().getOwner(), - item->getUUID(), - LLUUID::null, - std::string(), - cb); - } - else if(item) - { - // *TODO: We should fetch the item details, and then do - // the operation above. - LLNotifications::instance().add("CannotWearInfoNotComplete"); - } - } - */ -} - -void LLWearableBridge::buildContextMenu(LLMenuGL& menu, U32 flags) -{ - lldebugs << "LLWearableBridge::buildContextMenu()" << llendl; - std::vector<std::string> items; - std::vector<std::string> disabled_items; - if(isInTrash()) - { - items.push_back(std::string("Purge Item")); - if (!isItemRemovable()) - { - disabled_items.push_back(std::string("Purge Item")); - } - - items.push_back(std::string("Restore Item")); - } - else - { // FWIW, it looks like SUPPRESS_OPEN_ITEM is not set anywhere - BOOL no_open = ((flags & SUPPRESS_OPEN_ITEM) == SUPPRESS_OPEN_ITEM); - - // If we have clothing, don't add "Open" as it's the same action as "Wear" SL-18976 - LLViewerInventoryItem* item = getItem(); - if( !no_open && item ) - { - no_open = (item->getType() == LLAssetType::AT_CLOTHING) || - (item->getType() == LLAssetType::AT_BODYPART); - } - if (!no_open) - { - items.push_back(std::string("Open")); - } - - if (item && item->getIsLinkType()) - { - items.push_back(std::string("Goto Link")); - } - - items.push_back(std::string("Properties")); - - getClipboardEntries(true, items, disabled_items, flags); - - items.push_back(std::string("Wearable Separator")); - - items.push_back(std::string("Wearable Wear")); - items.push_back(std::string("Wearable Add")); - items.push_back(std::string("Wearable Edit")); - - if ((flags & FIRST_SELECTED_ITEM) == 0) - { - disabled_items.push_back(std::string("Wearable Edit")); - } - // Don't allow items to be worn if their baseobj is in the trash. - if (isLinkedObjectInTrash()) - { - disabled_items.push_back(std::string("Wearable Wear")); - disabled_items.push_back(std::string("Wearable Add")); - disabled_items.push_back(std::string("Wearable Edit")); - } - - // Disable wear and take off based on whether the item is worn. - if(item) - { - switch (item->getType()) - { - case LLAssetType::AT_CLOTHING: - items.push_back(std::string("Take Off")); - case LLAssetType::AT_BODYPART: - if (gAgentWearables.isWearingItem(item->getUUID())) - { - disabled_items.push_back(std::string("Wearable Wear")); - disabled_items.push_back(std::string("Wearable Add")); - } - else - { - disabled_items.push_back(std::string("Take Off")); - } - break; - default: - break; - } - } - } - hide_context_entries(menu, items, disabled_items); -} - -// Called from menus -// static -BOOL LLWearableBridge::canWearOnAvatar(void* user_data) -{ - LLWearableBridge* self = (LLWearableBridge*)user_data; - if(!self) return FALSE; - if(!self->isAgentInventory()) - { - LLViewerInventoryItem* item = (LLViewerInventoryItem*)self->getItem(); - if(!item || !item->isComplete()) return FALSE; - } - return (!gAgentWearables.isWearingItem(self->mUUID)); -} - -// Called from menus -// static -void LLWearableBridge::onWearOnAvatar(void* user_data) -{ - LLWearableBridge* self = (LLWearableBridge*)user_data; - if(!self) return; - self->wearOnAvatar(); -} - -void LLWearableBridge::wearOnAvatar() -{ - // Don't wear anything until initial wearables are loaded, can - // destroy clothing items. - if (!gAgentWearables.areWearablesLoaded()) - { - LLNotifications::instance().add("CanNotChangeAppearanceUntilLoaded"); - return; - } - - LLViewerInventoryItem* item = getItem(); - if(item) - { - if(!isAgentInventory()) - { - LLPointer<LLInventoryCallback> cb = new WearOnAvatarCallback(); - copy_inventory_item( - gAgent.getID(), - item->getPermissions().getOwner(), - item->getUUID(), - LLUUID::null, - std::string(), - cb); - } - else - { - wear_inventory_item_on_avatar(item); - } - } -} - -void LLWearableBridge::wearAddOnAvatar() -{ - // Don't wear anything until initial wearables are loaded, can - // destroy clothing items. - if (!gAgentWearables.areWearablesLoaded()) - { - LLNotifications::instance().add("CanNotChangeAppearanceUntilLoaded"); - return; - } - - LLViewerInventoryItem* item = getItem(); - if(item) - { - if(!isAgentInventory()) - { - LLPointer<LLInventoryCallback> cb = new WearOnAvatarCallback(); - copy_inventory_item( - gAgent.getID(), - item->getPermissions().getOwner(), - item->getUUID(), - LLUUID::null, - std::string(), - cb); - } - else - { - wear_add_inventory_item_on_avatar(item); - } - } -} - -// static -void LLWearableBridge::onWearOnAvatarArrived( LLWearable* wearable, void* userdata ) -{ - LLUUID* item_id = (LLUUID*) userdata; - if(wearable) - { - LLViewerInventoryItem* item = NULL; - item = (LLViewerInventoryItem*)gInventory.getItem(*item_id); - if(item) - { - if(item->getAssetUUID() == wearable->getAssetID()) - { - gAgentWearables.setWearableItem(item, wearable); - gInventory.notifyObservers(); - //self->getFolderItem()->refreshFromRoot(); - } - else - { - llinfos << "By the time wearable asset arrived, its inv item already pointed to a different asset." << llendl; - } - } - } - delete item_id; -} - -// static -// BAP remove the "add" code path once everything is fully COF-ified. -void LLWearableBridge::onWearAddOnAvatarArrived( LLWearable* wearable, void* userdata ) -{ - LLUUID* item_id = (LLUUID*) userdata; - if(wearable) - { - LLViewerInventoryItem* item = NULL; - item = (LLViewerInventoryItem*)gInventory.getItem(*item_id); - if(item) - { - if(item->getAssetUUID() == wearable->getAssetID()) - { - bool do_append = true; - gAgentWearables.setWearableItem(item, wearable, do_append); - gInventory.notifyObservers(); - //self->getFolderItem()->refreshFromRoot(); - } - else - { - llinfos << "By the time wearable asset arrived, its inv item already pointed to a different asset." << llendl; - } - } - } - delete item_id; -} - -// static -BOOL LLWearableBridge::canEditOnAvatar(void* user_data) -{ - LLWearableBridge* self = (LLWearableBridge*)user_data; - if(!self) return FALSE; - - return (gAgentWearables.isWearingItem(self->mUUID)); -} - -// static -void LLWearableBridge::onEditOnAvatar(void* user_data) -{ - LLWearableBridge* self = (LLWearableBridge*)user_data; - if(self) - { - self->editOnAvatar(); - } -} - -void LLWearableBridge::editOnAvatar() -{ - const LLWearable* wearable = gAgentWearables.getWearableFromItemID(mUUID); - if( wearable ) - { - // Set the tab to the right wearable. - if (gFloaterCustomize) - gFloaterCustomize->setCurrentWearableType( wearable->getType() ); - - if( CAMERA_MODE_CUSTOMIZE_AVATAR != gAgent.getCameraMode() ) - { - // Start Avatar Customization - gAgent.changeCameraToCustomizeAvatar(); - } - } -} - -// static -BOOL LLWearableBridge::canRemoveFromAvatar(void* user_data) -{ - LLWearableBridge* self = (LLWearableBridge*)user_data; - if( self && (LLAssetType::AT_BODYPART != self->mAssetType) ) - { - return gAgentWearables.isWearingItem( self->mUUID ); - } - return FALSE; -} - -// static -void LLWearableBridge::onRemoveFromAvatar(void* user_data) -{ - LLWearableBridge* self = (LLWearableBridge*)user_data; - if(!self) return; - if(gAgentWearables.isWearingItem(self->mUUID)) - { - LLViewerInventoryItem* item = self->getItem(); - if (item) - { - LLUUID parent_id = item->getParentUUID(); - LLWearableList::instance().getAsset(item->getAssetUUID(), - item->getName(), - item->getType(), - onRemoveFromAvatarArrived, - new OnRemoveStruct(LLUUID(self->mUUID))); - } - } -} - -// static -void LLWearableBridge::onRemoveFromAvatarArrived(LLWearable* wearable, - void* userdata) -{ - OnRemoveStruct *on_remove_struct = (OnRemoveStruct*) userdata; - const LLUUID &item_id = gInventory.getLinkedItemID(on_remove_struct->mUUID); - if(wearable) - { - if( gAgentWearables.isWearingItem( item_id ) ) - { - EWearableType type = wearable->getType(); - - if( !(type==WT_SHAPE || type==WT_SKIN || type==WT_HAIR || type==WT_EYES ) ) //&& - //!((!gAgent.isTeen()) && ( type==WT_UNDERPANTS || type==WT_UNDERSHIRT )) ) - { - // MULTI_WEARABLE: FIXME HACK - always remove all - bool do_remove_all = false; - gAgentWearables.removeWearable( type, do_remove_all, 0 ); - } - } - } - - // Find and remove this item from the COF. - LLInventoryModel::item_array_t items = gInventory.collectLinkedItems(item_id, LLAppearanceManager::getCOF()); - llassert(items.size() == 1); // Should always have one and only one item linked to this in the COF. - for (LLInventoryModel::item_array_t::const_iterator iter = items.begin(); - iter != items.end(); - ++iter) - { - const LLViewerInventoryItem *linked_item = (*iter); - const LLUUID &item_id = linked_item->getUUID(); - gInventory.purgeObject(item_id); - } - gInventory.notifyObservers(); - - delete on_remove_struct; -} - -LLInvFVBridgeAction* LLInvFVBridgeAction::createAction(LLAssetType::EType asset_type, - const LLUUID& uuid,LLInventoryModel* model) -{ - LLInvFVBridgeAction* action = NULL; - switch(asset_type) - { - case LLAssetType::AT_TEXTURE: - action = new LLTextureBridgeAction(uuid,model); - break; - - case LLAssetType::AT_SOUND: - action = new LLSoundBridgeAction(uuid,model); - break; - - case LLAssetType::AT_LANDMARK: - action = new LLLandmarkBridgeAction(uuid,model); - break; - - case LLAssetType::AT_CALLINGCARD: - action = new LLCallingCardBridgeAction(uuid,model); - break; - - case LLAssetType::AT_OBJECT: - action = new LLObjectBridgeAction(uuid,model); - break; - - case LLAssetType::AT_NOTECARD: - action = new LLNotecardBridgeAction(uuid,model); - break; - - case LLAssetType::AT_ANIMATION: - action = new LLAnimationBridgeAction(uuid,model); - break; - - case LLAssetType::AT_GESTURE: - action = new LLGestureBridgeAction(uuid,model); - break; - - case LLAssetType::AT_LSL_TEXT: - action = new LLLSLTextBridgeAction(uuid,model); - break; - - case LLAssetType::AT_CLOTHING: - case LLAssetType::AT_BODYPART: - action = new LLWearableBridgeAction(uuid,model); - - break; - - default: - break; - } - return action; -} - -//static -void LLInvFVBridgeAction::doAction(LLAssetType::EType asset_type, - const LLUUID& uuid,LLInventoryModel* model) -{ - LLInvFVBridgeAction* action = createAction(asset_type,uuid,model); - if(action) - { - action->doIt(); - delete action; - } -} - -//static -void LLInvFVBridgeAction::doAction(const LLUUID& uuid, LLInventoryModel* model) -{ - LLAssetType::EType asset_type = model->getItem(uuid)->getType(); - LLInvFVBridgeAction* action = createAction(asset_type,uuid,model); - if(action) - { - action->doIt(); - delete action; - } -} - -LLViewerInventoryItem* LLInvFVBridgeAction::getItem() const -{ - if(mModel) - return (LLViewerInventoryItem*)mModel->getItem(mUUID); - return NULL; -} - -//virtual -void LLTextureBridgeAction::doIt() -{ - if (getItem()) - { - LLFloaterReg::showInstance("preview_texture", LLSD(mUUID), TAKE_FOCUS_YES); - } - - LLInvFVBridgeAction::doIt(); -} - -//virtual -void LLSoundBridgeAction::doIt() -{ - LLViewerInventoryItem* item = getItem(); - if(item) - { - LLFloaterReg::showInstance("preview_sound", LLSD(mUUID), TAKE_FOCUS_YES); - } - - LLInvFVBridgeAction::doIt(); -} - - -//virtual -void LLLandmarkBridgeAction::doIt() -{ - LLViewerInventoryItem* item = getItem(); - if( item ) - { - // Opening (double-clicking) a landmark immediately teleports, - // but warns you the first time. - LLSD payload; - payload["asset_id"] = item->getAssetUUID(); - LLNotifications::instance().add("TeleportFromLandmark", LLSD(), payload); - } - - LLInvFVBridgeAction::doIt(); -} - - -//virtual -void LLCallingCardBridgeAction::doIt() -{ - LLViewerInventoryItem* item = getItem(); - if(item && item->getCreatorUUID().notNull()) - { - LLAvatarActions::showProfile(item->getCreatorUUID()); - } - - LLInvFVBridgeAction::doIt(); -} - -//virtual -void -LLNotecardBridgeAction::doIt() -{ - LLViewerInventoryItem* item = getItem(); - if (item) - { - LLFloaterReg::showInstance("preview_notecard", LLSD(item->getUUID()), TAKE_FOCUS_YES); - } - - LLInvFVBridgeAction::doIt(); -} - -//virtual -void LLGestureBridgeAction::doIt() -{ - LLViewerInventoryItem* item = getItem(); - if (item) - { - LLPreviewGesture* preview = LLPreviewGesture::show(mUUID, LLUUID::null); - preview->setFocus(TRUE); - } - - LLInvFVBridgeAction::doIt(); -} - -//virtual -void LLAnimationBridgeAction::doIt() -{ - LLViewerInventoryItem* item = getItem(); - if (item) - { - LLFloaterReg::showInstance("preview_anim", LLSD(mUUID), TAKE_FOCUS_YES); - } - - LLInvFVBridgeAction::doIt(); -} - - -//virtual -void LLObjectBridgeAction::doIt() -{ - LLFloaterReg::showInstance("properties", mUUID); - - LLInvFVBridgeAction::doIt(); -} - - -//virtual -void LLLSLTextBridgeAction::doIt() -{ - LLViewerInventoryItem* item = getItem(); - if (item) - { - LLFloaterReg::showInstance("preview_script", LLSD(mUUID), TAKE_FOCUS_YES); - } - - LLInvFVBridgeAction::doIt(); -} - - -BOOL LLWearableBridgeAction::isInTrash() const -{ - if(!mModel) return FALSE; - const LLUUID trash_id = mModel->findCategoryUUIDForType(LLFolderType::FT_TRASH); - return mModel->isObjectDescendentOf(mUUID, trash_id); -} - -BOOL LLWearableBridgeAction::isAgentInventory() const -{ - if(!mModel) return FALSE; - if(gInventory.getRootFolderID() == mUUID) return TRUE; - return mModel->isObjectDescendentOf(mUUID, gInventory.getRootFolderID()); -} - -void LLWearableBridgeAction::wearOnAvatar() -{ - // Don't wear anything until initial wearables are loaded, can - // destroy clothing items. - if (!gAgentWearables.areWearablesLoaded()) - { - LLNotifications::instance().add("CanNotChangeAppearanceUntilLoaded"); - return; - } - - LLViewerInventoryItem* item = getItem(); - if(item) - { - if(!isAgentInventory()) - { - LLPointer<LLInventoryCallback> cb = new WearOnAvatarCallback(); - copy_inventory_item( - gAgent.getID(), - item->getPermissions().getOwner(), - item->getUUID(), - LLUUID::null, - std::string(), - cb); - } - else - { - wear_inventory_item_on_avatar(item); - } - } -} - -//virtual -void LLWearableBridgeAction::doIt() -{ - if(isInTrash()) - { - LLNotifications::instance().add("CannotWearTrash"); - } - else if(isAgentInventory()) - { - if(!gAgentWearables.isWearingItem(mUUID)) - { - wearOnAvatar(); - } - } - else - { - // must be in the inventory library. copy it to our inventory - // and put it on right away. - LLViewerInventoryItem* item = getItem(); - if(item && item->isComplete()) - { - LLPointer<LLInventoryCallback> cb = new WearOnAvatarCallback(); - copy_inventory_item( - gAgent.getID(), - item->getPermissions().getOwner(), - item->getUUID(), - LLUUID::null, - std::string(), - cb); - } - else if(item) - { - // *TODO: We should fetch the item details, and then do - // the operation above. - LLNotifications::instance().add("CannotWearInfoNotComplete"); - } - } - - LLInvFVBridgeAction::doIt(); -} - -// +=================================================+ -// | LLLinkItemBridge | -// +=================================================+ -// For broken links - -std::string LLLinkItemBridge::sPrefix("Link: "); - - -LLUIImagePtr LLLinkItemBridge::getIcon() const -{ - if (LLViewerInventoryItem *item = getItem()) - { - return get_item_icon(item->getActualType(), LLInventoryType::IT_NONE, 0, FALSE); - } - return get_item_icon(LLAssetType::AT_LINK, LLInventoryType::IT_NONE, 0, FALSE); -} - -void LLLinkItemBridge::buildContextMenu(LLMenuGL& menu, U32 flags) -{ - // *TODO: Translate - lldebugs << "LLLink::buildContextMenu()" << llendl; - std::vector<std::string> items; - std::vector<std::string> disabled_items; - - if(isInTrash()) - { - items.push_back(std::string("Purge Item")); - if (!isItemRemovable()) - { - disabled_items.push_back(std::string("Purge Item")); - } - - items.push_back(std::string("Restore Item")); - } - else - { - items.push_back(std::string("Delete")); - if (!isItemRemovable()) - { - disabled_items.push_back(std::string("Delete")); - } - } - hide_context_entries(menu, items, disabled_items); -} - - -// +=================================================+ -// | LLLinkBridge | -// +=================================================+ -// For broken links. - -std::string LLLinkFolderBridge::sPrefix("Link: "); - - -LLUIImagePtr LLLinkFolderBridge::getIcon() const -{ - LLFolderType::EType preferred_type = LLFolderType::FT_NONE; - if (LLViewerInventoryItem *item = getItem()) - { - if (const LLViewerInventoryCategory* cat = item->getLinkedCategory()) - { - preferred_type = cat->getPreferredType(); - } - } - return LLFolderBridge::getIcon(preferred_type); -} - -void LLLinkFolderBridge::buildContextMenu(LLMenuGL& menu, U32 flags) -{ - // *TODO: Translate - lldebugs << "LLLink::buildContextMenu()" << llendl; - std::vector<std::string> items; - std::vector<std::string> disabled_items; - - if(isInTrash()) - { - items.push_back(std::string("Purge Item")); - if (!isItemRemovable()) - { - disabled_items.push_back(std::string("Purge Item")); - } - - items.push_back(std::string("Restore Item")); - } - else - { - items.push_back(std::string("Goto Link")); - items.push_back(std::string("Delete")); - if (!isItemRemovable()) - { - disabled_items.push_back(std::string("Delete")); - } - } - hide_context_entries(menu, items, disabled_items); -} - -void LLLinkFolderBridge::performAction(LLFolderView* folder, LLInventoryModel* model, std::string action) -{ - if ("goto" == action) - { - gotoItem(folder); - return; - } - LLItemBridge::performAction(folder,model,action); -} - -void LLLinkFolderBridge::gotoItem(LLFolderView *folder) -{ - const LLUUID &cat_uuid = getFolderID(); - if (!cat_uuid.isNull()) - { - if (LLFolderViewItem *base_folder = folder->getItemByID(cat_uuid)) - { - if (LLInventoryModel* model = getInventoryModel()) - { - model->fetchDescendentsOf(cat_uuid); - } - base_folder->setOpen(TRUE); - folder->setSelectionFromRoot(base_folder,TRUE); - folder->scrollToShowSelection(); - } - } -} - -const LLUUID &LLLinkFolderBridge::getFolderID() const -{ - if (LLViewerInventoryItem *link_item = getItem()) - { - if (const LLViewerInventoryCategory *cat = link_item->getLinkedCategory()) - { - const LLUUID& cat_uuid = cat->getUUID(); - return cat_uuid; - } - } - return LLUUID::null; -} +/**
+ * @file llinventorybridge.cpp
+ * @brief Implementation of the Inventory-Folder-View-Bridge classes.
+ *
+ * $LicenseInfo:firstyear=2001&license=viewergpl$
+ *
+ * Copyright (c) 2001-2009, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include <utility> // for std::pair<>
+
+#include "llfloaterinventory.h"
+#include "llinventorybridge.h"
+
+#include "message.h"
+
+#include "llagent.h"
+#include "llagentwearables.h"
+#include "llcallingcard.h"
+#include "llcheckboxctrl.h" // for radio buttons
+#include "llfloaterreg.h"
+#include "llradiogroup.h"
+#include "llspinctrl.h"
+#include "lltextbox.h"
+#include "llui.h"
+
+#include "llviewercontrol.h"
+#include "llfirstuse.h"
+#include "llfoldertype.h"
+#include "llfloaterchat.h"
+#include "llfloatercustomize.h"
+#include "llfloaterproperties.h"
+#include "llfloaterworldmap.h"
+#include "llfocusmgr.h"
+#include "llfolderview.h"
+#include "llfriendcard.h"
+#include "llavataractions.h"
+#include "llgesturemgr.h"
+#include "lliconctrl.h"
+#include "llinventoryfunctions.h"
+#include "llinventorymodel.h"
+#include "llinventorypanel.h"
+#include "llinventoryclipboard.h"
+#include "lllineeditor.h"
+#include "llmenugl.h"
+#include "llpreviewanim.h"
+#include "llpreviewgesture.h"
+#include "llpreviewnotecard.h"
+#include "llpreviewscript.h"
+#include "llpreviewsound.h"
+#include "llpreviewtexture.h"
+#include "llresmgr.h"
+#include "llscrollcontainer.h"
+#include "llimview.h"
+#include "lltooldraganddrop.h"
+#include "llviewerfoldertype.h"
+#include "llviewertexturelist.h"
+#include "llviewerinventory.h"
+#include "llviewerobjectlist.h"
+#include "llviewerwindow.h"
+#include "llvoavatar.h"
+#include "llwearable.h"
+#include "llwearablelist.h"
+#include "llviewerassettype.h"
+#include "llviewermessage.h"
+#include "llviewerregion.h"
+#include "llvoavatarself.h"
+#include "lltabcontainer.h"
+#include "lluictrlfactory.h"
+#include "llselectmgr.h"
+#include "llsidetray.h"
+#include "llfloateropenobject.h"
+#include "lltrans.h"
+#include "llappearancemgr.h"
+#include "llimfloater.h"
+
+using namespace LLOldEvents;
+
+// Helpers
+// bug in busy count inc/dec right now, logic is complex... do we really need it?
+void inc_busy_count()
+{
+// gViewerWindow->getWindow()->incBusyCount();
+// check balance of these calls if this code is changed to ever actually
+// *do* something!
+}
+void dec_busy_count()
+{
+// gViewerWindow->getWindow()->decBusyCount();
+// check balance of these calls if this code is changed to ever actually
+// *do* something!
+}
+
+// Function declarations
+void wear_add_inventory_item_on_avatar(LLInventoryItem* item);
+void remove_inventory_category_from_avatar(LLInventoryCategory* category);
+void remove_inventory_category_from_avatar_step2( BOOL proceed, LLUUID category_id);
+bool move_task_inventory_callback(const LLSD& notification, const LLSD& response, LLMoveInv*);
+bool confirm_replace_attachment_rez(const LLSD& notification, const LLSD& response);
+
+std::string ICON_NAME[ICON_NAME_COUNT] =
+{
+ "Inv_Texture",
+ "Inv_Sound",
+ "Inv_CallingCard",
+ "Inv_CallingCard",
+ "Inv_Landmark",
+ "Inv_Landmark",
+ "Inv_Script",
+ "Inv_Clothing",
+ "Inv_Object",
+ "Inv_Object",
+ "Inv_Notecard",
+ "Inv_Skin",
+ "Inv_Snapshot",
+
+ "Inv_BodyShape",
+ "Inv_Skin",
+ "Inv_Hair",
+ "Inv_Eye",
+ "Inv_Shirt",
+ "Inv_Pants",
+ "Inv_Shoe",
+ "Inv_Socks",
+ "Inv_Jacket",
+ "Inv_Gloves",
+ "Inv_Undershirt",
+ "Inv_Underpants",
+ "Inv_Skirt",
+ "Inv_Alpha",
+ "Inv_Tattoo",
+
+ "Inv_Animation",
+ "Inv_Gesture",
+
+ "inv_item_linkitem.tga",
+ "inv_item_linkfolder.tga"
+};
+
+
+// +=================================================+
+// | LLInventoryPanelObserver |
+// +=================================================+
+void LLInventoryPanelObserver::changed(U32 mask)
+{
+ mIP->modelChanged(mask);
+}
+
+
+// +=================================================+
+// | LLInvFVBridge |
+// +=================================================+
+
+LLInvFVBridge::LLInvFVBridge(LLInventoryPanel* inventory, const LLUUID& uuid) :
+mUUID(uuid), mInvType(LLInventoryType::IT_NONE)
+{
+ mInventoryPanel = inventory->getHandle();
+}
+
+const std::string& LLInvFVBridge::getName() const
+{
+ LLInventoryObject* obj = getInventoryObject();
+ if(obj)
+ {
+ return obj->getName();
+ }
+ return LLStringUtil::null;
+}
+
+const std::string& LLInvFVBridge::getDisplayName() const
+{
+ return getName();
+}
+
+// Folders have full perms
+PermissionMask LLInvFVBridge::getPermissionMask() const
+{
+
+ return PERM_ALL;
+}
+
+// virtual
+LLFolderType::EType LLInvFVBridge::getPreferredType() const
+{
+ return LLFolderType::FT_NONE;
+}
+
+
+// Folders don't have creation dates.
+time_t LLInvFVBridge::getCreationDate() const
+{
+ return 0;
+}
+
+// Can be destoryed (or moved to trash)
+BOOL LLInvFVBridge::isItemRemovable()
+{
+ LLInventoryModel* model = getInventoryModel();
+ if(!model) return FALSE;
+ if(model->isObjectDescendentOf(mUUID, gInventory.getRootFolderID()))
+ {
+ return TRUE;
+ }
+ return FALSE;
+}
+
+// Sends an update to all link items that point to the base item.
+void LLInvFVBridge::renameLinkedItems(const LLUUID &item_id, const std::string& new_name)
+{
+ LLInventoryModel* model = getInventoryModel();
+ if(!model) return;
+
+ LLInventoryItem* itemp = model->getItem(mUUID);
+ if (!itemp) return;
+
+ if (itemp->getIsLinkType())
+ {
+ return;
+ }
+
+ LLInventoryModel::item_array_t item_array = model->collectLinkedItems(item_id);
+ for (LLInventoryModel::item_array_t::iterator iter = item_array.begin();
+ iter != item_array.end();
+ iter++)
+ {
+ LLViewerInventoryItem *linked_item = (*iter);
+ if (linked_item->getUUID() == item_id) continue;
+
+ LLPointer<LLViewerInventoryItem> new_item = new LLViewerInventoryItem(linked_item);
+ new_item->rename(new_name);
+ new_item->updateServer(FALSE);
+ model->updateItem(new_item);
+ // model->addChangedMask(LLInventoryObserver::LABEL, linked_item->getUUID());
+ }
+ model->notifyObservers();
+}
+
+// Can be moved to another folder
+BOOL LLInvFVBridge::isItemMovable() const
+{
+ return TRUE;
+}
+
+/*virtual*/
+/**
+ * @brief Adds this item into clipboard storage
+ */
+void LLInvFVBridge::cutToClipboard()
+{
+ if(isItemMovable())
+ {
+ LLInventoryClipboard::instance().cut(mUUID);
+ }
+}
+// *TODO: make sure this does the right thing
+void LLInvFVBridge::showProperties()
+{
+ LLSD key;
+ key["id"] = mUUID;
+ LLSideTray::getInstance()->showPanel("sidepanel_inventory", key);
+
+ LLFloaterReg::showInstance("properties", mUUID);
+}
+
+void LLInvFVBridge::removeBatch(LLDynamicArray<LLFolderViewEventListener*>& batch)
+{
+ // Deactivate gestures when moving them into Trash
+ LLInvFVBridge* bridge;
+ LLInventoryModel* model = getInventoryModel();
+ LLViewerInventoryItem* item = NULL;
+ LLViewerInventoryCategory* cat = NULL;
+ LLInventoryModel::cat_array_t descendent_categories;
+ LLInventoryModel::item_array_t descendent_items;
+ S32 count = batch.count();
+ S32 i,j;
+ for(i = 0; i < count; ++i)
+ {
+ bridge = (LLInvFVBridge*)(batch.get(i));
+ if(!bridge || !bridge->isItemRemovable()) continue;
+ item = (LLViewerInventoryItem*)model->getItem(bridge->getUUID());
+ if (item)
+ {
+ if(LLAssetType::AT_GESTURE == item->getType())
+ {
+ LLGestureManager::instance().deactivateGesture(item->getUUID());
+ }
+ }
+ }
+ for(i = 0; i < count; ++i)
+ {
+ bridge = (LLInvFVBridge*)(batch.get(i));
+ if(!bridge || !bridge->isItemRemovable()) continue;
+ cat = (LLViewerInventoryCategory*)model->getCategory(bridge->getUUID());
+ if (cat)
+ {
+ gInventory.collectDescendents( cat->getUUID(), descendent_categories, descendent_items, FALSE );
+ for (j=0; j<descendent_items.count(); j++)
+ {
+ if(LLAssetType::AT_GESTURE == descendent_items[j]->getType())
+ {
+ LLGestureManager::instance().deactivateGesture(descendent_items[j]->getUUID());
+ }
+ }
+ }
+ }
+ removeBatchNoCheck(batch);
+}
+
+void LLInvFVBridge::removeBatchNoCheck(LLDynamicArray<LLFolderViewEventListener*>& batch)
+{
+ // this method moves a bunch of items and folders to the trash. As
+ // per design guidelines for the inventory model, the message is
+ // built and the accounting is performed first. After all of that,
+ // we call LLInventoryModel::moveObject() to move everything
+ // around.
+ LLInvFVBridge* bridge;
+ LLInventoryModel* model = getInventoryModel();
+ if(!model) return;
+ LLMessageSystem* msg = gMessageSystem;
+ const LLUUID trash_id = model->findCategoryUUIDForType(LLFolderType::FT_TRASH);
+ LLViewerInventoryItem* item = NULL;
+ LLViewerInventoryCategory* cat = NULL;
+ std::vector<LLUUID> move_ids;
+ LLInventoryModel::update_map_t update;
+ bool start_new_message = true;
+ S32 count = batch.count();
+ S32 i;
+ for(i = 0; i < count; ++i)
+ {
+ bridge = (LLInvFVBridge*)(batch.get(i));
+ if(!bridge || !bridge->isItemRemovable()) continue;
+ item = (LLViewerInventoryItem*)model->getItem(bridge->getUUID());
+ if(item)
+ {
+ if(item->getParentUUID() == trash_id) continue;
+ move_ids.push_back(item->getUUID());
+ LLPreview::hide(item->getUUID());
+ --update[item->getParentUUID()];
+ ++update[trash_id];
+ if(start_new_message)
+ {
+ start_new_message = false;
+ msg->newMessageFast(_PREHASH_MoveInventoryItem);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->addBOOLFast(_PREHASH_Stamp, TRUE);
+ }
+ msg->nextBlockFast(_PREHASH_InventoryData);
+ msg->addUUIDFast(_PREHASH_ItemID, item->getUUID());
+ msg->addUUIDFast(_PREHASH_FolderID, trash_id);
+ msg->addString("NewName", NULL);
+ if(msg->isSendFullFast(_PREHASH_InventoryData))
+ {
+ start_new_message = true;
+ gAgent.sendReliableMessage();
+ gInventory.accountForUpdate(update);
+ update.clear();
+ }
+ }
+ }
+ if(!start_new_message)
+ {
+ start_new_message = true;
+ gAgent.sendReliableMessage();
+ gInventory.accountForUpdate(update);
+ update.clear();
+ }
+ for(i = 0; i < count; ++i)
+ {
+ bridge = (LLInvFVBridge*)(batch.get(i));
+ if(!bridge || !bridge->isItemRemovable()) continue;
+ cat = (LLViewerInventoryCategory*)model->getCategory(bridge->getUUID());
+ if(cat)
+ {
+ if(cat->getParentUUID() == trash_id) continue;
+ move_ids.push_back(cat->getUUID());
+ --update[cat->getParentUUID()];
+ ++update[trash_id];
+ if(start_new_message)
+ {
+ start_new_message = false;
+ msg->newMessageFast(_PREHASH_MoveInventoryFolder);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->addBOOL("Stamp", TRUE);
+ }
+ msg->nextBlockFast(_PREHASH_InventoryData);
+ msg->addUUIDFast(_PREHASH_FolderID, cat->getUUID());
+ msg->addUUIDFast(_PREHASH_ParentID, trash_id);
+ if(msg->isSendFullFast(_PREHASH_InventoryData))
+ {
+ start_new_message = true;
+ gAgent.sendReliableMessage();
+ gInventory.accountForUpdate(update);
+ update.clear();
+ }
+ }
+ }
+ if(!start_new_message)
+ {
+ gAgent.sendReliableMessage();
+ gInventory.accountForUpdate(update);
+ }
+
+ // move everything.
+ std::vector<LLUUID>::iterator it = move_ids.begin();
+ std::vector<LLUUID>::iterator end = move_ids.end();
+ for(; it != end; ++it)
+ {
+ gInventory.moveObject((*it), trash_id);
+ }
+
+ // notify inventory observers.
+ model->notifyObservers();
+}
+
+BOOL LLInvFVBridge::isClipboardPasteable() const
+{
+ if (!LLInventoryClipboard::instance().hasContents() || !isAgentInventory())
+ {
+ return FALSE;
+ }
+ LLInventoryModel* model = getInventoryModel();
+ if (!model)
+ {
+ return FALSE;
+ }
+
+ const LLUUID &agent_id = gAgent.getID();
+
+ LLDynamicArray<LLUUID> objects;
+ LLInventoryClipboard::instance().retrieve(objects);
+ S32 count = objects.count();
+ for(S32 i = 0; i < count; i++)
+ {
+ const LLUUID &item_id = objects.get(i);
+
+ // Can't paste folders
+ const LLInventoryCategory *cat = model->getCategory(item_id);
+ if (cat)
+ {
+ return FALSE;
+ }
+
+ const LLInventoryItem *item = model->getItem(item_id);
+ if (item)
+ {
+ if (!item->getPermissions().allowCopyBy(agent_id))
+ {
+ return FALSE;
+ }
+ }
+ }
+ return TRUE;
+}
+
+BOOL LLInvFVBridge::isClipboardPasteableAsLink() const
+{
+ if (!LLInventoryClipboard::instance().hasContents() || !isAgentInventory())
+ {
+ return FALSE;
+ }
+ const LLInventoryModel* model = getInventoryModel();
+ if (!model)
+ {
+ return FALSE;
+ }
+
+ LLDynamicArray<LLUUID> objects;
+ LLInventoryClipboard::instance().retrieve(objects);
+ S32 count = objects.count();
+ for(S32 i = 0; i < count; i++)
+ {
+ const LLInventoryItem *item = model->getItem(objects.get(i));
+ if (item)
+ {
+ if (!LLAssetType::lookupCanLink(item->getActualType()))
+ {
+ return FALSE;
+ }
+ }
+ const LLViewerInventoryCategory *cat = model->getCategory(objects.get(i));
+ if (cat && !LLFolderType::lookupIsProtectedType(cat->getPreferredType()))
+ {
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+void hide_context_entries(LLMenuGL& menu,
+ const std::vector<std::string> &entries_to_show,
+ const std::vector<std::string> &disabled_entries)
+{
+ const LLView::child_list_t *list = menu.getChildList();
+
+ LLView::child_list_t::const_iterator itor;
+ for (itor = list->begin(); itor != list->end(); ++itor)
+ {
+ std::string name = (*itor)->getName();
+
+ // descend into split menus:
+ LLMenuItemBranchGL* branchp = dynamic_cast<LLMenuItemBranchGL*>(*itor);
+ if ((name == "More") && branchp)
+ {
+ hide_context_entries(*branchp->getBranch(), entries_to_show, disabled_entries);
+ }
+
+
+ bool found = false;
+ std::vector<std::string>::const_iterator itor2;
+ for (itor2 = entries_to_show.begin(); itor2 != entries_to_show.end(); ++itor2)
+ {
+ if (*itor2 == name)
+ {
+ found = true;
+ }
+ }
+ if (!found)
+ {
+ (*itor)->setVisible(FALSE);
+ }
+ else
+ {
+ for (itor2 = disabled_entries.begin(); itor2 != disabled_entries.end(); ++itor2)
+ {
+ if (*itor2 == name)
+ {
+ (*itor)->setEnabled(FALSE);
+ }
+ }
+ }
+ }
+}
+
+// Helper for commonly-used entries
+void LLInvFVBridge::getClipboardEntries(bool show_asset_id,
+ std::vector<std::string> &items,
+ std::vector<std::string> &disabled_items, U32 flags)
+{
+ items.push_back(std::string("Rename"));
+ if (!isItemRenameable() || (flags & FIRST_SELECTED_ITEM) == 0)
+ {
+ disabled_items.push_back(std::string("Rename"));
+ }
+
+ if (show_asset_id)
+ {
+ items.push_back(std::string("Copy Asset UUID"));
+ if ( (! ( isItemPermissive() || gAgent.isGodlike() ) )
+ || (flags & FIRST_SELECTED_ITEM) == 0)
+ {
+ disabled_items.push_back(std::string("Copy Asset UUID"));
+ }
+ }
+
+ items.push_back(std::string("Copy Separator"));
+
+ items.push_back(std::string("Copy"));
+ if (!isItemCopyable())
+ {
+ disabled_items.push_back(std::string("Copy"));
+ }
+
+ items.push_back(std::string("Paste"));
+ if (!isClipboardPasteable() || (flags & FIRST_SELECTED_ITEM) == 0)
+ {
+ disabled_items.push_back(std::string("Paste"));
+ }
+
+ items.push_back(std::string("Paste As Link"));
+ if (!isClipboardPasteableAsLink() || (flags & FIRST_SELECTED_ITEM) == 0)
+ {
+ disabled_items.push_back(std::string("Paste As Link"));
+ }
+ items.push_back(std::string("Paste Separator"));
+
+ items.push_back(std::string("Delete"));
+ if (!isItemRemovable())
+ {
+ disabled_items.push_back(std::string("Delete"));
+ }
+
+ // If multiple items are selected, disable properties (if it exists).
+ if ((flags & FIRST_SELECTED_ITEM) == 0)
+ {
+ disabled_items.push_back(std::string("Properties"));
+ }
+}
+
+void LLInvFVBridge::buildContextMenu(LLMenuGL& menu, U32 flags)
+{
+ lldebugs << "LLInvFVBridge::buildContextMenu()" << llendl;
+ std::vector<std::string> items;
+ std::vector<std::string> disabled_items;
+ if(isInTrash())
+ {
+ items.push_back(std::string("PurgeItem"));
+ if (!isItemRemovable())
+ {
+ disabled_items.push_back(std::string("PurgeItem"));
+ }
+ items.push_back(std::string("RestoreItem"));
+ }
+ else
+ {
+ items.push_back(std::string("Open"));
+ items.push_back(std::string("Properties"));
+
+ getClipboardEntries(true, items, disabled_items, flags);
+ }
+ hide_context_entries(menu, items, disabled_items);
+}
+
+// *TODO: remove this
+BOOL LLInvFVBridge::startDrag(EDragAndDropType* type, LLUUID* id) const
+{
+ BOOL rv = FALSE;
+
+ const LLInventoryObject* obj = getInventoryObject();
+
+ if(obj)
+ {
+ *type = LLViewerAssetType::lookupDragAndDropType(obj->getActualType());
+ if(*type == DAD_NONE)
+ {
+ return FALSE;
+ }
+
+ *id = obj->getUUID();
+ //object_ids.put(obj->getUUID());
+
+ if (*type == DAD_CATEGORY)
+ {
+ gInventory.startBackgroundFetch(obj->getUUID());
+ }
+
+ rv = TRUE;
+ }
+
+ return rv;
+}
+
+LLInventoryObject* LLInvFVBridge::getInventoryObject() const
+{
+ LLInventoryObject* obj = NULL;
+ LLInventoryModel* model = getInventoryModel();
+ if(model)
+ {
+ obj = (LLInventoryObject*)model->getObject(mUUID);
+ }
+ return obj;
+}
+
+LLInventoryModel* LLInvFVBridge::getInventoryModel() const
+{
+ LLInventoryPanel* panel = dynamic_cast<LLInventoryPanel*>(mInventoryPanel.get());
+ return panel ? panel->getModel() : NULL;
+}
+
+BOOL LLInvFVBridge::isInTrash() const
+{
+ LLInventoryModel* model = getInventoryModel();
+ if(!model) return FALSE;
+ const LLUUID trash_id = model->findCategoryUUIDForType(LLFolderType::FT_TRASH);
+ return model->isObjectDescendentOf(mUUID, trash_id);
+}
+
+BOOL LLInvFVBridge::isLinkedObjectInTrash() const
+{
+ if (isInTrash()) return TRUE;
+
+ const LLInventoryObject *obj = getInventoryObject();
+ if (obj && obj->getIsLinkType())
+ {
+ LLInventoryModel* model = getInventoryModel();
+ if(!model) return FALSE;
+ const LLUUID trash_id = model->findCategoryUUIDForType(LLFolderType::FT_TRASH);
+ return model->isObjectDescendentOf(obj->getLinkedUUID(), trash_id);
+ }
+ return FALSE;
+}
+
+BOOL LLInvFVBridge::isAgentInventory() const
+{
+ const LLInventoryModel* model = getInventoryModel();
+ if(!model) return FALSE;
+ if(gInventory.getRootFolderID() == mUUID) return TRUE;
+ return model->isObjectDescendentOf(mUUID, gInventory.getRootFolderID());
+}
+
+BOOL LLInvFVBridge::isCOFFolder() const
+{
+ const LLInventoryModel* model = getInventoryModel();
+ if(!model) return TRUE;
+ const LLUUID cof_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT);
+ if (mUUID == cof_id || model->isObjectDescendentOf(mUUID, cof_id))
+ {
+ return TRUE;
+ }
+ return FALSE;
+}
+
+BOOL LLInvFVBridge::isItemPermissive() const
+{
+ return FALSE;
+}
+
+// static
+void LLInvFVBridge::changeItemParent(LLInventoryModel* model,
+ LLViewerInventoryItem* item,
+ const LLUUID& new_parent,
+ BOOL restamp)
+{
+ if(item->getParentUUID() != new_parent)
+ {
+ LLInventoryModel::update_list_t update;
+ LLInventoryModel::LLCategoryUpdate old_folder(item->getParentUUID(),-1);
+ update.push_back(old_folder);
+ LLInventoryModel::LLCategoryUpdate new_folder(new_parent, 1);
+ update.push_back(new_folder);
+ gInventory.accountForUpdate(update);
+
+ LLPointer<LLViewerInventoryItem> new_item = new LLViewerInventoryItem(item);
+ new_item->setParent(new_parent);
+ new_item->updateParentOnServer(restamp);
+ model->updateItem(new_item);
+ model->notifyObservers();
+ }
+}
+
+// static
+void LLInvFVBridge::changeCategoryParent(LLInventoryModel* model,
+ LLViewerInventoryCategory* cat,
+ const LLUUID& new_parent,
+ BOOL restamp)
+{
+ if(cat->getParentUUID() != new_parent)
+ {
+ LLInventoryModel::update_list_t update;
+ LLInventoryModel::LLCategoryUpdate old_folder(cat->getParentUUID(), -1);
+ update.push_back(old_folder);
+ LLInventoryModel::LLCategoryUpdate new_folder(new_parent, 1);
+ update.push_back(new_folder);
+ gInventory.accountForUpdate(update);
+
+ LLPointer<LLViewerInventoryCategory> new_cat = new LLViewerInventoryCategory(cat);
+ new_cat->setParent(new_parent);
+ new_cat->updateParentOnServer(restamp);
+ model->updateCategory(new_cat);
+ model->notifyObservers();
+ }
+}
+
+
+const std::string safe_inv_type_lookup(LLInventoryType::EType inv_type)
+{
+ const std::string rv= LLInventoryType::lookup(inv_type);
+ if(rv.empty())
+ {
+ return std::string("<invalid>");
+ }
+ return rv;
+}
+
+LLInvFVBridge* LLInvFVBridge::createBridge(LLAssetType::EType asset_type,
+ LLAssetType::EType actual_asset_type,
+ LLInventoryType::EType inv_type,
+ LLInventoryPanel* inventory,
+ const LLUUID& uuid,
+ U32 flags)
+{
+ LLInvFVBridge* new_listener = NULL;
+ switch(asset_type)
+ {
+ case LLAssetType::AT_TEXTURE:
+ if(!(inv_type == LLInventoryType::IT_TEXTURE || inv_type == LLInventoryType::IT_SNAPSHOT))
+ {
+ llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << safe_inv_type_lookup(inv_type) << " on uuid " << uuid << llendl;
+ }
+ new_listener = new LLTextureBridge(inventory, uuid, inv_type);
+ break;
+
+ case LLAssetType::AT_SOUND:
+ if(!(inv_type == LLInventoryType::IT_SOUND))
+ {
+ llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << safe_inv_type_lookup(inv_type) << " on uuid " << uuid << llendl;
+ }
+ new_listener = new LLSoundBridge(inventory, uuid);
+ break;
+
+ case LLAssetType::AT_LANDMARK:
+ if(!(inv_type == LLInventoryType::IT_LANDMARK))
+ {
+ llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << safe_inv_type_lookup(inv_type) << " on uuid " << uuid << llendl;
+ }
+ new_listener = new LLLandmarkBridge(inventory, uuid, flags);
+ break;
+
+ case LLAssetType::AT_CALLINGCARD:
+ if(!(inv_type == LLInventoryType::IT_CALLINGCARD))
+ {
+ llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << safe_inv_type_lookup(inv_type) << " on uuid " << uuid << llendl;
+ }
+ new_listener = new LLCallingCardBridge(inventory, uuid);
+ break;
+
+ case LLAssetType::AT_SCRIPT:
+ if(!(inv_type == LLInventoryType::IT_LSL))
+ {
+ llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << safe_inv_type_lookup(inv_type) << " on uuid " << uuid << llendl;
+ }
+ new_listener = new LLScriptBridge(inventory, uuid);
+ break;
+
+ case LLAssetType::AT_OBJECT:
+ if(!(inv_type == LLInventoryType::IT_OBJECT || inv_type == LLInventoryType::IT_ATTACHMENT))
+ {
+ llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << safe_inv_type_lookup(inv_type) << " on uuid " << uuid << llendl;
+ }
+ new_listener = new LLObjectBridge(inventory, uuid, inv_type, flags);
+ break;
+
+ case LLAssetType::AT_NOTECARD:
+ if(!(inv_type == LLInventoryType::IT_NOTECARD))
+ {
+ llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << safe_inv_type_lookup(inv_type) << " on uuid " << uuid << llendl;
+ }
+ new_listener = new LLNotecardBridge(inventory, uuid);
+ break;
+
+ case LLAssetType::AT_ANIMATION:
+ if(!(inv_type == LLInventoryType::IT_ANIMATION))
+ {
+ llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << safe_inv_type_lookup(inv_type) << " on uuid " << uuid << llendl;
+ }
+ new_listener = new LLAnimationBridge(inventory, uuid);
+ break;
+
+ case LLAssetType::AT_GESTURE:
+ if(!(inv_type == LLInventoryType::IT_GESTURE))
+ {
+ llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << safe_inv_type_lookup(inv_type) << " on uuid " << uuid << llendl;
+ }
+ new_listener = new LLGestureBridge(inventory, uuid);
+ break;
+
+ case LLAssetType::AT_LSL_TEXT:
+ if(!(inv_type == LLInventoryType::IT_LSL))
+ {
+ llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << safe_inv_type_lookup(inv_type) << " on uuid " << uuid << llendl;
+ }
+ new_listener = new LLLSLTextBridge(inventory, uuid);
+ break;
+
+ case LLAssetType::AT_CLOTHING:
+ case LLAssetType::AT_BODYPART:
+ if(!(inv_type == LLInventoryType::IT_WEARABLE))
+ {
+ llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << safe_inv_type_lookup(inv_type) << " on uuid " << uuid << llendl;
+ }
+ new_listener = new LLWearableBridge(inventory, uuid, asset_type, inv_type, (EWearableType)flags);
+ break;
+ case LLAssetType::AT_CATEGORY:
+ case LLAssetType::AT_ROOT_CATEGORY:
+ if (actual_asset_type == LLAssetType::AT_LINK_FOLDER)
+ {
+ // Create a link folder handler instead.
+ new_listener = new LLLinkFolderBridge(inventory, uuid);
+ break;
+ }
+ new_listener = new LLFolderBridge(inventory, uuid);
+ break;
+ case LLAssetType::AT_LINK:
+ // Only should happen for broken links.
+ new_listener = new LLLinkItemBridge(inventory, uuid);
+ break;
+ case LLAssetType::AT_LINK_FOLDER:
+ // Only should happen for broken links.
+ new_listener = new LLLinkItemBridge(inventory, uuid);
+ break;
+ default:
+ llinfos << "Unhandled asset type (llassetstorage.h): "
+ << (S32)asset_type << llendl;
+ break;
+ }
+
+ if (new_listener)
+ {
+ new_listener->mInvType = inv_type;
+ }
+
+ return new_listener;
+}
+
+void LLInvFVBridge::purgeItem(LLInventoryModel *model, const LLUUID &uuid)
+{
+ LLInventoryCategory* cat = model->getCategory(uuid);
+ if (cat)
+ {
+ model->purgeDescendentsOf(uuid);
+ model->notifyObservers();
+ }
+ LLInventoryObject* obj = model->getObject(uuid);
+ if (obj)
+ {
+ model->purgeObject(uuid);
+ model->notifyObservers();
+ }
+}
+
+// +=================================================+
+// | InventoryFVBridgeBuilder |
+// +=================================================+
+LLInvFVBridge* LLInventoryFVBridgeBuilder::createBridge(LLAssetType::EType asset_type,
+ LLAssetType::EType actual_asset_type,
+ LLInventoryType::EType inv_type,
+ LLInventoryPanel* inventory,
+ const LLUUID& uuid,
+ U32 flags /* = 0x00 */) const
+{
+ return LLInvFVBridge::createBridge(asset_type,
+ actual_asset_type,
+ inv_type,
+ inventory,
+ uuid,
+ flags);
+}
+
+// +=================================================+
+// | LLItemBridge |
+// +=================================================+
+
+void LLItemBridge::performAction(LLFolderView* folder, LLInventoryModel* model, std::string action)
+{
+ if ("goto" == action)
+ {
+ gotoItem(folder);
+ }
+ if ("open" == action)
+ {
+ openItem();
+ return;
+ }
+ else if ("properties" == action)
+ {
+ showProperties();
+ return;
+ }
+ else if ("purge" == action)
+ {
+ purgeItem(model, mUUID);
+ return;
+ }
+ else if ("restoreToWorld" == action)
+ {
+ restoreToWorld();
+ return;
+ }
+ else if ("restore" == action)
+ {
+ restoreItem();
+ return;
+ }
+ else if ("copy_uuid" == action)
+ {
+ // Single item only
+ LLInventoryItem* item = model->getItem(mUUID);
+ if(!item) return;
+ LLUUID asset_id = item->getAssetUUID();
+ std::string buffer;
+ asset_id.toString(buffer);
+
+ gViewerWindow->mWindow->copyTextToClipboard(utf8str_to_wstring(buffer));
+ return;
+ }
+ else if ("copy" == action)
+ {
+ copyToClipboard();
+ return;
+ }
+ else if ("paste" == action)
+ {
+ // Single item only
+ LLInventoryItem* itemp = model->getItem(mUUID);
+ if (!itemp) return;
+
+ LLFolderViewItem* folder_view_itemp = folder->getItemByID(itemp->getParentUUID());
+ if (!folder_view_itemp) return;
+
+ folder_view_itemp->getListener()->pasteFromClipboard();
+ return;
+ }
+ else if ("paste_link" == action)
+ {
+ // Single item only
+ LLInventoryItem* itemp = model->getItem(mUUID);
+ if (!itemp) return;
+
+ LLFolderViewItem* folder_view_itemp = folder->getItemByID(itemp->getParentUUID());
+ if (!folder_view_itemp) return;
+
+ folder_view_itemp->getListener()->pasteLinkFromClipboard();
+ return;
+ }
+}
+
+void LLItemBridge::selectItem()
+{
+ LLViewerInventoryItem* item = (LLViewerInventoryItem*)getItem();
+ if(item && !item->isComplete())
+ {
+ item->fetchFromServer();
+ }
+}
+
+void LLItemBridge::restoreItem()
+{
+ LLViewerInventoryItem* item = (LLViewerInventoryItem*)getItem();
+ if(item)
+ {
+ LLInventoryModel* model = getInventoryModel();
+ const LLUUID new_parent = model->findCategoryUUIDForType(LLFolderType::assetTypeToFolderType(item->getType()));
+ // do not restamp on restore.
+ LLInvFVBridge::changeItemParent(model, item, new_parent, FALSE);
+ }
+}
+
+void LLItemBridge::restoreToWorld()
+{
+ LLViewerInventoryItem* itemp = (LLViewerInventoryItem*)getItem();
+ if (itemp)
+ {
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessage("RezRestoreToWorld");
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+
+ msg->nextBlockFast(_PREHASH_InventoryData);
+ itemp->packMessage(msg);
+ msg->sendReliable(gAgent.getRegion()->getHost());
+ }
+
+ //Similar functionality to the drag and drop rez logic
+ BOOL remove_from_inventory = FALSE;
+
+ //remove local inventory copy, sim will deal with permissions and removing the item
+ //from the actual inventory if its a no-copy etc
+ if(!itemp->getPermissions().allowCopyBy(gAgent.getID()))
+ {
+ remove_from_inventory = TRUE;
+ }
+
+ // Check if it's in the trash. (again similar to the normal rez logic)
+ const LLUUID trash_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_TRASH);
+ if(gInventory.isObjectDescendentOf(itemp->getUUID(), trash_id))
+ {
+ remove_from_inventory = TRUE;
+ }
+
+ if(remove_from_inventory)
+ {
+ gInventory.deleteObject(itemp->getUUID());
+ gInventory.notifyObservers();
+ }
+}
+
+void LLItemBridge::gotoItem(LLFolderView *folder)
+{
+ LLInventoryObject *obj = getInventoryObject();
+ if (obj && obj->getIsLinkType())
+ {
+ LLInventoryPanel* active_panel = LLFloaterInventory::getActiveInventory()->getPanel();
+ if (active_panel)
+ {
+ active_panel->setSelection(obj->getLinkedUUID(), TAKE_FOCUS_NO);
+ }
+ }
+}
+
+LLUIImagePtr LLItemBridge::getIcon() const
+{
+ return LLUI::getUIImage(ICON_NAME[OBJECT_ICON_NAME]);
+}
+
+PermissionMask LLItemBridge::getPermissionMask() const
+{
+ LLViewerInventoryItem* item = getItem();
+ PermissionMask perm_mask = 0;
+ if(item)
+ {
+ BOOL copy = item->getPermissions().allowCopyBy(gAgent.getID());
+ BOOL mod = item->getPermissions().allowModifyBy(gAgent.getID());
+ BOOL xfer = item->getPermissions().allowOperationBy(PERM_TRANSFER,
+ gAgent.getID());
+
+ if (copy) perm_mask |= PERM_COPY;
+ if (mod) perm_mask |= PERM_MODIFY;
+ if (xfer) perm_mask |= PERM_TRANSFER;
+
+ }
+ return perm_mask;
+}
+
+const std::string& LLItemBridge::getDisplayName() const
+{
+ if(mDisplayName.empty())
+ {
+ buildDisplayName(getItem(), mDisplayName);
+ }
+ return mDisplayName;
+}
+
+void LLItemBridge::buildDisplayName(LLInventoryItem* item, std::string& name)
+{
+ if(item)
+ {
+ name.assign(item->getName());
+ }
+ else
+ {
+ name.assign(LLStringUtil::null);
+ }
+}
+
+LLFontGL::StyleFlags LLItemBridge::getLabelStyle() const
+{
+ U8 font = LLFontGL::NORMAL;
+
+ if( gAgentWearables.isWearingItem( mUUID ) )
+ {
+ // llinfos << "BOLD" << llendl;
+ font |= LLFontGL::BOLD;
+ }
+
+ const LLViewerInventoryItem* item = getItem();
+ if (item && item->getIsLinkType())
+ {
+ font |= LLFontGL::ITALIC;
+ }
+ return (LLFontGL::StyleFlags)font;
+}
+
+std::string LLItemBridge::getLabelSuffix() const
+{
+ // String table is loaded before login screen and inventory items are
+ // loaded after login, so LLTrans should be ready.
+ static std::string NO_COPY =LLTrans::getString("no_copy");
+ static std::string NO_MOD = LLTrans::getString("no_modify");
+ static std::string NO_XFER = LLTrans::getString("no_transfer");
+ static std::string LINK = LLTrans::getString("link");
+ static std::string BROKEN_LINK = LLTrans::getString("broken_link");
+ std::string suffix;
+ LLInventoryItem* item = getItem();
+ if(item)
+ {
+ // it's a bit confusing to put nocopy/nomod/etc on calling cards.
+ if(LLAssetType::AT_CALLINGCARD != item->getType()
+ && item->getPermissions().getOwner() == gAgent.getID())
+ {
+ BOOL broken_link = LLAssetType::lookupIsLinkType(item->getType());
+ if (broken_link) return BROKEN_LINK;
+
+ BOOL link = item->getIsLinkType();
+ if (link) return LINK;
+
+ BOOL copy = item->getPermissions().allowCopyBy(gAgent.getID());
+ if (!copy)
+ {
+ suffix += NO_COPY;
+ }
+ BOOL mod = item->getPermissions().allowModifyBy(gAgent.getID());
+ if (!mod)
+ {
+ suffix += NO_MOD;
+ }
+ BOOL xfer = item->getPermissions().allowOperationBy(PERM_TRANSFER,
+ gAgent.getID());
+ if (!xfer)
+ {
+ suffix += NO_XFER;
+ }
+ }
+ }
+ return suffix;
+}
+
+time_t LLItemBridge::getCreationDate() const
+{
+ LLViewerInventoryItem* item = getItem();
+ if (item)
+ {
+ return item->getCreationDate();
+ }
+ return 0;
+}
+
+
+BOOL LLItemBridge::isItemRenameable() const
+{
+ LLViewerInventoryItem* item = getItem();
+ if(item)
+ {
+ // (For now) Don't allow calling card rename since that may confuse users as to
+ // what the calling card points to.
+ if (item->getInventoryType() == LLInventoryType::IT_CALLINGCARD)
+ {
+ return FALSE;
+ }
+ return (item->getPermissions().allowModifyBy(gAgent.getID()));
+ }
+ return FALSE;
+}
+
+BOOL LLItemBridge::renameItem(const std::string& new_name)
+{
+ if(!isItemRenameable())
+ return FALSE;
+ LLPreview::dirty(mUUID);
+ LLInventoryModel* model = getInventoryModel();
+ if(!model)
+ return FALSE;
+ LLViewerInventoryItem* item = getItem();
+ if(item && (item->getName() != new_name))
+ {
+ LLPointer<LLViewerInventoryItem> new_item = new LLViewerInventoryItem(item);
+ new_item->rename(new_name);
+ buildDisplayName(new_item, mDisplayName);
+ new_item->updateServer(FALSE);
+ model->updateItem(new_item);
+
+ model->notifyObservers();
+ }
+ // return FALSE because we either notified observers (& therefore
+ // rebuilt) or we didn't update.
+ return FALSE;
+}
+
+
+BOOL LLItemBridge::removeItem()
+{
+ if(!isItemRemovable())
+ {
+ return FALSE;
+ }
+ // move it to the trash
+ LLPreview::hide(mUUID, TRUE);
+ LLInventoryModel* model = getInventoryModel();
+ if(!model) return FALSE;
+ const LLUUID trash_id = model->findCategoryUUIDForType(LLFolderType::FT_TRASH);
+ LLViewerInventoryItem* item = getItem();
+
+ // if item is not already in trash
+ if(item && !model->isObjectDescendentOf(mUUID, trash_id))
+ {
+ // move to trash, and restamp
+ LLInvFVBridge::changeItemParent(model, item, trash_id, TRUE);
+ // delete was successful
+ return TRUE;
+ }
+ else
+ {
+ // tried to delete already item in trash (should purge?)
+ return FALSE;
+ }
+}
+
+BOOL LLItemBridge::isItemCopyable() const
+{
+ LLViewerInventoryItem* item = getItem();
+ if (item)
+ {
+ // can't copy worn objects. DEV-15183
+ LLVOAvatarSelf *avatarp = gAgent.getAvatarObject();
+ if( !avatarp )
+ {
+ return FALSE;
+ }
+
+ if(avatarp->isWearingAttachment(mUUID))
+ {
+ return FALSE;
+ }
+
+ // All items can be copied, not all can be pasted.
+ // The only time an item can't be copied is if it's a link
+ // return (item->getPermissions().allowCopyBy(gAgent.getID()));
+ if (item->getIsLinkType())
+ {
+ return FALSE;
+ }
+ return TRUE;
+ }
+ return FALSE;
+}
+BOOL LLItemBridge::copyToClipboard() const
+{
+ if(isItemCopyable())
+ {
+ LLInventoryClipboard::instance().add(mUUID);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+LLViewerInventoryItem* LLItemBridge::getItem() const
+{
+ LLViewerInventoryItem* item = NULL;
+ LLInventoryModel* model = getInventoryModel();
+ if(model)
+ {
+ item = (LLViewerInventoryItem*)model->getItem(mUUID);
+ }
+ return item;
+}
+
+BOOL LLItemBridge::isItemPermissive() const
+{
+ LLViewerInventoryItem* item = getItem();
+ if(item)
+ {
+ U32 mask = item->getPermissions().getMaskBase();
+ if((mask & PERM_ITEM_UNRESTRICTED) == PERM_ITEM_UNRESTRICTED)
+ {
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+// +=================================================+
+// | LLFolderBridge |
+// +=================================================+
+
+LLFolderBridge* LLFolderBridge::sSelf=NULL;
+
+// Can be moved to another folder
+BOOL LLFolderBridge::isItemMovable() const
+{
+ LLInventoryObject* obj = getInventoryObject();
+ if(obj)
+ {
+ return (!LLFolderType::lookupIsProtectedType(((LLInventoryCategory*)obj)->getPreferredType()));
+ }
+ return FALSE;
+}
+
+void LLFolderBridge::selectItem()
+{
+}
+
+
+// Can be destroyed (or moved to trash)
+BOOL LLFolderBridge::isItemRemovable()
+{
+ LLInventoryModel* model = getInventoryModel();
+ if(!model)
+ {
+ return FALSE;
+ }
+
+ if(!model->isObjectDescendentOf(mUUID, gInventory.getRootFolderID()))
+ {
+ return FALSE;
+ }
+
+ LLVOAvatarSelf* avatar = gAgent.getAvatarObject();
+ if( !avatar )
+ {
+ return FALSE;
+ }
+
+ LLInventoryCategory* category = model->getCategory(mUUID);
+ if( !category )
+ {
+ return FALSE;
+ }
+
+ if(LLFolderType::lookupIsProtectedType(category->getPreferredType()))
+ {
+ return FALSE;
+ }
+
+ LLInventoryModel::cat_array_t descendent_categories;
+ LLInventoryModel::item_array_t descendent_items;
+ gInventory.collectDescendents( mUUID, descendent_categories, descendent_items, FALSE );
+
+ S32 i;
+ for( i = 0; i < descendent_categories.count(); i++ )
+ {
+ LLInventoryCategory* category = descendent_categories[i];
+ if(LLFolderType::lookupIsProtectedType(category->getPreferredType()))
+ {
+ return FALSE;
+ }
+ }
+
+ for( i = 0; i < descendent_items.count(); i++ )
+ {
+ LLInventoryItem* item = descendent_items[i];
+ if( (item->getType() == LLAssetType::AT_CLOTHING) ||
+ (item->getType() == LLAssetType::AT_BODYPART) )
+ {
+ if(gAgentWearables.isWearingItem(item->getUUID()))
+ {
+ return FALSE;
+ }
+ }
+ else
+ if( item->getType() == LLAssetType::AT_OBJECT )
+ {
+ if(avatar->isWearingAttachment(item->getUUID()))
+ {
+ return FALSE;
+ }
+ }
+ }
+
+ return TRUE;
+}
+
+BOOL LLFolderBridge::isUpToDate() const
+{
+ LLInventoryModel* model = getInventoryModel();
+ if(!model) return FALSE;
+ LLViewerInventoryCategory* category = (LLViewerInventoryCategory*)model->getCategory(mUUID);
+ if( !category )
+ {
+ return FALSE;
+ }
+
+ return category->getVersion() != LLViewerInventoryCategory::VERSION_UNKNOWN;
+}
+
+BOOL LLFolderBridge::isItemCopyable() const
+{
+ return TRUE;
+}
+
+BOOL LLFolderBridge::copyToClipboard() const
+{
+ if(isItemCopyable())
+ {
+ LLInventoryClipboard::instance().add(mUUID);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+BOOL LLFolderBridge::isClipboardPasteable() const
+{
+ if ( ! LLInvFVBridge::isClipboardPasteable() )
+ return FALSE;
+
+ // Don't allow pasting duplicates to the Calling Card/Friends subfolders, see bug EXT-1599
+ if ( LLFriendCardsManager::instance().isCategoryInFriendFolder( getCategory() ) )
+ {
+ LLInventoryModel* model = getInventoryModel();
+ if ( !model )
+ {
+ return FALSE;
+ }
+
+ LLDynamicArray<LLUUID> objects;
+ LLInventoryClipboard::instance().retrieve(objects);
+ const LLViewerInventoryCategory *current_cat = getCategory();
+
+ // Search for the direct descendent of current Friends subfolder among all pasted items,
+ // and return false if is found.
+ for(S32 i = objects.count() - 1; i >= 0; --i)
+ {
+ const LLUUID &obj_id = objects.get(i);
+ if ( LLFriendCardsManager::instance().isObjDirectDescendentOfCategory(model->getObject(obj_id), current_cat) )
+ {
+ return FALSE;
+ }
+ }
+
+ }
+ return TRUE;
+}
+
+BOOL LLFolderBridge::isClipboardPasteableAsLink() const
+{
+ // Check normal paste-as-link permissions
+ if (!LLInvFVBridge::isClipboardPasteableAsLink())
+ {
+ return FALSE;
+ }
+
+ const LLInventoryModel* model = getInventoryModel();
+ if (!model)
+ {
+ return FALSE;
+ }
+
+ const LLViewerInventoryCategory *current_cat = getCategory();
+ if (current_cat)
+ {
+ const BOOL is_in_friend_folder = LLFriendCardsManager::instance().isCategoryInFriendFolder( current_cat );
+ const LLUUID ¤t_cat_id = current_cat->getUUID();
+ LLDynamicArray<LLUUID> objects;
+ LLInventoryClipboard::instance().retrieve(objects);
+ S32 count = objects.count();
+ for(S32 i = 0; i < count; i++)
+ {
+ const LLUUID &obj_id = objects.get(i);
+ const LLInventoryCategory *cat = model->getCategory(obj_id);
+ if (cat)
+ {
+ const LLUUID &cat_id = cat->getUUID();
+ // Don't allow recursive pasting
+ if ((cat_id == current_cat_id) ||
+ model->isObjectDescendentOf(current_cat_id, cat_id))
+ {
+ return FALSE;
+ }
+ }
+ // Don't allow pasting duplicates to the Calling Card/Friends subfolders, see bug EXT-1599
+ if ( is_in_friend_folder )
+ {
+ // If object is direct descendent of current Friends subfolder than return false.
+ // Note: We can't use 'const LLInventoryCategory *cat', because it may be null
+ // in case type of obj_id is LLInventoryItem.
+ if ( LLFriendCardsManager::instance().isObjDirectDescendentOfCategory(model->getObject(obj_id), current_cat) )
+ {
+ return FALSE;
+ }
+ }
+ }
+ }
+ return TRUE;
+
+}
+
+BOOL LLFolderBridge::dragCategoryIntoFolder(LLInventoryCategory* inv_cat,
+ BOOL drop)
+{
+ // This should never happen, but if an inventory item is incorrectly parented,
+ // the UI will get confused and pass in a NULL.
+ if(!inv_cat) return FALSE;
+
+ LLInventoryModel* model = getInventoryModel();
+ if(!model) return FALSE;
+
+ LLVOAvatarSelf* avatar = gAgent.getAvatarObject();
+ if(!avatar) return FALSE;
+
+ // cannot drag categories into library
+ if(!isAgentInventory())
+ {
+ return FALSE;
+ }
+
+ // check to make sure source is agent inventory, and is represented there.
+ LLToolDragAndDrop::ESource source = LLToolDragAndDrop::getInstance()->getSource();
+ BOOL is_agent_inventory = (model->getCategory(inv_cat->getUUID()) != NULL)
+ && (LLToolDragAndDrop::SOURCE_AGENT == source);
+
+ BOOL accept = FALSE;
+ S32 i;
+ LLInventoryModel::cat_array_t descendent_categories;
+ LLInventoryModel::item_array_t descendent_items;
+ if(is_agent_inventory)
+ {
+ const LLUUID& cat_id = inv_cat->getUUID();
+
+ // Is the destination the trash?
+ const LLUUID trash_id = model->findCategoryUUIDForType(LLFolderType::FT_TRASH);
+ BOOL move_is_into_trash = (mUUID == trash_id)
+ || model->isObjectDescendentOf(mUUID, trash_id);
+ BOOL is_movable = (!LLFolderType::lookupIsProtectedType(inv_cat->getPreferredType()));
+ const LLUUID current_outfit_id = model->findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT);
+ BOOL move_is_into_current_outfit = (mUUID == current_outfit_id);
+ BOOL move_is_into_outfit = (getCategory() && getCategory()->getPreferredType()==LLFolderType::FT_OUTFIT);
+ if (move_is_into_current_outfit || move_is_into_outfit)
+ {
+ // BAP - restrictions?
+ is_movable = true;
+ }
+
+ if (mUUID == gInventory.findCategoryUUIDForType(LLFolderType::FT_FAVORITE))
+ {
+ is_movable = FALSE; // It's generally movable but not into Favorites folder. EXT-1604
+ }
+
+ if( is_movable )
+ {
+ gInventory.collectDescendents( cat_id, descendent_categories, descendent_items, FALSE );
+
+ for( i = 0; i < descendent_categories.count(); i++ )
+ {
+ LLInventoryCategory* category = descendent_categories[i];
+ if(LLFolderType::lookupIsProtectedType(category->getPreferredType()))
+ {
+ // ...can't move "special folders" like Textures
+ is_movable = FALSE;
+ break;
+ }
+ }
+
+ if( is_movable )
+ {
+ if( move_is_into_trash )
+ {
+ for( i = 0; i < descendent_items.count(); i++ )
+ {
+ LLInventoryItem* item = descendent_items[i];
+ if( (item->getType() == LLAssetType::AT_CLOTHING) ||
+ (item->getType() == LLAssetType::AT_BODYPART) )
+ {
+ if( gAgentWearables.isWearingItem( item->getUUID() ) )
+ {
+ is_movable = FALSE; // It's generally movable, but not into the trash!
+ break;
+ }
+ }
+ else
+ if( item->getType() == LLAssetType::AT_OBJECT )
+ {
+ if( avatar->isWearingAttachment( item->getUUID() ) )
+ {
+ is_movable = FALSE; // It's generally movable, but not into the trash!
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+
+
+ accept = is_movable
+ && (mUUID != cat_id) // Can't move a folder into itself
+ && (mUUID != inv_cat->getParentUUID()) // Avoid moves that would change nothing
+ && !(model->isObjectDescendentOf(mUUID, cat_id)); // Avoid circularity
+ if(accept && drop)
+ {
+ // Look for any gestures and deactivate them
+ if (move_is_into_trash)
+ {
+ for (i = 0; i < descendent_items.count(); i++)
+ {
+ LLInventoryItem* item = descendent_items[i];
+ if (item->getType() == LLAssetType::AT_GESTURE
+ && LLGestureManager::instance().isGestureActive(item->getUUID()))
+ {
+ LLGestureManager::instance().deactivateGesture(item->getUUID());
+ }
+ }
+ }
+ // if target is an outfit or current outfit folder we use link
+ if (move_is_into_current_outfit || move_is_into_outfit)
+ {
+#if SUPPORT_ENSEMBLES
+ // BAP - should skip if dup.
+ if (move_is_into_current_outfit)
+ {
+ LLAppearanceManager::wearEnsemble(inv_cat);
+ }
+ else
+ {
+ LLPointer<LLInventoryCallback> cb = NULL;
+ link_inventory_item(
+ gAgent.getID(),
+ inv_cat->getUUID(),
+ mUUID,
+ inv_cat->getName(),
+ LLAssetType::AT_LINK_FOLDER,
+ cb);
+ }
+#endif
+ }
+ else
+ {
+
+ // Reparent the folder and restamp children if it's moving
+ // into trash.
+ LLInvFVBridge::changeCategoryParent(
+ model,
+ (LLViewerInventoryCategory*)inv_cat,
+ mUUID,
+ move_is_into_trash);
+ }
+ }
+ }
+ else if(LLToolDragAndDrop::SOURCE_WORLD == source)
+ {
+ // content category has same ID as object itself
+ LLUUID object_id = inv_cat->getUUID();
+ LLUUID category_id = mUUID;
+ accept = move_inv_category_world_to_agent(object_id, category_id, drop);
+ }
+ return accept;
+}
+
+void warn_move_inventory(LLViewerObject* object, LLMoveInv* move_inv)
+{
+ const char* dialog = NULL;
+ if (object->flagScripted())
+ {
+ dialog = "MoveInventoryFromScriptedObject";
+ }
+ else
+ {
+ dialog = "MoveInventoryFromObject";
+ }
+ LLNotifications::instance().add(dialog, LLSD(), LLSD(), boost::bind(move_task_inventory_callback, _1, _2, move_inv));
+}
+
+// Move/copy all inventory items from the Contents folder of an in-world
+// object to the agent's inventory, inside a given category.
+BOOL move_inv_category_world_to_agent(const LLUUID& object_id,
+ const LLUUID& category_id,
+ BOOL drop,
+ void (*callback)(S32, void*),
+ void* user_data)
+{
+ // Make sure the object exists. If we allowed dragging from
+ // anonymous objects, it would be possible to bypass
+ // permissions.
+ // content category has same ID as object itself
+ LLViewerObject* object = gObjectList.findObject(object_id);
+ if(!object)
+ {
+ llinfos << "Object not found for drop." << llendl;
+ return FALSE;
+ }
+
+ // this folder is coming from an object, as there is only one folder in an object, the root,
+ // we need to collect the entire contents and handle them as a group
+ InventoryObjectList inventory_objects;
+ object->getInventoryContents(inventory_objects);
+
+ if (inventory_objects.empty())
+ {
+ llinfos << "Object contents not found for drop." << llendl;
+ return FALSE;
+ }
+
+ BOOL accept = TRUE;
+ BOOL is_move = FALSE;
+
+ // coming from a task. Need to figure out if the person can
+ // move/copy this item.
+ InventoryObjectList::iterator it = inventory_objects.begin();
+ InventoryObjectList::iterator end = inventory_objects.end();
+ for ( ; it != end; ++it)
+ {
+ // coming from a task. Need to figure out if the person can
+ // move/copy this item.
+ LLPermissions perm(((LLInventoryItem*)((LLInventoryObject*)(*it)))->getPermissions());
+ if((perm.allowCopyBy(gAgent.getID(), gAgent.getGroupID())
+ && perm.allowTransferTo(gAgent.getID())))
+// || gAgent.isGodlike())
+ {
+ accept = TRUE;
+ }
+ else if(object->permYouOwner())
+ {
+ // If the object cannot be copied, but the object the
+ // inventory is owned by the agent, then the item can be
+ // moved from the task to agent inventory.
+ is_move = TRUE;
+ accept = TRUE;
+ }
+ else
+ {
+ accept = FALSE;
+ break;
+ }
+ }
+
+ if(drop && accept)
+ {
+ it = inventory_objects.begin();
+ InventoryObjectList::iterator first_it = inventory_objects.begin();
+ LLMoveInv* move_inv = new LLMoveInv;
+ move_inv->mObjectID = object_id;
+ move_inv->mCategoryID = category_id;
+ move_inv->mCallback = callback;
+ move_inv->mUserData = user_data;
+
+ for ( ; it != end; ++it)
+ {
+ two_uuids_t two(category_id, (*it)->getUUID());
+ move_inv->mMoveList.push_back(two);
+ }
+
+ if(is_move)
+ {
+ // Callback called from within here.
+ warn_move_inventory(object, move_inv);
+ }
+ else
+ {
+ LLNotification::Params params("MoveInventoryFromObject");
+ params.functor.function(boost::bind(move_task_inventory_callback, _1, _2, move_inv));
+ LLNotifications::instance().forceResponse(params, 0);
+ }
+ }
+ return accept;
+}
+
+bool LLFindCOFValidItems::operator()(LLInventoryCategory* cat,
+ LLInventoryItem* item)
+{
+ // Valid COF items are:
+ // - links to wearables (body parts or clothing)
+ // - links to attachments
+ // - links to gestures
+ // - links to ensemble folders
+ LLViewerInventoryItem *linked_item = ((LLViewerInventoryItem*)item)->getLinkedItem(); // BAP - safe?
+ if (linked_item)
+ {
+ LLAssetType::EType type = linked_item->getType();
+ return (type == LLAssetType::AT_CLOTHING ||
+ type == LLAssetType::AT_BODYPART ||
+ type == LLAssetType::AT_GESTURE ||
+ type == LLAssetType::AT_OBJECT);
+ }
+ else
+ {
+ LLViewerInventoryCategory *linked_category = ((LLViewerInventoryItem*)item)->getLinkedCategory(); // BAP - safe?
+ // BAP remove AT_NONE support after ensembles are fully working?
+ return (linked_category &&
+ ((linked_category->getPreferredType() == LLFolderType::FT_NONE) ||
+ (LLFolderType::lookupIsEnsembleType(linked_category->getPreferredType()))));
+ }
+}
+
+
+bool LLFindWearables::operator()(LLInventoryCategory* cat,
+ LLInventoryItem* item)
+{
+ if(item)
+ {
+ if((item->getType() == LLAssetType::AT_CLOTHING)
+ || (item->getType() == LLAssetType::AT_BODYPART))
+ {
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+
+
+//Used by LLFolderBridge as callback for directory recursion.
+class LLRightClickInventoryFetchObserver : public LLInventoryFetchObserver
+{
+public:
+ LLRightClickInventoryFetchObserver() :
+ mCopyItems(false)
+ { };
+ LLRightClickInventoryFetchObserver(const LLUUID& cat_id, bool copy_items) :
+ mCatID(cat_id),
+ mCopyItems(copy_items)
+ { };
+ virtual void done()
+ {
+ // we've downloaded all the items, so repaint the dialog
+ LLFolderBridge::staticFolderOptionsMenu();
+
+ gInventory.removeObserver(this);
+ delete this;
+ }
+
+
+protected:
+ LLUUID mCatID;
+ bool mCopyItems;
+
+};
+
+//Used by LLFolderBridge as callback for directory recursion.
+class LLRightClickInventoryFetchDescendentsObserver : public LLInventoryFetchDescendentsObserver
+{
+public:
+ LLRightClickInventoryFetchDescendentsObserver(bool copy_items) : mCopyItems(copy_items) {}
+ ~LLRightClickInventoryFetchDescendentsObserver() {}
+ virtual void done();
+protected:
+ bool mCopyItems;
+};
+
+void LLRightClickInventoryFetchDescendentsObserver::done()
+{
+ // Avoid passing a NULL-ref as mCompleteFolders.front() down to
+ // gInventory.collectDescendents()
+ if( mCompleteFolders.empty() )
+ {
+ llwarns << "LLRightClickInventoryFetchDescendentsObserver::done with empty mCompleteFolders" << llendl;
+ dec_busy_count();
+ gInventory.removeObserver(this);
+ delete this;
+ return;
+ }
+
+ // What we do here is get the complete information on the items in
+ // the library, and set up an observer that will wait for that to
+ // happen.
+ LLInventoryModel::cat_array_t cat_array;
+ LLInventoryModel::item_array_t item_array;
+ gInventory.collectDescendents(mCompleteFolders.front(),
+ cat_array,
+ item_array,
+ LLInventoryModel::EXCLUDE_TRASH);
+ S32 count = item_array.count();
+#if 0 // HACK/TODO: Why?
+ // This early causes a giant menu to get produced, and doesn't seem to be needed.
+ if(!count)
+ {
+ llwarns << "Nothing fetched in category " << mCompleteFolders.front()
+ << llendl;
+ dec_busy_count();
+ gInventory.removeObserver(this);
+ delete this;
+ return;
+ }
+#endif
+
+ LLRightClickInventoryFetchObserver* outfit;
+ outfit = new LLRightClickInventoryFetchObserver(mCompleteFolders.front(), mCopyItems);
+ LLInventoryFetchObserver::item_ref_t ids;
+ for(S32 i = 0; i < count; ++i)
+ {
+ ids.push_back(item_array.get(i)->getUUID());
+ }
+
+ // clean up, and remove this as an observer since the call to the
+ // outfit could notify observers and throw us into an infinite
+ // loop.
+ dec_busy_count();
+ gInventory.removeObserver(this);
+ delete this;
+
+ // increment busy count and either tell the inventory to check &
+ // call done, or add this object to the inventory for observation.
+ inc_busy_count();
+
+ // do the fetch
+ outfit->fetchItems(ids);
+ outfit->done(); //Not interested in waiting and this will be right 99% of the time.
+//Uncomment the following code for laggy Inventory UI.
+/* if(outfit->isEverythingComplete())
+ {
+ // everything is already here - call done.
+ outfit->done();
+ }
+ else
+ {
+ // it's all on it's way - add an observer, and the inventory
+ // will call done for us when everything is here.
+ gInventory.addObserver(outfit);
+ }*/
+}
+
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLInventoryWearObserver
+//
+// Observer for "copy and wear" operation to support knowing
+// when the all of the contents have been added to inventory.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+class LLInventoryCopyAndWearObserver : public LLInventoryObserver
+{
+public:
+ LLInventoryCopyAndWearObserver(const LLUUID& cat_id, int count) :mCatID(cat_id), mContentsCount(count), mFolderAdded(FALSE) {}
+ virtual ~LLInventoryCopyAndWearObserver() {}
+ virtual void changed(U32 mask);
+
+protected:
+ LLUUID mCatID;
+ int mContentsCount;
+ BOOL mFolderAdded;
+};
+
+
+
+void LLInventoryCopyAndWearObserver::changed(U32 mask)
+{
+ if((mask & (LLInventoryObserver::ADD)) != 0)
+ {
+ if (!mFolderAdded)
+ {
+ const std::set<LLUUID>& changed_items = gInventory.getChangedIDs();
+
+ std::set<LLUUID>::const_iterator id_it = changed_items.begin();
+ std::set<LLUUID>::const_iterator id_end = changed_items.end();
+ for (;id_it != id_end; ++id_it)
+ {
+ if ((*id_it) == mCatID)
+ {
+ mFolderAdded = TRUE;
+ break;
+ }
+ }
+ }
+
+ if (mFolderAdded)
+ {
+ LLViewerInventoryCategory* category = gInventory.getCategory(mCatID);
+
+ if (NULL == category)
+ {
+ llwarns << "gInventory.getCategory(" << mCatID
+ << ") was NULL" << llendl;
+ }
+ else
+ {
+ if (category->getDescendentCount() ==
+ mContentsCount)
+ {
+ gInventory.removeObserver(this);
+ LLAppearanceManager::wearInventoryCategory(category, FALSE, TRUE);
+ delete this;
+ }
+ }
+ }
+
+ }
+}
+
+
+
+void LLFolderBridge::performAction(LLFolderView* folder, LLInventoryModel* model, std::string action)
+{
+ if ("open" == action)
+ {
+ openItem();
+ return;
+ }
+ else if ("paste" == action)
+ {
+ pasteFromClipboard();
+ return;
+ }
+ else if ("paste_link" == action)
+ {
+ pasteLinkFromClipboard();
+ return;
+ }
+ else if ("properties" == action)
+ {
+ showProperties();
+ return;
+ }
+ else if ("replaceoutfit" == action)
+ {
+ modifyOutfit(FALSE);
+ return;
+ }
+#if SUPPORT_ENSEMBLES
+ else if ("wearasensemble" == action)
+ {
+ LLInventoryModel* model = getInventoryModel();
+ if(!model) return;
+ LLViewerInventoryCategory* cat = getCategory();
+ if(!cat) return;
+ LLAppearanceManager::wearEnsemble(cat,true);
+ return;
+ }
+#endif
+ else if ("addtooutfit" == action)
+ {
+ modifyOutfit(TRUE);
+ return;
+ }
+ else if ("copy" == action)
+ {
+ copyToClipboard();
+ return;
+ }
+ else if ("removefromoutfit" == action)
+ {
+ LLInventoryModel* model = getInventoryModel();
+ if(!model) return;
+ LLViewerInventoryCategory* cat = getCategory();
+ if(!cat) return;
+
+ remove_inventory_category_from_avatar ( cat );
+ return;
+ }
+ else if ("purge" == action)
+ {
+ purgeItem(model, mUUID);
+ return;
+ }
+ else if ("restore" == action)
+ {
+ restoreItem();
+ return;
+ }
+}
+
+void LLFolderBridge::openItem()
+{
+ lldebugs << "LLFolderBridge::openItem()" << llendl;
+ LLInventoryModel* model = getInventoryModel();
+ if(!model) return;
+ bool fetching_inventory = model->fetchDescendentsOf(mUUID);
+ // Only change folder type if we have the folder contents.
+ if (!fetching_inventory)
+ {
+ // Disabling this for now, it's causing crash when new items are added to folders
+ // since folder type may change before new item item has finished processing.
+ // determineFolderType();
+ }
+}
+
+void LLFolderBridge::closeItem()
+{
+ determineFolderType();
+}
+
+void LLFolderBridge::determineFolderType()
+{
+ if (isUpToDate())
+ {
+ LLInventoryModel* model = getInventoryModel();
+ LLViewerInventoryCategory* category = model->getCategory(mUUID);
+ category->determineFolderType();
+ }
+}
+
+BOOL LLFolderBridge::isItemRenameable() const
+{
+ LLViewerInventoryCategory* cat = (LLViewerInventoryCategory*)getCategory();
+ if(cat && !LLFolderType::lookupIsProtectedType(cat->getPreferredType())
+ && (cat->getOwnerID() == gAgent.getID()))
+ {
+ return TRUE;
+ }
+ return FALSE;
+}
+
+void LLFolderBridge::restoreItem()
+{
+ LLViewerInventoryCategory* cat;
+ cat = (LLViewerInventoryCategory*)getCategory();
+ if(cat)
+ {
+ LLInventoryModel* model = getInventoryModel();
+ const LLUUID new_parent = model->findCategoryUUIDForType(LLFolderType::assetTypeToFolderType(cat->getType()));
+ // do not restamp children on restore
+ LLInvFVBridge::changeCategoryParent(model, cat, new_parent, FALSE);
+ }
+}
+
+LLFolderType::EType LLFolderBridge::getPreferredType() const
+{
+ LLFolderType::EType preferred_type = LLFolderType::FT_NONE;
+ LLViewerInventoryCategory* cat = getCategory();
+ if(cat)
+ {
+ preferred_type = cat->getPreferredType();
+ }
+
+ return preferred_type;
+}
+
+// Icons for folders are based on the preferred type
+LLUIImagePtr LLFolderBridge::getIcon() const
+{
+ LLFolderType::EType preferred_type = LLFolderType::FT_NONE;
+ LLViewerInventoryCategory* cat = getCategory();
+ if(cat)
+ {
+ preferred_type = cat->getPreferredType();
+ }
+ return getIcon(preferred_type);
+}
+
+LLUIImagePtr LLFolderBridge::getIcon(LLFolderType::EType preferred_type)
+{
+ // we only have one folder image now
+ return LLUI::getUIImage("Inv_FolderClosed");
+}
+
+BOOL LLFolderBridge::renameItem(const std::string& new_name)
+{
+ if(!isItemRenameable())
+ return FALSE;
+ LLInventoryModel* model = getInventoryModel();
+ if(!model)
+ return FALSE;
+ LLViewerInventoryCategory* cat = getCategory();
+ if(cat && (cat->getName() != new_name))
+ {
+ LLPointer<LLViewerInventoryCategory> new_cat = new LLViewerInventoryCategory(cat);
+ new_cat->rename(new_name);
+ new_cat->updateServer(FALSE);
+ model->updateCategory(new_cat);
+
+ model->notifyObservers();
+ }
+ // return FALSE because we either notified observers (& therefore
+ // rebuilt) or we didn't update.
+ return FALSE;
+}
+
+BOOL LLFolderBridge::removeItem()
+{
+ if(!isItemRemovable())
+ {
+ return FALSE;
+ }
+ // move it to the trash
+ LLPreview::hide(mUUID);
+ LLInventoryModel* model = getInventoryModel();
+ if(!model) return FALSE;
+
+ const LLUUID trash_id = model->findCategoryUUIDForType(LLFolderType::FT_TRASH);
+
+ // Look for any gestures and deactivate them
+ LLInventoryModel::cat_array_t descendent_categories;
+ LLInventoryModel::item_array_t descendent_items;
+ gInventory.collectDescendents( mUUID, descendent_categories, descendent_items, FALSE );
+
+ S32 i;
+ for (i = 0; i < descendent_items.count(); i++)
+ {
+ LLInventoryItem* item = descendent_items[i];
+ if (item->getType() == LLAssetType::AT_GESTURE
+ && LLGestureManager::instance().isGestureActive(item->getUUID()))
+ {
+ LLGestureManager::instance().deactivateGesture(item->getUUID());
+ }
+ }
+
+ // go ahead and do the normal remove if no 'last calling
+ // cards' are being removed.
+ LLViewerInventoryCategory* cat = getCategory();
+ if(cat)
+ {
+ LLInvFVBridge::changeCategoryParent(model, cat, trash_id, TRUE);
+ }
+
+ return TRUE;
+}
+
+void LLFolderBridge::pasteFromClipboard()
+{
+ LLInventoryModel* model = getInventoryModel();
+ if(model && isClipboardPasteable())
+ {
+ LLInventoryItem* item = NULL;
+ LLDynamicArray<LLUUID> objects;
+ LLInventoryClipboard::instance().retrieve(objects);
+ S32 count = objects.count();
+ const LLUUID parent_id(mUUID);
+ for(S32 i = 0; i < count; i++)
+ {
+ item = model->getItem(objects.get(i));
+ if (item)
+ {
+ if(LLInventoryClipboard::instance().isCutMode())
+ {
+ // move_inventory_item() is not enough,
+ //we have to update inventory locally too
+ changeItemParent(model, dynamic_cast<LLViewerInventoryItem*>(item), parent_id, FALSE);
+ }
+ else
+ {
+ copy_inventory_item(
+ gAgent.getID(),
+ item->getPermissions().getOwner(),
+ item->getUUID(),
+ parent_id,
+ std::string(),
+ LLPointer<LLInventoryCallback>(NULL));
+ }
+ }
+ }
+ }
+}
+
+void LLFolderBridge::pasteLinkFromClipboard()
+{
+ const LLInventoryModel* model = getInventoryModel();
+ if(model)
+ {
+ LLDynamicArray<LLUUID> objects;
+ LLInventoryClipboard::instance().retrieve(objects);
+ S32 count = objects.count();
+ LLUUID parent_id(mUUID);
+ for(S32 i = 0; i < count; i++)
+ {
+ const LLUUID &object_id = objects.get(i);
+#if SUPPORT_ENSEMBLES
+ if (LLInventoryCategory *cat = model->getCategory(object_id))
+ {
+ link_inventory_item(
+ gAgent.getID(),
+ cat->getUUID(),
+ parent_id,
+ cat->getName(),
+ LLAssetType::AT_LINK_FOLDER,
+ LLPointer<LLInventoryCallback>(NULL));
+ }
+ else
+#endif
+ if (LLInventoryItem *item = model->getItem(object_id))
+ {
+ link_inventory_item(
+ gAgent.getID(),
+ item->getUUID(),
+ parent_id,
+ item->getName(),
+ LLAssetType::AT_LINK,
+ LLPointer<LLInventoryCallback>(NULL));
+ }
+ }
+ }
+}
+
+void LLFolderBridge::staticFolderOptionsMenu()
+{
+ if (!sSelf) return;
+ sSelf->folderOptionsMenu();
+}
+
+void LLFolderBridge::folderOptionsMenu()
+{
+ std::vector<std::string> disabled_items;
+
+ LLInventoryModel* model = getInventoryModel();
+ if(!model) return;
+
+ const LLInventoryCategory* category = model->getCategory(mUUID);
+ LLFolderType::EType type = category->getPreferredType();
+ const bool is_default_folder = category && LLFolderType::lookupIsProtectedType(type);
+ // BAP change once we're no longer treating regular categories as ensembles.
+ const bool is_ensemble = category && (type == LLFolderType::FT_NONE ||
+ LLFolderType::lookupIsEnsembleType(type));
+
+ // calling card related functionality for folders.
+
+ // Only enable calling-card related options for non-default folders.
+ if (!is_default_folder)
+ {
+ LLIsType is_callingcard(LLAssetType::AT_CALLINGCARD);
+ if (mCallingCards || checkFolderForContentsOfType(model, is_callingcard))
+ {
+ mItems.push_back(std::string("Calling Card Separator"));
+ mItems.push_back(std::string("Conference Chat Folder"));
+ mItems.push_back(std::string("IM All Contacts In Folder"));
+ }
+ }
+
+ // wearables related functionality for folders.
+ //is_wearable
+ LLFindWearables is_wearable;
+ LLIsType is_object( LLAssetType::AT_OBJECT );
+ LLIsType is_gesture( LLAssetType::AT_GESTURE );
+
+ if (mWearables ||
+ checkFolderForContentsOfType(model, is_wearable) ||
+ checkFolderForContentsOfType(model, is_object) ||
+ checkFolderForContentsOfType(model, is_gesture) )
+ {
+ mItems.push_back(std::string("Folder Wearables Separator"));
+
+ // Only enable add/replace outfit for non-default folders.
+ if (!is_default_folder)
+ {
+ mItems.push_back(std::string("Add To Outfit"));
+ mItems.push_back(std::string("Replace Outfit"));
+ }
+ if (is_ensemble)
+ {
+ mItems.push_back(std::string("Wear As Ensemble"));
+ }
+ mItems.push_back(std::string("Take Off Items"));
+ }
+ hide_context_entries(*mMenu, mItems, disabled_items);
+}
+
+BOOL LLFolderBridge::checkFolderForContentsOfType(LLInventoryModel* model, LLInventoryCollectFunctor& is_type)
+{
+ LLInventoryModel::cat_array_t cat_array;
+ LLInventoryModel::item_array_t item_array;
+ model->collectDescendentsIf(mUUID,
+ cat_array,
+ item_array,
+ LLInventoryModel::EXCLUDE_TRASH,
+ is_type);
+ return ((item_array.count() > 0) ? TRUE : FALSE );
+}
+
+// Flags unused
+void LLFolderBridge::buildContextMenu(LLMenuGL& menu, U32 flags)
+{
+ mItems.clear();
+ mDisabledItems.clear();
+
+ lldebugs << "LLFolderBridge::buildContextMenu()" << llendl;
+// std::vector<std::string> disabled_items;
+ LLInventoryModel* model = getInventoryModel();
+ if(!model) return;
+ const LLUUID trash_id = model->findCategoryUUIDForType(LLFolderType::FT_TRASH);
+ const LLUUID lost_and_found_id = model->findCategoryUUIDForType(LLFolderType::FT_LOST_AND_FOUND);
+
+ mItems.clear(); //adding code to clear out member Items (which means Items should not have other data here at this point)
+ mDisabledItems.clear(); //adding code to clear out disabled members from previous
+ if (lost_and_found_id == mUUID)
+ {
+ // This is the lost+found folder.
+ mItems.push_back(std::string("Empty Lost And Found"));
+ }
+
+ if(trash_id == mUUID)
+ {
+ // This is the trash.
+ mItems.push_back(std::string("Empty Trash"));
+ }
+ else if(model->isObjectDescendentOf(mUUID, trash_id))
+ {
+ // This is a folder in the trash.
+ mItems.clear(); // clear any items that used to exist
+ mItems.push_back(std::string("Purge Item"));
+ if (!isItemRemovable())
+ {
+ mDisabledItems.push_back(std::string("Purge Item"));
+ }
+
+ mItems.push_back(std::string("Restore Item"));
+ }
+ else if(isAgentInventory()) // do not allow creating in library
+ {
+ LLViewerInventoryCategory *cat = getCategory();
+ // BAP removed protected check to re-enable standard ops in untyped folders.
+ // Not sure what the right thing is to do here.
+ if (!isCOFFolder() && cat /*&&
+ LLAssetType::lookupIsProtectedCategoryType(cat->getPreferredType())*/)
+ {
+ // Do not allow to create 2-level subfolder in the Calling Card/Friends folder. EXT-694.
+ if (!LLFriendCardsManager::instance().isCategoryInFriendFolder(cat))
+ mItems.push_back(std::string("New Folder"));
+ mItems.push_back(std::string("New Script"));
+ mItems.push_back(std::string("New Note"));
+ mItems.push_back(std::string("New Gesture"));
+ mItems.push_back(std::string("New Clothes"));
+ mItems.push_back(std::string("New Body Parts"));
+ mItems.push_back(std::string("Change Type"));
+
+ LLViewerInventoryCategory *cat = getCategory();
+ if (cat && LLFolderType::lookupIsProtectedType(cat->getPreferredType()))
+ {
+ mDisabledItems.push_back(std::string("Change Type"));
+ }
+
+ getClipboardEntries(false, mItems, mDisabledItems, flags);
+ }
+ else
+ {
+ // Want some but not all of the items from getClipboardEntries for outfits.
+ if (cat && cat->getPreferredType()==LLFolderType::FT_OUTFIT)
+ {
+ mItems.push_back(std::string("Rename"));
+ mItems.push_back(std::string("Delete"));
+ }
+ }
+
+ //Added by spatters to force inventory pull on right-click to display folder options correctly. 07-17-06
+ mCallingCards = mWearables = FALSE;
+
+ LLIsType is_callingcard(LLAssetType::AT_CALLINGCARD);
+ if (checkFolderForContentsOfType(model, is_callingcard))
+ {
+ mCallingCards=TRUE;
+ }
+
+ LLFindWearables is_wearable;
+ LLIsType is_object( LLAssetType::AT_OBJECT );
+ LLIsType is_gesture( LLAssetType::AT_GESTURE );
+
+ if (checkFolderForContentsOfType(model, is_wearable) ||
+ checkFolderForContentsOfType(model, is_object) ||
+ checkFolderForContentsOfType(model, is_gesture) )
+ {
+ mWearables=TRUE;
+ }
+
+ mMenu = &menu;
+ sSelf = this;
+ LLRightClickInventoryFetchDescendentsObserver* fetch = new LLRightClickInventoryFetchDescendentsObserver(FALSE);
+
+ LLInventoryFetchDescendentsObserver::folder_ref_t folders;
+ LLViewerInventoryCategory* category = (LLViewerInventoryCategory*)model->getCategory(mUUID);
+ if (category)
+ {
+ folders.push_back(category->getUUID());
+ }
+ fetch->fetchDescendents(folders);
+ inc_busy_count();
+ if(fetch->isEverythingComplete())
+ {
+ // everything is already here - call done.
+ fetch->done();
+ }
+ else
+ {
+ // it's all on it's way - add an observer, and the inventory
+ // will call done for us when everything is here.
+ gInventory.addObserver(fetch);
+ }
+ }
+ else
+ {
+ mItems.push_back(std::string("--no options--"));
+ mDisabledItems.push_back(std::string("--no options--"));
+ }
+ hide_context_entries(menu, mItems, mDisabledItems);
+}
+
+BOOL LLFolderBridge::hasChildren() const
+{
+ LLInventoryModel* model = getInventoryModel();
+ if(!model) return FALSE;
+ LLInventoryModel::EHasChildren has_children;
+ has_children = gInventory.categoryHasChildren(mUUID);
+ return has_children != LLInventoryModel::CHILDREN_NO;
+}
+
+BOOL LLFolderBridge::dragOrDrop(MASK mask, BOOL drop,
+ EDragAndDropType cargo_type,
+ void* cargo_data)
+{
+ //llinfos << "LLFolderBridge::dragOrDrop()" << llendl;
+ BOOL accept = FALSE;
+ switch(cargo_type)
+ {
+ case DAD_TEXTURE:
+ case DAD_SOUND:
+ case DAD_CALLINGCARD:
+ case DAD_LANDMARK:
+ case DAD_SCRIPT:
+ case DAD_OBJECT:
+ case DAD_NOTECARD:
+ case DAD_CLOTHING:
+ case DAD_BODYPART:
+ case DAD_ANIMATION:
+ case DAD_GESTURE:
+ case DAD_LINK:
+ accept = dragItemIntoFolder((LLInventoryItem*)cargo_data,
+ drop);
+ break;
+ case DAD_CATEGORY:
+ if (LLFriendCardsManager::instance().isAnyFriendCategory(mUUID))
+ {
+ accept = FALSE;
+ }
+ else
+ {
+ accept = dragCategoryIntoFolder((LLInventoryCategory*)cargo_data, drop);
+ }
+ break;
+ default:
+ break;
+ }
+ return accept;
+}
+
+LLViewerInventoryCategory* LLFolderBridge::getCategory() const
+{
+ LLViewerInventoryCategory* cat = NULL;
+ LLInventoryModel* model = getInventoryModel();
+ if(model)
+ {
+ cat = (LLViewerInventoryCategory*)model->getCategory(mUUID);
+ }
+ return cat;
+}
+
+
+// static
+void LLFolderBridge::pasteClipboard(void* user_data)
+{
+ LLFolderBridge* self = (LLFolderBridge*)user_data;
+ if(self) self->pasteFromClipboard();
+}
+
+void LLFolderBridge::createNewCategory(void* user_data)
+{
+ LLFolderBridge* bridge = (LLFolderBridge*)user_data;
+ if(!bridge) return;
+ LLInventoryPanel* panel = dynamic_cast<LLInventoryPanel*>(bridge->mInventoryPanel.get());
+ if (!panel) return;
+ LLInventoryModel* model = panel->getModel();
+ if(!model) return;
+ LLUUID id;
+ id = model->createNewCategory(bridge->getUUID(),
+ LLFolderType::FT_NONE,
+ LLStringUtil::null);
+ model->notifyObservers();
+
+ // At this point, the bridge has probably been deleted, but the
+ // view is still there.
+ panel->setSelection(id, TAKE_FOCUS_YES);
+}
+
+void LLFolderBridge::createNewShirt(void* user_data)
+{
+ LLFolderBridge::createWearable((LLFolderBridge*)user_data, WT_SHIRT);
+}
+
+void LLFolderBridge::createNewPants(void* user_data)
+{
+ LLFolderBridge::createWearable((LLFolderBridge*)user_data, WT_PANTS);
+}
+
+void LLFolderBridge::createNewShoes(void* user_data)
+{
+ LLFolderBridge::createWearable((LLFolderBridge*)user_data, WT_SHOES);
+}
+
+void LLFolderBridge::createNewSocks(void* user_data)
+{
+ LLFolderBridge::createWearable((LLFolderBridge*)user_data, WT_SOCKS);
+}
+
+void LLFolderBridge::createNewJacket(void* user_data)
+{
+ LLFolderBridge::createWearable((LLFolderBridge*)user_data, WT_JACKET);
+}
+
+void LLFolderBridge::createNewSkirt(void* user_data)
+{
+ LLFolderBridge::createWearable((LLFolderBridge*)user_data, WT_SKIRT);
+}
+
+void LLFolderBridge::createNewGloves(void* user_data)
+{
+ LLFolderBridge::createWearable((LLFolderBridge*)user_data, WT_GLOVES);
+}
+
+void LLFolderBridge::createNewUndershirt(void* user_data)
+{
+ LLFolderBridge::createWearable((LLFolderBridge*)user_data, WT_UNDERSHIRT);
+}
+
+void LLFolderBridge::createNewUnderpants(void* user_data)
+{
+ LLFolderBridge::createWearable((LLFolderBridge*)user_data, WT_UNDERPANTS);
+}
+
+void LLFolderBridge::createNewShape(void* user_data)
+{
+ LLFolderBridge::createWearable((LLFolderBridge*)user_data, WT_SHAPE);
+}
+
+void LLFolderBridge::createNewSkin(void* user_data)
+{
+ LLFolderBridge::createWearable((LLFolderBridge*)user_data, WT_SKIN);
+}
+
+void LLFolderBridge::createNewHair(void* user_data)
+{
+ LLFolderBridge::createWearable((LLFolderBridge*)user_data, WT_HAIR);
+}
+
+void LLFolderBridge::createNewEyes(void* user_data)
+{
+ LLFolderBridge::createWearable((LLFolderBridge*)user_data, WT_EYES);
+}
+
+// static
+void LLFolderBridge::createWearable(LLFolderBridge* bridge, EWearableType type)
+{
+ if(!bridge) return;
+ LLUUID parent_id = bridge->getUUID();
+ createWearable(parent_id, type);
+}
+
+// Separate function so can be called by global menu as well as right-click
+// menu.
+// static
+void LLFolderBridge::createWearable(const LLUUID &parent_id, EWearableType type)
+{
+ LLWearable* wearable = LLWearableList::instance().createNewWearable(type);
+ LLAssetType::EType asset_type = wearable->getAssetType();
+ LLInventoryType::EType inv_type = LLInventoryType::IT_WEARABLE;
+ create_inventory_item(gAgent.getID(), gAgent.getSessionID(),
+ parent_id, wearable->getTransactionID(), wearable->getName(),
+ wearable->getDescription(), asset_type, inv_type, wearable->getType(),
+ wearable->getPermissions().getMaskNextOwner(),
+ LLPointer<LLInventoryCallback>(NULL));
+}
+
+void LLFolderBridge::modifyOutfit(BOOL append)
+{
+ LLInventoryModel* model = getInventoryModel();
+ if(!model) return;
+ LLViewerInventoryCategory* cat = getCategory();
+ if(!cat) return;
+
+ // BAP - was:
+ // wear_inventory_category_on_avatar( cat, append );
+ LLAppearanceManager::wearInventoryCategory( cat, FALSE, append );
+}
+
+// helper stuff
+bool move_task_inventory_callback(const LLSD& notification, const LLSD& response, LLMoveInv* move_inv)
+{
+ LLFloaterOpenObject::LLCatAndWear* cat_and_wear = (LLFloaterOpenObject::LLCatAndWear* )move_inv->mUserData;
+ LLViewerObject* object = gObjectList.findObject(move_inv->mObjectID);
+ S32 option = LLNotification::getSelectedOption(notification, response);
+
+ if(option == 0 && object)
+ {
+ if (cat_and_wear && cat_and_wear->mWear)
+ {
+ InventoryObjectList inventory_objects;
+ object->getInventoryContents(inventory_objects);
+ int contents_count = inventory_objects.size()-1; //subtract one for containing folder
+
+ LLInventoryCopyAndWearObserver* inventoryObserver = new LLInventoryCopyAndWearObserver(cat_and_wear->mCatID, contents_count);
+ gInventory.addObserver(inventoryObserver);
+ }
+
+ two_uuids_list_t::iterator move_it;
+ for (move_it = move_inv->mMoveList.begin();
+ move_it != move_inv->mMoveList.end();
+ ++move_it)
+ {
+ object->moveInventory(move_it->first, move_it->second);
+ }
+
+ // update the UI.
+ dialog_refresh_all();
+ }
+
+ if (move_inv->mCallback)
+ {
+ move_inv->mCallback(option, move_inv->mUserData);
+ }
+
+ delete move_inv;
+ return false;
+}
+
+/*
+Next functions intended to reorder items in the inventory folder and save order on server
+Is now used for Favorites folder.
+
+*TODO: refactoring is needed with Favorites Bar functionality. Probably should be moved in LLInventoryModel
+*/
+void saveItemsOrder(LLInventoryModel::item_array_t& items)
+{
+ int sortField = 0;
+
+ // current order is saved by setting incremental values (1, 2, 3, ...) for the sort field
+ for (LLInventoryModel::item_array_t::iterator i = items.begin(); i != items.end(); ++i)
+ {
+ LLViewerInventoryItem* item = *i;
+
+ item->setSortField(++sortField);
+ item->setComplete(TRUE);
+ item->updateServer(FALSE);
+
+ gInventory.updateItem(item);
+ }
+
+ gInventory.notifyObservers();
+}
+
+LLInventoryModel::item_array_t::iterator findItemByUUID(LLInventoryModel::item_array_t& items, const LLUUID& id)
+{
+ LLInventoryModel::item_array_t::iterator result = items.end();
+
+ for (LLInventoryModel::item_array_t::iterator i = items.begin(); i != items.end(); ++i)
+ {
+ if ((*i)->getUUID() == id)
+ {
+ result = i;
+ break;
+ }
+ }
+
+ return result;
+}
+
+void updateItemsOrder(LLInventoryModel::item_array_t& items, const LLUUID& srcItemId, const LLUUID& destItemId)
+{
+ LLViewerInventoryItem* srcItem = gInventory.getItem(srcItemId);
+ LLViewerInventoryItem* destItem = gInventory.getItem(destItemId);
+
+ items.erase(findItemByUUID(items, srcItem->getUUID()));
+ items.insert(findItemByUUID(items, destItem->getUUID()), srcItem);
+}
+
+BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item,
+ BOOL drop)
+{
+ LLInventoryModel* model = getInventoryModel();
+ if(!model) return FALSE;
+
+ // cannot drag into library
+ if(!isAgentInventory())
+ {
+ return FALSE;
+ }
+
+ LLVOAvatarSelf* avatar = gAgent.getAvatarObject();
+ if(!avatar) return FALSE;
+
+ LLToolDragAndDrop::ESource source = LLToolDragAndDrop::getInstance()->getSource();
+ BOOL accept = FALSE;
+ LLViewerObject* object = NULL;
+ if(LLToolDragAndDrop::SOURCE_AGENT == source)
+ {
+
+ BOOL is_movable = TRUE;
+ switch( inv_item->getActualType() )
+ {
+ case LLAssetType::AT_ROOT_CATEGORY:
+ is_movable = FALSE;
+ break;
+
+ case LLAssetType::AT_CATEGORY:
+ is_movable = !LLFolderType::lookupIsProtectedType(((LLInventoryCategory*)inv_item)->getPreferredType());
+ break;
+ default:
+ break;
+ }
+
+ const LLUUID trash_id = model->findCategoryUUIDForType(LLFolderType::FT_TRASH);
+ BOOL move_is_into_trash = (mUUID == trash_id) || model->isObjectDescendentOf(mUUID, trash_id);
+ const LLUUID current_outfit_id = model->findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT);
+ BOOL move_is_into_current_outfit = (mUUID == current_outfit_id);
+ BOOL move_is_into_outfit = (getCategory() && getCategory()->getPreferredType()==LLFolderType::FT_OUTFIT);
+
+ if(is_movable && move_is_into_trash)
+ {
+ switch(inv_item->getType())
+ {
+ case LLAssetType::AT_CLOTHING:
+ case LLAssetType::AT_BODYPART:
+ is_movable = !gAgentWearables.isWearingItem(inv_item->getUUID());
+ break;
+
+ case LLAssetType::AT_OBJECT:
+ is_movable = !avatar->isWearingAttachment(inv_item->getUUID());
+ break;
+ default:
+ break;
+ }
+ }
+
+ if ( is_movable )
+ {
+ // Don't allow creating duplicates in the Calling Card/Friends
+ // subfolders, see bug EXT-1599. Check is item direct descendent
+ // of target folder and forbid item's movement if it so.
+ // Note: isItemDirectDescendentOfCategory checks if
+ // passed category is in the Calling Card/Friends folder
+ is_movable = ! LLFriendCardsManager::instance()
+ .isObjDirectDescendentOfCategory (inv_item, getCategory());
+ }
+
+ const LLUUID& favorites_id = model->findCategoryUUIDForType(LLFolderType::FT_FAVORITE);
+
+ // we can move item inside a folder only if this folder is Favorites. See EXT-719
+ accept = is_movable && ((mUUID != inv_item->getParentUUID()) || (mUUID == favorites_id));
+ if(accept && drop)
+ {
+ if (inv_item->getType() == LLAssetType::AT_GESTURE
+ && LLGestureManager::instance().isGestureActive(inv_item->getUUID()) && move_is_into_trash)
+ {
+ LLGestureManager::instance().deactivateGesture(inv_item->getUUID());
+ }
+ // If an item is being dragged between windows, unselect
+ // everything in the active window so that we don't follow
+ // the selection to its new location (which is very
+ // annoying).
+ if (LLFloaterInventory::getActiveInventory())
+ {
+ LLInventoryPanel* active_panel = LLFloaterInventory::getActiveInventory()->getPanel();
+ LLInventoryPanel* panel = dynamic_cast<LLInventoryPanel*>(mInventoryPanel.get());
+ if (active_panel && (panel != active_panel))
+ {
+ active_panel->unSelectAll();
+ }
+ }
+
+ // if dragging from/into favorites folder only reorder items
+ if ((mUUID == inv_item->getParentUUID()) && (favorites_id == mUUID))
+ {
+ LLInventoryModel::cat_array_t cats;
+ LLInventoryModel::item_array_t items;
+ LLIsType is_type(LLAssetType::AT_LANDMARK);
+ model->collectDescendentsIf(favorites_id, cats, items, LLInventoryModel::EXCLUDE_TRASH, is_type);
+
+ LLInventoryPanel* panel = dynamic_cast<LLInventoryPanel*>(mInventoryPanel.get());
+ LLFolderViewItem* itemp = panel ? panel->getRootFolder()->getDraggingOverItem() : NULL;
+ if (itemp)
+ {
+ LLUUID srcItemId = inv_item->getUUID();
+ LLUUID destItemId = itemp->getListener()->getUUID();
+
+ // update order
+ updateItemsOrder(items, srcItemId, destItemId);
+
+ saveItemsOrder(items);
+ }
+ }
+ else if (favorites_id == mUUID) // if target is the favorites folder we use copy
+ {
+ copy_inventory_item(
+ gAgent.getID(),
+ inv_item->getPermissions().getOwner(),
+ inv_item->getUUID(),
+ mUUID,
+ std::string(),
+ LLPointer<LLInventoryCallback>(NULL));
+ }
+ else if (move_is_into_current_outfit || move_is_into_outfit)
+ {
+ // BAP - should skip if dup.
+ if (move_is_into_current_outfit)
+ {
+ LLAppearanceManager::wearItem(inv_item);
+ }
+ else
+ {
+ LLPointer<LLInventoryCallback> cb = NULL;
+ link_inventory_item(
+ gAgent.getID(),
+ inv_item->getUUID(),
+ mUUID,
+ std::string(),
+ LLAssetType::AT_LINK,
+ cb);
+ }
+ }
+ else
+ {
+ // restamp if the move is into the trash.
+ LLInvFVBridge::changeItemParent(
+ model,
+ (LLViewerInventoryItem*)inv_item,
+ mUUID,
+ move_is_into_trash);
+ }
+ }
+ }
+ else if(LLToolDragAndDrop::SOURCE_WORLD == source)
+ {
+ // Make sure the object exists. If we allowed dragging from
+ // anonymous objects, it would be possible to bypass
+ // permissions.
+ object = gObjectList.findObject(inv_item->getParentUUID());
+ if(!object)
+ {
+ llinfos << "Object not found for drop." << llendl;
+ return FALSE;
+ }
+
+ // coming from a task. Need to figure out if the person can
+ // move/copy this item.
+ LLPermissions perm(inv_item->getPermissions());
+ BOOL is_move = FALSE;
+ if((perm.allowCopyBy(gAgent.getID(), gAgent.getGroupID())
+ && perm.allowTransferTo(gAgent.getID())))
+// || gAgent.isGodlike())
+
+ {
+ accept = TRUE;
+ }
+ else if(object->permYouOwner())
+ {
+ // If the object cannot be copied, but the object the
+ // inventory is owned by the agent, then the item can be
+ // moved from the task to agent inventory.
+ is_move = TRUE;
+ accept = TRUE;
+ }
+ if(drop && accept)
+ {
+ LLMoveInv* move_inv = new LLMoveInv;
+ move_inv->mObjectID = inv_item->getParentUUID();
+ two_uuids_t item_pair(mUUID, inv_item->getUUID());
+ move_inv->mMoveList.push_back(item_pair);
+ move_inv->mCallback = NULL;
+ move_inv->mUserData = NULL;
+ if(is_move)
+ {
+ warn_move_inventory(object, move_inv);
+ }
+ else
+ {
+ LLNotification::Params params("MoveInventoryFromObject");
+ params.functor.function(boost::bind(move_task_inventory_callback, _1, _2, move_inv));
+ LLNotifications::instance().forceResponse(params, 0);
+ }
+ }
+
+ }
+ else if(LLToolDragAndDrop::SOURCE_NOTECARD == source)
+ {
+ accept = TRUE;
+ if(drop)
+ {
+ copy_inventory_from_notecard(LLToolDragAndDrop::getInstance()->getObjectID(),
+ LLToolDragAndDrop::getInstance()->getSourceID(), inv_item);
+ }
+ }
+ else if(LLToolDragAndDrop::SOURCE_LIBRARY == source)
+ {
+ LLViewerInventoryItem* item = (LLViewerInventoryItem*)inv_item;
+ if(item && item->isComplete())
+ {
+ accept = TRUE;
+ if(drop)
+ {
+ copy_inventory_item(
+ gAgent.getID(),
+ inv_item->getPermissions().getOwner(),
+ inv_item->getUUID(),
+ mUUID,
+ std::string(),
+ LLPointer<LLInventoryCallback>(NULL));
+ }
+ }
+ }
+ else
+ {
+ llwarns << "unhandled drag source" << llendl;
+ }
+ return accept;
+}
+
+// +=================================================+
+// | LLScriptBridge (DEPRECTED) |
+// +=================================================+
+
+LLUIImagePtr LLScriptBridge::getIcon() const
+{
+ return get_item_icon(LLAssetType::AT_SCRIPT, LLInventoryType::IT_LSL, 0, FALSE);
+}
+
+// +=================================================+
+// | LLTextureBridge |
+// +=================================================+
+
+LLUIImagePtr LLTextureBridge::getIcon() const
+{
+ return get_item_icon(LLAssetType::AT_TEXTURE, mInvType, 0, FALSE);
+}
+
+void LLTextureBridge::openItem()
+{
+ LLViewerInventoryItem* item = getItem();
+
+ if (item)
+ {
+ LLInvFVBridgeAction::doAction(item->getType(),mUUID,getInventoryModel());
+ }
+}
+
+void LLTextureBridge::buildContextMenu(LLMenuGL& menu, U32 flags)
+{
+ lldebugs << "LLTextureBridge::buildContextMenu()" << llendl;
+ std::vector<std::string> items;
+ std::vector<std::string> disabled_items;
+ if(isInTrash())
+ {
+ items.push_back(std::string("Purge Item"));
+ if (!isItemRemovable())
+ {
+ disabled_items.push_back(std::string("Purge Item"));
+ }
+
+ items.push_back(std::string("Restore Item"));
+ }
+ else
+ {
+ items.push_back(std::string("Open"));
+ items.push_back(std::string("Properties"));
+
+ getClipboardEntries(true, items, disabled_items, flags);
+
+ items.push_back(std::string("Texture Separator"));
+ items.push_back(std::string("Save As"));
+ }
+ hide_context_entries(menu, items, disabled_items);
+}
+
+// virtual
+void LLTextureBridge::performAction(LLFolderView* folder, LLInventoryModel* model, std::string action)
+{
+ if ("save_as" == action)
+ {
+ LLFloaterReg::showInstance("preview_texture", LLSD(mUUID), TAKE_FOCUS_YES);
+ LLPreviewTexture* preview_texture = LLFloaterReg::findTypedInstance<LLPreviewTexture>("preview_texture", mUUID);
+ if (preview_texture)
+ {
+ preview_texture->openToSave();
+ }
+ }
+ else LLItemBridge::performAction(folder, model, action);
+}
+
+// +=================================================+
+// | LLSoundBridge |
+// +=================================================+
+
+LLUIImagePtr LLSoundBridge::getIcon() const
+{
+ return get_item_icon(LLAssetType::AT_SOUND, LLInventoryType::IT_SOUND, 0, FALSE);
+}
+
+void LLSoundBridge::openItem()
+{
+ LLViewerInventoryItem* item = getItem();
+
+ if (item)
+ {
+ LLInvFVBridgeAction::doAction(item->getType(),mUUID,getInventoryModel());
+ }
+/*
+// Changed this back to the way it USED to work:
+// only open the preview dialog through the contextual right-click menu
+// double-click just plays the sound
+
+ LLViewerInventoryItem* item = getItem();
+ if(item)
+ {
+ openSoundPreview((void*)this);
+ //send_uuid_sound_trigger(item->getAssetUUID(), 1.0);
+ }
+*/
+}
+
+void LLSoundBridge::previewItem()
+{
+ LLViewerInventoryItem* item = getItem();
+ if(item)
+ {
+ send_sound_trigger(item->getAssetUUID(), 1.0);
+ }
+}
+
+void LLSoundBridge::openSoundPreview(void* which)
+{
+ LLSoundBridge *me = (LLSoundBridge *)which;
+ LLFloaterReg::showInstance("preview_sound", LLSD(me->mUUID), TAKE_FOCUS_YES);
+}
+
+void LLSoundBridge::buildContextMenu(LLMenuGL& menu, U32 flags)
+{
+ lldebugs << "LLSoundBridge::buildContextMenu()" << llendl;
+ std::vector<std::string> items;
+ std::vector<std::string> disabled_items;
+
+ if(isInTrash())
+ {
+ items.push_back(std::string("Purge Item"));
+ if (!isItemRemovable())
+ {
+ disabled_items.push_back(std::string("Purge Item"));
+ }
+
+ items.push_back(std::string("Restore Item"));
+ }
+ else
+ {
+ items.push_back(std::string("Sound Open"));
+ items.push_back(std::string("Properties"));
+
+ getClipboardEntries(true, items, disabled_items, flags);
+ }
+
+ items.push_back(std::string("Sound Separator"));
+ items.push_back(std::string("Sound Play"));
+
+ hide_context_entries(menu, items, disabled_items);
+}
+
+// +=================================================+
+// | LLLandmarkBridge |
+// +=================================================+
+
+LLLandmarkBridge::LLLandmarkBridge(LLInventoryPanel* inventory, const LLUUID& uuid, U32 flags/* = 0x00*/) :
+LLItemBridge(inventory, uuid)
+{
+ mVisited = FALSE;
+ if (flags & LLInventoryItem::II_FLAGS_LANDMARK_VISITED)
+ {
+ mVisited = TRUE;
+ }
+}
+
+LLUIImagePtr LLLandmarkBridge::getIcon() const
+{
+ return get_item_icon(LLAssetType::AT_LANDMARK, LLInventoryType::IT_LANDMARK, mVisited, FALSE);
+}
+
+void LLLandmarkBridge::buildContextMenu(LLMenuGL& menu, U32 flags)
+{
+ std::vector<std::string> items;
+ std::vector<std::string> disabled_items;
+
+ lldebugs << "LLLandmarkBridge::buildContextMenu()" << llendl;
+ if(isInTrash())
+ {
+ items.push_back(std::string("Purge Item"));
+ if (!isItemRemovable())
+ {
+ disabled_items.push_back(std::string("Purge Item"));
+ }
+
+ items.push_back(std::string("Restore Item"));
+ }
+ else
+ {
+ items.push_back(std::string("Landmark Open"));
+ items.push_back(std::string("Properties"));
+
+ getClipboardEntries(true, items, disabled_items, flags);
+ }
+
+ items.push_back(std::string("Landmark Separator"));
+ items.push_back(std::string("About Landmark"));
+
+ // Disable "About Landmark" menu item for
+ // multiple landmarks selected. Only one landmark
+ // info panel can be shown at a time.
+ if ((flags & FIRST_SELECTED_ITEM) == 0)
+ {
+ disabled_items.push_back(std::string("About Landmark"));
+ }
+
+ hide_context_entries(menu, items, disabled_items);
+}
+
+// Convenience function for the two functions below.
+void teleport_via_landmark(const LLUUID& asset_id)
+{
+ gAgent.teleportViaLandmark( asset_id );
+
+ // we now automatically track the landmark you're teleporting to
+ // because you'll probably arrive at a telehub instead
+ LLFloaterWorldMap* floater_world_map = LLFloaterWorldMap::getInstance();
+ if( floater_world_map )
+ {
+ floater_world_map->trackLandmark( asset_id );
+ }
+}
+
+// virtual
+void LLLandmarkBridge::performAction(LLFolderView* folder, LLInventoryModel* model, std::string action)
+{
+ if ("teleport" == action)
+ {
+ LLViewerInventoryItem* item = getItem();
+ if(item)
+ {
+ teleport_via_landmark(item->getAssetUUID());
+ }
+ }
+ else if ("about" == action)
+ {
+ LLViewerInventoryItem* item = getItem();
+ if(item)
+ {
+ LLSD key;
+ key["type"] = "landmark";
+ key["id"] = item->getUUID();
+
+ LLSideTray::getInstance()->showPanel("panel_places", key);
+ }
+ }
+ else
+ {
+ LLItemBridge::performAction(folder, model, action);
+ }
+}
+
+static bool open_landmark_callback(const LLSD& notification, const LLSD& response)
+{
+ S32 option = LLNotification::getSelectedOption(notification, response);
+
+ LLUUID asset_id = notification["payload"]["asset_id"].asUUID();
+ if (option == 0)
+ {
+ teleport_via_landmark(asset_id);
+ }
+
+ return false;
+}
+static LLNotificationFunctorRegistration open_landmark_callback_reg("TeleportFromLandmark", open_landmark_callback);
+
+
+void LLLandmarkBridge::openItem()
+{
+ LLViewerInventoryItem* item = getItem();
+
+ if (item)
+ {
+ LLInvFVBridgeAction::doAction(item->getType(),mUUID,getInventoryModel());
+ }
+/*
+ LLViewerInventoryItem* item = getItem();
+ if( item )
+ {
+ // Opening (double-clicking) a landmark immediately teleports,
+ // but warns you the first time.
+ // open_landmark(item);
+ LLSD payload;
+ payload["asset_id"] = item->getAssetUUID();
+ LLNotifications::instance().add("TeleportFromLandmark", LLSD(), payload);
+ }
+*/
+}
+
+
+// +=================================================+
+// | LLCallingCardObserver |
+// +=================================================+
+void LLCallingCardObserver::changed(U32 mask)
+{
+ mBridgep->refreshFolderViewItem();
+}
+
+// +=================================================+
+// | LLCallingCardBridge |
+// +=================================================+
+
+LLCallingCardBridge::LLCallingCardBridge( LLInventoryPanel* inventory, const LLUUID& uuid ) :
+ LLItemBridge(inventory, uuid)
+{
+ mObserver = new LLCallingCardObserver(this);
+ LLAvatarTracker::instance().addObserver(mObserver);
+}
+
+LLCallingCardBridge::~LLCallingCardBridge()
+{
+ LLAvatarTracker::instance().removeObserver(mObserver);
+ delete mObserver;
+}
+
+void LLCallingCardBridge::refreshFolderViewItem()
+{
+ LLInventoryPanel* panel = dynamic_cast<LLInventoryPanel*>(mInventoryPanel.get());
+ LLFolderViewItem* itemp = panel ? panel->getRootFolder()->getItemByID(mUUID) : NULL;
+ if (itemp)
+ {
+ itemp->refresh();
+ }
+}
+
+// virtual
+void LLCallingCardBridge::performAction(LLFolderView* folder, LLInventoryModel* model, std::string action)
+{
+ if ("begin_im" == action)
+ {
+ LLViewerInventoryItem *item = getItem();
+ if (item && (item->getCreatorUUID() != gAgent.getID()) &&
+ (!item->getCreatorUUID().isNull()))
+ {
+ std::string callingcard_name;
+ gCacheName->getFullName(item->getCreatorUUID(), callingcard_name);
+ LLUUID session_id = gIMMgr->addSession(callingcard_name, IM_NOTHING_SPECIAL, item->getCreatorUUID());
+ if (session_id != LLUUID::null)
+ {
+ LLIMFloater::show(session_id);
+ }
+ }
+ }
+ else if ("lure" == action)
+ {
+ LLViewerInventoryItem *item = getItem();
+ if (item && (item->getCreatorUUID() != gAgent.getID()) &&
+ (!item->getCreatorUUID().isNull()))
+ {
+ LLAvatarActions::offerTeleport(item->getCreatorUUID());
+ }
+ }
+ else LLItemBridge::performAction(folder, model, action);
+}
+
+LLUIImagePtr LLCallingCardBridge::getIcon() const
+{
+ BOOL online = FALSE;
+ LLViewerInventoryItem* item = getItem();
+ if(item)
+ {
+ online = LLAvatarTracker::instance().isBuddyOnline(item->getCreatorUUID());
+ }
+ return get_item_icon(LLAssetType::AT_CALLINGCARD, LLInventoryType::IT_CALLINGCARD, online, FALSE);
+}
+
+std::string LLCallingCardBridge::getLabelSuffix() const
+{
+ LLViewerInventoryItem* item = getItem();
+ if( item && LLAvatarTracker::instance().isBuddyOnline(item->getCreatorUUID()) )
+ {
+ return LLItemBridge::getLabelSuffix() + " (online)";
+ }
+ else
+ {
+ return LLItemBridge::getLabelSuffix();
+ }
+}
+
+void LLCallingCardBridge::openItem()
+{
+ LLViewerInventoryItem* item = getItem();
+
+ if (item)
+ {
+ LLInvFVBridgeAction::doAction(item->getType(),mUUID,getInventoryModel());
+ }
+/*
+ LLViewerInventoryItem* item = getItem();
+ if(item && !item->getCreatorUUID().isNull())
+ {
+ LLAvatarActions::showProfile(item->getCreatorUUID());
+ }
+*/
+}
+
+void LLCallingCardBridge::buildContextMenu(LLMenuGL& menu, U32 flags)
+{
+ lldebugs << "LLCallingCardBridge::buildContextMenu()" << llendl;
+ std::vector<std::string> items;
+ std::vector<std::string> disabled_items;
+
+ if(isInTrash())
+ {
+ items.push_back(std::string("Purge Item"));
+ if (!isItemRemovable())
+ {
+ disabled_items.push_back(std::string("Purge Item"));
+ }
+
+ items.push_back(std::string("Restore Item"));
+ }
+ else
+ {
+ items.push_back(std::string("Open"));
+ items.push_back(std::string("Properties"));
+
+ getClipboardEntries(true, items, disabled_items, flags);
+
+ LLInventoryItem* item = getItem();
+ BOOL good_card = (item
+ && (LLUUID::null != item->getCreatorUUID())
+ && (item->getCreatorUUID() != gAgent.getID()));
+ BOOL user_online = (LLAvatarTracker::instance().isBuddyOnline(item->getCreatorUUID()));
+ items.push_back(std::string("Send Instant Message Separator"));
+ items.push_back(std::string("Send Instant Message"));
+ items.push_back(std::string("Offer Teleport..."));
+ items.push_back(std::string("Conference Chat"));
+
+ if (!good_card)
+ {
+ disabled_items.push_back(std::string("Send Instant Message"));
+ }
+ if (!good_card || !user_online)
+ {
+ disabled_items.push_back(std::string("Offer Teleport..."));
+ disabled_items.push_back(std::string("Conference Chat"));
+ }
+ }
+ hide_context_entries(menu, items, disabled_items);
+}
+
+BOOL LLCallingCardBridge::dragOrDrop(MASK mask, BOOL drop,
+ EDragAndDropType cargo_type,
+ void* cargo_data)
+{
+ LLViewerInventoryItem* item = getItem();
+ BOOL rv = FALSE;
+ if(item)
+ {
+ // check the type
+ switch(cargo_type)
+ {
+ case DAD_TEXTURE:
+ case DAD_SOUND:
+ case DAD_LANDMARK:
+ case DAD_SCRIPT:
+ case DAD_CLOTHING:
+ case DAD_OBJECT:
+ case DAD_NOTECARD:
+ case DAD_BODYPART:
+ case DAD_ANIMATION:
+ case DAD_GESTURE:
+ {
+ LLInventoryItem* inv_item = (LLInventoryItem*)cargo_data;
+ const LLPermissions& perm = inv_item->getPermissions();
+ if(gInventory.getItem(inv_item->getUUID())
+ && perm.allowOperationBy(PERM_TRANSFER, gAgent.getID()))
+ {
+ rv = TRUE;
+ if(drop)
+ {
+ LLToolDragAndDrop::giveInventory(item->getCreatorUUID(),
+ (LLInventoryItem*)cargo_data);
+ }
+ }
+ else
+ {
+ // It's not in the user's inventory (it's probably in
+ // an object's contents), so disallow dragging it here.
+ // You can't give something you don't yet have.
+ rv = FALSE;
+ }
+ break;
+ }
+ case DAD_CATEGORY:
+ {
+ LLInventoryCategory* inv_cat = (LLInventoryCategory*)cargo_data;
+ if( gInventory.getCategory( inv_cat->getUUID() ) )
+ {
+ rv = TRUE;
+ if(drop)
+ {
+ LLToolDragAndDrop::giveInventoryCategory(
+ item->getCreatorUUID(),
+ inv_cat);
+ }
+ }
+ else
+ {
+ // It's not in the user's inventory (it's probably in
+ // an object's contents), so disallow dragging it here.
+ // You can't give something you don't yet have.
+ rv = FALSE;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ return rv;
+}
+
+BOOL LLCallingCardBridge::removeItem()
+{
+ if (LLFriendCardsManager::instance().isItemInAnyFriendsList(getItem()))
+ {
+ LLAvatarActions::removeFriendDialog(getItem()->getCreatorUUID());
+ return FALSE;
+ }
+ else
+ {
+ return LLItemBridge::removeItem();
+ }
+}
+// +=================================================+
+// | LLNotecardBridge |
+// +=================================================+
+
+LLUIImagePtr LLNotecardBridge::getIcon() const
+{
+ return get_item_icon(LLAssetType::AT_NOTECARD, LLInventoryType::IT_NOTECARD, 0, FALSE);
+}
+
+void LLNotecardBridge::openItem()
+{
+ LLViewerInventoryItem* item = getItem();
+
+ if (item)
+ {
+ LLInvFVBridgeAction::doAction(item->getType(),mUUID,getInventoryModel());
+ }
+
+/*
+ LLViewerInventoryItem* item = getItem();
+ if (item)
+ {
+ LLFloaterReg::showInstance("preview_notecard", LLSD(item->getUUID()), TAKE_FOCUS_YES);
+ }
+*/
+}
+
+
+// +=================================================+
+// | LLGestureBridge |
+// +=================================================+
+
+LLUIImagePtr LLGestureBridge::getIcon() const
+{
+ return get_item_icon(LLAssetType::AT_GESTURE, LLInventoryType::IT_GESTURE, 0, FALSE);
+}
+
+LLFontGL::StyleFlags LLGestureBridge::getLabelStyle() const
+{
+ if( LLGestureManager::instance().isGestureActive(mUUID) )
+ {
+ return LLFontGL::BOLD;
+ }
+ else
+ {
+ return LLFontGL::NORMAL;
+ }
+}
+
+std::string LLGestureBridge::getLabelSuffix() const
+{
+ if( LLGestureManager::instance().isGestureActive(mUUID) )
+ {
+ return LLItemBridge::getLabelSuffix() + " (active)";
+ }
+ else
+ {
+ return LLItemBridge::getLabelSuffix();
+ }
+}
+
+// virtual
+void LLGestureBridge::performAction(LLFolderView* folder, LLInventoryModel* model, std::string action)
+{
+ if ("activate" == action)
+ {
+ LLGestureManager::instance().activateGesture(mUUID);
+
+ LLViewerInventoryItem* item = gInventory.getItem(mUUID);
+ if (!item) return;
+
+ // Since we just changed the suffix to indicate (active)
+ // the server doesn't need to know, just the viewer.
+ gInventory.updateItem(item);
+ gInventory.notifyObservers();
+ }
+ else if ("deactivate" == action)
+ {
+ LLGestureManager::instance().deactivateGesture(mUUID);
+
+ LLViewerInventoryItem* item = gInventory.getItem(mUUID);
+ if (!item) return;
+
+ // Since we just changed the suffix to indicate (active)
+ // the server doesn't need to know, just the viewer.
+ gInventory.updateItem(item);
+ gInventory.notifyObservers();
+ }
+ else LLItemBridge::performAction(folder, model, action);
+}
+
+void LLGestureBridge::openItem()
+{
+ LLViewerInventoryItem* item = getItem();
+
+ if (item)
+ {
+ LLInvFVBridgeAction::doAction(item->getType(),mUUID,getInventoryModel());
+ }
+/*
+ LLViewerInventoryItem* item = getItem();
+ if (item)
+ {
+ LLPreviewGesture* preview = LLPreviewGesture::show(mUUID, LLUUID::null);
+ preview->setFocus(TRUE);
+ }
+*/
+}
+
+BOOL LLGestureBridge::removeItem()
+{
+ // Force close the preview window, if it exists
+ LLGestureManager::instance().deactivateGesture(mUUID);
+ return LLItemBridge::removeItem();
+}
+
+void LLGestureBridge::buildContextMenu(LLMenuGL& menu, U32 flags)
+{
+ lldebugs << "LLGestureBridge::buildContextMenu()" << llendl;
+ std::vector<std::string> items;
+ std::vector<std::string> disabled_items;
+ if(isInTrash())
+ {
+ items.push_back(std::string("Purge Item"));
+ if (!isItemRemovable())
+ {
+ disabled_items.push_back(std::string("Purge Item"));
+ }
+
+ items.push_back(std::string("Restore Item"));
+ }
+ else
+ {
+ items.push_back(std::string("Open"));
+ items.push_back(std::string("Properties"));
+
+ getClipboardEntries(true, items, disabled_items, flags);
+
+ items.push_back(std::string("Gesture Separator"));
+ items.push_back(std::string("Activate"));
+ items.push_back(std::string("Deactivate"));
+ }
+ hide_context_entries(menu, items, disabled_items);
+}
+
+// +=================================================+
+// | LLAnimationBridge |
+// +=================================================+
+
+LLUIImagePtr LLAnimationBridge::getIcon() const
+{
+ return get_item_icon(LLAssetType::AT_ANIMATION, LLInventoryType::IT_ANIMATION, 0, FALSE);
+}
+
+void LLAnimationBridge::buildContextMenu(LLMenuGL& menu, U32 flags)
+{
+ std::vector<std::string> items;
+ std::vector<std::string> disabled_items;
+
+ lldebugs << "LLAnimationBridge::buildContextMenu()" << llendl;
+ if(isInTrash())
+ {
+ items.push_back(std::string("Purge Item"));
+ if (!isItemRemovable())
+ {
+ disabled_items.push_back(std::string("Purge Item"));
+ }
+
+ items.push_back(std::string("Restore Item"));
+ }
+ else
+ {
+ items.push_back(std::string("Animation Open"));
+ items.push_back(std::string("Properties"));
+
+ getClipboardEntries(true, items, disabled_items, flags);
+ }
+
+ items.push_back(std::string("Animation Separator"));
+ items.push_back(std::string("Animation Play"));
+ items.push_back(std::string("Animation Audition"));
+
+ hide_context_entries(menu, items, disabled_items);
+
+}
+
+// virtual
+void LLAnimationBridge::performAction(LLFolderView* folder, LLInventoryModel* model, std::string action)
+{
+ if ((action == "playworld") || (action == "playlocal"))
+ {
+ if (getItem())
+ {
+ LLPreviewAnim::e_activation_type activate = LLPreviewAnim::NONE;
+ if ("playworld" == action) activate = LLPreviewAnim::PLAY;
+ if ("playlocal" == action) activate = LLPreviewAnim::AUDITION;
+
+ LLPreviewAnim* preview = LLFloaterReg::showTypedInstance<LLPreviewAnim>("preview_anim", LLSD(mUUID));
+ if (preview)
+ {
+ preview->activate(activate);
+ }
+ }
+ }
+ else
+ {
+ LLItemBridge::performAction(folder, model, action);
+ }
+}
+
+void LLAnimationBridge::openItem()
+{
+ LLViewerInventoryItem* item = getItem();
+
+ if (item)
+ {
+ LLInvFVBridgeAction::doAction(item->getType(),mUUID,getInventoryModel());
+ }
+/*
+ LLViewerInventoryItem* item = getItem();
+ if (item)
+ {
+ LLFloaterReg::showInstance("preview_anim", LLSD(mUUID), TAKE_FOCUS_YES);
+ }
+*/
+}
+
+// +=================================================+
+// | LLObjectBridge |
+// +=================================================+
+
+// static
+LLUUID LLObjectBridge::sContextMenuItemID;
+
+LLObjectBridge::LLObjectBridge(LLInventoryPanel* inventory, const LLUUID& uuid, LLInventoryType::EType type, U32 flags) :
+LLItemBridge(inventory, uuid), mInvType(type)
+{
+ mAttachPt = (flags & 0xff); // low bye of inventory flags
+
+ mIsMultiObject = ( flags & LLInventoryItem::II_FLAGS_OBJECT_HAS_MULTIPLE_ITEMS ) ? TRUE: FALSE;
+}
+
+BOOL LLObjectBridge::isItemRemovable()
+{
+ LLVOAvatarSelf* avatar = gAgent.getAvatarObject();
+ if(!avatar) return FALSE;
+ if(avatar->isWearingAttachment(mUUID)) return FALSE;
+ return LLInvFVBridge::isItemRemovable();
+}
+
+LLUIImagePtr LLObjectBridge::getIcon() const
+{
+ return get_item_icon(LLAssetType::AT_OBJECT, mInvType, mAttachPt, mIsMultiObject );
+}
+
+LLInventoryObject* LLObjectBridge::getObject() const
+{
+ LLInventoryObject* object = NULL;
+ LLInventoryModel* model = getInventoryModel();
+ if(model)
+ {
+ object = (LLInventoryObject*)model->getObject(mUUID);
+ }
+ return object;
+}
+
+// virtual
+void LLObjectBridge::performAction(LLFolderView* folder, LLInventoryModel* model, std::string action)
+{
+ if ("attach" == action)
+ {
+ LLUUID object_id = mUUID;
+ LLViewerInventoryItem* item;
+ item = (LLViewerInventoryItem*)gInventory.getItem(object_id);
+ if(item && gInventory.isObjectDescendentOf(object_id, gInventory.getRootFolderID()))
+ {
+ rez_attachment(item, NULL);
+ }
+ else if(item && item->isComplete())
+ {
+ // must be in library. copy it to our inventory and put it on.
+ LLPointer<LLInventoryCallback> cb = new RezAttachmentCallback(0);
+ copy_inventory_item(
+ gAgent.getID(),
+ item->getPermissions().getOwner(),
+ item->getUUID(),
+ LLUUID::null,
+ std::string(),
+ cb);
+ }
+ gFocusMgr.setKeyboardFocus(NULL);
+ }
+ else if ("detach" == action)
+ {
+ LLInventoryItem* item = gInventory.getItem(mUUID);
+ if(item)
+ {
+ gMessageSystem->newMessageFast(_PREHASH_DetachAttachmentIntoInv);
+ gMessageSystem->nextBlockFast(_PREHASH_ObjectData);
+ gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ gMessageSystem->addUUIDFast(_PREHASH_ItemID, item->getLinkedUUID());
+ gMessageSystem->sendReliable( gAgent.getRegion()->getHost());
+ }
+ // this object might have been selected, so let the selection manager know it's gone now
+ LLViewerObject *found_obj =
+ gObjectList.findObject(item->getUUID());
+ if (found_obj)
+ {
+ LLSelectMgr::getInstance()->remove(found_obj);
+ }
+ else
+ {
+ llwarns << "object not found - ignoring" << llendl;
+ }
+ }
+ else LLItemBridge::performAction(folder, model, action);
+}
+
+void LLObjectBridge::openItem()
+{
+ LLViewerInventoryItem* item = getItem();
+
+ if (item)
+ {
+ LLInvFVBridgeAction::doAction(item->getType(),mUUID,getInventoryModel());
+ }
+
+ LLSD key;
+ key["id"] = mUUID;
+ LLSideTray::getInstance()->showPanel("sidepanel_inventory", key);
+
+ /*
+ LLFloaterReg::showInstance("properties", mUUID);
+ */
+}
+
+LLFontGL::StyleFlags LLObjectBridge::getLabelStyle() const
+{
+ U8 font = LLFontGL::NORMAL;
+
+ LLVOAvatarSelf* avatar = gAgent.getAvatarObject();
+ if( avatar && avatar->isWearingAttachment( mUUID ) )
+ {
+ font |= LLFontGL::BOLD;
+ }
+
+ LLInventoryItem* item = getItem();
+ if (item && item->getIsLinkType())
+ {
+ font |= LLFontGL::ITALIC;
+ }
+
+ return (LLFontGL::StyleFlags)font;
+}
+
+std::string LLObjectBridge::getLabelSuffix() const
+{
+ LLVOAvatarSelf* avatar = gAgent.getAvatarObject();
+ if( avatar && avatar->isWearingAttachment( mUUID ) )
+ {
+ std::string attachment_point_name = avatar->getAttachedPointName(mUUID);
+ LLStringUtil::toLower(attachment_point_name);
+
+ LLStringUtil::format_map_t args;
+ args["[ATTACHMENT_POINT]"] = attachment_point_name.c_str();
+ return LLItemBridge::getLabelSuffix() + LLTrans::getString("WornOnAttachmentPoint", args);
+ }
+ else
+ {
+ return LLItemBridge::getLabelSuffix();
+ }
+}
+
+void rez_attachment(LLViewerInventoryItem* item, LLViewerJointAttachment* attachment)
+{
+ LLSD payload;
+ payload["item_id"] = item->getLinkedUUID(); // Wear the base object in case this is a link.
+
+ S32 attach_pt = 0;
+ if (gAgent.getAvatarObject() && attachment)
+ {
+ for (LLVOAvatar::attachment_map_t::iterator iter = gAgent.getAvatarObject()->mAttachmentPoints.begin();
+ iter != gAgent.getAvatarObject()->mAttachmentPoints.end(); ++iter)
+ {
+ if (iter->second == attachment)
+ {
+ attach_pt = iter->first;
+ break;
+ }
+ }
+ }
+
+ payload["attachment_point"] = attach_pt;
+
+#if !ENABLE_MULTIATTACHMENTS
+ if (attachment && attachment->getNumObjects() > 0)
+ {
+ LLNotifications::instance().add("ReplaceAttachment", LLSD(), payload, confirm_replace_attachment_rez);
+ }
+ else
+#endif
+ {
+ LLNotifications::instance().forceResponse(LLNotification::Params("ReplaceAttachment").payload(payload), 0/*YES*/);
+ }
+}
+
+bool confirm_replace_attachment_rez(const LLSD& notification, const LLSD& response)
+{
+ LLVOAvatar *avatarp = gAgent.getAvatarObject();
+
+ if (!avatarp->canAttachMoreObjects())
+ {
+ LLSD args;
+ args["MAX_ATTACHMENTS"] = llformat("%d", MAX_AGENT_ATTACHMENTS);
+ LLNotifications::instance().add("MaxAttachmentsOnOutfit", args);
+ return false;
+ }
+
+ S32 option = LLNotification::getSelectedOption(notification, response);
+ if (option == 0/*YES*/)
+ {
+ LLViewerInventoryItem* itemp = gInventory.getItem(notification["payload"]["item_id"].asUUID());
+
+ if (itemp)
+ {
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_RezSingleAttachmentFromInv);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_ObjectData);
+ msg->addUUIDFast(_PREHASH_ItemID, itemp->getUUID());
+ msg->addUUIDFast(_PREHASH_OwnerID, itemp->getPermissions().getOwner());
+ U8 attachment_pt = notification["payload"]["attachment_point"].asInteger();
+#if ENABLE_MULTIATTACHMENTS
+ attachment_pt |= ATTACHMENT_ADD;
+#endif
+ msg->addU8Fast(_PREHASH_AttachmentPt, attachment_pt);
+ pack_permissions_slam(msg, itemp->getFlags(), itemp->getPermissions());
+ msg->addStringFast(_PREHASH_Name, itemp->getName());
+ msg->addStringFast(_PREHASH_Description, itemp->getDescription());
+ msg->sendReliable(gAgent.getRegion()->getHost());
+ }
+ }
+ return false;
+}
+static LLNotificationFunctorRegistration confirm_replace_attachment_rez_reg("ReplaceAttachment", confirm_replace_attachment_rez);
+
+void LLObjectBridge::buildContextMenu(LLMenuGL& menu, U32 flags)
+{
+ std::vector<std::string> items;
+ std::vector<std::string> disabled_items;
+ if(isInTrash())
+ {
+ items.push_back(std::string("Purge Item"));
+ if (!isItemRemovable())
+ {
+ disabled_items.push_back(std::string("Purge Item"));
+ }
+
+ items.push_back(std::string("Restore Item"));
+ }
+ else
+ {
+ LLInventoryItem* item = getItem();
+ if (item && item->getIsLinkType())
+ {
+ items.push_back(std::string("Goto Link"));
+ }
+
+ items.push_back(std::string("Properties"));
+
+ getClipboardEntries(true, items, disabled_items, flags);
+
+ LLObjectBridge::sContextMenuItemID = mUUID;
+
+ if(item)
+ {
+ LLVOAvatarSelf* avatarp = gAgent.getAvatarObject();
+ if( !avatarp )
+ {
+ return;
+ }
+
+ if( avatarp->isWearingAttachment( mUUID ) )
+ {
+ items.push_back(std::string("Detach From Yourself"));
+ }
+ else
+ if( !isInTrash() && !isLinkedObjectInTrash() )
+ {
+ items.push_back(std::string("Attach Separator"));
+ items.push_back(std::string("Object Wear"));
+ items.push_back(std::string("Attach To"));
+ items.push_back(std::string("Attach To HUD"));
+ // commented out for DEV-32347
+ //items.push_back(std::string("Restore to Last Position"));
+
+ if (!avatarp->canAttachMoreObjects())
+ {
+ disabled_items.push_back(std::string("Object Wear"));
+ disabled_items.push_back(std::string("Attach To"));
+ disabled_items.push_back(std::string("Attach To HUD"));
+ }
+ LLMenuGL* attach_menu = menu.findChildMenuByName("Attach To", TRUE);
+ LLMenuGL* attach_hud_menu = menu.findChildMenuByName("Attach To HUD", TRUE);
+ LLVOAvatar *avatarp = gAgent.getAvatarObject();
+ if (attach_menu
+ && (attach_menu->getChildCount() == 0)
+ && attach_hud_menu
+ && (attach_hud_menu->getChildCount() == 0)
+ && avatarp)
+ {
+ for (LLVOAvatar::attachment_map_t::iterator iter = avatarp->mAttachmentPoints.begin();
+ iter != avatarp->mAttachmentPoints.end(); )
+ {
+ LLVOAvatar::attachment_map_t::iterator curiter = iter++;
+ LLViewerJointAttachment* attachment = curiter->second;
+ LLMenuItemCallGL::Params p;
+ std::string submenu_name = attachment->getName();
+ if (LLTrans::getString(submenu_name) != "")
+ {
+ p.name = (" ")+LLTrans::getString(submenu_name)+" ";
+ }
+ else
+ {
+ p.name = submenu_name;
+ }
+ LLSD cbparams;
+ cbparams["index"] = curiter->first;
+ cbparams["label"] = attachment->getName();
+ p.on_click.function_name = "Inventory.AttachObject";
+ p.on_click.parameter = LLSD(attachment->getName());
+ p.on_enable.function_name = "Attachment.Label";
+ p.on_enable.parameter = cbparams;
+ LLView* parent = attachment->getIsHUDAttachment() ? attach_hud_menu : attach_menu;
+ LLUICtrlFactory::create<LLMenuItemCallGL>(p, parent);
+ }
+ }
+ }
+ }
+ }
+ hide_context_entries(menu, items, disabled_items);
+}
+
+BOOL LLObjectBridge::renameItem(const std::string& new_name)
+{
+ if(!isItemRenameable())
+ return FALSE;
+ LLPreview::dirty(mUUID);
+ LLInventoryModel* model = getInventoryModel();
+ if(!model)
+ return FALSE;
+ LLViewerInventoryItem* item = getItem();
+ if(item && (item->getName() != new_name))
+ {
+ LLPointer<LLViewerInventoryItem> new_item = new LLViewerInventoryItem(item);
+ new_item->rename(new_name);
+ buildDisplayName(new_item, mDisplayName);
+ new_item->updateServer(FALSE);
+ model->updateItem(new_item);
+
+ model->notifyObservers();
+
+ LLVOAvatarSelf* avatar = gAgent.getAvatarObject();
+ if( avatar )
+ {
+ LLViewerObject* obj = avatar->getWornAttachment( item->getUUID() );
+ if( obj )
+ {
+ LLSelectMgr::getInstance()->deselectAll();
+ LLSelectMgr::getInstance()->addAsIndividual( obj, SELECT_ALL_TES, FALSE );
+ LLSelectMgr::getInstance()->selectionSetObjectName( new_name );
+ LLSelectMgr::getInstance()->deselectAll();
+ }
+ }
+ }
+ // return FALSE because we either notified observers (& therefore
+ // rebuilt) or we didn't update.
+ return FALSE;
+}
+
+// +=================================================+
+// | LLLSLTextBridge |
+// +=================================================+
+
+LLUIImagePtr LLLSLTextBridge::getIcon() const
+{
+ return get_item_icon(LLAssetType::AT_SCRIPT, LLInventoryType::IT_LSL, 0, FALSE);
+}
+
+void LLLSLTextBridge::openItem()
+{
+ LLViewerInventoryItem* item = getItem();
+
+ if (item)
+ {
+ LLInvFVBridgeAction::doAction(item->getType(),mUUID,getInventoryModel());
+ }
+ /*
+ LLViewerInventoryItem* item = getItem();
+ if (item)
+ {
+ LLFloaterReg::showInstance("preview_script", LLSD(mUUID), TAKE_FOCUS_YES);
+ }
+ */
+}
+
+// +=================================================+
+// | LLWearableBridge |
+// +=================================================+
+
+// *NOTE: hack to get from avatar inventory to avatar
+void wear_inventory_item_on_avatar( LLInventoryItem* item )
+{
+ if(item)
+ {
+ lldebugs << "wear_inventory_item_on_avatar( " << item->getName()
+ << " )" << llendl;
+
+ LLAppearanceManager::wearItem(item);
+ }
+}
+
+void wear_add_inventory_item_on_avatar( LLInventoryItem* item )
+{
+ if(item)
+ {
+ lldebugs << "wear_add_inventory_item_on_avatar( " << item->getName()
+ << " )" << llendl;
+
+ LLWearableList::instance().getAsset(item->getAssetUUID(),
+ item->getName(),
+ item->getType(),
+ LLWearableBridge::onWearAddOnAvatarArrived,
+ new LLUUID(item->getUUID()));
+ }
+}
+
+void remove_inventory_category_from_avatar( LLInventoryCategory* category )
+{
+ if(!category) return;
+ lldebugs << "remove_inventory_category_from_avatar( " << category->getName()
+ << " )" << llendl;
+
+
+ if( gFloaterCustomize )
+ {
+ gFloaterCustomize->askToSaveIfDirty(
+ boost::bind(remove_inventory_category_from_avatar_step2, _1, category->getUUID()));
+ }
+ else
+ {
+ remove_inventory_category_from_avatar_step2(TRUE, category->getUUID() );
+ }
+}
+
+struct OnRemoveStruct
+{
+ LLUUID mUUID;
+ OnRemoveStruct(const LLUUID& uuid):
+ mUUID(uuid)
+ {
+ }
+};
+
+void remove_inventory_category_from_avatar_step2( BOOL proceed, LLUUID category_id)
+{
+
+ // Find all the wearables that are in the category's subtree.
+ lldebugs << "remove_inventory_category_from_avatar_step2()" << llendl;
+ if(proceed)
+ {
+ LLInventoryModel::cat_array_t cat_array;
+ LLInventoryModel::item_array_t item_array;
+ LLFindWearables is_wearable;
+ gInventory.collectDescendentsIf(category_id,
+ cat_array,
+ item_array,
+ LLInventoryModel::EXCLUDE_TRASH,
+ is_wearable);
+ S32 i;
+ S32 wearable_count = item_array.count();
+
+ LLInventoryModel::cat_array_t obj_cat_array;
+ LLInventoryModel::item_array_t obj_item_array;
+ LLIsType is_object( LLAssetType::AT_OBJECT );
+ gInventory.collectDescendentsIf(category_id,
+ obj_cat_array,
+ obj_item_array,
+ LLInventoryModel::EXCLUDE_TRASH,
+ is_object);
+ S32 obj_count = obj_item_array.count();
+
+ // Find all gestures in this folder
+ LLInventoryModel::cat_array_t gest_cat_array;
+ LLInventoryModel::item_array_t gest_item_array;
+ LLIsType is_gesture( LLAssetType::AT_GESTURE );
+ gInventory.collectDescendentsIf(category_id,
+ gest_cat_array,
+ gest_item_array,
+ LLInventoryModel::EXCLUDE_TRASH,
+ is_gesture);
+ S32 gest_count = gest_item_array.count();
+
+ if (wearable_count > 0) //Loop through wearables. If worn, remove.
+ {
+ for(i = 0; i < wearable_count; ++i)
+ {
+ if( gAgentWearables.isWearingItem (item_array.get(i)->getUUID()) )
+ {
+ LLWearableList::instance().getAsset(item_array.get(i)->getAssetUUID(),
+ item_array.get(i)->getName(),
+ item_array.get(i)->getType(),
+ LLWearableBridge::onRemoveFromAvatarArrived,
+ new OnRemoveStruct(item_array.get(i)->getUUID()));
+
+ }
+ }
+ }
+
+
+ if (obj_count > 0)
+ {
+ for(i = 0; i < obj_count; ++i)
+ {
+ gMessageSystem->newMessageFast(_PREHASH_DetachAttachmentIntoInv);
+ gMessageSystem->nextBlockFast(_PREHASH_ObjectData );
+ gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID() );
+ gMessageSystem->addUUIDFast(_PREHASH_ItemID, obj_item_array.get(i)->getUUID() );
+
+ gMessageSystem->sendReliable( gAgent.getRegion()->getHost() );
+
+ // this object might have been selected, so let the selection manager know it's gone now
+ LLViewerObject *found_obj = gObjectList.findObject( obj_item_array.get(i)->getUUID());
+ if (found_obj)
+ {
+ LLSelectMgr::getInstance()->remove(found_obj);
+ }
+ else
+ {
+ llwarns << "object not found, ignoring" << llendl;
+ }
+ }
+ }
+
+ if (gest_count > 0)
+ {
+ for(i = 0; i < gest_count; ++i)
+ {
+ if ( LLGestureManager::instance().isGestureActive( gest_item_array.get(i)->getUUID()) )
+ {
+ LLGestureManager::instance().deactivateGesture( gest_item_array.get(i)->getUUID() );
+ gInventory.updateItem( gest_item_array.get(i) );
+ gInventory.notifyObservers();
+ }
+
+ }
+ }
+ }
+}
+
+BOOL LLWearableBridge::renameItem(const std::string& new_name)
+{
+ if( gAgentWearables.isWearingItem( mUUID ) )
+ {
+ gAgentWearables.setWearableName( mUUID, new_name );
+ }
+ return LLItemBridge::renameItem(new_name);
+}
+
+BOOL LLWearableBridge::isItemRemovable()
+{
+ if (gAgentWearables.isWearingItem(mUUID)) return FALSE;
+ return LLInvFVBridge::isItemRemovable();
+}
+
+std::string LLWearableBridge::getLabelSuffix() const
+{
+ if( gAgentWearables.isWearingItem( mUUID ) )
+ {
+ return LLItemBridge::getLabelSuffix() + LLTrans::getString("worn");
+ }
+ else
+ {
+ return LLItemBridge::getLabelSuffix();
+ }
+}
+
+LLUIImagePtr LLWearableBridge::getIcon() const
+{
+ return get_item_icon(mAssetType, mInvType, mWearableType, FALSE);
+}
+
+// virtual
+void LLWearableBridge::performAction(LLFolderView* folder, LLInventoryModel* model, std::string action)
+{
+ if ("wear" == action)
+ {
+ wearOnAvatar();
+ }
+ else if ("wear_add" == action)
+ {
+ wearAddOnAvatar();
+ }
+ else if ("edit" == action)
+ {
+ editOnAvatar();
+ return;
+ }
+ else if ("take_off" == action)
+ {
+ if(gAgentWearables.isWearingItem(mUUID))
+ {
+ LLViewerInventoryItem* item = getItem();
+ if (item)
+ {
+ LLWearableList::instance().getAsset(item->getAssetUUID(),
+ item->getName(),
+ item->getType(),
+ LLWearableBridge::onRemoveFromAvatarArrived,
+ new OnRemoveStruct(mUUID));
+ }
+ }
+ }
+ else LLItemBridge::performAction(folder, model, action);
+}
+
+void LLWearableBridge::openItem()
+{
+ LLViewerInventoryItem* item = getItem();
+
+ if (item)
+ {
+ LLInvFVBridgeAction::doAction(item->getType(),mUUID,getInventoryModel());
+ }
+ /*
+ if( isInTrash() )
+ {
+ LLNotifications::instance().add("CannotWearTrash");
+ }
+ else if(isAgentInventory())
+ {
+ if( !gAgentWearables.isWearingItem( mUUID ) )
+ {
+ wearOnAvatar();
+ }
+ }
+ else
+ {
+ // must be in the inventory library. copy it to our inventory
+ // and put it on right away.
+ LLViewerInventoryItem* item = getItem();
+ if(item && item->isComplete())
+ {
+ LLPointer<LLInventoryCallback> cb = new WearOnAvatarCallback();
+ copy_inventory_item(
+ gAgent.getID(),
+ item->getPermissions().getOwner(),
+ item->getUUID(),
+ LLUUID::null,
+ std::string(),
+ cb);
+ }
+ else if(item)
+ {
+ // *TODO: We should fetch the item details, and then do
+ // the operation above.
+ LLNotifications::instance().add("CannotWearInfoNotComplete");
+ }
+ }
+ */
+}
+
+void LLWearableBridge::buildContextMenu(LLMenuGL& menu, U32 flags)
+{
+ lldebugs << "LLWearableBridge::buildContextMenu()" << llendl;
+ std::vector<std::string> items;
+ std::vector<std::string> disabled_items;
+ if(isInTrash())
+ {
+ items.push_back(std::string("Purge Item"));
+ if (!isItemRemovable())
+ {
+ disabled_items.push_back(std::string("Purge Item"));
+ }
+
+ items.push_back(std::string("Restore Item"));
+ }
+ else
+ { // FWIW, it looks like SUPPRESS_OPEN_ITEM is not set anywhere
+ BOOL no_open = ((flags & SUPPRESS_OPEN_ITEM) == SUPPRESS_OPEN_ITEM);
+
+ // If we have clothing, don't add "Open" as it's the same action as "Wear" SL-18976
+ LLViewerInventoryItem* item = getItem();
+ if( !no_open && item )
+ {
+ no_open = (item->getType() == LLAssetType::AT_CLOTHING) ||
+ (item->getType() == LLAssetType::AT_BODYPART);
+ }
+ if (!no_open)
+ {
+ items.push_back(std::string("Open"));
+ }
+
+ if (item && item->getIsLinkType())
+ {
+ items.push_back(std::string("Goto Link"));
+ }
+
+ items.push_back(std::string("Properties"));
+
+ getClipboardEntries(true, items, disabled_items, flags);
+
+ items.push_back(std::string("Wearable Separator"));
+
+ items.push_back(std::string("Wearable Wear"));
+ items.push_back(std::string("Wearable Add"));
+ items.push_back(std::string("Wearable Edit"));
+
+ if ((flags & FIRST_SELECTED_ITEM) == 0)
+ {
+ disabled_items.push_back(std::string("Wearable Edit"));
+ }
+ // Don't allow items to be worn if their baseobj is in the trash.
+ if (isLinkedObjectInTrash())
+ {
+ disabled_items.push_back(std::string("Wearable Wear"));
+ disabled_items.push_back(std::string("Wearable Add"));
+ disabled_items.push_back(std::string("Wearable Edit"));
+ }
+
+ // Disable wear and take off based on whether the item is worn.
+ if(item)
+ {
+ switch (item->getType())
+ {
+ case LLAssetType::AT_CLOTHING:
+ items.push_back(std::string("Take Off"));
+ case LLAssetType::AT_BODYPART:
+ if (gAgentWearables.isWearingItem(item->getUUID()))
+ {
+ disabled_items.push_back(std::string("Wearable Wear"));
+ disabled_items.push_back(std::string("Wearable Add"));
+ }
+ else
+ {
+ disabled_items.push_back(std::string("Take Off"));
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ hide_context_entries(menu, items, disabled_items);
+}
+
+// Called from menus
+// static
+BOOL LLWearableBridge::canWearOnAvatar(void* user_data)
+{
+ LLWearableBridge* self = (LLWearableBridge*)user_data;
+ if(!self) return FALSE;
+ if(!self->isAgentInventory())
+ {
+ LLViewerInventoryItem* item = (LLViewerInventoryItem*)self->getItem();
+ if(!item || !item->isComplete()) return FALSE;
+ }
+ return (!gAgentWearables.isWearingItem(self->mUUID));
+}
+
+// Called from menus
+// static
+void LLWearableBridge::onWearOnAvatar(void* user_data)
+{
+ LLWearableBridge* self = (LLWearableBridge*)user_data;
+ if(!self) return;
+ self->wearOnAvatar();
+}
+
+void LLWearableBridge::wearOnAvatar()
+{
+ // Don't wear anything until initial wearables are loaded, can
+ // destroy clothing items.
+ if (!gAgentWearables.areWearablesLoaded())
+ {
+ LLNotifications::instance().add("CanNotChangeAppearanceUntilLoaded");
+ return;
+ }
+
+ LLViewerInventoryItem* item = getItem();
+ if(item)
+ {
+ if(!isAgentInventory())
+ {
+ LLPointer<LLInventoryCallback> cb = new WearOnAvatarCallback();
+ copy_inventory_item(
+ gAgent.getID(),
+ item->getPermissions().getOwner(),
+ item->getUUID(),
+ LLUUID::null,
+ std::string(),
+ cb);
+ }
+ else
+ {
+ wear_inventory_item_on_avatar(item);
+ }
+ }
+}
+
+void LLWearableBridge::wearAddOnAvatar()
+{
+ // Don't wear anything until initial wearables are loaded, can
+ // destroy clothing items.
+ if (!gAgentWearables.areWearablesLoaded())
+ {
+ LLNotifications::instance().add("CanNotChangeAppearanceUntilLoaded");
+ return;
+ }
+
+ LLViewerInventoryItem* item = getItem();
+ if(item)
+ {
+ if(!isAgentInventory())
+ {
+ LLPointer<LLInventoryCallback> cb = new WearOnAvatarCallback();
+ copy_inventory_item(
+ gAgent.getID(),
+ item->getPermissions().getOwner(),
+ item->getUUID(),
+ LLUUID::null,
+ std::string(),
+ cb);
+ }
+ else
+ {
+ wear_add_inventory_item_on_avatar(item);
+ }
+ }
+}
+
+// static
+void LLWearableBridge::onWearOnAvatarArrived( LLWearable* wearable, void* userdata )
+{
+ LLUUID* item_id = (LLUUID*) userdata;
+ if(wearable)
+ {
+ LLViewerInventoryItem* item = NULL;
+ item = (LLViewerInventoryItem*)gInventory.getItem(*item_id);
+ if(item)
+ {
+ if(item->getAssetUUID() == wearable->getAssetID())
+ {
+ gAgentWearables.setWearableItem(item, wearable);
+ gInventory.notifyObservers();
+ //self->getFolderItem()->refreshFromRoot();
+ }
+ else
+ {
+ llinfos << "By the time wearable asset arrived, its inv item already pointed to a different asset." << llendl;
+ }
+ }
+ }
+ delete item_id;
+}
+
+// static
+// BAP remove the "add" code path once everything is fully COF-ified.
+void LLWearableBridge::onWearAddOnAvatarArrived( LLWearable* wearable, void* userdata )
+{
+ LLUUID* item_id = (LLUUID*) userdata;
+ if(wearable)
+ {
+ LLViewerInventoryItem* item = NULL;
+ item = (LLViewerInventoryItem*)gInventory.getItem(*item_id);
+ if(item)
+ {
+ if(item->getAssetUUID() == wearable->getAssetID())
+ {
+ bool do_append = true;
+ gAgentWearables.setWearableItem(item, wearable, do_append);
+ gInventory.notifyObservers();
+ //self->getFolderItem()->refreshFromRoot();
+ }
+ else
+ {
+ llinfos << "By the time wearable asset arrived, its inv item already pointed to a different asset." << llendl;
+ }
+ }
+ }
+ delete item_id;
+}
+
+// static
+BOOL LLWearableBridge::canEditOnAvatar(void* user_data)
+{
+ LLWearableBridge* self = (LLWearableBridge*)user_data;
+ if(!self) return FALSE;
+
+ return (gAgentWearables.isWearingItem(self->mUUID));
+}
+
+// static
+void LLWearableBridge::onEditOnAvatar(void* user_data)
+{
+ LLWearableBridge* self = (LLWearableBridge*)user_data;
+ if(self)
+ {
+ self->editOnAvatar();
+ }
+}
+
+void LLWearableBridge::editOnAvatar()
+{
+ const LLWearable* wearable = gAgentWearables.getWearableFromItemID(mUUID);
+ if( wearable )
+ {
+ // Set the tab to the right wearable.
+ if (gFloaterCustomize)
+ gFloaterCustomize->setCurrentWearableType( wearable->getType() );
+
+ if( CAMERA_MODE_CUSTOMIZE_AVATAR != gAgent.getCameraMode() )
+ {
+ // Start Avatar Customization
+ gAgent.changeCameraToCustomizeAvatar();
+ }
+ }
+}
+
+// static
+BOOL LLWearableBridge::canRemoveFromAvatar(void* user_data)
+{
+ LLWearableBridge* self = (LLWearableBridge*)user_data;
+ if( self && (LLAssetType::AT_BODYPART != self->mAssetType) )
+ {
+ return gAgentWearables.isWearingItem( self->mUUID );
+ }
+ return FALSE;
+}
+
+// static
+void LLWearableBridge::onRemoveFromAvatar(void* user_data)
+{
+ LLWearableBridge* self = (LLWearableBridge*)user_data;
+ if(!self) return;
+ if(gAgentWearables.isWearingItem(self->mUUID))
+ {
+ LLViewerInventoryItem* item = self->getItem();
+ if (item)
+ {
+ LLUUID parent_id = item->getParentUUID();
+ LLWearableList::instance().getAsset(item->getAssetUUID(),
+ item->getName(),
+ item->getType(),
+ onRemoveFromAvatarArrived,
+ new OnRemoveStruct(LLUUID(self->mUUID)));
+ }
+ }
+}
+
+// static
+void LLWearableBridge::onRemoveFromAvatarArrived(LLWearable* wearable,
+ void* userdata)
+{
+ OnRemoveStruct *on_remove_struct = (OnRemoveStruct*) userdata;
+ const LLUUID &item_id = gInventory.getLinkedItemID(on_remove_struct->mUUID);
+ if(wearable)
+ {
+ if( gAgentWearables.isWearingItem( item_id ) )
+ {
+ EWearableType type = wearable->getType();
+
+ if( !(type==WT_SHAPE || type==WT_SKIN || type==WT_HAIR || type==WT_EYES ) ) //&&
+ //!((!gAgent.isTeen()) && ( type==WT_UNDERPANTS || type==WT_UNDERSHIRT )) )
+ {
+ // MULTI_WEARABLE: FIXME HACK - always remove all
+ bool do_remove_all = false;
+ gAgentWearables.removeWearable( type, do_remove_all, 0 );
+ }
+ }
+ }
+
+ // Find and remove this item from the COF.
+ LLInventoryModel::item_array_t items = gInventory.collectLinkedItems(item_id, LLAppearanceManager::getCOF());
+ llassert(items.size() == 1); // Should always have one and only one item linked to this in the COF.
+ for (LLInventoryModel::item_array_t::const_iterator iter = items.begin();
+ iter != items.end();
+ ++iter)
+ {
+ const LLViewerInventoryItem *linked_item = (*iter);
+ const LLUUID &item_id = linked_item->getUUID();
+ gInventory.purgeObject(item_id);
+ }
+ gInventory.notifyObservers();
+
+ delete on_remove_struct;
+}
+
+LLInvFVBridgeAction* LLInvFVBridgeAction::createAction(LLAssetType::EType asset_type,
+ const LLUUID& uuid,LLInventoryModel* model)
+{
+ LLInvFVBridgeAction* action = NULL;
+ switch(asset_type)
+ {
+ case LLAssetType::AT_TEXTURE:
+ action = new LLTextureBridgeAction(uuid,model);
+ break;
+
+ case LLAssetType::AT_SOUND:
+ action = new LLSoundBridgeAction(uuid,model);
+ break;
+
+ case LLAssetType::AT_LANDMARK:
+ action = new LLLandmarkBridgeAction(uuid,model);
+ break;
+
+ case LLAssetType::AT_CALLINGCARD:
+ action = new LLCallingCardBridgeAction(uuid,model);
+ break;
+
+ case LLAssetType::AT_OBJECT:
+ action = new LLObjectBridgeAction(uuid,model);
+ break;
+
+ case LLAssetType::AT_NOTECARD:
+ action = new LLNotecardBridgeAction(uuid,model);
+ break;
+
+ case LLAssetType::AT_ANIMATION:
+ action = new LLAnimationBridgeAction(uuid,model);
+ break;
+
+ case LLAssetType::AT_GESTURE:
+ action = new LLGestureBridgeAction(uuid,model);
+ break;
+
+ case LLAssetType::AT_LSL_TEXT:
+ action = new LLLSLTextBridgeAction(uuid,model);
+ break;
+
+ case LLAssetType::AT_CLOTHING:
+ case LLAssetType::AT_BODYPART:
+ action = new LLWearableBridgeAction(uuid,model);
+
+ break;
+
+ default:
+ break;
+ }
+ return action;
+}
+
+//static
+void LLInvFVBridgeAction::doAction(LLAssetType::EType asset_type,
+ const LLUUID& uuid,LLInventoryModel* model)
+{
+ LLInvFVBridgeAction* action = createAction(asset_type,uuid,model);
+ if(action)
+ {
+ action->doIt();
+ delete action;
+ }
+}
+
+//static
+void LLInvFVBridgeAction::doAction(const LLUUID& uuid, LLInventoryModel* model)
+{
+ LLAssetType::EType asset_type = model->getItem(uuid)->getType();
+ LLInvFVBridgeAction* action = createAction(asset_type,uuid,model);
+ if(action)
+ {
+ action->doIt();
+ delete action;
+ }
+}
+
+LLViewerInventoryItem* LLInvFVBridgeAction::getItem() const
+{
+ if(mModel)
+ return (LLViewerInventoryItem*)mModel->getItem(mUUID);
+ return NULL;
+}
+
+//virtual
+void LLTextureBridgeAction::doIt()
+{
+ if (getItem())
+ {
+ LLFloaterReg::showInstance("preview_texture", LLSD(mUUID), TAKE_FOCUS_YES);
+ }
+
+ LLInvFVBridgeAction::doIt();
+}
+
+//virtual
+void LLSoundBridgeAction::doIt()
+{
+ LLViewerInventoryItem* item = getItem();
+ if(item)
+ {
+ LLFloaterReg::showInstance("preview_sound", LLSD(mUUID), TAKE_FOCUS_YES);
+ }
+
+ LLInvFVBridgeAction::doIt();
+}
+
+
+//virtual
+void LLLandmarkBridgeAction::doIt()
+{
+ LLViewerInventoryItem* item = getItem();
+ if( item )
+ {
+ // Opening (double-clicking) a landmark immediately teleports,
+ // but warns you the first time.
+ LLSD payload;
+ payload["asset_id"] = item->getAssetUUID();
+ LLNotifications::instance().add("TeleportFromLandmark", LLSD(), payload);
+ }
+
+ LLInvFVBridgeAction::doIt();
+}
+
+
+//virtual
+void LLCallingCardBridgeAction::doIt()
+{
+ LLViewerInventoryItem* item = getItem();
+ if(item && item->getCreatorUUID().notNull())
+ {
+ LLAvatarActions::showProfile(item->getCreatorUUID());
+ }
+
+ LLInvFVBridgeAction::doIt();
+}
+
+//virtual
+void
+LLNotecardBridgeAction::doIt()
+{
+ LLViewerInventoryItem* item = getItem();
+ if (item)
+ {
+ LLFloaterReg::showInstance("preview_notecard", LLSD(item->getUUID()), TAKE_FOCUS_YES);
+ }
+
+ LLInvFVBridgeAction::doIt();
+}
+
+//virtual
+void LLGestureBridgeAction::doIt()
+{
+ LLViewerInventoryItem* item = getItem();
+ if (item)
+ {
+ LLPreviewGesture* preview = LLPreviewGesture::show(mUUID, LLUUID::null);
+ preview->setFocus(TRUE);
+ }
+
+ LLInvFVBridgeAction::doIt();
+}
+
+//virtual
+void LLAnimationBridgeAction::doIt()
+{
+ LLViewerInventoryItem* item = getItem();
+ if (item)
+ {
+ LLFloaterReg::showInstance("preview_anim", LLSD(mUUID), TAKE_FOCUS_YES);
+ }
+
+ LLInvFVBridgeAction::doIt();
+}
+
+
+//virtual
+void LLObjectBridgeAction::doIt()
+{
+ LLFloaterReg::showInstance("properties", mUUID);
+
+ LLInvFVBridgeAction::doIt();
+}
+
+
+//virtual
+void LLLSLTextBridgeAction::doIt()
+{
+ LLViewerInventoryItem* item = getItem();
+ if (item)
+ {
+ LLFloaterReg::showInstance("preview_script", LLSD(mUUID), TAKE_FOCUS_YES);
+ }
+
+ LLInvFVBridgeAction::doIt();
+}
+
+
+BOOL LLWearableBridgeAction::isInTrash() const
+{
+ if(!mModel) return FALSE;
+ const LLUUID trash_id = mModel->findCategoryUUIDForType(LLFolderType::FT_TRASH);
+ return mModel->isObjectDescendentOf(mUUID, trash_id);
+}
+
+BOOL LLWearableBridgeAction::isAgentInventory() const
+{
+ if(!mModel) return FALSE;
+ if(gInventory.getRootFolderID() == mUUID) return TRUE;
+ return mModel->isObjectDescendentOf(mUUID, gInventory.getRootFolderID());
+}
+
+void LLWearableBridgeAction::wearOnAvatar()
+{
+ // Don't wear anything until initial wearables are loaded, can
+ // destroy clothing items.
+ if (!gAgentWearables.areWearablesLoaded())
+ {
+ LLNotifications::instance().add("CanNotChangeAppearanceUntilLoaded");
+ return;
+ }
+
+ LLViewerInventoryItem* item = getItem();
+ if(item)
+ {
+ if(!isAgentInventory())
+ {
+ LLPointer<LLInventoryCallback> cb = new WearOnAvatarCallback();
+ copy_inventory_item(
+ gAgent.getID(),
+ item->getPermissions().getOwner(),
+ item->getUUID(),
+ LLUUID::null,
+ std::string(),
+ cb);
+ }
+ else
+ {
+ wear_inventory_item_on_avatar(item);
+ }
+ }
+}
+
+//virtual
+void LLWearableBridgeAction::doIt()
+{
+ if(isInTrash())
+ {
+ LLNotifications::instance().add("CannotWearTrash");
+ }
+ else if(isAgentInventory())
+ {
+ if(!gAgentWearables.isWearingItem(mUUID))
+ {
+ wearOnAvatar();
+ }
+ }
+ else
+ {
+ // must be in the inventory library. copy it to our inventory
+ // and put it on right away.
+ LLViewerInventoryItem* item = getItem();
+ if(item && item->isComplete())
+ {
+ LLPointer<LLInventoryCallback> cb = new WearOnAvatarCallback();
+ copy_inventory_item(
+ gAgent.getID(),
+ item->getPermissions().getOwner(),
+ item->getUUID(),
+ LLUUID::null,
+ std::string(),
+ cb);
+ }
+ else if(item)
+ {
+ // *TODO: We should fetch the item details, and then do
+ // the operation above.
+ LLNotifications::instance().add("CannotWearInfoNotComplete");
+ }
+ }
+
+ LLInvFVBridgeAction::doIt();
+}
+
+// +=================================================+
+// | LLLinkItemBridge |
+// +=================================================+
+// For broken links
+
+std::string LLLinkItemBridge::sPrefix("Link: ");
+
+
+LLUIImagePtr LLLinkItemBridge::getIcon() const
+{
+ if (LLViewerInventoryItem *item = getItem())
+ {
+ return get_item_icon(item->getActualType(), LLInventoryType::IT_NONE, 0, FALSE);
+ }
+ return get_item_icon(LLAssetType::AT_LINK, LLInventoryType::IT_NONE, 0, FALSE);
+}
+
+void LLLinkItemBridge::buildContextMenu(LLMenuGL& menu, U32 flags)
+{
+ // *TODO: Translate
+ lldebugs << "LLLink::buildContextMenu()" << llendl;
+ std::vector<std::string> items;
+ std::vector<std::string> disabled_items;
+
+ if(isInTrash())
+ {
+ items.push_back(std::string("Purge Item"));
+ if (!isItemRemovable())
+ {
+ disabled_items.push_back(std::string("Purge Item"));
+ }
+
+ items.push_back(std::string("Restore Item"));
+ }
+ else
+ {
+ items.push_back(std::string("Delete"));
+ if (!isItemRemovable())
+ {
+ disabled_items.push_back(std::string("Delete"));
+ }
+ }
+ hide_context_entries(menu, items, disabled_items);
+}
+
+
+// +=================================================+
+// | LLLinkBridge |
+// +=================================================+
+// For broken links.
+
+std::string LLLinkFolderBridge::sPrefix("Link: ");
+
+
+LLUIImagePtr LLLinkFolderBridge::getIcon() const
+{
+ LLFolderType::EType preferred_type = LLFolderType::FT_NONE;
+ if (LLViewerInventoryItem *item = getItem())
+ {
+ if (const LLViewerInventoryCategory* cat = item->getLinkedCategory())
+ {
+ preferred_type = cat->getPreferredType();
+ }
+ }
+ return LLFolderBridge::getIcon(preferred_type);
+}
+
+void LLLinkFolderBridge::buildContextMenu(LLMenuGL& menu, U32 flags)
+{
+ // *TODO: Translate
+ lldebugs << "LLLink::buildContextMenu()" << llendl;
+ std::vector<std::string> items;
+ std::vector<std::string> disabled_items;
+
+ if(isInTrash())
+ {
+ items.push_back(std::string("Purge Item"));
+ if (!isItemRemovable())
+ {
+ disabled_items.push_back(std::string("Purge Item"));
+ }
+
+ items.push_back(std::string("Restore Item"));
+ }
+ else
+ {
+ items.push_back(std::string("Goto Link"));
+ items.push_back(std::string("Delete"));
+ if (!isItemRemovable())
+ {
+ disabled_items.push_back(std::string("Delete"));
+ }
+ }
+ hide_context_entries(menu, items, disabled_items);
+}
+
+void LLLinkFolderBridge::performAction(LLFolderView* folder, LLInventoryModel* model, std::string action)
+{
+ if ("goto" == action)
+ {
+ gotoItem(folder);
+ return;
+ }
+ LLItemBridge::performAction(folder,model,action);
+}
+
+void LLLinkFolderBridge::gotoItem(LLFolderView *folder)
+{
+ const LLUUID &cat_uuid = getFolderID();
+ if (!cat_uuid.isNull())
+ {
+ if (LLFolderViewItem *base_folder = folder->getItemByID(cat_uuid))
+ {
+ if (LLInventoryModel* model = getInventoryModel())
+ {
+ model->fetchDescendentsOf(cat_uuid);
+ }
+ base_folder->setOpen(TRUE);
+ folder->setSelectionFromRoot(base_folder,TRUE);
+ folder->scrollToShowSelection();
+ }
+ }
+}
+
+const LLUUID &LLLinkFolderBridge::getFolderID() const
+{
+ if (LLViewerInventoryItem *link_item = getItem())
+ {
+ if (const LLViewerInventoryCategory *cat = link_item->getLinkedCategory())
+ {
+ const LLUUID& cat_uuid = cat->getUUID();
+ return cat_uuid;
+ }
+ }
+ return LLUUID::null;
+}
diff --git a/indra/newview/llinventorybridge.h b/indra/newview/llinventorybridge.h index f95e8f9361..3ffeb55d6c 100644 --- a/indra/newview/llinventorybridge.h +++ b/indra/newview/llinventorybridge.h @@ -383,6 +383,8 @@ class LLTextureBridge : public LLItemBridge public: virtual LLUIImagePtr getIcon() const; virtual void openItem(); + virtual void buildContextMenu(LLMenuGL& menu, U32 flags); + virtual void performAction(LLFolderView* folder, LLInventoryModel* model, std::string action); protected: LLTextureBridge(LLInventoryPanel* inventory, const LLUUID& uuid, LLInventoryType::EType type) : diff --git a/indra/newview/llinventorypanel.cpp b/indra/newview/llinventorypanel.cpp index 80710610d4..7f69ce9641 100644 --- a/indra/newview/llinventorypanel.cpp +++ b/indra/newview/llinventorypanel.cpp @@ -1,907 +1,907 @@ -/** - * @file llfloaterinventory.cpp - * @brief Implementation of the inventory view and associated stuff. - * - * $LicenseInfo:firstyear=2001&license=viewergpl$ - * - * Copyright (c) 2001-2009, Linden Research, Inc. - * - * Second Life Viewer Source Code - * The source code in this file ("Source Code") is provided by Linden Lab - * to you under the terms of the GNU General Public License, version 2.0 - * ("GPL"), unless you have obtained a separate licensing agreement - * ("Other License"), formally executed by you and Linden Lab. Terms of - * the GPL can be found in doc/GPL-license.txt in this distribution, or - * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 - * - * There are special exceptions to the terms and conditions of the GPL as - * it is applied to this Source Code. View the full text of the exception - * in the file doc/FLOSS-exception.txt in this software distribution, or - * online at - * http://secondlifegrid.net/programs/open_source/licensing/flossexception - * - * By copying, modifying or distributing this software, you acknowledge - * that you have read and understood your obligations described above, - * and agree to abide by those obligations. - * - * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO - * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, - * COMPLETENESS OR PERFORMANCE. - * $/LicenseInfo$ - */ - -#include "llviewerprecompiledheaders.h" - -#include <utility> // for std::pair<> - -#include "llinventorypanel.h" - -// Seraph TODO: Remove unnecessary headers - -// library includes -#include "llagent.h" -#include "llagentwearables.h" -#include "llcallingcard.h" -#include "llfloaterreg.h" -#include "llsdserialize.h" -#include "llfiltereditor.h" -#include "llspinctrl.h" -#include "llui.h" -#include "message.h" - -// newview includes -#include "llappearancemgr.h" -#include "llappviewer.h" -#include "llfirstuse.h" -#include "llfloaterchat.h" -#include "llfloatercustomize.h" -#include "llfocusmgr.h" -#include "llfolderview.h" -#include "llgesturemgr.h" -#include "lliconctrl.h" -#include "llimview.h" -#include "llinventorybridge.h" -#include "llinventoryclipboard.h" -#include "llinventorymodel.h" -#include "lllineeditor.h" -#include "llmenugl.h" -#include "llpreviewanim.h" -#include "llpreviewgesture.h" -#include "llpreviewnotecard.h" -#include "llpreviewscript.h" -#include "llpreviewsound.h" -#include "llpreviewtexture.h" -#include "llresmgr.h" -#include "llscrollbar.h" -#include "llscrollcontainer.h" -#include "llselectmgr.h" -#include "lltabcontainer.h" -#include "lltooldraganddrop.h" -#include "lluictrlfactory.h" -#include "llviewerfoldertype.h" -#include "llviewerinventory.h" -#include "llviewermessage.h" -#include "llviewerobjectlist.h" -#include "llviewerregion.h" -#include "llviewerwindow.h" -#include "llvoavatarself.h" -#include "llwearablelist.h" -#include "llimfloater.h" - -static LLDefaultChildRegistry::Register<LLInventoryPanel> r("inventory_panel"); - -const std::string LLInventoryPanel::DEFAULT_SORT_ORDER = std::string("InventorySortOrder"); -const std::string LLInventoryPanel::RECENTITEMS_SORT_ORDER = std::string("RecentItemsSortOrder"); -const std::string LLInventoryPanel::INHERIT_SORT_ORDER = std::string(""); -static const LLInventoryFVBridgeBuilder INVENTORY_BRIDGE_BUILDER; - -LLInventoryPanel::LLInventoryPanel(const LLInventoryPanel::Params& p) : - LLPanel(p), - mInventoryObserver(NULL), - mFolders(NULL), - mScroller(NULL), - mSortOrderSetting(p.sort_order_setting), - mInventory(p.inventory), - mAllowMultiSelect(p.allow_multi_select), - mHasInventoryConnection(false), - mStartFolderString(p.start_folder), - mBuildDefaultHierarchy(true), - mInvFVBridgeBuilder(NULL) -{ - mInvFVBridgeBuilder = &INVENTORY_BRIDGE_BUILDER; - - // contex menu callbacks - mCommitCallbackRegistrar.add("Inventory.DoToSelected", boost::bind(&LLInventoryPanel::doToSelected, this, _2)); - mCommitCallbackRegistrar.add("Inventory.EmptyTrash", boost::bind(&LLInventoryModel::emptyFolderType, &gInventory, "ConfirmEmptyTrash", LLFolderType::FT_TRASH)); - mCommitCallbackRegistrar.add("Inventory.EmptyLostAndFound", boost::bind(&LLInventoryModel::emptyFolderType, &gInventory, "ConfirmEmptyLostAndFound", LLFolderType::FT_LOST_AND_FOUND)); - mCommitCallbackRegistrar.add("Inventory.DoCreate", boost::bind(&LLInventoryPanel::doCreate, this, _2)); - mCommitCallbackRegistrar.add("Inventory.AttachObject", boost::bind(&LLInventoryPanel::attachObject, this, _2)); - mCommitCallbackRegistrar.add("Inventory.BeginIMSession", boost::bind(&LLInventoryPanel::beginIMSession, this)); - - setBackgroundColor(LLUIColorTable::instance().getColor("InventoryBackgroundColor")); - setBackgroundVisible(TRUE); - setBackgroundOpaque(TRUE); -} - -BOOL LLInventoryPanel::postBuild() -{ - LLMemType mt(LLMemType::MTYPE_INVENTORY_POST_BUILD); - - mCommitCallbackRegistrar.pushScope(); // registered as a widget; need to push callback scope ourselves - - // create root folder - { - LLRect folder_rect(0, - 0, - getRect().getWidth(), - 0); - LLFolderView::Params p; - p.name = getName(); - p.rect = folder_rect; - p.parent_panel = this; - mFolders = LLUICtrlFactory::create<LLFolderView>(p); - mFolders->setAllowMultiSelect(mAllowMultiSelect); - } - - mCommitCallbackRegistrar.popScope(); - - mFolders->setCallbackRegistrar(&mCommitCallbackRegistrar); - - // scroller - { - LLRect scroller_view_rect = getRect(); - scroller_view_rect.translate(-scroller_view_rect.mLeft, -scroller_view_rect.mBottom); - LLScrollContainer::Params p; - p.name("Inventory Scroller"); - p.rect(scroller_view_rect); - p.follows.flags(FOLLOWS_ALL); - p.reserve_scroll_corner(true); - p.tab_stop(true); - mScroller = LLUICtrlFactory::create<LLScrollContainer>(p); - } - addChild(mScroller); - mScroller->addChild(mFolders); - - mFolders->setScrollContainer(mScroller); - - // set up the callbacks from the inventory we're viewing, and then - // build everything. - mInventoryObserver = new LLInventoryPanelObserver(this); - mInventory->addObserver(mInventoryObserver); - - // determine the root folder, if any, so inventory contents show just the children - // of that folder (i.e. not including the folder itself). - const LLFolderType::EType preferred_type = LLViewerFolderType::lookupTypeFromNewCategoryName(mStartFolderString); - - if ("INVENTORY" == mStartFolderString) - { - mStartFolderID = gInventory.getRootFolderID(); - } - else if ("LIBRARY" == mStartFolderString) - { - mStartFolderID = gInventory.getLibraryRootFolderID(); - } - else - { - mStartFolderID = (preferred_type != LLFolderType::FT_NONE ? gInventory.findCategoryUUIDForType(preferred_type) : LLUUID::null); - } - - // build view of inventory if we need default full hierarchy and inventory ready, otherwise wait for modelChanged() callback - if (mBuildDefaultHierarchy && mInventory->isInventoryUsable() && !mHasInventoryConnection) - { - rebuildViewsFor(mStartFolderID); - mHasInventoryConnection = true; - } - - // bit of a hack to make sure the inventory is open. - mFolders->openFolder(preferred_type != LLFolderType::FT_NONE ? LLViewerFolderType::lookupNewCategoryName(preferred_type) : "My Inventory"); - - if (mSortOrderSetting != INHERIT_SORT_ORDER) - { - setSortOrder(gSavedSettings.getU32(mSortOrderSetting)); - } - else - { - setSortOrder(gSavedSettings.getU32(DEFAULT_SORT_ORDER)); - } - mFolders->setSortOrder(mFolders->getFilter()->getSortOrder()); - - return TRUE; -} - -LLInventoryPanel::~LLInventoryPanel() -{ - // should this be a global setting? - if (mFolders) - { - U32 sort_order = mFolders->getSortOrder(); - if (mSortOrderSetting != INHERIT_SORT_ORDER) - { - gSavedSettings.setU32(mSortOrderSetting, sort_order); - } - } - - // LLView destructor will take care of the sub-views. - mInventory->removeObserver(mInventoryObserver); - delete mInventoryObserver; - mScroller = NULL; -} - -LLMemType mt(LLMemType::MTYPE_INVENTORY_FROM_XML); // ! BUG ! Should this be removed? -void LLInventoryPanel::draw() -{ - // select the desired item (in case it wasn't loaded when the selection was requested) - mFolders->updateSelection(); - LLPanel::draw(); -} - -LLInventoryFilter* LLInventoryPanel::getFilter() -{ - if (mFolders) return mFolders->getFilter(); - return NULL; -} - -void LLInventoryPanel::setFilterTypes(U64 filter_types, BOOL filter_for_categories) -{ - mFolders->getFilter()->setFilterTypes(filter_types, filter_for_categories); -} - -void LLInventoryPanel::setFilterPermMask(PermissionMask filter_perm_mask) -{ - mFolders->getFilter()->setFilterPermissions(filter_perm_mask); -} - -void LLInventoryPanel::setFilterSubString(const std::string& string) -{ - mFolders->getFilter()->setFilterSubString(string); -} - -void LLInventoryPanel::setSortOrder(U32 order) -{ - mFolders->getFilter()->setSortOrder(order); - if (mFolders->getFilter()->isModified()) - { - mFolders->setSortOrder(order); - // try to keep selection onscreen, even if it wasn't to start with - mFolders->scrollToShowSelection(); - } -} - -void LLInventoryPanel::setSinceLogoff(BOOL sl) -{ - mFolders->getFilter()->setDateRangeLastLogoff(sl); -} - -void LLInventoryPanel::setHoursAgo(U32 hours) -{ - mFolders->getFilter()->setHoursAgo(hours); -} - -void LLInventoryPanel::setShowFolderState(LLInventoryFilter::EFolderShow show) -{ - mFolders->getFilter()->setShowFolderState(show); -} - -LLInventoryFilter::EFolderShow LLInventoryPanel::getShowFolderState() -{ - return mFolders->getFilter()->getShowFolderState(); -} - -static LLFastTimer::DeclareTimer FTM_REFRESH("Inventory Refresh"); - -void LLInventoryPanel::modelChanged(U32 mask) -{ - LLFastTimer t2(FTM_REFRESH); - - bool handled = false; - - // inventory just initialized, do complete build - if ((mask & LLInventoryObserver::ADD) && gInventory.getChangedIDs().empty() && !mHasInventoryConnection) - { - rebuildViewsFor(mStartFolderID); - mHasInventoryConnection = true; - return; - } - - if(mask & LLInventoryObserver::LABEL) - { - handled = true; - // label change - empty out the display name for each object - // in this change set. - const std::set<LLUUID>& changed_items = gInventory.getChangedIDs(); - std::set<LLUUID>::const_iterator id_it = changed_items.begin(); - std::set<LLUUID>::const_iterator id_end = changed_items.end(); - LLFolderViewItem* view = NULL; - LLInvFVBridge* bridge = NULL; - for (;id_it != id_end; ++id_it) - { - view = mFolders->getItemByID(*id_it); - if(view) - { - // request refresh on this item (also flags for filtering) - bridge = (LLInvFVBridge*)view->getListener(); - if(bridge) - { // Clear the display name first, so it gets properly re-built during refresh() - bridge->clearDisplayName(); - } - view->refresh(); - } - } - } - if((mask & (LLInventoryObserver::STRUCTURE - | LLInventoryObserver::ADD - | LLInventoryObserver::REMOVE)) != 0) - { - handled = true; - // Record which folders are open by uuid. - LLInventoryModel* model = getModel(); - if (model) - { - const std::set<LLUUID>& changed_items = gInventory.getChangedIDs(); - - std::set<LLUUID>::const_iterator id_it = changed_items.begin(); - std::set<LLUUID>::const_iterator id_end = changed_items.end(); - for (;id_it != id_end; ++id_it) - { - // sync view with model - LLInventoryObject* model_item = model->getObject(*id_it); - LLFolderViewItem* view_item = mFolders->getItemByID(*id_it); - - if (model_item) - { - if (!view_item) - { - // this object was just created, need to build a view for it - if ((mask & LLInventoryObserver::ADD) != LLInventoryObserver::ADD) - { - llwarns << *id_it << " is in model but not in view, but ADD flag not set" << llendl; - } - buildNewViews(*id_it); - - // select any newly created object - // that has the auto rename at top of folder - // root set - if(mFolders->getRoot()->needsAutoRename()) - { - setSelection(*id_it, FALSE); - } - } - else - { - // this object was probably moved, check its parent - if ((mask & LLInventoryObserver::STRUCTURE) != LLInventoryObserver::STRUCTURE) - { - llwarns << *id_it << " is in model and in view, but STRUCTURE flag not set" << llendl; - } - - LLFolderViewFolder* new_parent = (LLFolderViewFolder*)mFolders->getItemByID(model_item->getParentUUID()); - - // added check against NULL for cases when Inventory panel contains startFolder. - // in this case parent is LLFolderView (LLInventoryPanel::mFolders) itself. - // this check is a fix for bug EXT-1859. - if (NULL != new_parent && view_item->getParentFolder() != new_parent) - { - view_item->getParentFolder()->extractItem(view_item); - view_item->addToFolder(new_parent, mFolders); - } -/* - on the other side in case Inventory Panel has content of the any folder - it is possible that item moved to some folder which is absent in current - Panel. For ex. removing item (via moving to trash). - In this case we need to check if new parent is other then inventory start folder - and simply remove its View from the hierarchy. - See details in EXT-2098. -*/ - // So, let check if item was moved into folder out of this Inventory Panel. - else if (mStartFolderID.notNull() && NULL == new_parent && model_item->getParentUUID() != mStartFolderID) - { - view_item->getParentFolder()->extractItem(view_item); - } - } - } - else - { - if (view_item) - { - if ((mask & LLInventoryObserver::REMOVE) != LLInventoryObserver::REMOVE) - { - llwarns << *id_it << " is not in model but in view, but REMOVE flag not set" << llendl; - } - // item in view but not model, need to delete view - view_item->destroyView(); - } - else - { - llwarns << *id_it << "Item does not exist in either view or model, but notification triggered" << llendl; - } - } - } - } - } - - if (!handled) - { - // it's a small change that only requires a refresh. - // *TODO: figure out a more efficient way to do the refresh - // since it is expensive on large inventories - mFolders->refresh(); - } -} - - -void LLInventoryPanel::rebuildViewsFor(const LLUUID& id) -{ - LLFolderViewItem* old_view = NULL; - - // get old LLFolderViewItem - old_view = mFolders->getItemByID(id); - if (old_view && id.notNull()) - { - old_view->destroyView(); - } - - buildNewViews(id); -} - -void LLInventoryPanel::buildNewViews(const LLUUID& id) -{ - LLMemType mt(LLMemType::MTYPE_INVENTORY_BUILD_NEW_VIEWS); - LLFolderViewItem* itemp = NULL; - LLInventoryObject* objectp = NULL; - - // Don't add the start folder (the inventory panel will show contents - // beginning with the children of the starting folder, excluding the starting folder itself). - if (id != mStartFolderID) - { - objectp = gInventory.getObject(id); - if (objectp) - { - const LLUUID &parent_id = objectp->getParentUUID(); - // If this item's parent is the starting folder, then just add it to the top level (recall that - // the starting folder isn't actually represented in the view, parent_folder would be NULL in - // this case otherwise). - LLFolderViewFolder* parent_folder = (parent_id == mStartFolderID ? - mFolders : (LLFolderViewFolder*)mFolders->getItemByID(parent_id)); - - // This item exists outside the inventory's hierarchy, so don't add it. - if (!parent_folder) - { - return; - } - - if (objectp->getType() <= LLAssetType::AT_NONE || - objectp->getType() >= LLAssetType::AT_COUNT) - { - llwarns << "LLInventoryPanel::buildNewViews called with invalid objectp->mType : " << - ((S32) objectp->getType()) << llendl; - return; - } - - if (objectp->getType() == LLAssetType::AT_CATEGORY && - objectp->getActualType() != LLAssetType::AT_LINK_FOLDER) - { - LLInvFVBridge* new_listener = mInvFVBridgeBuilder->createBridge(objectp->getType(), - objectp->getType(), - LLInventoryType::IT_CATEGORY, - this, - objectp->getUUID()); - - if (new_listener) - { - LLFolderViewFolder::Params p; - p.name = new_listener->getDisplayName(); - p.icon = new_listener->getIcon(); - p.root = mFolders; - p.listener = new_listener; - LLFolderViewFolder* folderp = LLUICtrlFactory::create<LLFolderViewFolder>(p); - - folderp->setItemSortOrder(mFolders->getSortOrder()); - itemp = folderp; - } - } - else - { - // Build new view for item - LLInventoryItem* item = (LLInventoryItem*)objectp; - LLInvFVBridge* new_listener = mInvFVBridgeBuilder->createBridge(item->getType(), - item->getActualType(), - item->getInventoryType(), - this, - item->getUUID(), - item->getFlags()); - - if (new_listener) - { - LLFolderViewItem::Params params; - params.name(new_listener->getDisplayName()); - params.icon(new_listener->getIcon()); - params.creation_date(new_listener->getCreationDate()); - params.root(mFolders); - params.listener(new_listener); - params.rect(LLRect (0, 0, 0, 0)); - itemp = LLUICtrlFactory::create<LLFolderViewItem> (params); - } - } - - if (itemp) - { - itemp->addToFolder(parent_folder, mFolders); - } - } - } - - // If this is a folder, add the children of the folder and recursively add any - // child folders. - if ((id == mStartFolderID) || - (objectp && objectp->getType() == LLAssetType::AT_CATEGORY)) - { - LLViewerInventoryCategory::cat_array_t* categories; - LLViewerInventoryItem::item_array_t* items; - - mInventory->lockDirectDescendentArrays(id, categories, items); - if(categories) - { - S32 count = categories->count(); - for(S32 i = 0; i < count; ++i) - { - LLInventoryCategory* cat = categories->get(i); - buildNewViews(cat->getUUID()); - } - } - if(items) - { - S32 count = items->count(); - for(S32 i = 0; i < count; ++i) - { - LLInventoryItem* item = items->get(i); - buildNewViews(item->getUUID()); - } - } - mInventory->unlockDirectDescendentArrays(id); - } -} - -struct LLConfirmPurgeData -{ - LLUUID mID; - LLInventoryModel* mModel; -}; - -class LLIsNotWorn : public LLInventoryCollectFunctor -{ -public: - LLIsNotWorn() {} - virtual ~LLIsNotWorn() {} - virtual bool operator()(LLInventoryCategory* cat, - LLInventoryItem* item) - { - return !gAgentWearables.isWearingItem(item->getUUID()); - } -}; - -class LLOpenFolderByID : public LLFolderViewFunctor -{ -public: - LLOpenFolderByID(const LLUUID& id) : mID(id) {} - virtual ~LLOpenFolderByID() {} - virtual void doFolder(LLFolderViewFolder* folder) - { - if (folder->getListener() && folder->getListener()->getUUID() == mID) folder->setOpenArrangeRecursively(TRUE, LLFolderViewFolder::RECURSE_UP); - } - virtual void doItem(LLFolderViewItem* item) {} -protected: - const LLUUID& mID; -}; - - -void LLInventoryPanel::openSelected() -{ - LLFolderViewItem* folder_item = mFolders->getCurSelectedItem(); - if(!folder_item) return; - LLInvFVBridge* bridge = (LLInvFVBridge*)folder_item->getListener(); - if(!bridge) return; - bridge->openItem(); -} - -BOOL LLInventoryPanel::handleHover(S32 x, S32 y, MASK mask) -{ - BOOL handled = LLView::handleHover(x, y, mask); - if(handled) - { - ECursorType cursor = getWindow()->getCursor(); - if (LLInventoryModel::backgroundFetchActive() && cursor == UI_CURSOR_ARROW) - { - // replace arrow cursor with arrow and hourglass cursor - getWindow()->setCursor(UI_CURSOR_WORKING); - } - } - else - { - getWindow()->setCursor(UI_CURSOR_ARROW); - } - return TRUE; -} - -BOOL LLInventoryPanel::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, - EDragAndDropType cargo_type, - void* cargo_data, - EAcceptance* accept, - std::string& tooltip_msg) -{ - - BOOL handled = LLPanel::handleDragAndDrop(x, y, mask, drop, cargo_type, cargo_data, accept, tooltip_msg); - - if (handled) - { - mFolders->setDragAndDropThisFrame(); - } - - return handled; -} - -void LLInventoryPanel::onFocusLost() -{ - // inventory no longer handles cut/copy/paste/delete - if (LLEditMenuHandler::gEditMenuHandler == mFolders) - { - LLEditMenuHandler::gEditMenuHandler = NULL; - } - - LLPanel::onFocusLost(); -} - -void LLInventoryPanel::onFocusReceived() -{ - // inventory now handles cut/copy/paste/delete - LLEditMenuHandler::gEditMenuHandler = mFolders; - - LLPanel::onFocusReceived(); -} - - -void LLInventoryPanel::openAllFolders() -{ - mFolders->setOpenArrangeRecursively(TRUE, LLFolderViewFolder::RECURSE_DOWN); - mFolders->arrangeAll(); -} - -void LLInventoryPanel::openDefaultFolderForType(LLFolderType::EType type) -{ - LLUUID category_id = mInventory->findCategoryUUIDForType(type); - LLOpenFolderByID opener(category_id); - mFolders->applyFunctorRecursively(opener); -} - -void LLInventoryPanel::setSelection(const LLUUID& obj_id, BOOL take_keyboard_focus) -{ - // Don't select objects in COF (e.g. to prevent refocus when items are worn). - const LLInventoryObject *obj = gInventory.getObject(obj_id); - if (obj && obj->getParentUUID() == LLAppearanceManager::getCOF()) - { - return; - } - mFolders->setSelectionByID(obj_id, take_keyboard_focus); -} - -void LLInventoryPanel::clearSelection() -{ - mFolders->clearSelection(); -} - -void LLInventoryPanel::onSelectionChange(const std::deque<LLFolderViewItem*>& items, BOOL user_action) -{ - LLFolderView* fv = getRootFolder(); - if (fv->needsAutoRename()) // auto-selecting a new user-created asset and preparing to rename - { - fv->setNeedsAutoRename(FALSE); - if (items.size()) // new asset is visible and selected - { - fv->startRenamingSelectedItem(); - } - } - // Seraph - Put determineFolderType in here for ensemble typing? -} - -//---------------------------------------------------------------------------- - -void LLInventoryPanel::doToSelected(const LLSD& userdata) -{ - mFolders->doToSelected(&gInventory, userdata); -} - -void LLInventoryPanel::doCreate(const LLSD& userdata) -{ - menu_create_inventory_item(mFolders, LLFolderBridge::sSelf, userdata); -} - -bool LLInventoryPanel::beginIMSession() -{ - std::set<LLUUID> selected_items; - mFolders->getSelectionList(selected_items); - - std::string name; - static int session_num = 1; - - LLDynamicArray<LLUUID> members; - EInstantMessage type = IM_SESSION_CONFERENCE_START; - - std::set<LLUUID>::const_iterator iter; - for (iter = selected_items.begin(); iter != selected_items.end(); iter++) - { - - LLUUID item = *iter; - LLFolderViewItem* folder_item = mFolders->getItemByID(item); - - if(folder_item) - { - LLFolderViewEventListener* fve_listener = folder_item->getListener(); - if (fve_listener && (fve_listener->getInventoryType() == LLInventoryType::IT_CATEGORY)) - { - - LLFolderBridge* bridge = (LLFolderBridge*)folder_item->getListener(); - if(!bridge) return true; - LLViewerInventoryCategory* cat = bridge->getCategory(); - if(!cat) return true; - name = cat->getName(); - LLUniqueBuddyCollector is_buddy; - LLInventoryModel::cat_array_t cat_array; - LLInventoryModel::item_array_t item_array; - gInventory.collectDescendentsIf(bridge->getUUID(), - cat_array, - item_array, - LLInventoryModel::EXCLUDE_TRASH, - is_buddy); - S32 count = item_array.count(); - if(count > 0) - { - LLFloaterReg::showInstance("communicate"); - // create the session - LLAvatarTracker& at = LLAvatarTracker::instance(); - LLUUID id; - for(S32 i = 0; i < count; ++i) - { - id = item_array.get(i)->getCreatorUUID(); - if(at.isBuddyOnline(id)) - { - members.put(id); - } - } - } - } - else - { - LLFolderViewItem* folder_item = mFolders->getItemByID(item); - if(!folder_item) return true; - LLInvFVBridge* listenerp = (LLInvFVBridge*)folder_item->getListener(); - - if (listenerp->getInventoryType() == LLInventoryType::IT_CALLINGCARD) - { - LLInventoryItem* inv_item = gInventory.getItem(listenerp->getUUID()); - - if (inv_item) - { - LLAvatarTracker& at = LLAvatarTracker::instance(); - LLUUID id = inv_item->getCreatorUUID(); - - if(at.isBuddyOnline(id)) - { - members.put(id); - } - } - } //if IT_CALLINGCARD - } //if !IT_CATEGORY - } - } //for selected_items - - // the session_id is randomly generated UUID which will be replaced later - // with a server side generated number - - if (name.empty()) - { - name = llformat("Session %d", session_num++); - } - - LLUUID session_id = gIMMgr->addSession(name, type, members[0], members); - if (session_id != LLUUID::null) - { - LLIMFloater::show(session_id); - } - - return true; -} - -bool LLInventoryPanel::attachObject(const LLSD& userdata) -{ - std::set<LLUUID> selected_items; - mFolders->getSelectionList(selected_items); - - std::string joint_name = userdata.asString(); - LLVOAvatar *avatarp = static_cast<LLVOAvatar*>(gAgent.getAvatarObject()); - LLViewerJointAttachment* attachmentp = NULL; - for (LLVOAvatar::attachment_map_t::iterator iter = avatarp->mAttachmentPoints.begin(); - iter != avatarp->mAttachmentPoints.end(); ) - { - LLVOAvatar::attachment_map_t::iterator curiter = iter++; - LLViewerJointAttachment* attachment = curiter->second; - if (attachment->getName() == joint_name) - { - attachmentp = attachment; - break; - } - } - if (attachmentp == NULL) - { - return true; - } - - for (std::set<LLUUID>::const_iterator set_iter = selected_items.begin(); - set_iter != selected_items.end(); - ++set_iter) - { - const LLUUID &id = *set_iter; - LLViewerInventoryItem* item = (LLViewerInventoryItem*)gInventory.getItem(id); - if(item && gInventory.isObjectDescendentOf(id, gInventory.getRootFolderID())) - { - rez_attachment(item, attachmentp); - } - else if(item && item->isComplete()) - { - // must be in library. copy it to our inventory and put it on. - LLPointer<LLInventoryCallback> cb = new RezAttachmentCallback(attachmentp); - copy_inventory_item(gAgent.getID(), - item->getPermissions().getOwner(), - item->getUUID(), - LLUUID::null, - std::string(), - cb); - } - } - gFocusMgr.setKeyboardFocus(NULL); - - return true; -} - - -//---------------------------------------------------------------------------- - -// static DEBUG ONLY: -void LLInventoryPanel::dumpSelectionInformation(void* user_data) -{ - LLInventoryPanel* iv = (LLInventoryPanel*)user_data; - iv->mFolders->dumpSelectionInformation(); -} - -BOOL LLInventoryPanel::getSinceLogoff() -{ - return mFolders->getFilter()->isSinceLogoff(); -} - -void example_param_block_usage() -{ - LLInventoryPanel::Params param_block; - param_block.name(std::string("inventory")); - - param_block.sort_order_setting(LLInventoryPanel::RECENTITEMS_SORT_ORDER); - param_block.allow_multi_select(true); - param_block.filter(LLInventoryPanel::Filter() - .sort_order(1) - .types(0xffff0000)); - param_block.inventory(&gInventory); - param_block.has_border(true); - - LLUICtrlFactory::create<LLInventoryPanel>(param_block); - - param_block = LLInventoryPanel::Params(); - param_block.name(std::string("inventory")); - - //LLSD param_block_sd; - //param_block_sd["sort_order_setting"] = LLInventoryPanel::RECENTITEMS_SORT_ORDER; - //param_block_sd["allow_multi_select"] = true; - //param_block_sd["filter"]["sort_order"] = 1; - //param_block_sd["filter"]["types"] = (S32)0xffff0000; - //param_block_sd["has_border"] = true; - - //LLInitParam::LLSDParser(param_block_sd).parse(param_block); - - LLUICtrlFactory::create<LLInventoryPanel>(param_block); -} +/*
+ * @file llinventorypanel.cpp
+ * @brief Implementation of the inventory panel and associated stuff.
+ *
+ * $LicenseInfo:firstyear=2001&license=viewergpl$
+ *
+ * Copyright (c) 2001-2009, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include <utility> // for std::pair<>
+
+#include "llinventorypanel.h"
+
+// Seraph TODO: Remove unnecessary headers
+
+// library includes
+#include "llagent.h"
+#include "llagentwearables.h"
+#include "llcallingcard.h"
+#include "llfloaterreg.h"
+#include "llsdserialize.h"
+#include "llfiltereditor.h"
+#include "llspinctrl.h"
+#include "llui.h"
+#include "message.h"
+
+// newview includes
+#include "llappearancemgr.h"
+#include "llappviewer.h"
+#include "llfirstuse.h"
+#include "llfloaterchat.h"
+#include "llfloatercustomize.h"
+#include "llfocusmgr.h"
+#include "llfolderview.h"
+#include "llgesturemgr.h"
+#include "lliconctrl.h"
+#include "llimview.h"
+#include "llinventorybridge.h"
+#include "llinventoryclipboard.h"
+#include "llinventorymodel.h"
+#include "lllineeditor.h"
+#include "llmenugl.h"
+#include "llpreviewanim.h"
+#include "llpreviewgesture.h"
+#include "llpreviewnotecard.h"
+#include "llpreviewscript.h"
+#include "llpreviewsound.h"
+#include "llpreviewtexture.h"
+#include "llresmgr.h"
+#include "llscrollbar.h"
+#include "llscrollcontainer.h"
+#include "llselectmgr.h"
+#include "lltabcontainer.h"
+#include "lltooldraganddrop.h"
+#include "lluictrlfactory.h"
+#include "llviewerfoldertype.h"
+#include "llviewerinventory.h"
+#include "llviewermessage.h"
+#include "llviewerobjectlist.h"
+#include "llviewerregion.h"
+#include "llviewerwindow.h"
+#include "llvoavatarself.h"
+#include "llwearablelist.h"
+#include "llimfloater.h"
+
+static LLDefaultChildRegistry::Register<LLInventoryPanel> r("inventory_panel");
+
+const std::string LLInventoryPanel::DEFAULT_SORT_ORDER = std::string("InventorySortOrder");
+const std::string LLInventoryPanel::RECENTITEMS_SORT_ORDER = std::string("RecentItemsSortOrder");
+const std::string LLInventoryPanel::INHERIT_SORT_ORDER = std::string("");
+static const LLInventoryFVBridgeBuilder INVENTORY_BRIDGE_BUILDER;
+
+LLInventoryPanel::LLInventoryPanel(const LLInventoryPanel::Params& p) :
+ LLPanel(p),
+ mInventoryObserver(NULL),
+ mFolders(NULL),
+ mScroller(NULL),
+ mSortOrderSetting(p.sort_order_setting),
+ mInventory(p.inventory),
+ mAllowMultiSelect(p.allow_multi_select),
+ mHasInventoryConnection(false),
+ mStartFolderString(p.start_folder),
+ mBuildDefaultHierarchy(true),
+ mInvFVBridgeBuilder(NULL)
+{
+ mInvFVBridgeBuilder = &INVENTORY_BRIDGE_BUILDER;
+
+ // contex menu callbacks
+ mCommitCallbackRegistrar.add("Inventory.DoToSelected", boost::bind(&LLInventoryPanel::doToSelected, this, _2));
+ mCommitCallbackRegistrar.add("Inventory.EmptyTrash", boost::bind(&LLInventoryModel::emptyFolderType, &gInventory, "ConfirmEmptyTrash", LLFolderType::FT_TRASH));
+ mCommitCallbackRegistrar.add("Inventory.EmptyLostAndFound", boost::bind(&LLInventoryModel::emptyFolderType, &gInventory, "ConfirmEmptyLostAndFound", LLFolderType::FT_LOST_AND_FOUND));
+ mCommitCallbackRegistrar.add("Inventory.DoCreate", boost::bind(&LLInventoryPanel::doCreate, this, _2));
+ mCommitCallbackRegistrar.add("Inventory.AttachObject", boost::bind(&LLInventoryPanel::attachObject, this, _2));
+ mCommitCallbackRegistrar.add("Inventory.BeginIMSession", boost::bind(&LLInventoryPanel::beginIMSession, this));
+
+ setBackgroundColor(LLUIColorTable::instance().getColor("InventoryBackgroundColor"));
+ setBackgroundVisible(TRUE);
+ setBackgroundOpaque(TRUE);
+}
+
+BOOL LLInventoryPanel::postBuild()
+{
+ LLMemType mt(LLMemType::MTYPE_INVENTORY_POST_BUILD);
+
+ mCommitCallbackRegistrar.pushScope(); // registered as a widget; need to push callback scope ourselves
+
+ // create root folder
+ {
+ LLRect folder_rect(0,
+ 0,
+ getRect().getWidth(),
+ 0);
+ LLFolderView::Params p;
+ p.name = getName();
+ p.rect = folder_rect;
+ p.parent_panel = this;
+ mFolders = LLUICtrlFactory::create<LLFolderView>(p);
+ mFolders->setAllowMultiSelect(mAllowMultiSelect);
+ }
+
+ mCommitCallbackRegistrar.popScope();
+
+ mFolders->setCallbackRegistrar(&mCommitCallbackRegistrar);
+
+ // scroller
+ {
+ LLRect scroller_view_rect = getRect();
+ scroller_view_rect.translate(-scroller_view_rect.mLeft, -scroller_view_rect.mBottom);
+ LLScrollContainer::Params p;
+ p.name("Inventory Scroller");
+ p.rect(scroller_view_rect);
+ p.follows.flags(FOLLOWS_ALL);
+ p.reserve_scroll_corner(true);
+ p.tab_stop(true);
+ mScroller = LLUICtrlFactory::create<LLScrollContainer>(p);
+ }
+ addChild(mScroller);
+ mScroller->addChild(mFolders);
+
+ mFolders->setScrollContainer(mScroller);
+
+ // set up the callbacks from the inventory we're viewing, and then
+ // build everything.
+ mInventoryObserver = new LLInventoryPanelObserver(this);
+ mInventory->addObserver(mInventoryObserver);
+
+ // determine the root folder, if any, so inventory contents show just the children
+ // of that folder (i.e. not including the folder itself).
+ const LLFolderType::EType preferred_type = LLViewerFolderType::lookupTypeFromNewCategoryName(mStartFolderString);
+
+ if ("INVENTORY" == mStartFolderString)
+ {
+ mStartFolderID = gInventory.getRootFolderID();
+ }
+ else if ("LIBRARY" == mStartFolderString)
+ {
+ mStartFolderID = gInventory.getLibraryRootFolderID();
+ }
+ else
+ {
+ mStartFolderID = (preferred_type != LLFolderType::FT_NONE ? gInventory.findCategoryUUIDForType(preferred_type) : LLUUID::null);
+ }
+
+ // build view of inventory if we need default full hierarchy and inventory ready, otherwise wait for modelChanged() callback
+ if (mBuildDefaultHierarchy && mInventory->isInventoryUsable() && !mHasInventoryConnection)
+ {
+ rebuildViewsFor(mStartFolderID);
+ mHasInventoryConnection = true;
+ }
+
+ // bit of a hack to make sure the inventory is open.
+ mFolders->openFolder(preferred_type != LLFolderType::FT_NONE ? LLViewerFolderType::lookupNewCategoryName(preferred_type) : "My Inventory");
+
+ if (mSortOrderSetting != INHERIT_SORT_ORDER)
+ {
+ setSortOrder(gSavedSettings.getU32(mSortOrderSetting));
+ }
+ else
+ {
+ setSortOrder(gSavedSettings.getU32(DEFAULT_SORT_ORDER));
+ }
+ mFolders->setSortOrder(mFolders->getFilter()->getSortOrder());
+
+ return TRUE;
+}
+
+LLInventoryPanel::~LLInventoryPanel()
+{
+ // should this be a global setting?
+ if (mFolders)
+ {
+ U32 sort_order = mFolders->getSortOrder();
+ if (mSortOrderSetting != INHERIT_SORT_ORDER)
+ {
+ gSavedSettings.setU32(mSortOrderSetting, sort_order);
+ }
+ }
+
+ // LLView destructor will take care of the sub-views.
+ mInventory->removeObserver(mInventoryObserver);
+ delete mInventoryObserver;
+ mScroller = NULL;
+}
+
+LLMemType mt(LLMemType::MTYPE_INVENTORY_FROM_XML); // ! BUG ! Should this be removed?
+void LLInventoryPanel::draw()
+{
+ // select the desired item (in case it wasn't loaded when the selection was requested)
+ mFolders->updateSelection();
+ LLPanel::draw();
+}
+
+LLInventoryFilter* LLInventoryPanel::getFilter()
+{
+ if (mFolders) return mFolders->getFilter();
+ return NULL;
+}
+
+void LLInventoryPanel::setFilterTypes(U64 filter_types, BOOL filter_for_categories)
+{
+ mFolders->getFilter()->setFilterTypes(filter_types, filter_for_categories);
+}
+
+void LLInventoryPanel::setFilterPermMask(PermissionMask filter_perm_mask)
+{
+ mFolders->getFilter()->setFilterPermissions(filter_perm_mask);
+}
+
+void LLInventoryPanel::setFilterSubString(const std::string& string)
+{
+ mFolders->getFilter()->setFilterSubString(string);
+}
+
+void LLInventoryPanel::setSortOrder(U32 order)
+{
+ mFolders->getFilter()->setSortOrder(order);
+ if (mFolders->getFilter()->isModified())
+ {
+ mFolders->setSortOrder(order);
+ // try to keep selection onscreen, even if it wasn't to start with
+ mFolders->scrollToShowSelection();
+ }
+}
+
+void LLInventoryPanel::setSinceLogoff(BOOL sl)
+{
+ mFolders->getFilter()->setDateRangeLastLogoff(sl);
+}
+
+void LLInventoryPanel::setHoursAgo(U32 hours)
+{
+ mFolders->getFilter()->setHoursAgo(hours);
+}
+
+void LLInventoryPanel::setShowFolderState(LLInventoryFilter::EFolderShow show)
+{
+ mFolders->getFilter()->setShowFolderState(show);
+}
+
+LLInventoryFilter::EFolderShow LLInventoryPanel::getShowFolderState()
+{
+ return mFolders->getFilter()->getShowFolderState();
+}
+
+static LLFastTimer::DeclareTimer FTM_REFRESH("Inventory Refresh");
+
+void LLInventoryPanel::modelChanged(U32 mask)
+{
+ LLFastTimer t2(FTM_REFRESH);
+
+ bool handled = false;
+
+ // inventory just initialized, do complete build
+ if ((mask & LLInventoryObserver::ADD) && gInventory.getChangedIDs().empty() && !mHasInventoryConnection)
+ {
+ rebuildViewsFor(mStartFolderID);
+ mHasInventoryConnection = true;
+ return;
+ }
+
+ if(mask & LLInventoryObserver::LABEL)
+ {
+ handled = true;
+ // label change - empty out the display name for each object
+ // in this change set.
+ const std::set<LLUUID>& changed_items = gInventory.getChangedIDs();
+ std::set<LLUUID>::const_iterator id_it = changed_items.begin();
+ std::set<LLUUID>::const_iterator id_end = changed_items.end();
+ LLFolderViewItem* view = NULL;
+ LLInvFVBridge* bridge = NULL;
+ for (;id_it != id_end; ++id_it)
+ {
+ view = mFolders->getItemByID(*id_it);
+ if(view)
+ {
+ // request refresh on this item (also flags for filtering)
+ bridge = (LLInvFVBridge*)view->getListener();
+ if(bridge)
+ { // Clear the display name first, so it gets properly re-built during refresh()
+ bridge->clearDisplayName();
+ }
+ view->refresh();
+ }
+ }
+ }
+ if((mask & (LLInventoryObserver::STRUCTURE
+ | LLInventoryObserver::ADD
+ | LLInventoryObserver::REMOVE)) != 0)
+ {
+ handled = true;
+ // Record which folders are open by uuid.
+ LLInventoryModel* model = getModel();
+ if (model)
+ {
+ const std::set<LLUUID>& changed_items = gInventory.getChangedIDs();
+
+ std::set<LLUUID>::const_iterator id_it = changed_items.begin();
+ std::set<LLUUID>::const_iterator id_end = changed_items.end();
+ for (;id_it != id_end; ++id_it)
+ {
+ // sync view with model
+ LLInventoryObject* model_item = model->getObject(*id_it);
+ LLFolderViewItem* view_item = mFolders->getItemByID(*id_it);
+
+ if (model_item)
+ {
+ if (!view_item)
+ {
+ // this object was just created, need to build a view for it
+ if ((mask & LLInventoryObserver::ADD) != LLInventoryObserver::ADD)
+ {
+ llwarns << *id_it << " is in model but not in view, but ADD flag not set" << llendl;
+ }
+ buildNewViews(*id_it);
+
+ // select any newly created object
+ // that has the auto rename at top of folder
+ // root set
+ if(mFolders->getRoot()->needsAutoRename())
+ {
+ setSelection(*id_it, FALSE);
+ }
+ }
+ else
+ {
+ // this object was probably moved, check its parent
+ if ((mask & LLInventoryObserver::STRUCTURE) != LLInventoryObserver::STRUCTURE)
+ {
+ llwarns << *id_it << " is in model and in view, but STRUCTURE flag not set" << llendl;
+ }
+
+ LLFolderViewFolder* new_parent = (LLFolderViewFolder*)mFolders->getItemByID(model_item->getParentUUID());
+
+ // added check against NULL for cases when Inventory panel contains startFolder.
+ // in this case parent is LLFolderView (LLInventoryPanel::mFolders) itself.
+ // this check is a fix for bug EXT-1859.
+ if (NULL != new_parent && view_item->getParentFolder() != new_parent)
+ {
+ view_item->getParentFolder()->extractItem(view_item);
+ view_item->addToFolder(new_parent, mFolders);
+ }
+/*
+ on the other side in case Inventory Panel has content of the any folder
+ it is possible that item moved to some folder which is absent in current
+ Panel. For ex. removing item (via moving to trash).
+ In this case we need to check if new parent is other then inventory start folder
+ and simply remove its View from the hierarchy.
+ See details in EXT-2098.
+*/
+ // So, let check if item was moved into folder out of this Inventory Panel.
+ else if (mStartFolderID.notNull() && NULL == new_parent && model_item->getParentUUID() != mStartFolderID)
+ {
+ view_item->getParentFolder()->extractItem(view_item);
+ }
+ }
+ }
+ else
+ {
+ if (view_item)
+ {
+ if ((mask & LLInventoryObserver::REMOVE) != LLInventoryObserver::REMOVE)
+ {
+ llwarns << *id_it << " is not in model but in view, but REMOVE flag not set" << llendl;
+ }
+ // item in view but not model, need to delete view
+ view_item->destroyView();
+ }
+ else
+ {
+ llwarns << *id_it << "Item does not exist in either view or model, but notification triggered" << llendl;
+ }
+ }
+ }
+ }
+ }
+
+ if (!handled)
+ {
+ // it's a small change that only requires a refresh.
+ // *TODO: figure out a more efficient way to do the refresh
+ // since it is expensive on large inventories
+ mFolders->refresh();
+ }
+}
+
+
+void LLInventoryPanel::rebuildViewsFor(const LLUUID& id)
+{
+ LLFolderViewItem* old_view = NULL;
+
+ // get old LLFolderViewItem
+ old_view = mFolders->getItemByID(id);
+ if (old_view && id.notNull())
+ {
+ old_view->destroyView();
+ }
+
+ buildNewViews(id);
+}
+
+void LLInventoryPanel::buildNewViews(const LLUUID& id)
+{
+ LLMemType mt(LLMemType::MTYPE_INVENTORY_BUILD_NEW_VIEWS);
+ LLFolderViewItem* itemp = NULL;
+ LLInventoryObject* objectp = NULL;
+
+ // Don't add the start folder (the inventory panel will show contents
+ // beginning with the children of the starting folder, excluding the starting folder itself).
+ if (id != mStartFolderID)
+ {
+ objectp = gInventory.getObject(id);
+ if (objectp)
+ {
+ const LLUUID &parent_id = objectp->getParentUUID();
+ // If this item's parent is the starting folder, then just add it to the top level (recall that
+ // the starting folder isn't actually represented in the view, parent_folder would be NULL in
+ // this case otherwise).
+ LLFolderViewFolder* parent_folder = (parent_id == mStartFolderID ?
+ mFolders : (LLFolderViewFolder*)mFolders->getItemByID(parent_id));
+
+ // This item exists outside the inventory's hierarchy, so don't add it.
+ if (!parent_folder)
+ {
+ return;
+ }
+
+ if (objectp->getType() <= LLAssetType::AT_NONE ||
+ objectp->getType() >= LLAssetType::AT_COUNT)
+ {
+ lldebugs << "LLInventoryPanel::buildNewViews called with invalid objectp->mType : " <<
+ ((S32) objectp->getType()) << " name " << objectp->getName() << " UUID " << objectp->getUUID() << llendl;
+ return;
+ }
+
+ if (objectp->getType() == LLAssetType::AT_CATEGORY &&
+ objectp->getActualType() != LLAssetType::AT_LINK_FOLDER)
+ {
+ LLInvFVBridge* new_listener = mInvFVBridgeBuilder->createBridge(objectp->getType(),
+ objectp->getType(),
+ LLInventoryType::IT_CATEGORY,
+ this,
+ objectp->getUUID());
+
+ if (new_listener)
+ {
+ LLFolderViewFolder::Params p;
+ p.name = new_listener->getDisplayName();
+ p.icon = new_listener->getIcon();
+ p.root = mFolders;
+ p.listener = new_listener;
+ LLFolderViewFolder* folderp = LLUICtrlFactory::create<LLFolderViewFolder>(p);
+
+ folderp->setItemSortOrder(mFolders->getSortOrder());
+ itemp = folderp;
+ }
+ }
+ else
+ {
+ // Build new view for item
+ LLInventoryItem* item = (LLInventoryItem*)objectp;
+ LLInvFVBridge* new_listener = mInvFVBridgeBuilder->createBridge(item->getType(),
+ item->getActualType(),
+ item->getInventoryType(),
+ this,
+ item->getUUID(),
+ item->getFlags());
+
+ if (new_listener)
+ {
+ LLFolderViewItem::Params params;
+ params.name(new_listener->getDisplayName());
+ params.icon(new_listener->getIcon());
+ params.creation_date(new_listener->getCreationDate());
+ params.root(mFolders);
+ params.listener(new_listener);
+ params.rect(LLRect (0, 0, 0, 0));
+ itemp = LLUICtrlFactory::create<LLFolderViewItem> (params);
+ }
+ }
+
+ if (itemp)
+ {
+ itemp->addToFolder(parent_folder, mFolders);
+ }
+ }
+ }
+
+ // If this is a folder, add the children of the folder and recursively add any
+ // child folders.
+ if ((id == mStartFolderID) ||
+ (objectp && objectp->getType() == LLAssetType::AT_CATEGORY))
+ {
+ LLViewerInventoryCategory::cat_array_t* categories;
+ LLViewerInventoryItem::item_array_t* items;
+
+ mInventory->lockDirectDescendentArrays(id, categories, items);
+ if(categories)
+ {
+ S32 count = categories->count();
+ for(S32 i = 0; i < count; ++i)
+ {
+ LLInventoryCategory* cat = categories->get(i);
+ buildNewViews(cat->getUUID());
+ }
+ }
+ if(items)
+ {
+ S32 count = items->count();
+ for(S32 i = 0; i < count; ++i)
+ {
+ LLInventoryItem* item = items->get(i);
+ buildNewViews(item->getUUID());
+ }
+ }
+ mInventory->unlockDirectDescendentArrays(id);
+ }
+}
+
+struct LLConfirmPurgeData
+{
+ LLUUID mID;
+ LLInventoryModel* mModel;
+};
+
+class LLIsNotWorn : public LLInventoryCollectFunctor
+{
+public:
+ LLIsNotWorn() {}
+ virtual ~LLIsNotWorn() {}
+ virtual bool operator()(LLInventoryCategory* cat,
+ LLInventoryItem* item)
+ {
+ return !gAgentWearables.isWearingItem(item->getUUID());
+ }
+};
+
+class LLOpenFolderByID : public LLFolderViewFunctor
+{
+public:
+ LLOpenFolderByID(const LLUUID& id) : mID(id) {}
+ virtual ~LLOpenFolderByID() {}
+ virtual void doFolder(LLFolderViewFolder* folder)
+ {
+ if (folder->getListener() && folder->getListener()->getUUID() == mID) folder->setOpenArrangeRecursively(TRUE, LLFolderViewFolder::RECURSE_UP);
+ }
+ virtual void doItem(LLFolderViewItem* item) {}
+protected:
+ const LLUUID& mID;
+};
+
+
+void LLInventoryPanel::openSelected()
+{
+ LLFolderViewItem* folder_item = mFolders->getCurSelectedItem();
+ if(!folder_item) return;
+ LLInvFVBridge* bridge = (LLInvFVBridge*)folder_item->getListener();
+ if(!bridge) return;
+ bridge->openItem();
+}
+
+BOOL LLInventoryPanel::handleHover(S32 x, S32 y, MASK mask)
+{
+ BOOL handled = LLView::handleHover(x, y, mask);
+ if(handled)
+ {
+ ECursorType cursor = getWindow()->getCursor();
+ if (LLInventoryModel::backgroundFetchActive() && cursor == UI_CURSOR_ARROW)
+ {
+ // replace arrow cursor with arrow and hourglass cursor
+ getWindow()->setCursor(UI_CURSOR_WORKING);
+ }
+ }
+ else
+ {
+ getWindow()->setCursor(UI_CURSOR_ARROW);
+ }
+ return TRUE;
+}
+
+BOOL LLInventoryPanel::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop,
+ EDragAndDropType cargo_type,
+ void* cargo_data,
+ EAcceptance* accept,
+ std::string& tooltip_msg)
+{
+
+ BOOL handled = LLPanel::handleDragAndDrop(x, y, mask, drop, cargo_type, cargo_data, accept, tooltip_msg);
+
+ if (handled)
+ {
+ mFolders->setDragAndDropThisFrame();
+ }
+
+ return handled;
+}
+
+void LLInventoryPanel::onFocusLost()
+{
+ // inventory no longer handles cut/copy/paste/delete
+ if (LLEditMenuHandler::gEditMenuHandler == mFolders)
+ {
+ LLEditMenuHandler::gEditMenuHandler = NULL;
+ }
+
+ LLPanel::onFocusLost();
+}
+
+void LLInventoryPanel::onFocusReceived()
+{
+ // inventory now handles cut/copy/paste/delete
+ LLEditMenuHandler::gEditMenuHandler = mFolders;
+
+ LLPanel::onFocusReceived();
+}
+
+
+void LLInventoryPanel::openAllFolders()
+{
+ mFolders->setOpenArrangeRecursively(TRUE, LLFolderViewFolder::RECURSE_DOWN);
+ mFolders->arrangeAll();
+}
+
+void LLInventoryPanel::openDefaultFolderForType(LLFolderType::EType type)
+{
+ LLUUID category_id = mInventory->findCategoryUUIDForType(type);
+ LLOpenFolderByID opener(category_id);
+ mFolders->applyFunctorRecursively(opener);
+}
+
+void LLInventoryPanel::setSelection(const LLUUID& obj_id, BOOL take_keyboard_focus)
+{
+ // Don't select objects in COF (e.g. to prevent refocus when items are worn).
+ const LLInventoryObject *obj = gInventory.getObject(obj_id);
+ if (obj && obj->getParentUUID() == LLAppearanceManager::getCOF())
+ {
+ return;
+ }
+ mFolders->setSelectionByID(obj_id, take_keyboard_focus);
+}
+
+void LLInventoryPanel::clearSelection()
+{
+ mFolders->clearSelection();
+}
+
+void LLInventoryPanel::onSelectionChange(const std::deque<LLFolderViewItem*>& items, BOOL user_action)
+{
+ LLFolderView* fv = getRootFolder();
+ if (fv->needsAutoRename()) // auto-selecting a new user-created asset and preparing to rename
+ {
+ fv->setNeedsAutoRename(FALSE);
+ if (items.size()) // new asset is visible and selected
+ {
+ fv->startRenamingSelectedItem();
+ }
+ }
+ // Seraph - Put determineFolderType in here for ensemble typing?
+}
+
+//----------------------------------------------------------------------------
+
+void LLInventoryPanel::doToSelected(const LLSD& userdata)
+{
+ mFolders->doToSelected(&gInventory, userdata);
+}
+
+void LLInventoryPanel::doCreate(const LLSD& userdata)
+{
+ menu_create_inventory_item(mFolders, LLFolderBridge::sSelf, userdata);
+}
+
+bool LLInventoryPanel::beginIMSession()
+{
+ std::set<LLUUID> selected_items;
+ mFolders->getSelectionList(selected_items);
+
+ std::string name;
+ static int session_num = 1;
+
+ LLDynamicArray<LLUUID> members;
+ EInstantMessage type = IM_SESSION_CONFERENCE_START;
+
+ std::set<LLUUID>::const_iterator iter;
+ for (iter = selected_items.begin(); iter != selected_items.end(); iter++)
+ {
+
+ LLUUID item = *iter;
+ LLFolderViewItem* folder_item = mFolders->getItemByID(item);
+
+ if(folder_item)
+ {
+ LLFolderViewEventListener* fve_listener = folder_item->getListener();
+ if (fve_listener && (fve_listener->getInventoryType() == LLInventoryType::IT_CATEGORY))
+ {
+
+ LLFolderBridge* bridge = (LLFolderBridge*)folder_item->getListener();
+ if(!bridge) return true;
+ LLViewerInventoryCategory* cat = bridge->getCategory();
+ if(!cat) return true;
+ name = cat->getName();
+ LLUniqueBuddyCollector is_buddy;
+ LLInventoryModel::cat_array_t cat_array;
+ LLInventoryModel::item_array_t item_array;
+ gInventory.collectDescendentsIf(bridge->getUUID(),
+ cat_array,
+ item_array,
+ LLInventoryModel::EXCLUDE_TRASH,
+ is_buddy);
+ S32 count = item_array.count();
+ if(count > 0)
+ {
+ LLFloaterReg::showInstance("communicate");
+ // create the session
+ LLAvatarTracker& at = LLAvatarTracker::instance();
+ LLUUID id;
+ for(S32 i = 0; i < count; ++i)
+ {
+ id = item_array.get(i)->getCreatorUUID();
+ if(at.isBuddyOnline(id))
+ {
+ members.put(id);
+ }
+ }
+ }
+ }
+ else
+ {
+ LLFolderViewItem* folder_item = mFolders->getItemByID(item);
+ if(!folder_item) return true;
+ LLInvFVBridge* listenerp = (LLInvFVBridge*)folder_item->getListener();
+
+ if (listenerp->getInventoryType() == LLInventoryType::IT_CALLINGCARD)
+ {
+ LLInventoryItem* inv_item = gInventory.getItem(listenerp->getUUID());
+
+ if (inv_item)
+ {
+ LLAvatarTracker& at = LLAvatarTracker::instance();
+ LLUUID id = inv_item->getCreatorUUID();
+
+ if(at.isBuddyOnline(id))
+ {
+ members.put(id);
+ }
+ }
+ } //if IT_CALLINGCARD
+ } //if !IT_CATEGORY
+ }
+ } //for selected_items
+
+ // the session_id is randomly generated UUID which will be replaced later
+ // with a server side generated number
+
+ if (name.empty())
+ {
+ name = llformat("Session %d", session_num++);
+ }
+
+ LLUUID session_id = gIMMgr->addSession(name, type, members[0], members);
+ if (session_id != LLUUID::null)
+ {
+ LLIMFloater::show(session_id);
+ }
+
+ return true;
+}
+
+bool LLInventoryPanel::attachObject(const LLSD& userdata)
+{
+ std::set<LLUUID> selected_items;
+ mFolders->getSelectionList(selected_items);
+
+ std::string joint_name = userdata.asString();
+ LLVOAvatar *avatarp = static_cast<LLVOAvatar*>(gAgent.getAvatarObject());
+ LLViewerJointAttachment* attachmentp = NULL;
+ for (LLVOAvatar::attachment_map_t::iterator iter = avatarp->mAttachmentPoints.begin();
+ iter != avatarp->mAttachmentPoints.end(); )
+ {
+ LLVOAvatar::attachment_map_t::iterator curiter = iter++;
+ LLViewerJointAttachment* attachment = curiter->second;
+ if (attachment->getName() == joint_name)
+ {
+ attachmentp = attachment;
+ break;
+ }
+ }
+ if (attachmentp == NULL)
+ {
+ return true;
+ }
+
+ for (std::set<LLUUID>::const_iterator set_iter = selected_items.begin();
+ set_iter != selected_items.end();
+ ++set_iter)
+ {
+ const LLUUID &id = *set_iter;
+ LLViewerInventoryItem* item = (LLViewerInventoryItem*)gInventory.getItem(id);
+ if(item && gInventory.isObjectDescendentOf(id, gInventory.getRootFolderID()))
+ {
+ rez_attachment(item, attachmentp);
+ }
+ else if(item && item->isComplete())
+ {
+ // must be in library. copy it to our inventory and put it on.
+ LLPointer<LLInventoryCallback> cb = new RezAttachmentCallback(attachmentp);
+ copy_inventory_item(gAgent.getID(),
+ item->getPermissions().getOwner(),
+ item->getUUID(),
+ LLUUID::null,
+ std::string(),
+ cb);
+ }
+ }
+ gFocusMgr.setKeyboardFocus(NULL);
+
+ return true;
+}
+
+
+//----------------------------------------------------------------------------
+
+// static DEBUG ONLY:
+void LLInventoryPanel::dumpSelectionInformation(void* user_data)
+{
+ LLInventoryPanel* iv = (LLInventoryPanel*)user_data;
+ iv->mFolders->dumpSelectionInformation();
+}
+
+BOOL LLInventoryPanel::getSinceLogoff()
+{
+ return mFolders->getFilter()->isSinceLogoff();
+}
+
+void example_param_block_usage()
+{
+ LLInventoryPanel::Params param_block;
+ param_block.name(std::string("inventory"));
+
+ param_block.sort_order_setting(LLInventoryPanel::RECENTITEMS_SORT_ORDER);
+ param_block.allow_multi_select(true);
+ param_block.filter(LLInventoryPanel::Filter()
+ .sort_order(1)
+ .types(0xffff0000));
+ param_block.inventory(&gInventory);
+ param_block.has_border(true);
+
+ LLUICtrlFactory::create<LLInventoryPanel>(param_block);
+
+ param_block = LLInventoryPanel::Params();
+ param_block.name(std::string("inventory"));
+
+ //LLSD param_block_sd;
+ //param_block_sd["sort_order_setting"] = LLInventoryPanel::RECENTITEMS_SORT_ORDER;
+ //param_block_sd["allow_multi_select"] = true;
+ //param_block_sd["filter"]["sort_order"] = 1;
+ //param_block_sd["filter"]["types"] = (S32)0xffff0000;
+ //param_block_sd["has_border"] = true;
+
+ //LLInitParam::LLSDParser(param_block_sd).parse(param_block);
+
+ LLUICtrlFactory::create<LLInventoryPanel>(param_block);
+}
diff --git a/indra/newview/lllandmarkactions.cpp b/indra/newview/lllandmarkactions.cpp index b1829b3945..e0dc1b6f0f 100644 --- a/indra/newview/lllandmarkactions.cpp +++ b/indra/newview/lllandmarkactions.cpp @@ -49,6 +49,7 @@ #include "llstring.h" #include "llviewerinventory.h" #include "llviewerparcelmgr.h" +#include "llworldmapmessage.h" #include "llviewerwindow.h" #include "llwindow.h" #include "llworldmap.h" @@ -305,13 +306,13 @@ void LLLandmarkActions::getSLURLfromPosGlobal(const LLVector3d& global_pos, slur { U64 new_region_handle = to_region_handle(global_pos); - LLWorldMap::url_callback_t url_cb = boost::bind(&LLLandmarkActions::onRegionResponseSLURL, + LLWorldMapMessage::url_callback_t url_cb = boost::bind(&LLLandmarkActions::onRegionResponseSLURL, cb, global_pos, escaped, _2); - LLWorldMap::getInstance()->sendHandleRegionRequest(new_region_handle, url_cb, std::string("unused"), false); + LLWorldMapMessage::getInstance()->sendHandleRegionRequest(new_region_handle, url_cb, std::string("unused"), false); } } @@ -322,18 +323,19 @@ void LLLandmarkActions::getRegionNameAndCoordsFromPosGlobal(const LLVector3d& gl if (sim_infop) { LLVector3 pos = sim_infop->getLocalPos(global_pos); - cb(sim_infop->mName, llround(pos.mV[VX]), llround(pos.mV[VY])); + std::string name = sim_infop->getName() ; + cb(name, llround(pos.mV[VX]), llround(pos.mV[VY])); } else { U64 new_region_handle = to_region_handle(global_pos); - LLWorldMap::url_callback_t url_cb = boost::bind(&LLLandmarkActions::onRegionResponseNameAndCoords, + LLWorldMapMessage::url_callback_t url_cb = boost::bind(&LLLandmarkActions::onRegionResponseNameAndCoords, cb, global_pos, _1); - LLWorldMap::getInstance()->sendHandleRegionRequest(new_region_handle, url_cb, std::string("unused"), false); + LLWorldMapMessage::getInstance()->sendHandleRegionRequest(new_region_handle, url_cb, std::string("unused"), false); } } @@ -365,7 +367,8 @@ void LLLandmarkActions::onRegionResponseNameAndCoords(region_name_and_coords_cal if (sim_infop) { LLVector3 local_pos = sim_infop->getLocalPos(global_pos); - cb(sim_infop->mName, llround(local_pos.mV[VX]), llround(local_pos.mV[VY])); + std::string name = sim_infop->getName() ; + cb(name, llround(local_pos.mV[VX]), llround(local_pos.mV[VY])); } } diff --git a/indra/newview/llnavigationbar.cpp b/indra/newview/llnavigationbar.cpp index 4f145891ea..63794be085 100644 --- a/indra/newview/llnavigationbar.cpp +++ b/indra/newview/llnavigationbar.cpp @@ -52,7 +52,7 @@ #include "llviewerinventory.h" #include "llviewermenu.h" #include "llviewerparcelmgr.h" -#include "llworldmap.h" +#include "llworldmapmessage.h" #include "llappviewer.h" #include "llviewercontrol.h" #include "llfloatermediabrowser.h" @@ -393,14 +393,13 @@ void LLNavigationBar::onLocationSelection() // Resolve the region name to its global coordinates. // If resolution succeeds we'll teleport. - LLWorldMap::url_callback_t cb = boost::bind( + LLWorldMapMessage::url_callback_t cb = boost::bind( &LLNavigationBar::onRegionNameResponse, this, typed_location, region_name, local_coords, _1, _2, _3, _4); // connect the callback each time, when user enter new location to get real location of agent after teleport mTeleportFinishConnection = LLViewerParcelMgr::getInstance()-> setTeleportFinishedCallback(boost::bind(&LLNavigationBar::onTeleportFinished, this, _1,typed_location)); - - LLWorldMap::getInstance()->sendNamedRegionRequest(region_name, cb, std::string("unused"), false); + LLWorldMapMessage::getInstance()->sendNamedRegionRequest(region_name, cb, std::string("unused"), false); } void LLNavigationBar::onTeleportFinished(const LLVector3d& global_agent_pos, const std::string& typed_location) diff --git a/indra/newview/llnetmap.cpp b/indra/newview/llnetmap.cpp index b6b433c28f..4286582cdc 100644 --- a/indra/newview/llnetmap.cpp +++ b/indra/newview/llnetmap.cpp @@ -65,9 +65,15 @@ static LLDefaultChildRegistry::Register<LLNetMap> r1("net_map"); +const F32 LLNetMap::MAP_SCALE_MIN = 32; +const F32 LLNetMap::MAP_SCALE_MID = 1024; +const F32 LLNetMap::MAP_SCALE_MAX = 4096; + const F32 MAP_SCALE_INCREMENT = 16; -const F32 MAP_MIN_PICK_DIST = 4; -const F32 MAX_PRIM_RADIUS = 256.0f; // Don't try to draw giant mega-prims on the mini map +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; LLNetMap::LLNetMap (const Params & p) : LLUICtrl (p), @@ -89,6 +95,7 @@ LLNetMap::LLNetMap (const Params & p) mRotateMap(FALSE), mToolTipMsg() { + mDotRadius = llmax(DOT_SCALE * mPixelsPerMeter, MIN_DOT_RADIUS); } LLNetMap::~LLNetMap() @@ -101,17 +108,18 @@ void LLNetMap::setScale( F32 scale ) if (mObjectImagep.notNull()) { - F32 half_width = (F32)(getRect().getWidth() / 2); - F32 half_height = (F32)(getRect().getHeight() / 2); - F32 radius = sqrt( half_width * half_width + half_height * half_height ); - F32 region_widths = (2.f*radius)/mScale; + 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 = 2.f*radius; + mObjectMapTPM = num_pixels / meters; + mObjectMapPixels = diameter; } mPixelsPerMeter = mScale / REGION_WIDTH_METERS; + mDotRadius = llmax(DOT_SCALE * mPixelsPerMeter, MIN_DOT_RADIUS); mUpdateNow = TRUE; } @@ -302,6 +310,7 @@ void LLNetMap::draw() LLUI::getMousePositionLocal(this, &local_mouse_x, &local_mouse_y); mClosestAgentToCursor.setNull(); F32 closest_dist = F32_MAX; + F32 min_pick_dist = mDotRadius * MIN_PICK_SCALE; // Draw avatars for (LLWorld::region_list_t::const_iterator iter = LLWorld::getInstance()->getRegionList().begin(); @@ -345,10 +354,10 @@ void LLNetMap::draw() LLWorldMapView::drawAvatar( pos_map.mV[VX], pos_map.mV[VY], show_as_friend ? map_avatar_friend_color : map_avatar_color, - pos_map.mV[VZ]); + pos_map.mV[VZ], mDotRadius); F32 dist_to_cursor = dist_vec(LLVector2(pos_map.mV[VX], pos_map.mV[VY]), LLVector2(local_mouse_x,local_mouse_y)); - if(dist_to_cursor < MAP_MIN_PICK_DIST && dist_to_cursor < closest_dist) + if(dist_to_cursor < min_pick_dist && dist_to_cursor < closest_dist) { closest_dist = dist_to_cursor; mClosestAgentToCursor = regionp->mMapAvatarIDs.get(i); @@ -378,10 +387,12 @@ void LLNetMap::draw() // Draw dot for self avatar position pos_global = gAgent.getPositionGlobal(); pos_map = globalPosToView(pos_global); - LLUIImagePtr you = LLWorldMapView::sAvatarYouSmallImage; - you->draw( - llround(pos_map.mV[VX]) - you->getWidth()/2, - llround(pos_map.mV[VY]) - you->getHeight()/2); + LLUIImagePtr you = LLWorldMapView::sAvatarYouLargeImage; + S32 dot_width = llround(mDotRadius * 2.f); + you->draw(llround(pos_map.mV[VX] - mDotRadius), + llround(pos_map.mV[VY] - mDotRadius), + dot_width, + dot_width); // Draw frustum F32 meters_to_pixels = mScale/ LLWorld::getInstance()->getRegionWidthInMeters(); @@ -429,6 +440,12 @@ void LLNetMap::draw() 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 relative_pos_global = global_pos - gAgent.getCameraPositionGlobal(); @@ -504,8 +521,12 @@ LLVector3d LLNetMap::viewPosToGlobal( S32 x, S32 y ) BOOL LLNetMap::handleScrollWheel(S32 x, S32 y, S32 clicks) { - // note that clicks are reversed from what you'd think - setScale(llclamp(mScale - clicks*MAP_SCALE_INCREMENT, MAP_SCALE_MIN, MAP_SCALE_MAX)); + // note that clicks are reversed from what you'd think: i.e. > 0 means zoom out, < 0 means zoom in + F32 scale = mScale; + + scale *= pow(MAP_SCALE_ZOOM_FACTOR, -clicks); + setScale(llclamp(scale, MAP_SCALE_MIN, MAP_SCALE_MAX)); + return TRUE; } @@ -567,9 +588,7 @@ void LLNetMap::renderScaledPointGlobal( const LLVector3d& pos, const LLColor4U & LLVector3 local_pos; local_pos.setVec( pos - mObjectImageCenterGlobal ); - F32 radius_clamped = llmin(radius_meters, MAX_PRIM_RADIUS); - - S32 diameter_pixels = llround(2 * radius_clamped * mObjectMapTPM); + S32 diameter_pixels = llround(2 * radius_meters * mObjectMapTPM); renderPoint( local_pos, color, diameter_pixels ); } @@ -662,13 +681,13 @@ void LLNetMap::renderPoint(const LLVector3 &pos_local, const LLColor4U &color, void LLNetMap::createObjectImage() { // Find the size of the side of a square that surrounds the circle that surrounds getRect(). - F32 half_width = (F32)(getRect().getWidth() / 2); - F32 half_height = (F32)(getRect().getHeight() / 2); - F32 radius = sqrt( half_width * half_width + half_height * half_height ); - S32 square_size = S32( 2 * radius ); + // ... which is, the diagonal of the rect. + F32 width = (F32)getRect().getWidth(); + F32 height = (F32)getRect().getHeight(); + S32 square_size = llround( sqrt(width*width + height*height) ); // Find the least power of two >= the minimum size. - const S32 MIN_SIZE = 32; + 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) ) @@ -684,7 +703,7 @@ void LLNetMap::createObjectImage() U8* data = mObjectRawImagep->getData(); memset( data, 0, img_size * img_size * 4 ); mObjectImagep = LLViewerTextureManager::getLocalTexture( mObjectRawImagep.get(), FALSE); - setScale(mScale); } + setScale(mScale); mUpdateNow = TRUE; } diff --git a/indra/newview/llnetmap.h b/indra/newview/llnetmap.h index 5ebdd13384..7088ab3e70 100644 --- a/indra/newview/llnetmap.h +++ b/indra/newview/llnetmap.h @@ -70,9 +70,14 @@ protected: public: virtual ~LLNetMap(); + static const F32 MAP_SCALE_MIN; + static const F32 MAP_SCALE_MID; + static const F32 MAP_SCALE_MAX; + /*virtual*/ void draw(); /*virtual*/ BOOL handleScrollWheel(S32 x, S32 y, S32 clicks); /*virtual*/ BOOL handleToolTip( S32 x, S32 y, MASK mask); + /*virtual*/ void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE); void setScale( F32 scale ); void setRotateMap( BOOL b ) { mRotateMap = b; } @@ -94,16 +99,17 @@ private: void drawTracking( const LLVector3d& pos_global, const LLColor4& color, BOOL draw_arrow = TRUE); - - void createObjectImage(); + void createObjectImage(); + private: LLUIColor mBackgroundColor; F32 mScale; // Size of a region in pixels F32 mPixelsPerMeter; // world meters to map pixels F32 mObjectMapTPM; // texels per meter on map - F32 mObjectMapPixels; // Width of object map in pixels; + F32 mObjectMapPixels; // Width of object map in pixels + F32 mDotRadius; // Size of avatar markers F32 mTargetPanX; F32 mTargetPanY; F32 mCurPanX; diff --git a/indra/newview/llpanellandmarks.cpp b/indra/newview/llpanellandmarks.cpp index 9840c91158..ce1a7f98df 100644 --- a/indra/newview/llpanellandmarks.cpp +++ b/indra/newview/llpanellandmarks.cpp @@ -267,7 +267,7 @@ LLLandmark* LLLandmarksPanel::getCurSelectedLandmark() const return NULL; } -LLFolderViewItem* LLLandmarksPanel::getCurSelectedItem () const +LLFolderViewItem* LLLandmarksPanel::getCurSelectedItem() const { return mCurrentSelectedList ? mCurrentSelectedList->getRootFolder()->getCurSelectedItem() : NULL; } diff --git a/indra/newview/llpanellandmarks.h b/indra/newview/llpanellandmarks.h index d54761ed59..745f9364c2 100644 --- a/indra/newview/llpanellandmarks.h +++ b/indra/newview/llpanellandmarks.h @@ -69,7 +69,7 @@ protected: bool isLandmarkSelected() const; bool isReceivedFolderSelected() const; LLLandmark* getCurSelectedLandmark() const; - LLFolderViewItem* getCurSelectedItem () const; + LLFolderViewItem* getCurSelectedItem() const; void updateSortOrder(LLInventoryPanel* panel, bool byDate); //LLRemoteParcelInfoObserver interface diff --git a/indra/newview/llpanelmaininventory.cpp b/indra/newview/llpanelmaininventory.cpp index 25be09fa24..15a75cb930 100644 --- a/indra/newview/llpanelmaininventory.cpp +++ b/indra/newview/llpanelmaininventory.cpp @@ -1,818 +1,1025 @@ -/** - * @file llsidepanelmaininventory.cpp - * @brief Implementation of llsidepanelmaininventory. - * - * $LicenseInfo:firstyear=2001&license=viewergpl$ - * - * Copyright (c) 2001-2009, Linden Research, Inc. - * - * Second Life Viewer Source Code - * The source code in this file ("Source Code") is provided by Linden Lab - * to you under the terms of the GNU General Public License, version 2.0 - * ("GPL"), unless you have obtained a separate licensing agreement - * ("Other License"), formally executed by you and Linden Lab. Terms of - * the GPL can be found in doc/GPL-license.txt in this distribution, or - * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 - * - * There are special exceptions to the terms and conditions of the GPL as - * it is applied to this Source Code. View the full text of the exception - * in the file doc/FLOSS-exception.txt in this software distribution, or - * online at - * http://secondlifegrid.net/programs/open_source/licensing/flossexception - * - * By copying, modifying or distributing this software, you acknowledge - * that you have read and understood your obligations described above, - * and agree to abide by those obligations. - * - * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO - * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, - * COMPLETENESS OR PERFORMANCE. - * $/LicenseInfo$ - */ - -#include "llviewerprecompiledheaders.h" -#include "llpanelmaininventory.h" - -#include "llfloaterinventory.h" -#include "llinventorybridge.h" -#include "llinventoryfunctions.h" -#include "llinventorypanel.h" -#include "llfiltereditor.h" -#include "llfloaterreg.h" -#include "llscrollcontainer.h" -#include "llsdserialize.h" -#include "llspinctrl.h" -#include "lltooldraganddrop.h" - -static LLRegisterPanelClassWrapper<LLPanelMainInventory> t_inventory("panel_main_inventory"); // Seraph is this redundant with constructor? - -///---------------------------------------------------------------------------- -/// LLFloaterInventoryFinder -///---------------------------------------------------------------------------- - -class LLFloaterInventoryFinder : public LLFloater -{ -public: - LLFloaterInventoryFinder( LLPanelMainInventory* inventory_view); - virtual void draw(); - /*virtual*/ BOOL postBuild(); - void changeFilter(LLInventoryFilter* filter); - void updateElementsFromFilter(); - BOOL getCheckShowEmpty(); - BOOL getCheckSinceLogoff(); - - static void onTimeAgo(LLUICtrl*, void *); - static void onCheckSinceLogoff(LLUICtrl*, void *); - static void onCloseBtn(void* user_data); - static void selectAllTypes(void* user_data); - static void selectNoTypes(void* user_data); -private: - LLPanelMainInventory* mPanelInventoryDecorated; - LLSpinCtrl* mSpinSinceDays; - LLSpinCtrl* mSpinSinceHours; - LLInventoryFilter* mFilter; -}; - -///---------------------------------------------------------------------------- -/// LLPanelMainInventory -///---------------------------------------------------------------------------- - -LLPanelMainInventory::LLPanelMainInventory() - : LLPanel() -{ - LLMemType mt(LLMemType::MTYPE_INVENTORY_VIEW_INIT); - // Menu Callbacks (non contex menus) - mCommitCallbackRegistrar.add("Inventory.DoToSelected", boost::bind(&LLPanelMainInventory::doToSelected, this, _2)); - mCommitCallbackRegistrar.add("Inventory.CloseAllFolders", boost::bind(&LLPanelMainInventory::closeAllFolders, this)); - mCommitCallbackRegistrar.add("Inventory.EmptyTrash", boost::bind(&LLInventoryModel::emptyFolderType, &gInventory, "ConfirmEmptyTrash", LLFolderType::FT_TRASH)); - mCommitCallbackRegistrar.add("Inventory.EmptyLostAndFound", boost::bind(&LLInventoryModel::emptyFolderType, &gInventory, "ConfirmEmptyLostAndFound", LLFolderType::FT_LOST_AND_FOUND)); - mCommitCallbackRegistrar.add("Inventory.DoCreate", boost::bind(&LLPanelMainInventory::doCreate, this, _2)); - mCommitCallbackRegistrar.add("Inventory.NewWindow", boost::bind(&LLPanelMainInventory::newWindow, this)); - mCommitCallbackRegistrar.add("Inventory.ShowFilters", boost::bind(&LLPanelMainInventory::toggleFindOptions, this)); - mCommitCallbackRegistrar.add("Inventory.ResetFilters", boost::bind(&LLPanelMainInventory::resetFilters, this)); - mCommitCallbackRegistrar.add("Inventory.SetSortBy", boost::bind(&LLPanelMainInventory::setSortBy, this, _2)); - - // Controls - // *TODO: Just use persistant settings for each of these - U32 sort_order = gSavedSettings.getU32("InventorySortOrder"); - BOOL sort_by_name = ! ( sort_order & LLInventoryFilter::SO_DATE ); - BOOL sort_folders_by_name = ( sort_order & LLInventoryFilter::SO_FOLDERS_BY_NAME ); - BOOL sort_system_folders_to_top = ( sort_order & LLInventoryFilter::SO_SYSTEM_FOLDERS_TO_TOP ); - - gSavedSettings.declareBOOL("Inventory.SortByName", sort_by_name, "Declared in code", FALSE); - gSavedSettings.declareBOOL("Inventory.SortByDate", !sort_by_name, "Declared in code", FALSE); - gSavedSettings.declareBOOL("Inventory.FoldersAlwaysByName", sort_folders_by_name, "Declared in code", FALSE); - gSavedSettings.declareBOOL("Inventory.SystemFoldersToTop", sort_system_folders_to_top, "Declared in code", FALSE); - - mSavedFolderState = new LLSaveFolderState(); - mSavedFolderState->setApply(FALSE); -} - -BOOL LLPanelMainInventory::postBuild() -{ - gInventory.addObserver(this); - - mFilterTabs = getChild<LLTabContainer>("inventory filter tabs"); - mFilterTabs->setCommitCallback(boost::bind(&LLPanelMainInventory::onFilterSelected, this)); - - //panel->getFilter()->markDefault(); - - // Set up the default inv. panel/filter settings. - mActivePanel = getChild<LLInventoryPanel>("All Items"); - if (mActivePanel) - { - // "All Items" is the previous only view, so it gets the InventorySortOrder - mActivePanel->setSortOrder(gSavedSettings.getU32("InventorySortOrder")); - mActivePanel->getFilter()->markDefault(); - mActivePanel->getRootFolder()->applyFunctorRecursively(*mSavedFolderState); - mActivePanel->setSelectCallback(boost::bind(&LLInventoryPanel::onSelectionChange, mActivePanel, _1, _2)); - } - LLInventoryPanel* recent_items_panel = getChild<LLInventoryPanel>("Recent Items"); - if (recent_items_panel) - { - recent_items_panel->setSinceLogoff(TRUE); - recent_items_panel->setSortOrder(LLInventoryFilter::SO_DATE); - recent_items_panel->setShowFolderState(LLInventoryFilter::SHOW_NON_EMPTY_FOLDERS); - recent_items_panel->getFilter()->markDefault(); - recent_items_panel->setSelectCallback(boost::bind(&LLInventoryPanel::onSelectionChange, recent_items_panel, _1, _2)); - } - - // Now load the stored settings from disk, if available. - std::ostringstream filterSaveName; - filterSaveName << gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, "filters.xml"); - llinfos << "LLPanelMainInventory::init: reading from " << filterSaveName << llendl; - llifstream file(filterSaveName.str()); - LLSD savedFilterState; - if (file.is_open()) - { - LLSDSerialize::fromXML(savedFilterState, file); - file.close(); - - // Load the persistent "Recent Items" settings. - // Note that the "All Items" settings do not persist. - if(recent_items_panel) - { - if(savedFilterState.has(recent_items_panel->getFilter()->getName())) - { - LLSD recent_items = savedFilterState.get( - recent_items_panel->getFilter()->getName()); - recent_items_panel->getFilter()->fromLLSD(recent_items); - } - } - - } - - - mFilterEditor = getChild<LLFilterEditor>("inventory search editor"); - if (mFilterEditor) - { - mFilterEditor->setCommitCallback(boost::bind(&LLPanelMainInventory::onFilterEdit, this, _2)); - } - - // *TODO:Get the cost info from the server - const std::string upload_cost("10"); - childSetLabelArg("Upload Image", "[COST]", upload_cost); - childSetLabelArg("Upload Sound", "[COST]", upload_cost); - childSetLabelArg("Upload Animation", "[COST]", upload_cost); - childSetLabelArg("Bulk Upload", "[COST]", upload_cost); - - return TRUE; -} - -// Destroys the object -LLPanelMainInventory::~LLPanelMainInventory( void ) -{ - // Save the filters state. - LLSD filterRoot; - LLInventoryPanel* all_items_panel = getChild<LLInventoryPanel>("All Items"); - if (all_items_panel) - { - LLInventoryFilter* filter = all_items_panel->getFilter(); - if (filter) - { - LLSD filterState; - filter->toLLSD(filterState); - filterRoot[filter->getName()] = filterState; - } - } - - LLInventoryPanel* recent_items_panel = getChild<LLInventoryPanel>("Recent Items"); - if (recent_items_panel) - { - LLInventoryFilter* filter = recent_items_panel->getFilter(); - if (filter) - { - LLSD filterState; - filter->toLLSD(filterState); - filterRoot[filter->getName()] = filterState; - } - } - - std::ostringstream filterSaveName; - filterSaveName << gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, "filters.xml"); - llofstream filtersFile(filterSaveName.str()); - if(!LLSDSerialize::toPrettyXML(filterRoot, filtersFile)) - { - llwarns << "Could not write to filters save file " << filterSaveName << llendl; - } - else - filtersFile.close(); - - gInventory.removeObserver(this); - delete mSavedFolderState; -} - -void LLPanelMainInventory::startSearch() -{ - // this forces focus to line editor portion of search editor - if (mFilterEditor) - { - mFilterEditor->focusFirstItem(TRUE); - } -} - -BOOL LLPanelMainInventory::handleKeyHere(KEY key, MASK mask) -{ - LLFolderView* root_folder = mActivePanel ? mActivePanel->getRootFolder() : NULL; - if (root_folder) - { - // first check for user accepting current search results - if (mFilterEditor - && mFilterEditor->hasFocus() - && (key == KEY_RETURN - || key == KEY_DOWN) - && mask == MASK_NONE) - { - // move focus to inventory proper - mActivePanel->setFocus(TRUE); - root_folder->scrollToShowSelection(); - return TRUE; - } - - if (mActivePanel->hasFocus() && key == KEY_UP) - { - startSearch(); - } - } - - return LLPanel::handleKeyHere(key, mask); - -} - -//---------------------------------------------------------------------------- -// menu callbacks - -void LLPanelMainInventory::doToSelected(const LLSD& userdata) -{ - getPanel()->getRootFolder()->doToSelected(&gInventory, userdata); -} - -void LLPanelMainInventory::closeAllFolders() -{ - getPanel()->getRootFolder()->closeAllFolders(); -} - -void LLPanelMainInventory::newWindow() -{ - LLFloaterInventory::showAgentInventory(); -} - -void LLPanelMainInventory::doCreate(const LLSD& userdata) -{ - menu_create_inventory_item(getPanel()->getRootFolder(), NULL, userdata); -} - -void LLPanelMainInventory::resetFilters() -{ - LLFloaterInventoryFinder *finder = getFinder(); - getActivePanel()->getFilter()->resetDefault(); - if (finder) - { - finder->updateElementsFromFilter(); - } - - setFilterTextFromFilter(); -} - -void LLPanelMainInventory::setSortBy(const LLSD& userdata) -{ - std::string sort_field = userdata.asString(); - if (sort_field == "name") - { - U32 order = getActivePanel()->getSortOrder(); - getActivePanel()->setSortOrder( order & ~LLInventoryFilter::SO_DATE ); - - gSavedSettings.setBOOL("Inventory.SortByName", TRUE ); - gSavedSettings.setBOOL("Inventory.SortByDate", FALSE ); - } - else if (sort_field == "date") - { - U32 order = getActivePanel()->getSortOrder(); - getActivePanel()->setSortOrder( order | LLInventoryFilter::SO_DATE ); - - gSavedSettings.setBOOL("Inventory.SortByName", FALSE ); - gSavedSettings.setBOOL("Inventory.SortByDate", TRUE ); - } - else if (sort_field == "foldersalwaysbyname") - { - U32 order = getActivePanel()->getSortOrder(); - if ( order & LLInventoryFilter::SO_FOLDERS_BY_NAME ) - { - order &= ~LLInventoryFilter::SO_FOLDERS_BY_NAME; - - gSavedSettings.setBOOL("Inventory.FoldersAlwaysByName", FALSE ); - } - else - { - order |= LLInventoryFilter::SO_FOLDERS_BY_NAME; - - gSavedSettings.setBOOL("Inventory.FoldersAlwaysByName", TRUE ); - } - getActivePanel()->setSortOrder( order ); - } - else if (sort_field == "systemfolderstotop") - { - U32 order = getActivePanel()->getSortOrder(); - if ( order & LLInventoryFilter::SO_SYSTEM_FOLDERS_TO_TOP ) - { - order &= ~LLInventoryFilter::SO_SYSTEM_FOLDERS_TO_TOP; - - gSavedSettings.setBOOL("Inventory.SystemFoldersToTop", FALSE ); - } - else - { - order |= LLInventoryFilter::SO_SYSTEM_FOLDERS_TO_TOP; - - gSavedSettings.setBOOL("Inventory.SystemFoldersToTop", TRUE ); - } - getActivePanel()->setSortOrder( order ); - } -} - -// static -BOOL LLPanelMainInventory::filtersVisible(void* user_data) -{ - LLPanelMainInventory* self = (LLPanelMainInventory*)user_data; - if(!self) return FALSE; - - return self->getFinder() != NULL; -} - -void LLPanelMainInventory::onClearSearch() -{ - LLFloater *finder = getFinder(); - if (mActivePanel) - { - mActivePanel->setFilterSubString(LLStringUtil::null); - mActivePanel->setFilterTypes(0xffffffff); - } - - if (finder) - { - LLFloaterInventoryFinder::selectAllTypes(finder); - } - - // re-open folders that were initially open - if (mActivePanel) - { - mSavedFolderState->setApply(TRUE); - mActivePanel->getRootFolder()->applyFunctorRecursively(*mSavedFolderState); - LLOpenFoldersWithSelection opener; - mActivePanel->getRootFolder()->applyFunctorRecursively(opener); - mActivePanel->getRootFolder()->scrollToShowSelection(); - } -} - -void LLPanelMainInventory::onFilterEdit(const std::string& search_string ) -{ - if (search_string == "") - { - onClearSearch(); - } - if (!mActivePanel) - { - return; - } - - gInventory.startBackgroundFetch(); - - std::string uppercase_search_string = search_string; - LLStringUtil::toUpper(uppercase_search_string); - if (mActivePanel->getFilterSubString().empty() && uppercase_search_string.empty()) - { - // current filter and new filter empty, do nothing - return; - } - - // save current folder open state if no filter currently applied - if (!mActivePanel->getRootFolder()->isFilterModified()) - { - mSavedFolderState->setApply(FALSE); - mActivePanel->getRootFolder()->applyFunctorRecursively(*mSavedFolderState); - } - - // set new filter string - mActivePanel->setFilterSubString(uppercase_search_string); -} - - - //static - BOOL LLPanelMainInventory::incrementalFind(LLFolderViewItem* first_item, const char *find_text, BOOL backward) - { - LLPanelMainInventory* active_view = NULL; - - LLFloaterReg::const_instance_list_t& inst_list = LLFloaterReg::getFloaterList("inventory"); - for (LLFloaterReg::const_instance_list_t::const_iterator iter = inst_list.begin(); iter != inst_list.end(); ++iter) - { - LLPanelMainInventory* iv = dynamic_cast<LLPanelMainInventory*>(*iter); - if (iv) - { - if (gFocusMgr.childHasKeyboardFocus(iv)) - { - active_view = iv; - break; - } - } - } - - if (!active_view) - { - return FALSE; - } - - std::string search_string(find_text); - - if (search_string.empty()) - { - return FALSE; - } - - if (active_view->getPanel() && - active_view->getPanel()->getRootFolder()->search(first_item, search_string, backward)) - { - return TRUE; - } - - return FALSE; - } - -void LLPanelMainInventory::onFilterSelected() -{ - // Find my index - mActivePanel = (LLInventoryPanel*)childGetVisibleTab("inventory filter tabs"); - - if (!mActivePanel) - { - return; - } - LLInventoryFilter* filter = mActivePanel->getFilter(); - LLFloaterInventoryFinder *finder = getFinder(); - if (finder) - { - finder->changeFilter(filter); - } - if (filter->isActive()) - { - // If our filter is active we may be the first thing requiring a fetch so we better start it here. - gInventory.startBackgroundFetch(); - } - setFilterTextFromFilter(); -} - -const std::string LLPanelMainInventory::getFilterSubString() -{ - return mActivePanel->getFilterSubString(); -} - -void LLPanelMainInventory::setFilterSubString(const std::string& string) -{ - mActivePanel->setFilterSubString(string); -} - -BOOL LLPanelMainInventory::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, - EDragAndDropType cargo_type, - void* cargo_data, - EAcceptance* accept, - std::string& tooltip_msg) -{ - // Check to see if we are auto scrolling from the last frame - LLInventoryPanel* panel = (LLInventoryPanel*)this->getActivePanel(); - BOOL needsToScroll = panel->getScrollableContainer()->autoScroll(x, y); - if(mFilterTabs) - { - if(needsToScroll) - { - mFilterTabs->startDragAndDropDelayTimer(); - } - } - - BOOL handled = LLPanel::handleDragAndDrop(x, y, mask, drop, cargo_type, cargo_data, accept, tooltip_msg); - - return handled; -} - -void LLPanelMainInventory::changed(U32 mask) -{ -} - - -void LLPanelMainInventory::setFilterTextFromFilter() -{ - mFilterText = mActivePanel->getFilter()->getFilterText(); -} - -void LLPanelMainInventory::toggleFindOptions() -{ - LLMemType mt(LLMemType::MTYPE_INVENTORY_VIEW_TOGGLE); - LLFloater *floater = getFinder(); - if (!floater) - { - LLFloaterInventoryFinder * finder = new LLFloaterInventoryFinder(this); - mFinderHandle = finder->getHandle(); - finder->openFloater(); - - LLFloater* parent_floater = gFloaterView->getParentFloater(this); - if (parent_floater) // Seraph: Fix this, shouldn't be null even for sidepanel - parent_floater->addDependentFloater(mFinderHandle); - // start background fetch of folders - gInventory.startBackgroundFetch(); - } - else - { - floater->closeFloater(); - } -} - -void LLPanelMainInventory::setSelectCallback(const LLFolderView::signal_t::slot_type& cb) -{ - getChild<LLInventoryPanel>("All Items")->setSelectCallback(cb); - getChild<LLInventoryPanel>("Recent Items")->setSelectCallback(cb); -} - -///---------------------------------------------------------------------------- -/// LLFloaterInventoryFinder -///---------------------------------------------------------------------------- - -LLFloaterInventoryFinder* LLPanelMainInventory::getFinder() -{ - return (LLFloaterInventoryFinder*)mFinderHandle.get(); -} - - -LLFloaterInventoryFinder::LLFloaterInventoryFinder(LLPanelMainInventory* inventory_view) : - LLFloater(LLSD()), - mPanelInventoryDecorated(inventory_view), - mFilter(inventory_view->getPanel()->getFilter()) -{ - LLUICtrlFactory::getInstance()->buildFloater(this, "floater_inventory_view_finder.xml", NULL); - updateElementsFromFilter(); -} - - -void LLFloaterInventoryFinder::onCheckSinceLogoff(LLUICtrl *ctrl, void *user_data) -{ - LLFloaterInventoryFinder *self = (LLFloaterInventoryFinder *)user_data; - if (!self) return; - - bool since_logoff= self->childGetValue("check_since_logoff"); - - if (!since_logoff && - !( self->mSpinSinceDays->get() || self->mSpinSinceHours->get() ) ) - { - self->mSpinSinceHours->set(1.0f); - } -} -BOOL LLFloaterInventoryFinder::postBuild() -{ - const LLRect& viewrect = mPanelInventoryDecorated->getRect(); - setRect(LLRect(viewrect.mLeft - getRect().getWidth(), viewrect.mTop, viewrect.mLeft, viewrect.mTop - getRect().getHeight())); - - childSetAction("All", selectAllTypes, this); - childSetAction("None", selectNoTypes, this); - - mSpinSinceHours = getChild<LLSpinCtrl>("spin_hours_ago"); - childSetCommitCallback("spin_hours_ago", onTimeAgo, this); - - mSpinSinceDays = getChild<LLSpinCtrl>("spin_days_ago"); - childSetCommitCallback("spin_days_ago", onTimeAgo, this); - - // mCheckSinceLogoff = getChild<LLSpinCtrl>("check_since_logoff"); - childSetCommitCallback("check_since_logoff", onCheckSinceLogoff, this); - - childSetAction("Close", onCloseBtn, this); - - updateElementsFromFilter(); - return TRUE; -} -void LLFloaterInventoryFinder::onTimeAgo(LLUICtrl *ctrl, void *user_data) -{ - LLFloaterInventoryFinder *self = (LLFloaterInventoryFinder *)user_data; - if (!self) return; - - bool since_logoff=true; - if ( self->mSpinSinceDays->get() || self->mSpinSinceHours->get() ) - { - since_logoff = false; - } - self->childSetValue("check_since_logoff", since_logoff); -} - -void LLFloaterInventoryFinder::changeFilter(LLInventoryFilter* filter) -{ - mFilter = filter; - updateElementsFromFilter(); -} - -void LLFloaterInventoryFinder::updateElementsFromFilter() -{ - if (!mFilter) - return; - - // Get data needed for filter display - U32 filter_types = mFilter->getFilterTypes(); - std::string filter_string = mFilter->getFilterSubString(); - LLInventoryFilter::EFolderShow show_folders = mFilter->getShowFolderState(); - U32 hours = mFilter->getHoursAgo(); - - // update the ui elements - setTitle(mFilter->getName()); - - childSetValue("check_animation", (S32) (filter_types & 0x1 << LLInventoryType::IT_ANIMATION)); - - childSetValue("check_calling_card", (S32) (filter_types & 0x1 << LLInventoryType::IT_CALLINGCARD)); - childSetValue("check_clothing", (S32) (filter_types & 0x1 << LLInventoryType::IT_WEARABLE)); - childSetValue("check_gesture", (S32) (filter_types & 0x1 << LLInventoryType::IT_GESTURE)); - childSetValue("check_landmark", (S32) (filter_types & 0x1 << LLInventoryType::IT_LANDMARK)); - childSetValue("check_notecard", (S32) (filter_types & 0x1 << LLInventoryType::IT_NOTECARD)); - childSetValue("check_object", (S32) (filter_types & 0x1 << LLInventoryType::IT_OBJECT)); - childSetValue("check_script", (S32) (filter_types & 0x1 << LLInventoryType::IT_LSL)); - childSetValue("check_sound", (S32) (filter_types & 0x1 << LLInventoryType::IT_SOUND)); - childSetValue("check_texture", (S32) (filter_types & 0x1 << LLInventoryType::IT_TEXTURE)); - childSetValue("check_snapshot", (S32) (filter_types & 0x1 << LLInventoryType::IT_SNAPSHOT)); - childSetValue("check_show_empty", show_folders == LLInventoryFilter::SHOW_ALL_FOLDERS); - childSetValue("check_since_logoff", mFilter->isSinceLogoff()); - mSpinSinceHours->set((F32)(hours % 24)); - mSpinSinceDays->set((F32)(hours / 24)); -} - -void LLFloaterInventoryFinder::draw() -{ - LLMemType mt(LLMemType::MTYPE_INVENTORY_DRAW); - U32 filter = 0xffffffff; - BOOL filtered_by_all_types = TRUE; - - if (!childGetValue("check_animation")) - { - filter &= ~(0x1 << LLInventoryType::IT_ANIMATION); - filtered_by_all_types = FALSE; - } - - - if (!childGetValue("check_calling_card")) - { - filter &= ~(0x1 << LLInventoryType::IT_CALLINGCARD); - filtered_by_all_types = FALSE; - } - - if (!childGetValue("check_clothing")) - { - filter &= ~(0x1 << LLInventoryType::IT_WEARABLE); - filtered_by_all_types = FALSE; - } - - if (!childGetValue("check_gesture")) - { - filter &= ~(0x1 << LLInventoryType::IT_GESTURE); - filtered_by_all_types = FALSE; - } - - if (!childGetValue("check_landmark")) - - - { - filter &= ~(0x1 << LLInventoryType::IT_LANDMARK); - filtered_by_all_types = FALSE; - } - - if (!childGetValue("check_notecard")) - { - filter &= ~(0x1 << LLInventoryType::IT_NOTECARD); - filtered_by_all_types = FALSE; - } - - if (!childGetValue("check_object")) - { - filter &= ~(0x1 << LLInventoryType::IT_OBJECT); - filter &= ~(0x1 << LLInventoryType::IT_ATTACHMENT); - filtered_by_all_types = FALSE; - } - - if (!childGetValue("check_script")) - { - filter &= ~(0x1 << LLInventoryType::IT_LSL); - filtered_by_all_types = FALSE; - } - - if (!childGetValue("check_sound")) - { - filter &= ~(0x1 << LLInventoryType::IT_SOUND); - filtered_by_all_types = FALSE; - } - - if (!childGetValue("check_texture")) - { - filter &= ~(0x1 << LLInventoryType::IT_TEXTURE); - filtered_by_all_types = FALSE; - } - - if (!childGetValue("check_snapshot")) - { - filter &= ~(0x1 << LLInventoryType::IT_SNAPSHOT); - filtered_by_all_types = FALSE; - } - - if (!filtered_by_all_types) - { - // don't include folders in filter, unless I've selected everything - filter &= ~(0x1 << LLInventoryType::IT_CATEGORY); - } - - // update the panel, panel will update the filter - mPanelInventoryDecorated->getPanel()->setShowFolderState(getCheckShowEmpty() ? - LLInventoryFilter::SHOW_ALL_FOLDERS : LLInventoryFilter::SHOW_NON_EMPTY_FOLDERS); - mPanelInventoryDecorated->getPanel()->setFilterTypes(filter); - if (getCheckSinceLogoff()) - { - mSpinSinceDays->set(0); - mSpinSinceHours->set(0); - } - U32 days = (U32)mSpinSinceDays->get(); - U32 hours = (U32)mSpinSinceHours->get(); - if (hours > 24) - { - days += hours / 24; - hours = (U32)hours % 24; - mSpinSinceDays->set((F32)days); - mSpinSinceHours->set((F32)hours); - } - hours += days * 24; - mPanelInventoryDecorated->getPanel()->setHoursAgo(hours); - mPanelInventoryDecorated->getPanel()->setSinceLogoff(getCheckSinceLogoff()); - mPanelInventoryDecorated->setFilterTextFromFilter(); - - LLPanel::draw(); -} - -BOOL LLFloaterInventoryFinder::getCheckShowEmpty() -{ - return childGetValue("check_show_empty"); -} - -BOOL LLFloaterInventoryFinder::getCheckSinceLogoff() -{ - return childGetValue("check_since_logoff"); -} - -void LLFloaterInventoryFinder::onCloseBtn(void* user_data) -{ - LLFloaterInventoryFinder* finderp = (LLFloaterInventoryFinder*)user_data; - finderp->closeFloater(); -} - -// static -void LLFloaterInventoryFinder::selectAllTypes(void* user_data) -{ - LLFloaterInventoryFinder* self = (LLFloaterInventoryFinder*)user_data; - if(!self) return; - - self->childSetValue("check_animation", TRUE); - self->childSetValue("check_calling_card", TRUE); - self->childSetValue("check_clothing", TRUE); - self->childSetValue("check_gesture", TRUE); - self->childSetValue("check_landmark", TRUE); - self->childSetValue("check_notecard", TRUE); - self->childSetValue("check_object", TRUE); - self->childSetValue("check_script", TRUE); - self->childSetValue("check_sound", TRUE); - self->childSetValue("check_texture", TRUE); - self->childSetValue("check_snapshot", TRUE); -} - -//static -void LLFloaterInventoryFinder::selectNoTypes(void* user_data) -{ - LLFloaterInventoryFinder* self = (LLFloaterInventoryFinder*)user_data; - if(!self) return; - - self->childSetValue("check_animation", FALSE); - self->childSetValue("check_calling_card", FALSE); - self->childSetValue("check_clothing", FALSE); - self->childSetValue("check_gesture", FALSE); - self->childSetValue("check_landmark", FALSE); - self->childSetValue("check_notecard", FALSE); - self->childSetValue("check_object", FALSE); - self->childSetValue("check_script", FALSE); - self->childSetValue("check_sound", FALSE); - self->childSetValue("check_texture", FALSE); - self->childSetValue("check_snapshot", FALSE); -} +/**
+ * @file llsidepanelmaininventory.cpp
+ * @brief Implementation of llsidepanelmaininventory.
+ *
+ * $LicenseInfo:firstyear=2001&license=viewergpl$
+ *
+ * Copyright (c) 2001-2009, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#include "llviewerprecompiledheaders.h"
+#include "llpanelmaininventory.h"
+
+#include "lldndbutton.h"
+#include "llfilepicker.h"
+#include "llfloaterinventory.h"
+#include "llinventorybridge.h"
+#include "llinventoryfunctions.h"
+#include "llinventorypanel.h"
+#include "llfiltereditor.h"
+#include "llfloaterreg.h"
+#include "llpreviewtexture.h"
+#include "llscrollcontainer.h"
+#include "llsdserialize.h"
+#include "llspinctrl.h"
+#include "lltooldraganddrop.h"
+#include "llviewermenu.h"
+#include "llviewertexturelist.h"
+
+static LLRegisterPanelClassWrapper<LLPanelMainInventory> t_inventory("panel_main_inventory"); // Seraph is this redundant with constructor?
+
+void on_file_loaded_for_save(BOOL success,
+ LLViewerFetchedTexture *src_vi,
+ LLImageRaw* src,
+ LLImageRaw* aux_src,
+ S32 discard_level,
+ BOOL final,
+ void* userdata);
+
+///----------------------------------------------------------------------------
+/// LLFloaterInventoryFinder
+///----------------------------------------------------------------------------
+
+class LLFloaterInventoryFinder : public LLFloater
+{
+public:
+ LLFloaterInventoryFinder( LLPanelMainInventory* inventory_view);
+ virtual void draw();
+ /*virtual*/ BOOL postBuild();
+ void changeFilter(LLInventoryFilter* filter);
+ void updateElementsFromFilter();
+ BOOL getCheckShowEmpty();
+ BOOL getCheckSinceLogoff();
+
+ static void onTimeAgo(LLUICtrl*, void *);
+ static void onCheckSinceLogoff(LLUICtrl*, void *);
+ static void onCloseBtn(void* user_data);
+ static void selectAllTypes(void* user_data);
+ static void selectNoTypes(void* user_data);
+private:
+ LLPanelMainInventory* mPanelMainInventory;
+ LLSpinCtrl* mSpinSinceDays;
+ LLSpinCtrl* mSpinSinceHours;
+ LLInventoryFilter* mFilter;
+};
+
+///----------------------------------------------------------------------------
+/// LLPanelMainInventory
+///----------------------------------------------------------------------------
+
+LLPanelMainInventory::LLPanelMainInventory()
+ : LLPanel(),
+ mActivePanel(NULL),
+ mSavedFolderState(NULL),
+ mFilterText(""),
+ mMenuGearDefault(NULL),
+ mMenuAdd(NULL)
+{
+ LLMemType mt(LLMemType::MTYPE_INVENTORY_VIEW_INIT);
+ // Menu Callbacks (non contex menus)
+ mCommitCallbackRegistrar.add("Inventory.DoToSelected", boost::bind(&LLPanelMainInventory::doToSelected, this, _2));
+ mCommitCallbackRegistrar.add("Inventory.CloseAllFolders", boost::bind(&LLPanelMainInventory::closeAllFolders, this));
+ mCommitCallbackRegistrar.add("Inventory.EmptyTrash", boost::bind(&LLInventoryModel::emptyFolderType, &gInventory, "ConfirmEmptyTrash", LLFolderType::FT_TRASH));
+ mCommitCallbackRegistrar.add("Inventory.EmptyLostAndFound", boost::bind(&LLInventoryModel::emptyFolderType, &gInventory, "ConfirmEmptyLostAndFound", LLFolderType::FT_LOST_AND_FOUND));
+ mCommitCallbackRegistrar.add("Inventory.DoCreate", boost::bind(&LLPanelMainInventory::doCreate, this, _2));
+ mCommitCallbackRegistrar.add("Inventory.NewWindow", boost::bind(&LLPanelMainInventory::newWindow, this));
+ mCommitCallbackRegistrar.add("Inventory.ShowFilters", boost::bind(&LLPanelMainInventory::toggleFindOptions, this));
+ mCommitCallbackRegistrar.add("Inventory.ResetFilters", boost::bind(&LLPanelMainInventory::resetFilters, this));
+ mCommitCallbackRegistrar.add("Inventory.SetSortBy", boost::bind(&LLPanelMainInventory::setSortBy, this, _2));
+
+ // Controls
+ // *TODO: Just use persistant settings for each of these
+ U32 sort_order = gSavedSettings.getU32("InventorySortOrder");
+ BOOL sort_by_name = ! ( sort_order & LLInventoryFilter::SO_DATE );
+ BOOL sort_folders_by_name = ( sort_order & LLInventoryFilter::SO_FOLDERS_BY_NAME );
+ BOOL sort_system_folders_to_top = ( sort_order & LLInventoryFilter::SO_SYSTEM_FOLDERS_TO_TOP );
+
+ gSavedSettings.declareBOOL("Inventory.SortByName", sort_by_name, "Declared in code", FALSE);
+ gSavedSettings.declareBOOL("Inventory.SortByDate", !sort_by_name, "Declared in code", FALSE);
+ gSavedSettings.declareBOOL("Inventory.FoldersAlwaysByName", sort_folders_by_name, "Declared in code", FALSE);
+ gSavedSettings.declareBOOL("Inventory.SystemFoldersToTop", sort_system_folders_to_top, "Declared in code", FALSE);
+
+ mSavedFolderState = new LLSaveFolderState();
+ mSavedFolderState->setApply(FALSE);
+}
+
+BOOL LLPanelMainInventory::postBuild()
+{
+ gInventory.addObserver(this);
+
+ mFilterTabs = getChild<LLTabContainer>("inventory filter tabs");
+ mFilterTabs->setCommitCallback(boost::bind(&LLPanelMainInventory::onFilterSelected, this));
+
+ //panel->getFilter()->markDefault();
+
+ // Set up the default inv. panel/filter settings.
+ mActivePanel = getChild<LLInventoryPanel>("All Items");
+ if (mActivePanel)
+ {
+ // "All Items" is the previous only view, so it gets the InventorySortOrder
+ mActivePanel->setSortOrder(gSavedSettings.getU32("InventorySortOrder"));
+ mActivePanel->getFilter()->markDefault();
+ mActivePanel->getRootFolder()->applyFunctorRecursively(*mSavedFolderState);
+ mActivePanel->setSelectCallback(boost::bind(&LLPanelMainInventory::onSelectionChange, this, mActivePanel, _1, _2));
+ }
+ LLInventoryPanel* recent_items_panel = getChild<LLInventoryPanel>("Recent Items");
+ if (recent_items_panel)
+ {
+ recent_items_panel->setSinceLogoff(TRUE);
+ recent_items_panel->setSortOrder(LLInventoryFilter::SO_DATE);
+ recent_items_panel->setShowFolderState(LLInventoryFilter::SHOW_NON_EMPTY_FOLDERS);
+ recent_items_panel->getFilter()->markDefault();
+ recent_items_panel->setSelectCallback(boost::bind(&LLPanelMainInventory::onSelectionChange, this, recent_items_panel, _1, _2));
+ }
+
+ // Now load the stored settings from disk, if available.
+ std::ostringstream filterSaveName;
+ filterSaveName << gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, "filters.xml");
+ llinfos << "LLPanelMainInventory::init: reading from " << filterSaveName << llendl;
+ llifstream file(filterSaveName.str());
+ LLSD savedFilterState;
+ if (file.is_open())
+ {
+ LLSDSerialize::fromXML(savedFilterState, file);
+ file.close();
+
+ // Load the persistent "Recent Items" settings.
+ // Note that the "All Items" settings do not persist.
+ if(recent_items_panel)
+ {
+ if(savedFilterState.has(recent_items_panel->getFilter()->getName()))
+ {
+ LLSD recent_items = savedFilterState.get(
+ recent_items_panel->getFilter()->getName());
+ recent_items_panel->getFilter()->fromLLSD(recent_items);
+ }
+ }
+
+ }
+
+ mFilterEditor = getChild<LLFilterEditor>("inventory search editor");
+ if (mFilterEditor)
+ {
+ mFilterEditor->setCommitCallback(boost::bind(&LLPanelMainInventory::onFilterEdit, this, _2));
+ }
+
+ // *TODO:Get the cost info from the server
+ const std::string upload_cost("10");
+ childSetLabelArg("Upload Image", "[COST]", upload_cost);
+ childSetLabelArg("Upload Sound", "[COST]", upload_cost);
+ childSetLabelArg("Upload Animation", "[COST]", upload_cost);
+ childSetLabelArg("Bulk Upload", "[COST]", upload_cost);
+
+ initListCommandsHandlers();
+ return TRUE;
+}
+
+void LLPanelMainInventory::initListCommandsHandlers()
+{
+ mListCommands = getChild<LLPanel>("bottom_panel");
+
+ mListCommands->childSetAction("options_gear_btn", boost::bind(&LLPanelMainInventory::onGearButtonClick, this));
+ mListCommands->childSetAction("trash_btn", boost::bind(&LLPanelMainInventory::onTrashButtonClick, this));
+ mListCommands->childSetAction("add_btn", boost::bind(&LLPanelMainInventory::onAddButtonClick, this));
+ /*
+ mListCommands->getChild<LLButton>("add_btn")->setHeldDownCallback(boost::bind(&LLPanelMainInventory::onAddButtonHeldDown, this));
+ static const LLSD add_landmark_command("add_landmark");
+ mListCommands->childSetAction("add_btn", boost::bind(&LLPanelMainInventory::onAddAction, this, add_landmark_command));
+ */
+
+ LLDragAndDropButton* trash_btn = mListCommands->getChild<LLDragAndDropButton>("trash_btn");
+ trash_btn->setDragAndDropHandler(boost::bind(&LLPanelMainInventory::handleDragAndDropToTrash, this
+ , _4 // BOOL drop
+ , _5 // EDragAndDropType cargo_type
+ , _7 // EAcceptance* accept
+ ));
+
+ mCommitCallbackRegistrar.add("Inventory.GearDefault.Custom.Action", boost::bind(&LLPanelMainInventory::onCustomAction, this, _2));
+ mEnableCallbackRegistrar.add("Inventory.GearDefault.Enable", boost::bind(&LLPanelMainInventory::isActionEnabled, this, _2));
+ mMenuGearDefault = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>("menu_inventory_gear_default.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance());
+ mMenuAdd = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>("menu_inventory_add.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance());
+
+}
+
+// Destroys the object
+LLPanelMainInventory::~LLPanelMainInventory( void )
+{
+ // Save the filters state.
+ LLSD filterRoot;
+ LLInventoryPanel* all_items_panel = getChild<LLInventoryPanel>("All Items");
+ if (all_items_panel)
+ {
+ LLInventoryFilter* filter = all_items_panel->getFilter();
+ if (filter)
+ {
+ LLSD filterState;
+ filter->toLLSD(filterState);
+ filterRoot[filter->getName()] = filterState;
+ }
+ }
+
+ LLInventoryPanel* recent_items_panel = getChild<LLInventoryPanel>("Recent Items");
+ if (recent_items_panel)
+ {
+ LLInventoryFilter* filter = recent_items_panel->getFilter();
+ if (filter)
+ {
+ LLSD filterState;
+ filter->toLLSD(filterState);
+ filterRoot[filter->getName()] = filterState;
+ }
+ }
+
+ std::ostringstream filterSaveName;
+ filterSaveName << gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, "filters.xml");
+ llofstream filtersFile(filterSaveName.str());
+ if(!LLSDSerialize::toPrettyXML(filterRoot, filtersFile))
+ {
+ llwarns << "Could not write to filters save file " << filterSaveName << llendl;
+ }
+ else
+ filtersFile.close();
+
+ gInventory.removeObserver(this);
+ delete mSavedFolderState;
+}
+
+void LLPanelMainInventory::startSearch()
+{
+ // this forces focus to line editor portion of search editor
+ if (mFilterEditor)
+ {
+ mFilterEditor->focusFirstItem(TRUE);
+ }
+}
+
+BOOL LLPanelMainInventory::handleKeyHere(KEY key, MASK mask)
+{
+ LLFolderView* root_folder = mActivePanel ? mActivePanel->getRootFolder() : NULL;
+ if (root_folder)
+ {
+ // first check for user accepting current search results
+ if (mFilterEditor
+ && mFilterEditor->hasFocus()
+ && (key == KEY_RETURN
+ || key == KEY_DOWN)
+ && mask == MASK_NONE)
+ {
+ // move focus to inventory proper
+ mActivePanel->setFocus(TRUE);
+ root_folder->scrollToShowSelection();
+ return TRUE;
+ }
+
+ if (mActivePanel->hasFocus() && key == KEY_UP)
+ {
+ startSearch();
+ }
+ }
+
+ return LLPanel::handleKeyHere(key, mask);
+
+}
+
+//----------------------------------------------------------------------------
+// menu callbacks
+
+void LLPanelMainInventory::doToSelected(const LLSD& userdata)
+{
+ getPanel()->getRootFolder()->doToSelected(&gInventory, userdata);
+}
+
+void LLPanelMainInventory::closeAllFolders()
+{
+ getPanel()->getRootFolder()->closeAllFolders();
+}
+
+void LLPanelMainInventory::newWindow()
+{
+ LLFloaterInventory::showAgentInventory();
+}
+
+void LLPanelMainInventory::doCreate(const LLSD& userdata)
+{
+ menu_create_inventory_item(getPanel()->getRootFolder(), NULL, userdata);
+}
+
+void LLPanelMainInventory::resetFilters()
+{
+ LLFloaterInventoryFinder *finder = getFinder();
+ getActivePanel()->getFilter()->resetDefault();
+ if (finder)
+ {
+ finder->updateElementsFromFilter();
+ }
+
+ setFilterTextFromFilter();
+}
+
+void LLPanelMainInventory::setSortBy(const LLSD& userdata)
+{
+ std::string sort_field = userdata.asString();
+ if (sort_field == "name")
+ {
+ U32 order = getActivePanel()->getSortOrder();
+ getActivePanel()->setSortOrder( order & ~LLInventoryFilter::SO_DATE );
+
+ gSavedSettings.setBOOL("Inventory.SortByName", TRUE );
+ gSavedSettings.setBOOL("Inventory.SortByDate", FALSE );
+ }
+ else if (sort_field == "date")
+ {
+ U32 order = getActivePanel()->getSortOrder();
+ getActivePanel()->setSortOrder( order | LLInventoryFilter::SO_DATE );
+
+ gSavedSettings.setBOOL("Inventory.SortByName", FALSE );
+ gSavedSettings.setBOOL("Inventory.SortByDate", TRUE );
+ }
+ else if (sort_field == "foldersalwaysbyname")
+ {
+ U32 order = getActivePanel()->getSortOrder();
+ if ( order & LLInventoryFilter::SO_FOLDERS_BY_NAME )
+ {
+ order &= ~LLInventoryFilter::SO_FOLDERS_BY_NAME;
+
+ gSavedSettings.setBOOL("Inventory.FoldersAlwaysByName", FALSE );
+ }
+ else
+ {
+ order |= LLInventoryFilter::SO_FOLDERS_BY_NAME;
+
+ gSavedSettings.setBOOL("Inventory.FoldersAlwaysByName", TRUE );
+ }
+ getActivePanel()->setSortOrder( order );
+ }
+ else if (sort_field == "systemfolderstotop")
+ {
+ U32 order = getActivePanel()->getSortOrder();
+ if ( order & LLInventoryFilter::SO_SYSTEM_FOLDERS_TO_TOP )
+ {
+ order &= ~LLInventoryFilter::SO_SYSTEM_FOLDERS_TO_TOP;
+
+ gSavedSettings.setBOOL("Inventory.SystemFoldersToTop", FALSE );
+ }
+ else
+ {
+ order |= LLInventoryFilter::SO_SYSTEM_FOLDERS_TO_TOP;
+
+ gSavedSettings.setBOOL("Inventory.SystemFoldersToTop", TRUE );
+ }
+ getActivePanel()->setSortOrder( order );
+ }
+}
+
+// static
+BOOL LLPanelMainInventory::filtersVisible(void* user_data)
+{
+ LLPanelMainInventory* self = (LLPanelMainInventory*)user_data;
+ if(!self) return FALSE;
+
+ return self->getFinder() != NULL;
+}
+
+void LLPanelMainInventory::onClearSearch()
+{
+ LLFloater *finder = getFinder();
+ if (mActivePanel)
+ {
+ mActivePanel->setFilterSubString(LLStringUtil::null);
+ mActivePanel->setFilterTypes(0xffffffff);
+ }
+
+ if (finder)
+ {
+ LLFloaterInventoryFinder::selectAllTypes(finder);
+ }
+
+ // re-open folders that were initially open
+ if (mActivePanel)
+ {
+ mSavedFolderState->setApply(TRUE);
+ mActivePanel->getRootFolder()->applyFunctorRecursively(*mSavedFolderState);
+ LLOpenFoldersWithSelection opener;
+ mActivePanel->getRootFolder()->applyFunctorRecursively(opener);
+ mActivePanel->getRootFolder()->scrollToShowSelection();
+ }
+}
+
+void LLPanelMainInventory::onFilterEdit(const std::string& search_string )
+{
+ if (search_string == "")
+ {
+ onClearSearch();
+ }
+ if (!mActivePanel)
+ {
+ return;
+ }
+
+ gInventory.startBackgroundFetch();
+
+ std::string uppercase_search_string = search_string;
+ LLStringUtil::toUpper(uppercase_search_string);
+ if (mActivePanel->getFilterSubString().empty() && uppercase_search_string.empty())
+ {
+ // current filter and new filter empty, do nothing
+ return;
+ }
+
+ // save current folder open state if no filter currently applied
+ if (!mActivePanel->getRootFolder()->isFilterModified())
+ {
+ mSavedFolderState->setApply(FALSE);
+ mActivePanel->getRootFolder()->applyFunctorRecursively(*mSavedFolderState);
+ }
+
+ // set new filter string
+ mActivePanel->setFilterSubString(uppercase_search_string);
+}
+
+
+ //static
+ BOOL LLPanelMainInventory::incrementalFind(LLFolderViewItem* first_item, const char *find_text, BOOL backward)
+ {
+ LLPanelMainInventory* active_view = NULL;
+
+ LLFloaterReg::const_instance_list_t& inst_list = LLFloaterReg::getFloaterList("inventory");
+ for (LLFloaterReg::const_instance_list_t::const_iterator iter = inst_list.begin(); iter != inst_list.end(); ++iter)
+ {
+ LLPanelMainInventory* iv = dynamic_cast<LLPanelMainInventory*>(*iter);
+ if (iv)
+ {
+ if (gFocusMgr.childHasKeyboardFocus(iv))
+ {
+ active_view = iv;
+ break;
+ }
+ }
+ }
+
+ if (!active_view)
+ {
+ return FALSE;
+ }
+
+ std::string search_string(find_text);
+
+ if (search_string.empty())
+ {
+ return FALSE;
+ }
+
+ if (active_view->getPanel() &&
+ active_view->getPanel()->getRootFolder()->search(first_item, search_string, backward))
+ {
+ return TRUE;
+ }
+
+ return FALSE;
+ }
+
+void LLPanelMainInventory::onFilterSelected()
+{
+ // Find my index
+ mActivePanel = (LLInventoryPanel*)childGetVisibleTab("inventory filter tabs");
+
+ if (!mActivePanel)
+ {
+ return;
+ }
+ LLInventoryFilter* filter = mActivePanel->getFilter();
+ LLFloaterInventoryFinder *finder = getFinder();
+ if (finder)
+ {
+ finder->changeFilter(filter);
+ }
+ if (filter->isActive())
+ {
+ // If our filter is active we may be the first thing requiring a fetch so we better start it here.
+ gInventory.startBackgroundFetch();
+ }
+ setFilterTextFromFilter();
+}
+
+const std::string LLPanelMainInventory::getFilterSubString()
+{
+ return mActivePanel->getFilterSubString();
+}
+
+void LLPanelMainInventory::setFilterSubString(const std::string& string)
+{
+ mActivePanel->setFilterSubString(string);
+}
+
+BOOL LLPanelMainInventory::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop,
+ EDragAndDropType cargo_type,
+ void* cargo_data,
+ EAcceptance* accept,
+ std::string& tooltip_msg)
+{
+ // Check to see if we are auto scrolling from the last frame
+ LLInventoryPanel* panel = (LLInventoryPanel*)this->getActivePanel();
+ BOOL needsToScroll = panel->getScrollableContainer()->autoScroll(x, y);
+ if(mFilterTabs)
+ {
+ if(needsToScroll)
+ {
+ mFilterTabs->startDragAndDropDelayTimer();
+ }
+ }
+
+ BOOL handled = LLPanel::handleDragAndDrop(x, y, mask, drop, cargo_type, cargo_data, accept, tooltip_msg);
+
+ return handled;
+}
+
+void LLPanelMainInventory::changed(U32 mask)
+{
+}
+
+
+void LLPanelMainInventory::setFilterTextFromFilter()
+{
+ mFilterText = mActivePanel->getFilter()->getFilterText();
+}
+
+void LLPanelMainInventory::toggleFindOptions()
+{
+ LLMemType mt(LLMemType::MTYPE_INVENTORY_VIEW_TOGGLE);
+ LLFloater *floater = getFinder();
+ if (!floater)
+ {
+ LLFloaterInventoryFinder * finder = new LLFloaterInventoryFinder(this);
+ mFinderHandle = finder->getHandle();
+ finder->openFloater();
+
+ LLFloater* parent_floater = gFloaterView->getParentFloater(this);
+ if (parent_floater) // Seraph: Fix this, shouldn't be null even for sidepanel
+ parent_floater->addDependentFloater(mFinderHandle);
+ // start background fetch of folders
+ gInventory.startBackgroundFetch();
+ }
+ else
+ {
+ floater->closeFloater();
+ }
+}
+
+void LLPanelMainInventory::setSelectCallback(const LLFolderView::signal_t::slot_type& cb)
+{
+ getChild<LLInventoryPanel>("All Items")->setSelectCallback(cb);
+ getChild<LLInventoryPanel>("Recent Items")->setSelectCallback(cb);
+}
+
+void LLPanelMainInventory::onSelectionChange(LLInventoryPanel *panel, const std::deque<LLFolderViewItem*>& items, BOOL user_action)
+{
+ updateListCommands();
+ panel->onSelectionChange(items, user_action);
+}
+
+///----------------------------------------------------------------------------
+/// LLFloaterInventoryFinder
+///----------------------------------------------------------------------------
+
+LLFloaterInventoryFinder* LLPanelMainInventory::getFinder()
+{
+ return (LLFloaterInventoryFinder*)mFinderHandle.get();
+}
+
+
+LLFloaterInventoryFinder::LLFloaterInventoryFinder(LLPanelMainInventory* inventory_view) :
+ LLFloater(LLSD()),
+ mPanelMainInventory(inventory_view),
+ mFilter(inventory_view->getPanel()->getFilter())
+{
+ LLUICtrlFactory::getInstance()->buildFloater(this, "floater_inventory_view_finder.xml", NULL);
+ updateElementsFromFilter();
+}
+
+
+void LLFloaterInventoryFinder::onCheckSinceLogoff(LLUICtrl *ctrl, void *user_data)
+{
+ LLFloaterInventoryFinder *self = (LLFloaterInventoryFinder *)user_data;
+ if (!self) return;
+
+ bool since_logoff= self->childGetValue("check_since_logoff");
+
+ if (!since_logoff &&
+ !( self->mSpinSinceDays->get() || self->mSpinSinceHours->get() ) )
+ {
+ self->mSpinSinceHours->set(1.0f);
+ }
+}
+BOOL LLFloaterInventoryFinder::postBuild()
+{
+ const LLRect& viewrect = mPanelMainInventory->getRect();
+ setRect(LLRect(viewrect.mLeft - getRect().getWidth(), viewrect.mTop, viewrect.mLeft, viewrect.mTop - getRect().getHeight()));
+
+ childSetAction("All", selectAllTypes, this);
+ childSetAction("None", selectNoTypes, this);
+
+ mSpinSinceHours = getChild<LLSpinCtrl>("spin_hours_ago");
+ childSetCommitCallback("spin_hours_ago", onTimeAgo, this);
+
+ mSpinSinceDays = getChild<LLSpinCtrl>("spin_days_ago");
+ childSetCommitCallback("spin_days_ago", onTimeAgo, this);
+
+ // mCheckSinceLogoff = getChild<LLSpinCtrl>("check_since_logoff");
+ childSetCommitCallback("check_since_logoff", onCheckSinceLogoff, this);
+
+ childSetAction("Close", onCloseBtn, this);
+
+ updateElementsFromFilter();
+ return TRUE;
+}
+void LLFloaterInventoryFinder::onTimeAgo(LLUICtrl *ctrl, void *user_data)
+{
+ LLFloaterInventoryFinder *self = (LLFloaterInventoryFinder *)user_data;
+ if (!self) return;
+
+ bool since_logoff=true;
+ if ( self->mSpinSinceDays->get() || self->mSpinSinceHours->get() )
+ {
+ since_logoff = false;
+ }
+ self->childSetValue("check_since_logoff", since_logoff);
+}
+
+void LLFloaterInventoryFinder::changeFilter(LLInventoryFilter* filter)
+{
+ mFilter = filter;
+ updateElementsFromFilter();
+}
+
+void LLFloaterInventoryFinder::updateElementsFromFilter()
+{
+ if (!mFilter)
+ return;
+
+ // Get data needed for filter display
+ U32 filter_types = mFilter->getFilterTypes();
+ std::string filter_string = mFilter->getFilterSubString();
+ LLInventoryFilter::EFolderShow show_folders = mFilter->getShowFolderState();
+ U32 hours = mFilter->getHoursAgo();
+
+ // update the ui elements
+ setTitle(mFilter->getName());
+
+ childSetValue("check_animation", (S32) (filter_types & 0x1 << LLInventoryType::IT_ANIMATION));
+
+ childSetValue("check_calling_card", (S32) (filter_types & 0x1 << LLInventoryType::IT_CALLINGCARD));
+ childSetValue("check_clothing", (S32) (filter_types & 0x1 << LLInventoryType::IT_WEARABLE));
+ childSetValue("check_gesture", (S32) (filter_types & 0x1 << LLInventoryType::IT_GESTURE));
+ childSetValue("check_landmark", (S32) (filter_types & 0x1 << LLInventoryType::IT_LANDMARK));
+ childSetValue("check_notecard", (S32) (filter_types & 0x1 << LLInventoryType::IT_NOTECARD));
+ childSetValue("check_object", (S32) (filter_types & 0x1 << LLInventoryType::IT_OBJECT));
+ childSetValue("check_script", (S32) (filter_types & 0x1 << LLInventoryType::IT_LSL));
+ childSetValue("check_sound", (S32) (filter_types & 0x1 << LLInventoryType::IT_SOUND));
+ childSetValue("check_texture", (S32) (filter_types & 0x1 << LLInventoryType::IT_TEXTURE));
+ childSetValue("check_snapshot", (S32) (filter_types & 0x1 << LLInventoryType::IT_SNAPSHOT));
+ childSetValue("check_show_empty", show_folders == LLInventoryFilter::SHOW_ALL_FOLDERS);
+ childSetValue("check_since_logoff", mFilter->isSinceLogoff());
+ mSpinSinceHours->set((F32)(hours % 24));
+ mSpinSinceDays->set((F32)(hours / 24));
+}
+
+void LLFloaterInventoryFinder::draw()
+{
+ LLMemType mt(LLMemType::MTYPE_INVENTORY_DRAW);
+ U32 filter = 0xffffffff;
+ BOOL filtered_by_all_types = TRUE;
+
+ if (!childGetValue("check_animation"))
+ {
+ filter &= ~(0x1 << LLInventoryType::IT_ANIMATION);
+ filtered_by_all_types = FALSE;
+ }
+
+
+ if (!childGetValue("check_calling_card"))
+ {
+ filter &= ~(0x1 << LLInventoryType::IT_CALLINGCARD);
+ filtered_by_all_types = FALSE;
+ }
+
+ if (!childGetValue("check_clothing"))
+ {
+ filter &= ~(0x1 << LLInventoryType::IT_WEARABLE);
+ filtered_by_all_types = FALSE;
+ }
+
+ if (!childGetValue("check_gesture"))
+ {
+ filter &= ~(0x1 << LLInventoryType::IT_GESTURE);
+ filtered_by_all_types = FALSE;
+ }
+
+ if (!childGetValue("check_landmark"))
+
+
+ {
+ filter &= ~(0x1 << LLInventoryType::IT_LANDMARK);
+ filtered_by_all_types = FALSE;
+ }
+
+ if (!childGetValue("check_notecard"))
+ {
+ filter &= ~(0x1 << LLInventoryType::IT_NOTECARD);
+ filtered_by_all_types = FALSE;
+ }
+
+ if (!childGetValue("check_object"))
+ {
+ filter &= ~(0x1 << LLInventoryType::IT_OBJECT);
+ filter &= ~(0x1 << LLInventoryType::IT_ATTACHMENT);
+ filtered_by_all_types = FALSE;
+ }
+
+ if (!childGetValue("check_script"))
+ {
+ filter &= ~(0x1 << LLInventoryType::IT_LSL);
+ filtered_by_all_types = FALSE;
+ }
+
+ if (!childGetValue("check_sound"))
+ {
+ filter &= ~(0x1 << LLInventoryType::IT_SOUND);
+ filtered_by_all_types = FALSE;
+ }
+
+ if (!childGetValue("check_texture"))
+ {
+ filter &= ~(0x1 << LLInventoryType::IT_TEXTURE);
+ filtered_by_all_types = FALSE;
+ }
+
+ if (!childGetValue("check_snapshot"))
+ {
+ filter &= ~(0x1 << LLInventoryType::IT_SNAPSHOT);
+ filtered_by_all_types = FALSE;
+ }
+
+ if (!filtered_by_all_types)
+ {
+ // don't include folders in filter, unless I've selected everything
+ filter &= ~(0x1 << LLInventoryType::IT_CATEGORY);
+ }
+
+ // update the panel, panel will update the filter
+ mPanelMainInventory->getPanel()->setShowFolderState(getCheckShowEmpty() ?
+ LLInventoryFilter::SHOW_ALL_FOLDERS : LLInventoryFilter::SHOW_NON_EMPTY_FOLDERS);
+ mPanelMainInventory->getPanel()->setFilterTypes(filter);
+ if (getCheckSinceLogoff())
+ {
+ mSpinSinceDays->set(0);
+ mSpinSinceHours->set(0);
+ }
+ U32 days = (U32)mSpinSinceDays->get();
+ U32 hours = (U32)mSpinSinceHours->get();
+ if (hours > 24)
+ {
+ days += hours / 24;
+ hours = (U32)hours % 24;
+ mSpinSinceDays->set((F32)days);
+ mSpinSinceHours->set((F32)hours);
+ }
+ hours += days * 24;
+ mPanelMainInventory->getPanel()->setHoursAgo(hours);
+ mPanelMainInventory->getPanel()->setSinceLogoff(getCheckSinceLogoff());
+ mPanelMainInventory->setFilterTextFromFilter();
+
+ LLPanel::draw();
+}
+
+BOOL LLFloaterInventoryFinder::getCheckShowEmpty()
+{
+ return childGetValue("check_show_empty");
+}
+
+BOOL LLFloaterInventoryFinder::getCheckSinceLogoff()
+{
+ return childGetValue("check_since_logoff");
+}
+
+void LLFloaterInventoryFinder::onCloseBtn(void* user_data)
+{
+ LLFloaterInventoryFinder* finderp = (LLFloaterInventoryFinder*)user_data;
+ finderp->closeFloater();
+}
+
+// static
+void LLFloaterInventoryFinder::selectAllTypes(void* user_data)
+{
+ LLFloaterInventoryFinder* self = (LLFloaterInventoryFinder*)user_data;
+ if(!self) return;
+
+ self->childSetValue("check_animation", TRUE);
+ self->childSetValue("check_calling_card", TRUE);
+ self->childSetValue("check_clothing", TRUE);
+ self->childSetValue("check_gesture", TRUE);
+ self->childSetValue("check_landmark", TRUE);
+ self->childSetValue("check_notecard", TRUE);
+ self->childSetValue("check_object", TRUE);
+ self->childSetValue("check_script", TRUE);
+ self->childSetValue("check_sound", TRUE);
+ self->childSetValue("check_texture", TRUE);
+ self->childSetValue("check_snapshot", TRUE);
+}
+
+//static
+void LLFloaterInventoryFinder::selectNoTypes(void* user_data)
+{
+ LLFloaterInventoryFinder* self = (LLFloaterInventoryFinder*)user_data;
+ if(!self) return;
+
+ self->childSetValue("check_animation", FALSE);
+ self->childSetValue("check_calling_card", FALSE);
+ self->childSetValue("check_clothing", FALSE);
+ self->childSetValue("check_gesture", FALSE);
+ self->childSetValue("check_landmark", FALSE);
+ self->childSetValue("check_notecard", FALSE);
+ self->childSetValue("check_object", FALSE);
+ self->childSetValue("check_script", FALSE);
+ self->childSetValue("check_sound", FALSE);
+ self->childSetValue("check_texture", FALSE);
+ self->childSetValue("check_snapshot", FALSE);
+}
+
+
+
+
+
+void LLPanelMainInventory::updateListCommands()
+{
+ bool trash_enabled = isActionEnabled("delete");
+
+ mListCommands->childSetEnabled("trash_btn", trash_enabled);
+}
+
+void LLPanelMainInventory::onGearButtonClick()
+{
+ showActionMenu(mMenuGearDefault,"options_gear_btn");
+}
+
+void LLPanelMainInventory::onAddButtonClick()
+{
+ showActionMenu(mMenuAdd,"add_btn");
+}
+
+void LLPanelMainInventory::showActionMenu(LLMenuGL* menu, std::string spawning_view_name)
+{
+ if (menu)
+ {
+ menu->buildDrawLabels();
+ menu->updateParent(LLMenuGL::sMenuContainer);
+ LLView* spawning_view = getChild<LLView> (spawning_view_name);
+ S32 menu_x, menu_y;
+ //show menu in co-ordinates of panel
+ spawning_view->localPointToOtherView(0, spawning_view->getRect().getHeight(), &menu_x, &menu_y, this);
+ menu_y += menu->getRect().getHeight();
+ LLMenuGL::showPopup(this, menu, menu_x, menu_y);
+ }
+}
+
+void LLPanelMainInventory::onTrashButtonClick()
+{
+ onClipboardAction("delete");
+}
+
+void LLPanelMainInventory::onClipboardAction(const LLSD& userdata)
+{
+ std::string command_name = userdata.asString();
+ getActivePanel()->getRootFolder()->doToSelected(getActivePanel()->getModel(),command_name);
+}
+
+void LLPanelMainInventory::onCustomAction(const LLSD& userdata)
+{
+ if (!isActionEnabled(userdata))
+ return;
+
+ const std::string command_name = userdata.asString();
+ if (command_name == "new_window")
+ {
+ newWindow();
+ }
+ if (command_name == "sort_by_name")
+ {
+ const LLSD arg = "name";
+ setSortBy(arg);
+ }
+ if (command_name == "sort_by_recent")
+ {
+ const LLSD arg = "date";
+ setSortBy(arg);
+ }
+ if (command_name == "show_filters")
+ {
+ toggleFindOptions();
+ }
+ if (command_name == "reset_filters")
+ {
+ resetFilters();
+ }
+ if (command_name == "close_folders")
+ {
+ closeAllFolders();
+ }
+ if (command_name == "empty_trash")
+ {
+ const std::string notification = "ConfirmEmptyTrash";
+ gInventory.emptyFolderType(notification, LLFolderType::FT_TRASH);
+ }
+ if (command_name == "empty_lostnfound")
+ {
+ const std::string notification = "ConfirmEmptyLostAndFound";
+ gInventory.emptyFolderType(notification, LLFolderType::FT_LOST_AND_FOUND);
+ }
+ if (command_name == "save_texture")
+ {
+ LLFolderViewItem* current_item = getActivePanel()->getRootFolder()->getCurSelectedItem();
+ if (!current_item)
+ {
+ return;
+ }
+
+ const LLUUID& item_id = current_item->getListener()->getUUID();
+ LLPreviewTexture* preview_texture = LLFloaterReg::showTypedInstance<LLPreviewTexture>("preview_texture", LLSD(item_id), TAKE_FOCUS_YES);
+ if (preview_texture)
+ {
+ preview_texture->openToSave();
+ }
+ }
+}
+
+BOOL LLPanelMainInventory::isActionEnabled(const LLSD& userdata)
+{
+ const std::string command_name = userdata.asString();
+ if (command_name == "delete")
+ {
+ BOOL can_delete = FALSE;
+ LLFolderView *folder = getActivePanel()->getRootFolder();
+ if (folder)
+ {
+ can_delete = TRUE;
+ std::set<LLUUID> selection_set;
+ folder->getSelectionList(selection_set);
+ for (std::set<LLUUID>::iterator iter = selection_set.begin();
+ iter != selection_set.end();
+ ++iter)
+ {
+ const LLUUID &item_id = (*iter);
+ LLFolderViewItem *item = folder->getItemByID(item_id);
+ can_delete &= item->getListener()->isItemRemovable();
+ }
+ return can_delete;
+ }
+ return FALSE;
+ }
+ if (command_name == "save_texture")
+ {
+ LLFolderViewItem* current_item = getActivePanel()->getRootFolder()->getCurSelectedItem();
+ if (current_item)
+ {
+ return (current_item->getListener()->getInventoryType() == LLInventoryType::IT_TEXTURE);
+ }
+ return FALSE;
+ }
+ return TRUE;
+}
+
+bool LLPanelMainInventory::handleDragAndDropToTrash(BOOL drop, EDragAndDropType cargo_type, EAcceptance* accept)
+{
+ *accept = ACCEPT_NO;
+
+ const bool is_enabled = isActionEnabled("delete");
+ if (is_enabled) *accept = ACCEPT_YES_MULTI;
+
+ if (is_enabled && drop)
+ {
+ onClipboardAction("delete");
+ }
+ return true;
+}
diff --git a/indra/newview/llpanelmaininventory.h b/indra/newview/llpanelmaininventory.h index a2b988e80c..29e9baa6cf 100644 --- a/indra/newview/llpanelmaininventory.h +++ b/indra/newview/llpanelmaininventory.h @@ -1,125 +1,145 @@ -/** - * @file llpanelmaininventory.h - * @brief llpanelmaininventory.h - * class definition - * - * $LicenseInfo:firstyear=2001&license=viewergpl$ - * - * Copyright (c) 2001-2009, Linden Research, Inc. - * - * Second Life Viewer Source Code - * The source code in this file ("Source Code") is provided by Linden Lab - * to you under the terms of the GNU General Public License, version 2.0 - * ("GPL"), unless you have obtained a separate licensing agreement - * ("Other License"), formally executed by you and Linden Lab. Terms of - * the GPL can be found in doc/GPL-license.txt in this distribution, or - * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 - * - * There are special exceptions to the terms and conditions of the GPL as - * it is applied to this Source Code. View the full text of the exception - * in the file doc/FLOSS-exception.txt in this software distribution, or - * online at - * http://secondlifegrid.net/programs/open_source/licensing/flossexception - * - * By copying, modifying or distributing this software, you acknowledge - * that you have read and understood your obligations described above, - * and agree to abide by those obligations. - * - * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO - * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, - * COMPLETENESS OR PERFORMANCE. - * $/LicenseInfo$ - */ - -#ifndef LL_LLPANELMAININVENTORY_H -#define LL_LLPANELMAININVENTORY_H - -#include "llpanel.h" -#include "llinventorymodel.h" -#include "llfolderview.h" - -class LLFolderViewItem; -class LLInventoryPanel; -class LLSaveFolderState; -class LLFilterEditor; -class LLTabContainer; -class LLFloaterInventoryFinder; - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// Class LLPanelMainInventory -// -// This is a panel used to view and control an agent's inventory, -// including all the fixin's (e.g. AllItems/RecentItems tabs, filter floaters). -// -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -class LLPanelMainInventory : public LLPanel, LLInventoryObserver -{ -public: - friend class LLFloaterInventoryFinder; - - LLPanelMainInventory(); - ~LLPanelMainInventory(); - - BOOL postBuild(); - - virtual BOOL handleKeyHere(KEY key, MASK mask); - - // Inherited functionality - /*virtual*/ BOOL handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, - EDragAndDropType cargo_type, - void* cargo_data, - EAcceptance* accept, - std::string& tooltip_msg); - /*virtual*/ void changed(U32 mask); - - LLInventoryPanel* getPanel() { return mActivePanel; } - LLInventoryPanel* getActivePanel() { return mActivePanel; } - - const std::string& getFilterText() const { return mFilterText; } - - void setSelectCallback(const LLFolderView::signal_t::slot_type& cb); - -protected: - // - // Misc functions - // - void setFilterTextFromFilter(); - void startSearch(); - - void toggleFindOptions(); - - static BOOL filtersVisible(void* user_data); - void onClearSearch(); - static void onFoldersByName(void *user_data); - static BOOL checkFoldersByName(void *user_data); - void onFilterEdit(const std::string& search_string ); - static BOOL incrementalFind(LLFolderViewItem* first_item, const char *find_text, BOOL backward); - void onFilterSelected(); - - const std::string getFilterSubString(); - void setFilterSubString(const std::string& string); - - // menu callbacks - void doToSelected(const LLSD& userdata); - void closeAllFolders(); - void newWindow(); - void doCreate(const LLSD& userdata); - void resetFilters(); - void setSortBy(const LLSD& userdata); - -private: - LLFloaterInventoryFinder* getFinder(); - - LLFilterEditor* mFilterEditor; - LLTabContainer* mFilterTabs; - LLHandle<LLFloater> mFinderHandle; - LLInventoryPanel* mActivePanel; - LLSaveFolderState* mSavedFolderState; - - std::string mFilterText; -}; - -#endif // LL_LLPANELMAININVENTORY_H - - - +/**
+ * @file llpanelmaininventory.h
+ * @brief llpanelmaininventory.h
+ * class definition
+ *
+ * $LicenseInfo:firstyear=2001&license=viewergpl$
+ *
+ * Copyright (c) 2001-2009, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLPANELMAININVENTORY_H
+#define LL_LLPANELMAININVENTORY_H
+
+#include "llpanel.h"
+#include "llinventorymodel.h"
+#include "llfolderview.h"
+
+class LLFolderViewItem;
+class LLInventoryPanel;
+class LLSaveFolderState;
+class LLFilterEditor;
+class LLTabContainer;
+class LLFloaterInventoryFinder;
+class LLMenuGL;
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLPanelMainInventory
+//
+// This is a panel used to view and control an agent's inventory,
+// including all the fixin's (e.g. AllItems/RecentItems tabs, filter floaters).
+//
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+class LLPanelMainInventory : public LLPanel, LLInventoryObserver
+{
+public:
+ friend class LLFloaterInventoryFinder;
+
+ LLPanelMainInventory();
+ ~LLPanelMainInventory();
+
+ BOOL postBuild();
+
+ virtual BOOL handleKeyHere(KEY key, MASK mask);
+
+ // Inherited functionality
+ /*virtual*/ BOOL handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop,
+ EDragAndDropType cargo_type,
+ void* cargo_data,
+ EAcceptance* accept,
+ std::string& tooltip_msg);
+ /*virtual*/ void changed(U32 mask);
+
+ LLInventoryPanel* getPanel() { return mActivePanel; }
+ LLInventoryPanel* getActivePanel() { return mActivePanel; }
+ const LLInventoryPanel* getActivePanel() const { return mActivePanel; }
+
+ const std::string& getFilterText() const { return mFilterText; }
+
+ void setSelectCallback(const LLFolderView::signal_t::slot_type& cb);
+
+protected:
+ //
+ // Misc functions
+ //
+ void setFilterTextFromFilter();
+ void startSearch();
+
+ void toggleFindOptions();
+ void onSelectionChange(LLInventoryPanel *panel, const std::deque<LLFolderViewItem*>& items, BOOL user_action);
+
+ static BOOL filtersVisible(void* user_data);
+ void onClearSearch();
+ static void onFoldersByName(void *user_data);
+ static BOOL checkFoldersByName(void *user_data);
+ void onFilterEdit(const std::string& search_string );
+ static BOOL incrementalFind(LLFolderViewItem* first_item, const char *find_text, BOOL backward);
+ void onFilterSelected();
+
+ const std::string getFilterSubString();
+ void setFilterSubString(const std::string& string);
+
+ // menu callbacks
+ void doToSelected(const LLSD& userdata);
+ void closeAllFolders();
+ void newWindow();
+ void doCreate(const LLSD& userdata);
+ void resetFilters();
+ void setSortBy(const LLSD& userdata);
+
+ // List Commands Handlers
+ void initListCommandsHandlers();
+ void updateListCommands();
+ void onGearButtonClick();
+ void onAddButtonClick();
+ void showActionMenu(LLMenuGL* menu, std::string spawning_view_name);
+ void onTrashButtonClick();
+ void onClipboardAction(const LLSD& userdata);
+ BOOL isActionEnabled(const LLSD& command_name);
+ void onCustomAction(const LLSD& command_name);
+ bool handleDragAndDropToTrash(BOOL drop, EDragAndDropType cargo_type, EAcceptance* accept);
+
+
+private:
+ LLFloaterInventoryFinder* getFinder();
+
+ LLFilterEditor* mFilterEditor;
+ LLTabContainer* mFilterTabs;
+ LLHandle<LLFloater> mFinderHandle;
+ LLInventoryPanel* mActivePanel;
+ LLSaveFolderState* mSavedFolderState;
+
+ LLPanel* mListCommands;
+ LLMenuGL* mMenuGearDefault;
+ LLMenuGL* mMenuAdd;
+
+ std::string mFilterText;
+};
+
+#endif // LL_LLPANELMAININVENTORY_H
+
+
+
diff --git a/indra/newview/llpanelmediasettingsgeneral.cpp b/indra/newview/llpanelmediasettingsgeneral.cpp index 85efe0f93e..5186a5888b 100644 --- a/indra/newview/llpanelmediasettingsgeneral.cpp +++ b/indra/newview/llpanelmediasettingsgeneral.cpp @@ -55,6 +55,8 @@ #include "llfloatertools.h" #include "lltrans.h" +const char *CHECKERBOARD_DATA_URL = "data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 width=%22100%%22 height=%22100%%22 %3E%3Cdefs%3E%3Cpattern id=%22checker%22 patternUnits=%22userSpaceOnUse%22 x=%220%22 y=%220%22 width=%22128%22 height=%22128%22 viewBox=%220 0 128 128%22 %3E%3Crect x=%220%22 y=%220%22 width=%2264%22 height=%2264%22 fill=%22#ddddff%22 /%3E%3Crect x=%2264%22 y=%2264%22 width=%2264%22 height=%2264%22 fill=%22#ddddff%22 /%3E%3C/pattern%3E%3C/defs%3E%3Crect x=%220%22 y=%220%22 width=%22100%%22 height=%22100%%22 fill=%22url(#checker)%22 /%3E%3C/svg%3E"; + //////////////////////////////////////////////////////////////////////////////// // LLPanelMediaSettingsGeneral::LLPanelMediaSettingsGeneral() : @@ -97,7 +99,6 @@ BOOL LLPanelMediaSettingsGeneral::postBuild() childSetCommitCallback( "current_url_reset_btn",onBtnResetCurrentUrl, this); // interrogates controls and updates widgets as required updateMediaPreview(); - updateCurrentURL(); return true; } @@ -160,9 +161,9 @@ void LLPanelMediaSettingsGeneral::draw() }; }; - // current URL can change over time. -// updateCurrentURL(); - + // current URL can change over time, update it here + updateCurrentUrl(); + LLPermissions perm; bool user_can_press_reset = mMediaEditable; @@ -215,26 +216,15 @@ void LLPanelMediaSettingsGeneral::clearValues( void* userdata, bool editable) self->updateMediaPreview(); } -//////////////////////////////////////////////////////////////////////////////// -// static -void LLPanelMediaSettingsGeneral::initValues( void* userdata, const LLSD& media_settings ,bool editable) +// static +bool LLPanelMediaSettingsGeneral::isMultiple() { - LLPanelMediaSettingsGeneral *self =(LLPanelMediaSettingsGeneral *)userdata; - self->mMediaEditable = editable; - - //llinfos << "---------------" << llendl; - //llinfos << ll_pretty_print_sd(media_settings) << llendl; - //llinfos << "---------------" << llendl; - // IF all the faces have media (or all dont have media) if ( LLFloaterMediaSettings::getInstance()->mIdenticalHasMediaInfo ) { if(LLFloaterMediaSettings::getInstance()->mMultipleMedia) { - self->clearValues(self, self->mMediaEditable); - // only show multiple - self->mHomeURL ->setText(LLTrans::getString("Multiple Media")); - return; + return true; } } @@ -242,13 +232,32 @@ void LLPanelMediaSettingsGeneral::initValues( void* userdata, const LLSD& media_ { if(LLFloaterMediaSettings::getInstance()->mMultipleValidMedia) { - self->clearValues(self, self->mMediaEditable); - // only show multiple - self->mHomeURL ->setText(LLTrans::getString("Multiple Media")); - return; - } - + return true; + } } + return false; +} + +//////////////////////////////////////////////////////////////////////////////// +// static +void LLPanelMediaSettingsGeneral::initValues( void* userdata, const LLSD& media_settings ,bool editable) +{ + LLPanelMediaSettingsGeneral *self =(LLPanelMediaSettingsGeneral *)userdata; + self->mMediaEditable = editable; + + //llinfos << "---------------" << llendl; + //llinfos << ll_pretty_print_sd(media_settings) << llendl; + //llinfos << "---------------" << llendl; + + if ( LLPanelMediaSettingsGeneral::isMultiple() ) + { + self->clearValues(self, self->mMediaEditable); + // only show multiple + self->mHomeURL->setText(LLTrans::getString("Multiple Media")); + self->mCurrentURL->setText(LLTrans::getString("Multiple Media")); + return; + } + std::string base_key( "" ); std::string tentative_key( "" ); @@ -305,7 +314,6 @@ void LLPanelMediaSettingsGeneral::initValues( void* userdata, const LLSD& media_ // interrogates controls and updates widgets as required self->updateMediaPreview(); - self->updateCurrentURL(); } //////////////////////////////////////////////////////////////////////////////// @@ -320,22 +328,11 @@ void LLPanelMediaSettingsGeneral::updateMediaPreview() // new home URL will be empty if media is deleted so display a // "preview goes here" data url page { - mPreviewMedia->navigateTo( "data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 width=%22100%%22 height=%22100%%22 %3E%3Cdefs%3E%3Cpattern id=%22checker%22 patternUnits=%22userSpaceOnUse%22 x=%220%22 y=%220%22 width=%22128%22 height=%22128%22 viewBox=%220 0 128 128%22 %3E%3Crect x=%220%22 y=%220%22 width=%2264%22 height=%2264%22 fill=%22#ddddff%22 /%3E%3Crect x=%2264%22 y=%2264%22 width=%2264%22 height=%2264%22 fill=%22#ddddff%22 /%3E%3C/pattern%3E%3C/defs%3E%3Crect x=%220%22 y=%220%22 width=%22100%%22 height=%22100%%22 fill=%22url(#checker)%22 /%3E%3C/svg%3E" ); + mPreviewMedia->navigateTo( CHECKERBOARD_DATA_URL ); }; } //////////////////////////////////////////////////////////////////////////////// -// Helper to set current URL -void LLPanelMediaSettingsGeneral::updateCurrentURL() -{ - if( mCurrentURL->getText().empty() ) - { - childSetText( "current_url", mHomeURL->getText() ); - } - -} - -//////////////////////////////////////////////////////////////////////////////// // virtual void LLPanelMediaSettingsGeneral::onClose(bool app_quitting) @@ -441,6 +438,8 @@ bool LLPanelMediaSettingsGeneral::navigateHomeSelectedFace() LLObjectSelectionHandle selected_objects =LLSelectMgr::getInstance()->getSelection(); selected_objects->getSelectedTEValue( &functor_navigate_media, all_face_media_navigated ); + // Note: we don't update the 'current URL' field until the media data itself changes + return all_face_media_navigated; } @@ -451,3 +450,31 @@ const std::string LLPanelMediaSettingsGeneral::getHomeUrl() return mHomeURL->getValue().asString(); } +//////////////////////////////////////////////////////////////////////////////// +// +void LLPanelMediaSettingsGeneral::updateCurrentUrl() +{ + // Get the current URL from the selection + + const LLMediaEntry default_media_data; + std::string value_str = default_media_data.getCurrentURL(); + struct functor_getter_current_url : public LLSelectedTEGetFunctor< std::string > + { + functor_getter_current_url(const LLMediaEntry& entry): mMediaEntry(entry) {} + + std::string get( LLViewerObject* object, S32 face ) + { + if ( object ) + if ( object->getTE(face) ) + if ( object->getTE(face)->getMediaData() ) + return object->getTE(face)->getMediaData()->getCurrentURL(); + return mMediaEntry.getCurrentURL(); + }; + + const LLMediaEntry & mMediaEntry; + + } func_current_url(default_media_data); + bool identical = LLSelectMgr::getInstance()->getSelection()->getSelectedTEValue( &func_current_url, value_str ); + mCurrentURL->setText(value_str); + mCurrentURL->setTentative(identical); +} diff --git a/indra/newview/llpanelmediasettingsgeneral.h b/indra/newview/llpanelmediasettingsgeneral.h index e82a31382e..b48e081a1b 100644 --- a/indra/newview/llpanelmediasettingsgeneral.h +++ b/indra/newview/llpanelmediasettingsgeneral.h @@ -63,7 +63,6 @@ public: bool navigateHomeSelectedFace(); void updateMediaPreview(); - void updateCurrentURL(); const std::string getHomeUrl(); @@ -72,8 +71,12 @@ protected: bool mMediaEditable; private: + void updateCurrentUrl(); + static void onBtnResetCurrentUrl(LLUICtrl* ctrl, void *userdata); static void onCommitHomeURL(LLUICtrl* ctrl, void *userdata ); + + static bool isMultiple(); LLComboBox* mControls; LLCheckBoxCtrl* mAutoLoop; diff --git a/indra/newview/llpanelprimmediacontrols.cpp b/indra/newview/llpanelprimmediacontrols.cpp index 0b2a7e8756..73e19b3b2a 100644 --- a/indra/newview/llpanelprimmediacontrols.cpp +++ b/indra/newview/llpanelprimmediacontrols.cpp @@ -228,6 +228,7 @@ void LLPanelPrimMediaControls::updateShape() bool can_navigate = parcel->getMediaAllowNavigate(); bool enabled = false; + bool is_zoomed = (mCurrentZoom != ZOOM_NONE); // There is no such thing as "has_focus" being different from normal controls set // anymore (as of user feedback from bri 10/09). So we cheat here and force 'has_focus' // to 'true' (or, actually, we use a setting) @@ -256,7 +257,7 @@ void LLPanelPrimMediaControls::updateShape() LLUICtrl* stop_ctrl = getChild<LLUICtrl>("stop"); LLUICtrl* media_stop_ctrl = getChild<LLUICtrl>("media_stop"); LLUICtrl* home_ctrl = getChild<LLUICtrl>("home"); - LLUICtrl* close_ctrl = getChild<LLUICtrl>("close"); + LLUICtrl* unzoom_ctrl = getChild<LLUICtrl>("close"); // This is actually "unzoom" LLUICtrl* open_ctrl = getChild<LLUICtrl>("new_window"); LLUICtrl* zoom_ctrl = getChild<LLUICtrl>("zoom_frame"); LLPanel* media_loading_panel = getChild<LLPanel>("media_progress_indicator"); @@ -283,7 +284,8 @@ void LLPanelPrimMediaControls::updateShape() reload_ctrl->setVisible(has_focus); stop_ctrl->setVisible(false); home_ctrl->setVisible(has_focus); - close_ctrl->setVisible(has_focus); + zoom_ctrl->setVisible(!is_zoomed); + unzoom_ctrl->setVisible(has_focus && is_zoomed); open_ctrl->setVisible(true); media_address_ctrl->setVisible(has_focus && !mini_controls); media_play_slider_panel->setVisible(has_focus && !mini_controls); @@ -294,6 +296,7 @@ void LLPanelPrimMediaControls::updateShape() whitelist_icon->setVisible(!mini_controls && (media_data)?media_data->getWhiteListEnable():false); // Disable zoom if HUD zoom_ctrl->setEnabled(!objectp->isHUDAttachment()); + unzoom_ctrl->setEnabled(!objectp->isHUDAttachment()); secure_lock_icon->setVisible(false); mCurrentURL = media_impl->getCurrentMediaURL(); diff --git a/indra/newview/llpanelteleporthistory.cpp b/indra/newview/llpanelteleporthistory.cpp index 58ecfb60c2..72856d5b82 100644 --- a/indra/newview/llpanelteleporthistory.cpp +++ b/indra/newview/llpanelteleporthistory.cpp @@ -359,7 +359,7 @@ void LLTeleportHistoryPanel::onCopySLURL() U64 new_region_handle = to_region_handle(global_pos); - LLWorldMap::url_callback_t cb = boost::bind( + LLWorldMapMessage::url_callback_t cb = boost::bind( &LLPanelPlacesTab::onRegionResponse, this, global_pos, _1, _2, _3, _4); diff --git a/indra/newview/llpreviewtexture.cpp b/indra/newview/llpreviewtexture.cpp index 86fa2c4695..3eab13fc4a 100644 --- a/indra/newview/llpreviewtexture.cpp +++ b/indra/newview/llpreviewtexture.cpp @@ -48,6 +48,7 @@ #include "lltextbox.h" #include "lltextureview.h" #include "llui.h" +#include "llviewerinventory.h" #include "llviewertexture.h" #include "llviewertexturelist.h" #include "lluictrlfactory.h" @@ -63,7 +64,7 @@ const F32 PREVIEW_TEXTURE_MIN_ASPECT = 0.005f; LLPreviewTexture::LLPreviewTexture(const LLSD& key) - : LLPreview( key ), + : LLPreview(key), mLoadingFullImage( FALSE ), mShowKeepDiscard(FALSE), mCopyToInv(FALSE), @@ -71,7 +72,8 @@ LLPreviewTexture::LLPreviewTexture(const LLSD& key) mUpdateDimensions(TRUE), mLastHeight(0), mLastWidth(0), - mAspectRatio(0.f) + mAspectRatio(0.f), + mPreviewToSave(FALSE) { const LLInventoryItem *item = getItem(); if(item) @@ -104,6 +106,10 @@ LLPreviewTexture::LLPreviewTexture(const LLSD& key) mIsCopyable = TRUE; } + if (key.has("save_as")) + { + mPreviewToSave = TRUE; + } //Called from floater reg: LLUICtrlFactory::getInstance()->buildFloater(this, "floater_preview_texture.xml", FALSE); } @@ -181,6 +187,12 @@ void LLPreviewTexture::draw() if ( mImage.notNull() ) { + // Automatically bring up SaveAs dialog if we opened this to save the texture. + if (mPreviewToSave) + { + mPreviewToSave = FALSE; + saveAs(); + } // Draw the texture glColor3f( 1.f, 1.f, 1.f ); gl_draw_scaled_image(interior.mLeft, @@ -209,7 +221,7 @@ void LLPreviewTexture::draw() if( mLoadingFullImage ) { - LLFontGL::getFontSansSerif()->renderUTF8(LLTrans::getString("Receiving:"), 0, + LLFontGL::getFontSansSerif()->renderUTF8(LLTrans::getString("Receiving"), 0, interior.mLeft + 4, interior.mBottom + 4, LLColor4::white, LLFontGL::LEFT, LLFontGL::BOTTOM, @@ -304,6 +316,11 @@ void LLPreviewTexture::onFocusReceived() LLPreview::onFocusReceived(); } +void LLPreviewTexture::openToSave() +{ + mPreviewToSave = TRUE; +} + // static void LLPreviewTexture::onFileLoadedForSave(BOOL success, LLViewerFetchedTexture *src_vi, @@ -356,6 +373,7 @@ void LLPreviewTexture::onFileLoadedForSave(BOOL success, { LLNotifications::instance().add("CannotDownloadFile"); } + } @@ -554,6 +572,7 @@ void LLPreviewTexture::loadAsset() { mImage = LLViewerTextureManager::getFetchedTexture(mImageID, MIPMAP_TRUE, LLViewerTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE); mImage->setBoostLevel(LLViewerTexture::BOOST_PREVIEW); + mImage->forceToSaveRawImage(0) ; mAssetStatus = PREVIEW_ASSET_LOADING; updateDimensions(); } diff --git a/indra/newview/llpreviewtexture.h b/indra/newview/llpreviewtexture.h index 520626b49f..9b3c91d831 100644 --- a/indra/newview/llpreviewtexture.h +++ b/indra/newview/llpreviewtexture.h @@ -67,8 +67,7 @@ public: S32 discard_level, BOOL final, void* userdata ); - - + void openToSave(); protected: void init(); /* virtual */ BOOL postBuild(); @@ -77,14 +76,17 @@ protected: private: void updateDimensions(); - LLUUID mImageID; + LLUUID mImageID; LLPointer<LLViewerFetchedTexture> mImage; BOOL mLoadingFullImage; std::string mSaveFileName; LLFrameTimer mSavedFileTimer; BOOL mShowKeepDiscard; BOOL mCopyToInv; - + + // Save the image once it's loaded. + BOOL mPreviewToSave; + // This is stored off in a member variable, because the save-as // button and drag and drop functionality need to know. BOOL mIsCopyable; @@ -94,6 +96,4 @@ private: F32 mAspectRatio; BOOL mUpdateDimensions; }; - - #endif // LL_LLPREVIEWTEXTURE_H diff --git a/indra/newview/llselectmgr.cpp b/indra/newview/llselectmgr.cpp index dd07a3c015..4cb561381d 100644 --- a/indra/newview/llselectmgr.cpp +++ b/indra/newview/llselectmgr.cpp @@ -67,6 +67,7 @@ #include "llinventorymodel.h" #include "llmenugl.h" #include "llmutelist.h" +#include "llsidepaneltaskinfo.h" #include "llslurl.h" #include "llstatusbar.h" #include "llsurface.h" @@ -4907,12 +4908,20 @@ void LLSelectMgr::renderSilhouettes(BOOL for_hud) } if (mSelectedObjects->getNumNodes()) { - LLFloaterInspect* inspect_instance = LLFloaterReg::getTypedInstance<LLFloaterInspect>("inspect"); LLUUID inspect_item_id= LLUUID::null; +#if 0 + LLFloaterInspect* inspect_instance = LLFloaterReg::getTypedInstance<LLFloaterInspect>("inspect"); if(inspect_instance) { inspect_item_id = inspect_instance->getSelectedUUID(); } +#endif + LLSidepanelTaskInfo *panel_task_info = LLSidepanelTaskInfo::getActivePanel(); + if (panel_task_info) + { + inspect_item_id = panel_task_info->getSelectedUUID(); + } + LLUUID focus_item_id = LLViewerMediaFocus::getInstance()->getFocusedObjectID(); for (S32 pass = 0; pass < 2; pass++) { @@ -5487,12 +5496,19 @@ void dialog_refresh_all() } LLFloaterProperties::dirtyAll(); - + +#if 0 LLFloaterInspect* inspect_instance = LLFloaterReg::getTypedInstance<LLFloaterInspect>("inspect"); if(inspect_instance) { inspect_instance->dirty(); } +#endif + LLSidepanelTaskInfo *panel_task_info = LLSidepanelTaskInfo::getActivePanel(); + if (panel_task_info) + { + panel_task_info->dirty(); + } } S32 get_family_count(LLViewerObject *parent) diff --git a/indra/newview/llsidepanelinventory.cpp b/indra/newview/llsidepanelinventory.cpp index 3cf17fb7f2..6aa5c53194 100644 --- a/indra/newview/llsidepanelinventory.cpp +++ b/indra/newview/llsidepanelinventory.cpp @@ -1,244 +1,274 @@ -/** - * @file LLSidepanelInventory.cpp - * @brief Side Bar "Inventory" panel - * - * $LicenseInfo:firstyear=2009&license=viewergpl$ - * - * Copyright (c) 2004-2009, Linden Research, Inc. - * - * Second Life Viewer Source Code - * The source code in this file ("Source Code") is provided by Linden Lab - * to you under the terms of the GNU General Public License, version 2.0 - * ("GPL"), unless you have obtained a separate licensing agreement - * ("Other License"), formally executed by you and Linden Lab. Terms of - * the GPL can be found in doc/GPL-license.txt in this distribution, or - * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 - * - * There are special exceptions to the terms and conditions of the GPL as - * it is applied to this Source Code. View the full text of the exception - * in the file doc/FLOSS-exception.txt in this software distribution, or - * online at http://secondlifegrid.net/programs/open_source/licensing/flossexception - * - * By copying, modifying or distributing this software, you acknowledge - * that you have read and understood your obligations described above, - * and agree to abide by those obligations. - * - * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO - * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, - * COMPLETENESS OR PERFORMANCE. - * $/LicenseInfo$ - */ - -#include "llviewerprecompiledheaders.h" -#include "llsidepanelinventory.h" - -#include "llagent.h" -#include "llbutton.h" -#include "llinventorybridge.h" -#include "llinventorypanel.h" -#include "llpanelmaininventory.h" -#include "llsidepanelobjectinfo.h" -#include "lltabcontainer.h" - -static const S32 LANDMARK_FOLDERS_MENU_WIDTH = 250; -static const std::string AGENT_INFO_TYPE = "agent"; -static const std::string CREATE_LANDMARK_INFO_TYPE = "create_landmark"; -static const std::string LANDMARK_INFO_TYPE = "landmark"; -static const std::string REMOTE_PLACE_INFO_TYPE = "remote_place"; -static const std::string TELEPORT_HISTORY_INFO_TYPE = "teleport_history"; - -static LLRegisterPanelClassWrapper<LLSidepanelInventory> t_inventory("sidepanel_inventory"); - -LLSidepanelInventory::LLSidepanelInventory() - : LLPanel(), - mSidepanelObjectInfo(NULL) -{ - - //LLUICtrlFactory::getInstance()->buildPanel(this, "panel_inventory.xml"); // Called from LLRegisterPanelClass::defaultPanelClassBuilder() -} - -LLSidepanelInventory::~LLSidepanelInventory() -{ -} - -BOOL LLSidepanelInventory::postBuild() -{ - mInfoBtn = getChild<LLButton>("info_btn"); - mInfoBtn->setClickedCallback(boost::bind(&LLSidepanelInventory::onInfoButtonClicked, this)); - - mShareBtn = getChild<LLButton>("share_btn"); - mShareBtn->setClickedCallback(boost::bind(&LLSidepanelInventory::onShareButtonClicked, this)); - - mShareBtn = getChild<LLButton>("share_btn"); - mShareBtn->setClickedCallback(boost::bind(&LLSidepanelInventory::onShareButtonClicked, this)); - - mWearBtn = getChild<LLButton>("wear_btn"); - mWearBtn->setClickedCallback(boost::bind(&LLSidepanelInventory::onWearButtonClicked, this)); - - mPlayBtn = getChild<LLButton>("play_btn"); - mPlayBtn->setClickedCallback(boost::bind(&LLSidepanelInventory::onPlayButtonClicked, this)); - - mTeleportBtn = getChild<LLButton>("teleport_btn"); - mTeleportBtn->setClickedCallback(boost::bind(&LLSidepanelInventory::onTeleportButtonClicked, this)); - - mOverflowBtn = getChild<LLButton>("overflow_btn"); - mOverflowBtn->setClickedCallback(boost::bind(&LLSidepanelInventory::onOverflowButtonClicked, this)); - - mTabContainer = getChild<LLTabContainer>("Inventory Tabs"); - mSidepanelObjectInfo = getChild<LLSidepanelObjectInfo>("sidepanel_object_info"); - - mPanelMainInventory = getChild<LLPanelMainInventory>("panel_main_inventory"); - mPanelMainInventory->setSelectCallback(boost::bind(&LLSidepanelInventory::onSelectionChange, this, _1, _2)); - - LLButton* back_btn = mSidepanelObjectInfo->getChild<LLButton>("back_btn"); - back_btn->setClickedCallback(boost::bind(&LLSidepanelInventory::onBackButtonClicked, this)); - - return TRUE; -} - -void LLSidepanelInventory::onOpen(const LLSD& key) -{ - if(key.size() == 0) - return; - - mSidepanelObjectInfo->reset(); - - if (key.has("id")) - { - mSidepanelObjectInfo->setItemID(key["id"].asUUID()); - } - - if (key.has("object")) - { - mSidepanelObjectInfo->setObjectID(key["object"].asUUID()); - } - - toggleObjectInfoPanel(TRUE); -} - -void LLSidepanelInventory::onInfoButtonClicked() -{ - LLInventoryItem *item = getSelectedItem(); - if (item) - { - mSidepanelObjectInfo->reset(); - mSidepanelObjectInfo->setItemID(item->getUUID()); - toggleObjectInfoPanel(TRUE); - } -} - -void LLSidepanelInventory::onShareButtonClicked() -{ -} - -void LLSidepanelInventory::performActionOnSelection(const std::string &action) -{ - LLInventoryPanel *panel = mPanelMainInventory->getActivePanel(); - LLFolderViewItem* current_item = panel->getRootFolder()->getCurSelectedItem(); - if (!current_item) - { - return; - } - current_item->getListener()->performAction(panel->getRootFolder(), panel->getModel(), action); -} - -void LLSidepanelInventory::onWearButtonClicked() -{ - performActionOnSelection("wear"); - performActionOnSelection("attach"); -} - -void LLSidepanelInventory::onPlayButtonClicked() -{ - performActionOnSelection("activate"); -} - -void LLSidepanelInventory::onTeleportButtonClicked() -{ - performActionOnSelection("teleport"); -} - -void LLSidepanelInventory::onOverflowButtonClicked() -{ -} - -void LLSidepanelInventory::onBackButtonClicked() -{ - toggleObjectInfoPanel(FALSE); - updateVerbs(); -} - -void LLSidepanelInventory::onSelectionChange(const std::deque<LLFolderViewItem*> &items, BOOL user_action) -{ - updateVerbs(); -} - -void LLSidepanelInventory::toggleObjectInfoPanel(BOOL visible) -{ - mSidepanelObjectInfo->setVisible(visible); - mTabContainer->setVisible(!visible); - - if (visible) - { - mSidepanelObjectInfo->reset(); - mSidepanelObjectInfo->setEditMode(FALSE); - - LLRect rect = getRect(); - LLRect new_rect = LLRect(rect.mLeft, rect.mTop, rect.mRight, mTabContainer->getRect().mBottom); - mSidepanelObjectInfo->reshape(new_rect.getWidth(),new_rect.getHeight()); - } -} - -void LLSidepanelInventory::updateVerbs() -{ - mInfoBtn->setEnabled(FALSE); - mShareBtn->setEnabled(FALSE); - - mWearBtn->setVisible(FALSE); - mWearBtn->setEnabled(FALSE); - mPlayBtn->setVisible(FALSE); - mPlayBtn->setEnabled(FALSE); - mTeleportBtn->setVisible(FALSE); - mTeleportBtn->setEnabled(FALSE); - - const LLInventoryItem *item = getSelectedItem(); - if (!item) - return; - - mInfoBtn->setEnabled(TRUE); - mShareBtn->setEnabled(TRUE); - - switch(item->getInventoryType()) - { - case LLInventoryType::IT_WEARABLE: - case LLInventoryType::IT_OBJECT: - case LLInventoryType::IT_ATTACHMENT: - mWearBtn->setVisible(TRUE); - mWearBtn->setEnabled(TRUE); - break; - case LLInventoryType::IT_SOUND: - case LLInventoryType::IT_GESTURE: - case LLInventoryType::IT_ANIMATION: - mPlayBtn->setVisible(TRUE); - mPlayBtn->setEnabled(TRUE); - break; - case LLInventoryType::IT_LANDMARK: - mTeleportBtn->setVisible(TRUE); - mTeleportBtn->setEnabled(TRUE); - break; - default: - break; - } -} - -LLInventoryItem *LLSidepanelInventory::getSelectedItem() -{ - LLFolderViewItem* current_item = mPanelMainInventory->getActivePanel()->getRootFolder()->getCurSelectedItem(); - if (!current_item) - { - return NULL; - } - const LLUUID &item_id = current_item->getListener()->getUUID(); - LLInventoryItem *item = gInventory.getItem(item_id); - return item; -} +/**
+ * @file LLSidepanelInventory.cpp
+ * @brief Side Bar "Inventory" panel
+ *
+ * $LicenseInfo:firstyear=2009&license=viewergpl$
+ *
+ * Copyright (c) 2004-2009, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#include "llviewerprecompiledheaders.h"
+#include "llsidepanelinventory.h"
+
+#include "llagent.h"
+#include "llbutton.h"
+#include "llinventorybridge.h"
+#include "llinventorypanel.h"
+#include "llpanelmaininventory.h"
+#include "llsidepaneliteminfo.h"
+#include "llsidepaneltaskinfo.h"
+#include "lltabcontainer.h"
+#include "llselectmgr.h"
+
+static LLRegisterPanelClassWrapper<LLSidepanelInventory> t_inventory("sidepanel_inventory");
+
+LLSidepanelInventory::LLSidepanelInventory()
+ : LLPanel(),
+ mItemPanel(NULL)
+{
+
+ //LLUICtrlFactory::getInstance()->buildPanel(this, "panel_inventory.xml"); // Called from LLRegisterPanelClass::defaultPanelClassBuilder()
+}
+
+LLSidepanelInventory::~LLSidepanelInventory()
+{
+}
+
+BOOL LLSidepanelInventory::postBuild()
+{
+ // UI elements from inventory panel
+ {
+ mInventoryPanel = getChild<LLPanel>("sidepanel__inventory_panel");
+
+ mInfoBtn = mInventoryPanel->getChild<LLButton>("info_btn");
+ mInfoBtn->setClickedCallback(boost::bind(&LLSidepanelInventory::onInfoButtonClicked, this));
+
+ mShareBtn = mInventoryPanel->getChild<LLButton>("share_btn");
+ mShareBtn->setClickedCallback(boost::bind(&LLSidepanelInventory::onShareButtonClicked, this));
+
+ mWearBtn = mInventoryPanel->getChild<LLButton>("wear_btn");
+ mWearBtn->setClickedCallback(boost::bind(&LLSidepanelInventory::onWearButtonClicked, this));
+
+ mPlayBtn = mInventoryPanel->getChild<LLButton>("play_btn");
+ mPlayBtn->setClickedCallback(boost::bind(&LLSidepanelInventory::onPlayButtonClicked, this));
+
+ mTeleportBtn = mInventoryPanel->getChild<LLButton>("teleport_btn");
+ mTeleportBtn->setClickedCallback(boost::bind(&LLSidepanelInventory::onTeleportButtonClicked, this));
+
+ mOverflowBtn = mInventoryPanel->getChild<LLButton>("overflow_btn");
+ mOverflowBtn->setClickedCallback(boost::bind(&LLSidepanelInventory::onOverflowButtonClicked, this));
+
+ LLPanelMainInventory *panel_main_inventory = mInventoryPanel->getChild<LLPanelMainInventory>("panel_main_inventory");
+ panel_main_inventory->setSelectCallback(boost::bind(&LLSidepanelInventory::onSelectionChange, this, _1, _2));
+ }
+
+ // UI elements from item panel
+ {
+ mItemPanel = getChild<LLSidepanelItemInfo>("sidepanel__item_panel");
+
+ LLButton* back_btn = mItemPanel->getChild<LLButton>("back_btn");
+ back_btn->setClickedCallback(boost::bind(&LLSidepanelInventory::onBackButtonClicked, this));
+ }
+
+ // UI elements from task panel
+ {
+ mTaskPanel = getChild<LLSidepanelTaskInfo>("sidepanel__task_panel");
+ if (mTaskPanel)
+ {
+ LLButton* back_btn = mTaskPanel->getChild<LLButton>("back_btn");
+ back_btn->setClickedCallback(boost::bind(&LLSidepanelInventory::onBackButtonClicked, this));
+ }
+ }
+
+ return TRUE;
+}
+
+void LLSidepanelInventory::onOpen(const LLSD& key)
+{
+ if(key.size() == 0)
+ return;
+
+ mItemPanel->reset();
+
+ if (key.has("id"))
+ {
+ mItemPanel->setItemID(key["id"].asUUID());
+ if (key.has("object"))
+ {
+ mItemPanel->setObjectID(key["object"].asUUID());
+ }
+ showItemInfoPanel();
+ }
+ if (key.has("task"))
+ {
+ if (mTaskPanel)
+ mTaskPanel->setObjectSelection(LLSelectMgr::getInstance()->getSelection());
+ showTaskInfoPanel();
+ }
+}
+
+void LLSidepanelInventory::onInfoButtonClicked()
+{
+ LLInventoryItem *item = getSelectedItem();
+ if (item)
+ {
+ mItemPanel->reset();
+ mItemPanel->setItemID(item->getUUID());
+ showItemInfoPanel();
+ }
+}
+
+void LLSidepanelInventory::onShareButtonClicked()
+{
+}
+
+void LLSidepanelInventory::performActionOnSelection(const std::string &action)
+{
+ LLPanelMainInventory *panel_main_inventory = mInventoryPanel->getChild<LLPanelMainInventory>("panel_main_inventory");
+ LLFolderViewItem* current_item = panel_main_inventory->getActivePanel()->getRootFolder()->getCurSelectedItem();
+ if (!current_item)
+ {
+ return;
+ }
+ current_item->getListener()->performAction(panel_main_inventory->getActivePanel()->getRootFolder(), panel_main_inventory->getActivePanel()->getModel(), action);
+}
+
+void LLSidepanelInventory::onWearButtonClicked()
+{
+ performActionOnSelection("wear");
+ performActionOnSelection("attach");
+}
+
+void LLSidepanelInventory::onPlayButtonClicked()
+{
+ performActionOnSelection("activate");
+}
+
+void LLSidepanelInventory::onTeleportButtonClicked()
+{
+ performActionOnSelection("teleport");
+}
+
+void LLSidepanelInventory::onOverflowButtonClicked()
+{
+}
+
+void LLSidepanelInventory::onBackButtonClicked()
+{
+ showInventoryPanel();
+}
+
+void LLSidepanelInventory::onSelectionChange(const std::deque<LLFolderViewItem*> &items, BOOL user_action)
+{
+ updateVerbs();
+}
+
+void LLSidepanelInventory::showItemInfoPanel()
+{
+ mItemPanel->setVisible(TRUE);
+ if (mTaskPanel)
+ mTaskPanel->setVisible(FALSE);
+ mInventoryPanel->setVisible(FALSE);
+
+ mItemPanel->dirty();
+ mItemPanel->setIsEditing(FALSE);
+}
+
+void LLSidepanelInventory::showTaskInfoPanel()
+{
+ mItemPanel->setVisible(FALSE);
+ mInventoryPanel->setVisible(FALSE);
+
+ if (mTaskPanel)
+ {
+ mTaskPanel->setVisible(TRUE);
+ mTaskPanel->dirty();
+ mTaskPanel->setIsEditing(FALSE);
+ }
+}
+
+void LLSidepanelInventory::showInventoryPanel()
+{
+ mItemPanel->setVisible(FALSE);
+ if (mTaskPanel)
+ mTaskPanel->setVisible(FALSE);
+ mInventoryPanel->setVisible(TRUE);
+ updateVerbs();
+}
+
+void LLSidepanelInventory::updateVerbs()
+{
+ mInfoBtn->setEnabled(FALSE);
+ mShareBtn->setEnabled(FALSE);
+
+ mWearBtn->setVisible(FALSE);
+ mWearBtn->setEnabled(FALSE);
+ mPlayBtn->setVisible(FALSE);
+ mPlayBtn->setEnabled(FALSE);
+ mTeleportBtn->setVisible(FALSE);
+ mTeleportBtn->setEnabled(FALSE);
+
+ const LLInventoryItem *item = getSelectedItem();
+ if (!item)
+ return;
+
+ mInfoBtn->setEnabled(TRUE);
+ mShareBtn->setEnabled(TRUE);
+
+ switch(item->getInventoryType())
+ {
+ case LLInventoryType::IT_WEARABLE:
+ case LLInventoryType::IT_OBJECT:
+ case LLInventoryType::IT_ATTACHMENT:
+ mWearBtn->setVisible(TRUE);
+ mWearBtn->setEnabled(TRUE);
+ break;
+ case LLInventoryType::IT_SOUND:
+ case LLInventoryType::IT_GESTURE:
+ case LLInventoryType::IT_ANIMATION:
+ mPlayBtn->setVisible(TRUE);
+ mPlayBtn->setEnabled(TRUE);
+ break;
+ case LLInventoryType::IT_LANDMARK:
+ mTeleportBtn->setVisible(TRUE);
+ mTeleportBtn->setEnabled(TRUE);
+ break;
+ default:
+ break;
+ }
+}
+
+LLInventoryItem *LLSidepanelInventory::getSelectedItem()
+{
+ LLPanelMainInventory *panel_main_inventory = mInventoryPanel->getChild<LLPanelMainInventory>("panel_main_inventory");
+ LLFolderViewItem* current_item = panel_main_inventory->getActivePanel()->getRootFolder()->getCurSelectedItem();
+ if (!current_item)
+ {
+ return NULL;
+ }
+ const LLUUID &item_id = current_item->getListener()->getUUID();
+ LLInventoryItem *item = gInventory.getItem(item_id);
+ return item;
+}
diff --git a/indra/newview/llsidepanelinventory.h b/indra/newview/llsidepanelinventory.h index 62eeecc5e2..681af7fafa 100644 --- a/indra/newview/llsidepanelinventory.h +++ b/indra/newview/llsidepanelinventory.h @@ -1,80 +1,90 @@ -/** - * @file LLSidepanelInventory.h - * @brief Side Bar "Inventory" panel - * - * $LicenseInfo:firstyear=2009&license=viewergpl$ - * - * Copyright (c) 2004-2009, Linden Research, Inc. - * - * Second Life Viewer Source Code - * The source code in this file ("Source Code") is provided by Linden Lab - * to you under the terms of the GNU General Public License, version 2.0 - * ("GPL"), unless you have obtained a separate licensing agreement - * ("Other License"), formally executed by you and Linden Lab. Terms of - * the GPL can be found in doc/GPL-license.txt in this distribution, or - * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 - * - * There are special exceptions to the terms and conditions of the GPL as - * it is applied to this Source Code. View the full text of the exception - * in the file doc/FLOSS-exception.txt in this software distribution, or - * online at http://secondlifegrid.net/programs/open_source/licensing/flossexception - * - * By copying, modifying or distributing this software, you acknowledge - * that you have read and understood your obligations described above, - * and agree to abide by those obligations. - * - * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO - * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, - * COMPLETENESS OR PERFORMANCE. - * $/LicenseInfo$ - */ - -#ifndef LL_LLSIDEPANELINVENTORY_H -#define LL_LLSIDEPANELINVENTORY_H - -#include "llpanel.h" - -class LLInventoryItem; -class LLSidepanelObjectInfo; -class LLTabContainer; -class LLPanelMainInventory; -class LLFolderViewItem; - -class LLSidepanelInventory : public LLPanel -{ -public: - LLSidepanelInventory(); - virtual ~LLSidepanelInventory(); - - /*virtual*/ BOOL postBuild(); - /*virtual*/ void onOpen(const LLSD& key); - -protected: - LLInventoryItem *getSelectedItem(); - void onSelectionChange(const std::deque<LLFolderViewItem*> &items, BOOL user_action); - void onTabSelected(); - void toggleObjectInfoPanel(BOOL visible); - void updateVerbs(); - void performActionOnSelection(const std::string &action); - - LLTabContainer* mTabContainer; - LLSidepanelObjectInfo* mSidepanelObjectInfo; - LLPanelMainInventory* mPanelMainInventory; - - void onInfoButtonClicked(); - void onShareButtonClicked(); - void onWearButtonClicked(); - void onPlayButtonClicked(); - void onTeleportButtonClicked(); - void onOverflowButtonClicked(); - void onBackButtonClicked(); - - LLButton* mInfoBtn; - LLButton* mShareBtn; - LLButton* mWearBtn; - LLButton* mPlayBtn; - LLButton* mTeleportBtn; - LLButton* mOverflowBtn; -}; - -#endif //LL_LLSIDEPANELINVENTORY_H +/**
+ * @file LLSidepanelInventory.h
+ * @brief Side Bar "Inventory" panel
+ *
+ * $LicenseInfo:firstyear=2009&license=viewergpl$
+ *
+ * Copyright (c) 2004-2009, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLSIDEPANELINVENTORY_H
+#define LL_LLSIDEPANELINVENTORY_H
+
+#include "llpanel.h"
+
+class LLFolderViewItem;
+class LLInventoryItem;
+class LLPanelMainInventory;
+class LLSidepanelItemInfo;
+class LLSidepanelTaskInfo;
+
+class LLSidepanelInventory : public LLPanel
+{
+public:
+ LLSidepanelInventory();
+ virtual ~LLSidepanelInventory();
+
+ /*virtual*/ BOOL postBuild();
+ /*virtual*/ void onOpen(const LLSD& key);
+
+protected:
+ // Tracks highlighted (selected) item in inventory panel.
+ LLInventoryItem *getSelectedItem();
+ void onSelectionChange(const std::deque<LLFolderViewItem*> &items, BOOL user_action);
+ // "wear", "teleport", etc.
+ void performActionOnSelection(const std::string &action);
+
+ void showItemInfoPanel();
+ void showTaskInfoPanel();
+ void showInventoryPanel();
+ void updateVerbs();
+
+ //
+ // UI Elements
+ //
+private:
+ LLPanel* mInventoryPanel; // Main inventory view
+ LLSidepanelItemInfo* mItemPanel; // Individual item view
+ LLSidepanelTaskInfo* mTaskPanel; // Individual in-world object view
+
+protected:
+ void onInfoButtonClicked();
+ void onShareButtonClicked();
+ void onWearButtonClicked();
+ void onPlayButtonClicked();
+ void onTeleportButtonClicked();
+ void onOverflowButtonClicked();
+ void onBackButtonClicked();
+private:
+ LLButton* mInfoBtn;
+ LLButton* mShareBtn;
+ LLButton* mWearBtn;
+ LLButton* mPlayBtn;
+ LLButton* mTeleportBtn;
+ LLButton* mOverflowBtn;
+
+};
+
+#endif //LL_LLSIDEPANELINVENTORY_H
diff --git a/indra/newview/llsidepanelinventorysubpanel.cpp b/indra/newview/llsidepanelinventorysubpanel.cpp new file mode 100644 index 0000000000..162198f1c5 --- /dev/null +++ b/indra/newview/llsidepanelinventorysubpanel.cpp @@ -0,0 +1,155 @@ +/**
+ * @file llsidepanelinventorysubpanel.cpp
+ * @brief A floater which shows an inventory item's properties.
+ *
+ * $LicenseInfo:firstyear=2002&license=viewergpl$
+ *
+ * Copyright (c) 2002-2009, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#include "llviewerprecompiledheaders.h"
+#include "llsidepanelinventorysubpanel.h"
+
+#include "roles_constants.h"
+
+#include "llagent.h"
+#include "llavataractions.h"
+#include "llbutton.h"
+#include "llfloaterreg.h"
+#include "llgroupactions.h"
+#include "llinventorymodel.h"
+#include "lllineeditor.h"
+#include "llradiogroup.h"
+#include "llviewercontrol.h"
+#include "llviewerinventory.h"
+#include "llviewerobjectlist.h"
+
+
+///----------------------------------------------------------------------------
+/// Class LLSidepanelInventorySubpanel
+///----------------------------------------------------------------------------
+
+// Default constructor
+LLSidepanelInventorySubpanel::LLSidepanelInventorySubpanel()
+ : LLPanel(),
+ mIsDirty(TRUE),
+ mIsEditing(FALSE),
+ mEditBtn(NULL),
+ mCancelBtn(NULL),
+ mSaveBtn(NULL)
+{
+}
+
+// Destroys the object
+LLSidepanelInventorySubpanel::~LLSidepanelInventorySubpanel()
+{
+}
+
+// virtual
+BOOL LLSidepanelInventorySubpanel::postBuild()
+{
+ mEditBtn = getChild<LLButton>("edit_btn");
+ mEditBtn->setClickedCallback(boost::bind(&LLSidepanelInventorySubpanel::onEditButtonClicked, this));
+
+ mSaveBtn = getChild<LLButton>("save_btn");
+ mSaveBtn->setClickedCallback(boost::bind(&LLSidepanelInventorySubpanel::onSaveButtonClicked, this));
+
+ mCancelBtn = getChild<LLButton>("cancel_btn");
+ mCancelBtn->setClickedCallback(boost::bind(&LLSidepanelInventorySubpanel::onCancelButtonClicked, this));
+ return TRUE;
+}
+
+void LLSidepanelInventorySubpanel::setVisible(BOOL visible)
+{
+ if (visible)
+ {
+ dirty();
+ }
+ LLPanel::setVisible(visible);
+}
+
+void LLSidepanelInventorySubpanel::setIsEditing(BOOL edit)
+{
+ mIsEditing = edit;
+ mIsDirty = TRUE;
+}
+
+BOOL LLSidepanelInventorySubpanel::getIsEditing() const
+{
+ return mIsEditing;
+}
+
+void LLSidepanelInventorySubpanel::reset()
+{
+ mIsDirty = TRUE;
+}
+
+void LLSidepanelInventorySubpanel::draw()
+{
+ if (mIsDirty)
+ {
+ mIsDirty = FALSE;
+ refresh();
+ updateVerbs();
+ }
+
+ LLPanel::draw();
+}
+
+void LLSidepanelInventorySubpanel::dirty()
+{
+ mIsDirty = TRUE;
+ setIsEditing(FALSE);
+}
+
+void LLSidepanelInventorySubpanel::updateVerbs()
+{
+ mEditBtn->setVisible(!mIsEditing);
+ mSaveBtn->setVisible(mIsEditing);
+ mCancelBtn->setVisible(mIsEditing);
+}
+
+void LLSidepanelInventorySubpanel::onEditButtonClicked()
+{
+ setIsEditing(TRUE);
+ refresh();
+ updateVerbs();
+}
+
+void LLSidepanelInventorySubpanel::onSaveButtonClicked()
+{
+ save();
+ setIsEditing(FALSE);
+ refresh();
+ updateVerbs();
+}
+
+void LLSidepanelInventorySubpanel::onCancelButtonClicked()
+{
+ setIsEditing(FALSE);
+ refresh();
+ updateVerbs();
+}
diff --git a/indra/newview/llsidepanelinventorysubpanel.h b/indra/newview/llsidepanelinventorysubpanel.h new file mode 100644 index 0000000000..6503887cd1 --- /dev/null +++ b/indra/newview/llsidepanelinventorysubpanel.h @@ -0,0 +1,82 @@ +/**
+ * @file llsidepanelinventorysubpanel.h
+ * @brief A panel which shows an inventory item's properties.
+ *
+ * $LicenseInfo:firstyear=2002&license=viewergpl$
+ *
+ * Copyright (c) 2002-2009, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLSIDEPANELINVENTORYSUBPANEL_H
+#define LL_LLSIDEPANELINVENTORYSUBPANEL_H
+
+#include "llpanel.h"
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLSidepanelInventorySubpanel
+// Base class for inventory sidepanel panels (e.g. item info, task info).
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+class LLButton;
+class LLInventoryItem;
+
+class LLSidepanelInventorySubpanel : public LLPanel
+{
+public:
+ LLSidepanelInventorySubpanel();
+ virtual ~LLSidepanelInventorySubpanel();
+
+ /*virtual*/ void setVisible(BOOL visible);
+ virtual BOOL postBuild();
+ virtual void draw();
+ virtual void reset();
+
+ void dirty();
+ void setIsEditing(BOOL edit);
+protected:
+ virtual void refresh() = 0;
+ virtual void save() = 0;
+ virtual void updateVerbs();
+
+ BOOL getIsEditing() const;
+
+ //
+ // UI Elements
+ //
+protected:
+ void onEditButtonClicked();
+ void onSaveButtonClicked();
+ void onCancelButtonClicked();
+ LLButton* mEditBtn;
+ LLButton* mSaveBtn;
+ LLButton* mCancelBtn;
+
+private:
+ BOOL mIsDirty; // item properties need to be updated
+ BOOL mIsEditing; // if we're in edit mode
+};
+
+#endif // LL_LLSIDEPANELINVENTORYSUBPANEL_H
diff --git a/indra/newview/llsidepaneliteminfo.cpp b/indra/newview/llsidepaneliteminfo.cpp new file mode 100644 index 0000000000..9d2960fbed --- /dev/null +++ b/indra/newview/llsidepaneliteminfo.cpp @@ -0,0 +1,882 @@ +/**
+ * @file llsidepaneliteminfo.cpp
+ * @brief A floater which shows an inventory item's properties.
+ *
+ * $LicenseInfo:firstyear=2002&license=viewergpl$
+ *
+ * Copyright (c) 2002-2009, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#include "llviewerprecompiledheaders.h"
+#include "llsidepaneliteminfo.h"
+
+#include "roles_constants.h"
+
+#include "llagent.h"
+#include "llavataractions.h"
+#include "llbutton.h"
+#include "llfloaterreg.h"
+#include "llgroupactions.h"
+#include "llinventorymodel.h"
+#include "lllineeditor.h"
+#include "llradiogroup.h"
+#include "llviewercontrol.h"
+#include "llviewerinventory.h"
+#include "llviewerobjectlist.h"
+
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLItemPropertiesObserver
+//
+// Helper class to watch for changes to the item.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+class LLItemPropertiesObserver : public LLInventoryObserver
+{
+public:
+ LLItemPropertiesObserver(LLSidepanelItemInfo* floater)
+ : mFloater(floater)
+ {
+ gInventory.addObserver(this);
+ }
+ virtual ~LLItemPropertiesObserver()
+ {
+ gInventory.removeObserver(this);
+ }
+ virtual void changed(U32 mask);
+private:
+ LLSidepanelItemInfo* mFloater;
+};
+
+void LLItemPropertiesObserver::changed(U32 mask)
+{
+ // if there's a change we're interested in.
+ if((mask & (LLInventoryObserver::LABEL | LLInventoryObserver::INTERNAL | LLInventoryObserver::REMOVE)) != 0)
+ {
+ mFloater->dirty();
+ }
+}
+
+
+
+///----------------------------------------------------------------------------
+/// Class LLSidepanelItemInfo
+///----------------------------------------------------------------------------
+
+static LLRegisterPanelClassWrapper<LLSidepanelItemInfo> t_item_info("sidepanel_item_info");
+
+// Default constructor
+LLSidepanelItemInfo::LLSidepanelItemInfo()
+ : mItemID(LLUUID::null)
+{
+ mPropertiesObserver = new LLItemPropertiesObserver(this);
+
+ //LLUICtrlFactory::getInstance()->buildFloater(this,"floater_inventory_item_properties.xml");
+}
+
+// Destroys the object
+LLSidepanelItemInfo::~LLSidepanelItemInfo()
+{
+ delete mPropertiesObserver;
+ mPropertiesObserver = NULL;
+}
+
+// virtual
+BOOL LLSidepanelItemInfo::postBuild()
+{
+ LLSidepanelInventorySubpanel::postBuild();
+
+ // build the UI
+ // item name & description
+ childSetPrevalidate("LabelItemName",&LLLineEditor::prevalidatePrintableNotPipe);
+ //getChild<LLUICtrl>("LabelItemName")->setCommitCallback(boost::bind(&LLSidepanelItemInfo::onCommitName,this));
+ childSetPrevalidate("LabelItemDesc",&LLLineEditor::prevalidatePrintableNotPipe);
+ //getChild<LLUICtrl>("LabelItemDesc")->setCommitCallback(boost::bind(&LLSidepanelItemInfo:: onCommitDescription, this));
+
+ // Creator information
+ getChild<LLUICtrl>("BtnCreator")->setCommitCallback(boost::bind(&LLSidepanelItemInfo::onClickCreator,this));
+
+ // owner information
+ getChild<LLUICtrl>("BtnOwner")->setCommitCallback(boost::bind(&LLSidepanelItemInfo::onClickOwner,this));
+
+ refresh();
+ return TRUE;
+}
+
+void LLSidepanelItemInfo::setObjectID(const LLUUID& object_id)
+{
+ mObjectID = object_id;
+}
+
+void LLSidepanelItemInfo::setItemID(const LLUUID& item_id)
+{
+ mItemID = item_id;
+}
+
+void LLSidepanelItemInfo::reset()
+{
+ LLSidepanelInventorySubpanel::reset();
+
+ mObjectID = LLUUID::null;
+ mItemID = LLUUID::null;
+}
+
+void LLSidepanelItemInfo::refresh()
+{
+ LLInventoryItem* item = findItem();
+ if(item)
+ {
+ refreshFromItem(item);
+ updateVerbs();
+ }
+ else
+ {
+ if (getIsEditing())
+ {
+ setIsEditing(FALSE);
+ return;
+ }
+ }
+
+ if (!getIsEditing())
+ {
+ const std::string no_item_names[]={
+ "LabelItemName",
+ "LabelItemDesc",
+ "LabelCreatorName",
+ "LabelOwnerName",
+ "CheckOwnerModify",
+ "CheckOwnerCopy",
+ "CheckOwnerTransfer",
+ "CheckShareWithGroup",
+ "CheckEveryoneCopy",
+ "CheckNextOwnerModify",
+ "CheckNextOwnerCopy",
+ "CheckNextOwnerTransfer",
+ "CheckPurchase",
+ "RadioSaleType",
+ "Edit Cost"
+ };
+
+ for(size_t t=0; t<LL_ARRAY_SIZE(no_item_names); ++t)
+ {
+ childSetEnabled(no_item_names[t],false);
+ }
+
+ const std::string hide_names[]={
+ "BaseMaskDebug",
+ "OwnerMaskDebug",
+ "GroupMaskDebug",
+ "EveryoneMaskDebug",
+ "NextMaskDebug"
+ };
+ for(size_t t=0; t<LL_ARRAY_SIZE(hide_names); ++t)
+ {
+ childSetVisible(hide_names[t],false);
+ }
+ }
+
+ if (!item)
+ {
+ const std::string no_edit_mode_names[]={
+ "BtnCreator",
+ "BtnOwner",
+ };
+ for(size_t t=0; t<LL_ARRAY_SIZE(no_edit_mode_names); ++t)
+ {
+ childSetEnabled(no_edit_mode_names[t],false);
+ }
+ }
+
+ updateVerbs();
+}
+
+void LLSidepanelItemInfo::refreshFromItem(LLInventoryItem* item)
+{
+ ////////////////////////
+ // PERMISSIONS LOOKUP //
+ ////////////////////////
+
+ // do not enable the UI for incomplete items.
+ LLViewerInventoryItem* i = (LLViewerInventoryItem*)item;
+ BOOL is_complete = i->isComplete();
+ const BOOL cannot_restrict_permissions = LLInventoryType::cannotRestrictPermissions(i->getInventoryType());
+ const BOOL is_calling_card = (i->getInventoryType() == LLInventoryType::IT_CALLINGCARD);
+ const LLPermissions& perm = item->getPermissions();
+ const BOOL can_agent_manipulate = gAgent.allowOperation(PERM_OWNER, perm,
+ GP_OBJECT_MANIPULATE);
+ const BOOL can_agent_sell = gAgent.allowOperation(PERM_OWNER, perm,
+ GP_OBJECT_SET_SALE) &&
+ !cannot_restrict_permissions;
+ const BOOL is_link = i->getIsLinkType();
+
+ // You need permission to modify the object to modify an inventory
+ // item in it.
+ LLViewerObject* object = NULL;
+ if(!mObjectID.isNull()) object = gObjectList.findObject(mObjectID);
+ BOOL is_obj_modify = TRUE;
+ if(object)
+ {
+ is_obj_modify = object->permOwnerModify();
+ }
+
+ //////////////////////
+ // ITEM NAME & DESC //
+ //////////////////////
+ BOOL is_modifiable = gAgent.allowOperation(PERM_MODIFY, perm,
+ GP_OBJECT_MANIPULATE)
+ && is_obj_modify && is_complete;
+
+ childSetEnabled("LabelItemNameTitle",TRUE);
+ childSetEnabled("LabelItemName",is_modifiable && !is_calling_card); // for now, don't allow rename of calling cards
+ childSetText("LabelItemName",item->getName());
+ childSetEnabled("LabelItemDescTitle",TRUE);
+ childSetEnabled("LabelItemDesc",is_modifiable);
+ childSetVisible("IconLocked",!is_modifiable);
+ childSetText("LabelItemDesc",item->getDescription());
+
+ //////////////////
+ // CREATOR NAME //
+ //////////////////
+ if(!gCacheName) return;
+ if(!gAgent.getRegion()) return;
+
+ if (item->getCreatorUUID().notNull())
+ {
+ std::string name;
+ gCacheName->getFullName(item->getCreatorUUID(), name);
+ childSetEnabled("BtnCreator",TRUE);
+ childSetEnabled("LabelCreatorTitle",TRUE);
+ childSetEnabled("LabelCreatorName",TRUE);
+ childSetText("LabelCreatorName",name);
+ }
+ else
+ {
+ childSetEnabled("BtnCreator",FALSE);
+ childSetEnabled("LabelCreatorTitle",FALSE);
+ childSetEnabled("LabelCreatorName",FALSE);
+ childSetText("LabelCreatorName",getString("unknown"));
+ }
+
+ ////////////////
+ // OWNER NAME //
+ ////////////////
+ if(perm.isOwned())
+ {
+ std::string name;
+ if (perm.isGroupOwned())
+ {
+ gCacheName->getGroupName(perm.getGroup(), name);
+ }
+ else
+ {
+ gCacheName->getFullName(perm.getOwner(), name);
+ }
+ childSetEnabled("BtnOwner",TRUE);
+ childSetEnabled("LabelOwnerTitle",TRUE);
+ childSetEnabled("LabelOwnerName",TRUE);
+ childSetText("LabelOwnerName",name);
+ }
+ else
+ {
+ childSetEnabled("BtnOwner",FALSE);
+ childSetEnabled("LabelOwnerTitle",FALSE);
+ childSetEnabled("LabelOwnerName",FALSE);
+ childSetText("LabelOwnerName",getString("public"));
+ }
+
+ //////////////////
+ // ACQUIRE DATE //
+ //////////////////
+
+ time_t time_utc = item->getCreationDate();
+ if (0 == time_utc)
+ {
+ childSetText("LabelAcquiredDate",getString("unknown"));
+ }
+ else
+ {
+ std::string timeStr = getString("acquiredDate");
+ LLSD substitution;
+ substitution["datetime"] = (S32) time_utc;
+ LLStringUtil::format (timeStr, substitution);
+ childSetText ("LabelAcquiredDate", timeStr);
+ }
+
+ ///////////////////////
+ // OWNER PERMISSIONS //
+ ///////////////////////
+ if(can_agent_manipulate)
+ {
+ childSetText("OwnerLabel",getString("you_can"));
+ }
+ else
+ {
+ childSetText("OwnerLabel",getString("owner_can"));
+ }
+
+ U32 base_mask = perm.getMaskBase();
+ U32 owner_mask = perm.getMaskOwner();
+ U32 group_mask = perm.getMaskGroup();
+ U32 everyone_mask = perm.getMaskEveryone();
+ U32 next_owner_mask = perm.getMaskNextOwner();
+
+ childSetEnabled("OwnerLabel",TRUE);
+ childSetEnabled("CheckOwnerModify",FALSE);
+ childSetValue("CheckOwnerModify",LLSD((BOOL)(owner_mask & PERM_MODIFY)));
+ childSetEnabled("CheckOwnerCopy",FALSE);
+ childSetValue("CheckOwnerCopy",LLSD((BOOL)(owner_mask & PERM_COPY)));
+ childSetEnabled("CheckOwnerTransfer",FALSE);
+ childSetValue("CheckOwnerTransfer",LLSD((BOOL)(owner_mask & PERM_TRANSFER)));
+
+ ///////////////////////
+ // DEBUG PERMISSIONS //
+ ///////////////////////
+
+ if( gSavedSettings.getBOOL("DebugPermissions") )
+ {
+ BOOL slam_perm = FALSE;
+ BOOL overwrite_group = FALSE;
+ BOOL overwrite_everyone = FALSE;
+
+ if (item->getType() == LLAssetType::AT_OBJECT)
+ {
+ U32 flags = item->getFlags();
+ slam_perm = flags & LLInventoryItem::II_FLAGS_OBJECT_SLAM_PERM;
+ overwrite_everyone = flags & LLInventoryItem::II_FLAGS_OBJECT_PERM_OVERWRITE_EVERYONE;
+ overwrite_group = flags & LLInventoryItem::II_FLAGS_OBJECT_PERM_OVERWRITE_GROUP;
+ }
+
+ std::string perm_string;
+
+ perm_string = "B: ";
+ perm_string += mask_to_string(base_mask);
+ childSetText("BaseMaskDebug",perm_string);
+ childSetVisible("BaseMaskDebug",TRUE);
+
+ perm_string = "O: ";
+ perm_string += mask_to_string(owner_mask);
+ childSetText("OwnerMaskDebug",perm_string);
+ childSetVisible("OwnerMaskDebug",TRUE);
+
+ perm_string = "G";
+ perm_string += overwrite_group ? "*: " : ": ";
+ perm_string += mask_to_string(group_mask);
+ childSetText("GroupMaskDebug",perm_string);
+ childSetVisible("GroupMaskDebug",TRUE);
+
+ perm_string = "E";
+ perm_string += overwrite_everyone ? "*: " : ": ";
+ perm_string += mask_to_string(everyone_mask);
+ childSetText("EveryoneMaskDebug",perm_string);
+ childSetVisible("EveryoneMaskDebug",TRUE);
+
+ perm_string = "N";
+ perm_string += slam_perm ? "*: " : ": ";
+ perm_string += mask_to_string(next_owner_mask);
+ childSetText("NextMaskDebug",perm_string);
+ childSetVisible("NextMaskDebug",TRUE);
+ }
+ else
+ {
+ childSetVisible("BaseMaskDebug",FALSE);
+ childSetVisible("OwnerMaskDebug",FALSE);
+ childSetVisible("GroupMaskDebug",FALSE);
+ childSetVisible("EveryoneMaskDebug",FALSE);
+ childSetVisible("NextMaskDebug",FALSE);
+ }
+
+ /////////////
+ // SHARING //
+ /////////////
+
+ // Check for ability to change values.
+ if (is_link || cannot_restrict_permissions)
+ {
+ childSetEnabled("CheckShareWithGroup",FALSE);
+ childSetEnabled("CheckEveryoneCopy",FALSE);
+ }
+ else if (is_obj_modify && can_agent_manipulate)
+ {
+ childSetEnabled("CheckShareWithGroup",TRUE);
+ childSetEnabled("CheckEveryoneCopy",(owner_mask & PERM_COPY) && (owner_mask & PERM_TRANSFER));
+ }
+ else
+ {
+ childSetEnabled("CheckShareWithGroup",FALSE);
+ childSetEnabled("CheckEveryoneCopy",FALSE);
+ }
+
+ // Set values.
+ BOOL is_group_copy = (group_mask & PERM_COPY) ? TRUE : FALSE;
+ BOOL is_group_modify = (group_mask & PERM_MODIFY) ? TRUE : FALSE;
+ BOOL is_group_move = (group_mask & PERM_MOVE) ? TRUE : FALSE;
+
+ if (is_group_copy && is_group_modify && is_group_move)
+ {
+ childSetValue("CheckShareWithGroup",LLSD((BOOL)TRUE));
+
+ LLCheckBoxCtrl* ctl = getChild<LLCheckBoxCtrl>("CheckShareWithGroup");
+ if(ctl)
+ {
+ ctl->setTentative(FALSE);
+ }
+ }
+ else if (!is_group_copy && !is_group_modify && !is_group_move)
+ {
+ childSetValue("CheckShareWithGroup",LLSD((BOOL)FALSE));
+ LLCheckBoxCtrl* ctl = getChild<LLCheckBoxCtrl>("CheckShareWithGroup");
+ if(ctl)
+ {
+ ctl->setTentative(FALSE);
+ }
+ }
+ else
+ {
+ LLCheckBoxCtrl* ctl = getChild<LLCheckBoxCtrl>("CheckShareWithGroup");
+ if(ctl)
+ {
+ ctl->setTentative(TRUE);
+ ctl->set(TRUE);
+ }
+ }
+
+ childSetValue("CheckEveryoneCopy",LLSD((BOOL)(everyone_mask & PERM_COPY)));
+
+ ///////////////
+ // SALE INFO //
+ ///////////////
+
+ const LLSaleInfo& sale_info = item->getSaleInfo();
+ BOOL is_for_sale = sale_info.isForSale();
+ // Check for ability to change values.
+ if (is_obj_modify && can_agent_sell
+ && gAgent.allowOperation(PERM_TRANSFER, perm, GP_OBJECT_MANIPULATE))
+ {
+ childSetEnabled("SaleLabel",is_complete);
+ childSetEnabled("CheckPurchase",is_complete);
+
+ childSetEnabled("NextOwnerLabel",TRUE);
+ childSetEnabled("CheckNextOwnerModify",(base_mask & PERM_MODIFY) && !cannot_restrict_permissions);
+ childSetEnabled("CheckNextOwnerCopy",(base_mask & PERM_COPY) && !cannot_restrict_permissions);
+ childSetEnabled("CheckNextOwnerTransfer",(next_owner_mask & PERM_COPY) && !cannot_restrict_permissions);
+
+ childSetEnabled("RadioSaleType",is_complete && is_for_sale);
+ childSetEnabled("TextPrice",is_complete && is_for_sale);
+ childSetEnabled("Edit Cost",is_complete && is_for_sale);
+ }
+ else
+ {
+ childSetEnabled("SaleLabel",FALSE);
+ childSetEnabled("CheckPurchase",FALSE);
+
+ childSetEnabled("NextOwnerLabel",FALSE);
+ childSetEnabled("CheckNextOwnerModify",FALSE);
+ childSetEnabled("CheckNextOwnerCopy",FALSE);
+ childSetEnabled("CheckNextOwnerTransfer",FALSE);
+
+ childSetEnabled("RadioSaleType",FALSE);
+ childSetEnabled("TextPrice",FALSE);
+ childSetEnabled("Edit Cost",FALSE);
+ }
+
+ // Set values.
+ childSetValue("CheckPurchase", is_for_sale);
+ childSetEnabled("combobox sale copy", is_for_sale);
+ childSetEnabled("Edit Cost", is_for_sale);
+ childSetValue("CheckNextOwnerModify",LLSD(BOOL(next_owner_mask & PERM_MODIFY)));
+ childSetValue("CheckNextOwnerCopy",LLSD(BOOL(next_owner_mask & PERM_COPY)));
+ childSetValue("CheckNextOwnerTransfer",LLSD(BOOL(next_owner_mask & PERM_TRANSFER)));
+
+ LLRadioGroup* radioSaleType = getChild<LLRadioGroup>("RadioSaleType");
+ if (is_for_sale)
+ {
+ radioSaleType->setSelectedIndex((S32)sale_info.getSaleType() - 1);
+ S32 numerical_price;
+ numerical_price = sale_info.getSalePrice();
+ childSetText("Edit Cost",llformat("%d",numerical_price));
+ }
+ else
+ {
+ radioSaleType->setSelectedIndex(-1);
+ childSetText("Edit Cost",llformat("%d",0));
+ }
+}
+
+void LLSidepanelItemInfo::onClickCreator()
+{
+ LLInventoryItem* item = findItem();
+ if(!item) return;
+ if(!item->getCreatorUUID().isNull())
+ {
+ LLAvatarActions::showProfile(item->getCreatorUUID());
+ }
+}
+
+// static
+void LLSidepanelItemInfo::onClickOwner()
+{
+ LLInventoryItem* item = findItem();
+ if(!item) return;
+ if(item->getPermissions().isGroupOwned())
+ {
+ LLGroupActions::show(item->getPermissions().getGroup());
+ }
+ else
+ {
+ LLAvatarActions::showProfile(item->getPermissions().getOwner());
+ }
+}
+
+// static
+void LLSidepanelItemInfo::onCommitName()
+{
+ //llinfos << "LLSidepanelItemInfo::onCommitName()" << llendl;
+ LLViewerInventoryItem* item = (LLViewerInventoryItem*)findItem();
+ if(!item)
+ {
+ return;
+ }
+ LLLineEditor* labelItemName = getChild<LLLineEditor>("LabelItemName");
+
+ if(labelItemName&&
+ (item->getName() != labelItemName->getText()) &&
+ (gAgent.allowOperation(PERM_MODIFY, item->getPermissions(), GP_OBJECT_MANIPULATE)) )
+ {
+ LLPointer<LLViewerInventoryItem> new_item = new LLViewerInventoryItem(item);
+ new_item->rename(labelItemName->getText());
+ if(mObjectID.isNull())
+ {
+ new_item->updateServer(FALSE);
+ gInventory.updateItem(new_item);
+ gInventory.notifyObservers();
+ }
+ else
+ {
+ LLViewerObject* object = gObjectList.findObject(mObjectID);
+ if(object)
+ {
+ object->updateInventory(
+ new_item,
+ TASK_INVENTORY_ITEM_KEY,
+ false);
+ }
+ }
+ }
+}
+
+void LLSidepanelItemInfo::onCommitDescription()
+{
+ //llinfos << "LLSidepanelItemInfo::onCommitDescription()" << llendl;
+ LLViewerInventoryItem* item = (LLViewerInventoryItem*)findItem();
+ if(!item) return;
+
+ LLLineEditor* labelItemDesc = getChild<LLLineEditor>("LabelItemDesc");
+ if(!labelItemDesc)
+ {
+ return;
+ }
+ if((item->getDescription() != labelItemDesc->getText()) &&
+ (gAgent.allowOperation(PERM_MODIFY, item->getPermissions(), GP_OBJECT_MANIPULATE)))
+ {
+ LLPointer<LLViewerInventoryItem> new_item = new LLViewerInventoryItem(item);
+
+ new_item->setDescription(labelItemDesc->getText());
+ if(mObjectID.isNull())
+ {
+ new_item->updateServer(FALSE);
+ gInventory.updateItem(new_item);
+ gInventory.notifyObservers();
+ }
+ else
+ {
+ LLViewerObject* object = gObjectList.findObject(mObjectID);
+ if(object)
+ {
+ object->updateInventory(
+ new_item,
+ TASK_INVENTORY_ITEM_KEY,
+ false);
+ }
+ }
+ }
+}
+
+// static
+void LLSidepanelItemInfo::onCommitPermissions()
+{
+ //llinfos << "LLSidepanelItemInfo::onCommitPermissions()" << llendl;
+ LLViewerInventoryItem* item = (LLViewerInventoryItem*)findItem();
+ if(!item) return;
+ LLPermissions perm(item->getPermissions());
+
+
+ LLCheckBoxCtrl* CheckShareWithGroup = getChild<LLCheckBoxCtrl>("CheckShareWithGroup");
+
+ if(CheckShareWithGroup)
+ {
+ perm.setGroupBits(gAgent.getID(), gAgent.getGroupID(),
+ CheckShareWithGroup->get(),
+ PERM_MODIFY | PERM_MOVE | PERM_COPY);
+ }
+ LLCheckBoxCtrl* CheckEveryoneCopy = getChild<LLCheckBoxCtrl>("CheckEveryoneCopy");
+ if(CheckEveryoneCopy)
+ {
+ perm.setEveryoneBits(gAgent.getID(), gAgent.getGroupID(),
+ CheckEveryoneCopy->get(), PERM_COPY);
+ }
+
+ LLCheckBoxCtrl* CheckNextOwnerModify = getChild<LLCheckBoxCtrl>("CheckNextOwnerModify");
+ if(CheckNextOwnerModify)
+ {
+ perm.setNextOwnerBits(gAgent.getID(), gAgent.getGroupID(),
+ CheckNextOwnerModify->get(), PERM_MODIFY);
+ }
+ LLCheckBoxCtrl* CheckNextOwnerCopy = getChild<LLCheckBoxCtrl>("CheckNextOwnerCopy");
+ if(CheckNextOwnerCopy)
+ {
+ perm.setNextOwnerBits(gAgent.getID(), gAgent.getGroupID(),
+ CheckNextOwnerCopy->get(), PERM_COPY);
+ }
+ LLCheckBoxCtrl* CheckNextOwnerTransfer = getChild<LLCheckBoxCtrl>("CheckNextOwnerTransfer");
+ if(CheckNextOwnerTransfer)
+ {
+ perm.setNextOwnerBits(gAgent.getID(), gAgent.getGroupID(),
+ CheckNextOwnerTransfer->get(), PERM_TRANSFER);
+ }
+ if(perm != item->getPermissions()
+ && item->isComplete())
+ {
+ LLPointer<LLViewerInventoryItem> new_item = new LLViewerInventoryItem(item);
+ new_item->setPermissions(perm);
+ U32 flags = new_item->getFlags();
+ // If next owner permissions have changed (and this is an object)
+ // then set the slam permissions flag so that they are applied on rez.
+ if((perm.getMaskNextOwner()!=item->getPermissions().getMaskNextOwner())
+ && (item->getType() == LLAssetType::AT_OBJECT))
+ {
+ flags |= LLInventoryItem::II_FLAGS_OBJECT_SLAM_PERM;
+ }
+ // If everyone permissions have changed (and this is an object)
+ // then set the overwrite everyone permissions flag so they
+ // are applied on rez.
+ if ((perm.getMaskEveryone()!=item->getPermissions().getMaskEveryone())
+ && (item->getType() == LLAssetType::AT_OBJECT))
+ {
+ flags |= LLInventoryItem::II_FLAGS_OBJECT_PERM_OVERWRITE_EVERYONE;
+ }
+ // If group permissions have changed (and this is an object)
+ // then set the overwrite group permissions flag so they
+ // are applied on rez.
+ if ((perm.getMaskGroup()!=item->getPermissions().getMaskGroup())
+ && (item->getType() == LLAssetType::AT_OBJECT))
+ {
+ flags |= LLInventoryItem::II_FLAGS_OBJECT_PERM_OVERWRITE_GROUP;
+ }
+ new_item->setFlags(flags);
+ if(mObjectID.isNull())
+ {
+ new_item->updateServer(FALSE);
+ gInventory.updateItem(new_item);
+ gInventory.notifyObservers();
+ }
+ else
+ {
+ LLViewerObject* object = gObjectList.findObject(mObjectID);
+ if(object)
+ {
+ object->updateInventory(
+ new_item,
+ TASK_INVENTORY_ITEM_KEY,
+ false);
+ }
+ }
+ }
+ else
+ {
+ // need to make sure we don't just follow the click
+ refresh();
+ }
+}
+
+// static
+void LLSidepanelItemInfo::onCommitSaleInfo()
+{
+ //llinfos << "LLSidepanelItemInfo::onCommitSaleInfo()" << llendl;
+ updateSaleInfo();
+}
+
+// static
+void LLSidepanelItemInfo::onCommitSaleType()
+{
+ //llinfos << "LLSidepanelItemInfo::onCommitSaleType()" << llendl;
+ updateSaleInfo();
+}
+
+void LLSidepanelItemInfo::updateSaleInfo()
+{
+ LLViewerInventoryItem* item = (LLViewerInventoryItem*)findItem();
+ if(!item) return;
+ LLSaleInfo sale_info(item->getSaleInfo());
+ if(!gAgent.allowOperation(PERM_TRANSFER, item->getPermissions(), GP_OBJECT_SET_SALE))
+ {
+ childSetValue("CheckPurchase",LLSD((BOOL)FALSE));
+ }
+
+ if((BOOL)childGetValue("CheckPurchase"))
+ {
+ // turn on sale info
+ LLSaleInfo::EForSale sale_type = LLSaleInfo::FS_COPY;
+
+ LLRadioGroup* RadioSaleType = getChild<LLRadioGroup>("RadioSaleType");
+ if(RadioSaleType)
+ {
+ switch (RadioSaleType->getSelectedIndex())
+ {
+ case 0:
+ sale_type = LLSaleInfo::FS_ORIGINAL;
+ break;
+ case 1:
+ sale_type = LLSaleInfo::FS_COPY;
+ break;
+ case 2:
+ sale_type = LLSaleInfo::FS_CONTENTS;
+ break;
+ default:
+ sale_type = LLSaleInfo::FS_COPY;
+ break;
+ }
+ }
+
+ if (sale_type == LLSaleInfo::FS_COPY
+ && !gAgent.allowOperation(PERM_COPY, item->getPermissions(),
+ GP_OBJECT_SET_SALE))
+ {
+ sale_type = LLSaleInfo::FS_ORIGINAL;
+ }
+
+
+
+ S32 price = -1;
+ price = getChild<LLUICtrl>("Edit Cost")->getValue().asInteger();;
+
+ // Invalid data - turn off the sale
+ if (price < 0)
+ {
+ sale_type = LLSaleInfo::FS_NOT;
+ price = 0;
+ }
+
+ sale_info.setSaleType(sale_type);
+ sale_info.setSalePrice(price);
+ }
+ else
+ {
+ sale_info.setSaleType(LLSaleInfo::FS_NOT);
+ }
+ if(sale_info != item->getSaleInfo()
+ && item->isComplete())
+ {
+ LLPointer<LLViewerInventoryItem> new_item = new LLViewerInventoryItem(item);
+
+ // Force an update on the sale price at rez
+ if (item->getType() == LLAssetType::AT_OBJECT)
+ {
+ U32 flags = new_item->getFlags();
+ flags |= LLInventoryItem::II_FLAGS_OBJECT_SLAM_SALE;
+ new_item->setFlags(flags);
+ }
+
+ new_item->setSaleInfo(sale_info);
+ if(mObjectID.isNull())
+ {
+ // This is in the agent's inventory.
+ new_item->updateServer(FALSE);
+ gInventory.updateItem(new_item);
+ gInventory.notifyObservers();
+ }
+ else
+ {
+ // This is in an object's contents.
+ LLViewerObject* object = gObjectList.findObject(mObjectID);
+ if(object)
+ {
+ object->updateInventory(
+ new_item,
+ TASK_INVENTORY_ITEM_KEY,
+ false);
+ }
+ }
+ }
+ else
+ {
+ // need to make sure we don't just follow the click
+ refresh();
+ }
+}
+
+LLInventoryItem* LLSidepanelItemInfo::findItem() const
+{
+ LLInventoryItem* item = NULL;
+ if(mObjectID.isNull())
+ {
+ // it is in agent inventory
+ item = gInventory.getItem(mItemID);
+ }
+ else
+ {
+ LLViewerObject* object = gObjectList.findObject(mObjectID);
+ if(object)
+ {
+ item = (LLInventoryItem*)object->getInventoryObject(mItemID);
+ }
+ }
+ return item;
+}
+
+// virtual
+void LLSidepanelItemInfo::updateVerbs()
+{
+ LLSidepanelInventorySubpanel::updateVerbs();
+
+ const LLViewerInventoryItem* item = (LLViewerInventoryItem*)findItem();
+ if (item)
+ {
+ const LLPermissions& perm = item->getPermissions();
+ BOOL is_modifiable = gAgent.allowOperation(PERM_MODIFY, perm,
+ GP_OBJECT_MANIPULATE);
+ mEditBtn->setEnabled(is_modifiable);
+ }
+}
+
+// virtual
+void LLSidepanelItemInfo::save()
+{
+ onCommitName();
+ onCommitDescription();
+ onCommitPermissions();
+ onCommitSaleInfo();
+ onCommitSaleType();
+}
diff --git a/indra/newview/llsidepaneliteminfo.h b/indra/newview/llsidepaneliteminfo.h new file mode 100644 index 0000000000..b348b5cceb --- /dev/null +++ b/indra/newview/llsidepaneliteminfo.h @@ -0,0 +1,91 @@ +/**
+ * @file llsidepaneliteminfo.h
+ * @brief A panel which shows an inventory item's properties.
+ *
+ * $LicenseInfo:firstyear=2002&license=viewergpl$
+ *
+ * Copyright (c) 2002-2009, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLSIDEPANELITEMINFO_H
+#define LL_LLSIDEPANELITEMINFO_H
+
+#include "llsidepanelinventorysubpanel.h"
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLSidepanelItemInfo
+// Object properties for inventory side panel.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+class LLButton;
+class LLInventoryItem;
+class LLItemPropertiesObserver;
+class LLViewerObject;
+class LLPermissions;
+
+class LLSidepanelItemInfo : public LLSidepanelInventorySubpanel
+{
+public:
+ LLSidepanelItemInfo();
+ virtual ~LLSidepanelItemInfo();
+
+ /*virtual*/ BOOL postBuild();
+ /*virtual*/ void reset();
+
+ void setObjectID(const LLUUID& object_id);
+ void setItemID(const LLUUID& item_id);
+ void setEditMode(BOOL edit);
+
+protected:
+ /*virtual*/ void refresh();
+ /*virtual*/ void save();
+ /*virtual*/ void updateVerbs();
+
+ LLInventoryItem* findItem() const;
+ LLViewerObject* findObject() const;
+
+ void refreshFromItem(LLInventoryItem* item);
+
+private:
+ LLUUID mItemID; // inventory UUID for the inventory item.
+ LLUUID mObjectID; // in-world task UUID, or null if in agent inventory.
+ LLItemPropertiesObserver* mPropertiesObserver; // for syncing changes to item
+
+ //
+ // UI Elements
+ //
+protected:
+ void onClickCreator();
+ void onClickOwner();
+ void onCommitName();
+ void onCommitDescription();
+ void onCommitPermissions();
+ void onCommitSaleInfo();
+ void onCommitSaleType();
+ void updateSaleInfo();
+};
+
+#endif // LL_LLSIDEPANELITEMINFO_H
diff --git a/indra/newview/llsidepaneltaskinfo.cpp b/indra/newview/llsidepaneltaskinfo.cpp new file mode 100644 index 0000000000..01c832d7d5 --- /dev/null +++ b/indra/newview/llsidepaneltaskinfo.cpp @@ -0,0 +1,1124 @@ +/** + * @file llsidepaneltaskinfo.cpp + * @brief LLSidepanelTaskInfo class implementation + * This class represents the panel in the build view for + * viewing/editing object names, owners, permissions, etc. + * + * $LicenseInfo:firstyear=2002&license=viewergpl$ + * + * Copyright (c) 2002-2009, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "llsidepaneltaskinfo.h" + +#include "lluuid.h" +#include "llpermissions.h" +#include "llcategory.h" +#include "llclickaction.h" +#include "llfocusmgr.h" +#include "llstring.h" + +#include "llviewerwindow.h" +#include "llresmgr.h" +#include "lltextbox.h" +#include "llbutton.h" +#include "llcheckboxctrl.h" +#include "llviewerobject.h" +#include "llselectmgr.h" +#include "llagent.h" +#include "llstatusbar.h" // for getBalance() +#include "lllineeditor.h" +#include "llcombobox.h" +#include "lluiconstants.h" +#include "lldbstrings.h" +#include "llfloatergroups.h" +#include "llfloaterreg.h" +#include "llavataractions.h" +#include "llnamebox.h" +#include "llviewercontrol.h" +#include "llviewermenu.h" +#include "lluictrlfactory.h" +#include "llspinctrl.h" +#include "roles_constants.h" +#include "llgroupactions.h" + +///---------------------------------------------------------------------------- +/// Class llsidepaneltaskinfo +///---------------------------------------------------------------------------- + +LLSidepanelTaskInfo* LLSidepanelTaskInfo::sActivePanel = NULL; + +static LLRegisterPanelClassWrapper<LLSidepanelTaskInfo> t_task_info("sidepanel_task_info"); + +// Default constructor +LLSidepanelTaskInfo::LLSidepanelTaskInfo() +{ + setMouseOpaque(FALSE); +} + + +LLSidepanelTaskInfo::~LLSidepanelTaskInfo() +{ + if (sActivePanel == this) + sActivePanel = NULL; +} + +// virtual +BOOL LLSidepanelTaskInfo::postBuild() +{ + LLSidepanelInventorySubpanel::postBuild(); + + mOpenBtn = getChild<LLButton>("open_btn"); + mOpenBtn->setClickedCallback(boost::bind(&LLSidepanelTaskInfo::onOpenButtonClicked, this)); + mPayBtn = getChild<LLButton>("pay_btn"); + mPayBtn->setClickedCallback(boost::bind(&LLSidepanelTaskInfo::onPayButtonClicked, this)); + mBuyBtn = getChild<LLButton>("buy_btn"); + mBuyBtn->setClickedCallback(boost::bind(&LLSidepanelTaskInfo::onBuyButtonClicked, this)); + + childSetPrevalidate("Object Name",LLLineEditor::prevalidatePrintableNotPipe); + childSetPrevalidate("Object Description",LLLineEditor::prevalidatePrintableNotPipe); + +// getChild<LLUICtrl>("button set group")->setCommitCallback(boost::bind(&LLSidepanelTaskInfo::onClickGroup,this)); +// childSetAction("button deed",LLSidepanelTaskInfo::onClickDeedToGroup,this); + + mLabelGroupName = getChild<LLNameBox>("Group Name Proxy"); + + return TRUE; +} + +// virtual +void LLSidepanelTaskInfo::setVisible(BOOL visible) +{ + LLPanel::setVisible(visible); + if (visible) + { + sActivePanel = this; + } + else + { + sActivePanel = NULL; + } +} + +void LLSidepanelTaskInfo::refresh() +{ + LLButton* BtnDeedToGroup = getChild<LLButton>("button deed"); + if(BtnDeedToGroup) + { + std::string deedText; + if (gWarningSettings.getBOOL("DeedObject")) + { + deedText = getString("text deed continued"); + } + else + { + deedText = getString("text deed"); + } + BtnDeedToGroup->setLabelSelected(deedText); + BtnDeedToGroup->setLabelUnselected(deedText); + } + BOOL root_selected = TRUE; + LLSelectNode* nodep = mObjectSelection->getFirstRootNode(); + S32 object_count = mObjectSelection->getRootObjectCount(); + if(!nodep || 0 == object_count) + { + nodep = mObjectSelection->getFirstNode(); + object_count = mObjectSelection->getObjectCount(); + root_selected = FALSE; + } + + //BOOL attachment_selected = mObjectSelection->isAttachment(); + //attachment_selected = false; + LLViewerObject* objectp = NULL; + if(nodep) objectp = nodep->getObject(); + if(!nodep || !objectp)// || attachment_selected) + { + // ...nothing selected + childSetEnabled("perm_modify",false); + childSetText("perm_modify",LLStringUtil::null); + + childSetEnabled("Creator:",false); + childSetText("Creator Name",LLStringUtil::null); + childSetEnabled("Creator Name",false); + + childSetEnabled("Owner:",false); + childSetText("Owner Name",LLStringUtil::null); + childSetEnabled("Owner Name",false); + + childSetEnabled("Group:",false); + childSetText("Group Name",LLStringUtil::null); + childSetEnabled("Group Name",false); + childSetEnabled("button set group",false); + + childSetText("Object Name",LLStringUtil::null); + childSetEnabled("Object Name",false); + childSetEnabled("Name:",false); + childSetText("Group Name",LLStringUtil::null); + childSetEnabled("Group Name",false); + childSetEnabled("Description:",false); + childSetText("Object Description",LLStringUtil::null); + childSetEnabled("Object Description",false); + + childSetEnabled("Permissions:",false); + + childSetValue("checkbox share with group",FALSE); + childSetEnabled("checkbox share with group",false); + childSetEnabled("button deed",false); + + childSetValue("checkbox allow everyone move",FALSE); + childSetEnabled("checkbox allow everyone move",false); + childSetValue("checkbox allow everyone copy",FALSE); + childSetEnabled("checkbox allow everyone copy",false); + + //Next owner can: + childSetEnabled("Next owner can:",false); + childSetValue("checkbox next owner can modify",FALSE); + childSetEnabled("checkbox next owner can modify",false); + childSetValue("checkbox next owner can copy",FALSE); + childSetEnabled("checkbox next owner can copy",false); + childSetValue("checkbox next owner can transfer",FALSE); + childSetEnabled("checkbox next owner can transfer",false); + + //checkbox for sale + childSetValue("checkbox for sale",FALSE); + childSetEnabled("checkbox for sale",false); + + //checkbox include in search + childSetValue("search_check", FALSE); + childSetEnabled("search_check", false); + + LLComboBox* combo_sale_type = getChild<LLComboBox>("sale type"); + combo_sale_type->setValue(LLSaleInfo::FS_COPY); + combo_sale_type->setEnabled(FALSE); + + childSetEnabled("Cost",false); + childSetText("Cost",getString("Cost Default")); + childSetText("Edit Cost",LLStringUtil::null); + childSetEnabled("Edit Cost",false); + + childSetEnabled("label click action",false); + LLComboBox* ComboClickAction = getChild<LLComboBox>("clickaction"); + if(ComboClickAction) + { + ComboClickAction->setEnabled(FALSE); + ComboClickAction->clear(); + } + childSetVisible("B:",false); + childSetVisible("O:",false); + childSetVisible("G:",false); + childSetVisible("E:",false); + childSetVisible("N:",false); + childSetVisible("F:",false); + + return; + } + + // figure out a few variables + BOOL is_one_object = (object_count == 1); + + // BUG: fails if a root and non-root are both single-selected. + BOOL is_perm_modify = (mObjectSelection->getFirstRootNode() + && LLSelectMgr::getInstance()->selectGetRootsModify()) + || LLSelectMgr::getInstance()->selectGetModify(); + const LLFocusableElement* keyboard_focus_view = gFocusMgr.getKeyboardFocus(); + S32 string_index = 0; + std::string MODIFY_INFO_STRINGS[] = + { + getString("text modify info 1"), + getString("text modify info 2"), + getString("text modify info 3"), + getString("text modify info 4") + }; + if(!is_perm_modify) + { + string_index += 2; + } + if(!is_one_object) + { + ++string_index; + } + childSetEnabled("perm_modify",true); + childSetText("perm_modify",MODIFY_INFO_STRINGS[string_index]); + + childSetEnabled("Permissions:",true); + + // Update creator text field + childSetEnabled("Creator:",true); + BOOL creators_identical; + std::string creator_name; + creators_identical = LLSelectMgr::getInstance()->selectGetCreator(mCreatorID, + creator_name); + + childSetText("Creator Name",creator_name); + childSetEnabled("Creator Name",TRUE); + + // Update owner text field + childSetEnabled("Owner:",true); + + BOOL owners_identical; + std::string owner_name; + owners_identical = LLSelectMgr::getInstance()->selectGetOwner(mOwnerID, owner_name); + +// llinfos << "owners_identical " << (owners_identical ? "TRUE": "FALSE") << llendl; + + if (mOwnerID.isNull()) + { + if(LLSelectMgr::getInstance()->selectIsGroupOwned()) + { + // Group owned already displayed by selectGetOwner + } + else + { + // Display last owner if public + std::string last_owner_name; + LLSelectMgr::getInstance()->selectGetLastOwner(mLastOwnerID, last_owner_name); + + // It should never happen that the last owner is null and the owner + // is null, but it seems to be a bug in the simulator right now. JC + if (!mLastOwnerID.isNull() && !last_owner_name.empty()) + { + owner_name.append(", last "); + owner_name.append( last_owner_name ); + } + } + } + + childSetText("Owner Name",owner_name); + childSetEnabled("Owner Name",TRUE); + + // update group text field + childSetEnabled("Group:",true); + childSetText("Group Name",LLStringUtil::null); + LLUUID group_id; + BOOL groups_identical = LLSelectMgr::getInstance()->selectGetGroup(group_id); + if (groups_identical) + { + if(mLabelGroupName) + { + mLabelGroupName->setNameID(group_id, TRUE); + mLabelGroupName->setEnabled(TRUE); + } + } + else + { + if(mLabelGroupName) + { + mLabelGroupName->setNameID(LLUUID::null, TRUE); + mLabelGroupName->refresh(LLUUID::null, LLStringUtil::null, LLStringUtil::null, TRUE); + mLabelGroupName->setEnabled(FALSE); + } + } + + childSetEnabled("button set group",owners_identical && (mOwnerID == gAgent.getID())); + + // figure out the contents of the name, description, & category + BOOL edit_name_desc = FALSE; + if(is_one_object && objectp->permModify()) + { + edit_name_desc = TRUE; + } + + childSetEnabled("Name:",true); + LLLineEditor* LineEditorObjectName = getChild<LLLineEditor>("Object Name"); + childSetEnabled("Description:",true); + LLLineEditor* LineEditorObjectDesc = getChild<LLLineEditor>("Object Description"); + + if(is_one_object) + { + if(keyboard_focus_view != LineEditorObjectName) + { + childSetText("Object Name",nodep->mName); + } + + if(LineEditorObjectDesc) + { + if(keyboard_focus_view != LineEditorObjectDesc) + { + LineEditorObjectDesc->setText(nodep->mDescription); + } + } + } + else + { + childSetText("Object Name",LLStringUtil::null); + LineEditorObjectDesc->setText(LLStringUtil::null); + } + + if(edit_name_desc) + { + childSetEnabled("Object Name",true); + childSetEnabled("Object Description",true); + } + else + { + childSetEnabled("Object Name",false); + childSetEnabled("Object Description",false); + } + + S32 total_sale_price = 0; + S32 individual_sale_price = 0; + BOOL is_for_sale_mixed = FALSE; + BOOL is_sale_price_mixed = FALSE; + U32 num_for_sale = FALSE; + LLSelectMgr::getInstance()->selectGetAggregateSaleInfo(num_for_sale, + is_for_sale_mixed, + is_sale_price_mixed, + total_sale_price, + individual_sale_price); + + const BOOL self_owned = (gAgent.getID() == mOwnerID); + const BOOL group_owned = LLSelectMgr::getInstance()->selectIsGroupOwned() ; + const BOOL public_owned = (mOwnerID.isNull() && !LLSelectMgr::getInstance()->selectIsGroupOwned()); + const BOOL can_transfer = LLSelectMgr::getInstance()->selectGetRootsTransfer(); + const BOOL can_copy = LLSelectMgr::getInstance()->selectGetRootsCopy(); + + if(!owners_identical) + { + childSetEnabled("Cost",false); + childSetText("Edit Cost",LLStringUtil::null); + childSetEnabled("Edit Cost",false); + } + // You own these objects. + else if(self_owned || (group_owned && gAgent.hasPowerInGroup(group_id,GP_OBJECT_SET_SALE))) + { + // If there are multiple items for sale then set text to PRICE PER UNIT. + if (num_for_sale > 1) + { + childSetText("Cost",getString("Cost Per Unit")); + } + else + { + childSetText("Cost",getString("Cost Default")); + } + + LLSpinCtrl *edit_price = getChild<LLSpinCtrl>("Edit Cost"); + if(!edit_price->hasFocus()) + { + // If the sale price is mixed then set the cost to MIXED, otherwise + // set to the actual cost. + if (num_for_sale > 0 && is_for_sale_mixed) + { + edit_price->setTentative(TRUE); + } + else if (num_for_sale > 0 && is_sale_price_mixed) + { + edit_price->setTentative(TRUE); + } + else + { + edit_price->setValue(individual_sale_price); + } + } + // The edit fields are only enabled if you can sell this object + // and the sale price is not mixed. + bool enable_edit = (num_for_sale && can_transfer) ? !is_for_sale_mixed : false; + childSetEnabled("Cost",enable_edit); + childSetEnabled("Edit Cost",enable_edit); + } + // Someone, not you, owns these objects. + else if(!public_owned) + { + childSetEnabled("Cost",false); + childSetEnabled("Edit Cost",false); + + // Don't show a price if none of the items are for sale. + if (num_for_sale) + childSetText("Edit Cost",llformat("%d",total_sale_price)); + else + childSetText("Edit Cost",LLStringUtil::null); + + // If multiple items are for sale, set text to TOTAL PRICE. + if (num_for_sale > 1) + childSetText("Cost",getString("Cost Total")); + else + childSetText("Cost",getString("Cost Default")); + } + // This is a public object. + else + { + childSetEnabled("Cost",false); + childSetText("Cost",getString("Cost Default")); + + childSetText("Edit Cost",LLStringUtil::null); + childSetEnabled("Edit Cost",false); + } + + // Enable and disable the permissions checkboxes + // based on who owns the object. + // TODO: Creator permissions + + BOOL valid_base_perms = FALSE; + BOOL valid_owner_perms = FALSE; + BOOL valid_group_perms = FALSE; + BOOL valid_everyone_perms = FALSE; + BOOL valid_next_perms = FALSE; + + U32 base_mask_on; + U32 base_mask_off; + U32 owner_mask_on; + U32 owner_mask_off; + U32 group_mask_on; + U32 group_mask_off; + U32 everyone_mask_on; + U32 everyone_mask_off; + U32 next_owner_mask_on = 0; + U32 next_owner_mask_off = 0; + + valid_base_perms = LLSelectMgr::getInstance()->selectGetPerm(PERM_BASE, + &base_mask_on, + &base_mask_off); + + valid_owner_perms = LLSelectMgr::getInstance()->selectGetPerm(PERM_OWNER, + &owner_mask_on, + &owner_mask_off); + + valid_group_perms = LLSelectMgr::getInstance()->selectGetPerm(PERM_GROUP, + &group_mask_on, + &group_mask_off); + + valid_everyone_perms = LLSelectMgr::getInstance()->selectGetPerm(PERM_EVERYONE, + &everyone_mask_on, + &everyone_mask_off); + + valid_next_perms = LLSelectMgr::getInstance()->selectGetPerm(PERM_NEXT_OWNER, + &next_owner_mask_on, + &next_owner_mask_off); + + + if( gSavedSettings.getBOOL("DebugPermissions") ) + { + std::string perm_string; + if (valid_base_perms) + { + perm_string = "B: "; + perm_string += mask_to_string(base_mask_on); + childSetText("B:",perm_string); + childSetVisible("B:",true); + + perm_string = "O: "; + perm_string += mask_to_string(owner_mask_on); + childSetText("O:",perm_string); + childSetVisible("O:",true); + + perm_string = "G: "; + perm_string += mask_to_string(group_mask_on); + childSetText("G:",perm_string); + childSetVisible("G:",true); + + perm_string = "E: "; + perm_string += mask_to_string(everyone_mask_on); + childSetText("E:",perm_string); + childSetVisible("E:",true); + + perm_string = "N: "; + perm_string += mask_to_string(next_owner_mask_on); + childSetText("N:",perm_string); + childSetVisible("N:",true); + } + perm_string = "F: "; + U32 flag_mask = 0x0; + if (objectp->permMove()) + flag_mask |= PERM_MOVE; + if (objectp->permModify()) + flag_mask |= PERM_MODIFY; + if (objectp->permCopy()) + flag_mask |= PERM_COPY; + if (objectp->permTransfer()) + flag_mask |= PERM_TRANSFER; + perm_string += mask_to_string(flag_mask); + childSetText("F:",perm_string); + childSetVisible("F:",true); + } + else + { + childSetVisible("B:",false); + childSetVisible("O:",false); + childSetVisible("G:",false); + childSetVisible("E:",false); + childSetVisible("N:",false); + childSetVisible("F:",false); + } + + bool has_change_perm_ability = false; + bool has_change_sale_ability = false; + + if(valid_base_perms + && (self_owned + || (group_owned && gAgent.hasPowerInGroup(group_id, GP_OBJECT_MANIPULATE)))) + { + has_change_perm_ability = true; + } + if(valid_base_perms + && (self_owned + || (group_owned && gAgent.hasPowerInGroup(group_id, GP_OBJECT_SET_SALE)))) + { + has_change_sale_ability = true; + } + + if (!has_change_perm_ability && !has_change_sale_ability && !root_selected) + { + // ...must select root to choose permissions + childSetValue("perm_modify", getString("text modify warning")); + } + + if (has_change_perm_ability) + { + childSetEnabled("checkbox share with group",true); + childSetEnabled("checkbox allow everyone move",owner_mask_on & PERM_MOVE); + childSetEnabled("checkbox allow everyone copy",owner_mask_on & PERM_COPY && owner_mask_on & PERM_TRANSFER); + } + else + { + childSetEnabled("checkbox share with group", FALSE); + childSetEnabled("checkbox allow everyone move", FALSE); + childSetEnabled("checkbox allow everyone copy", FALSE); + } + + if (has_change_sale_ability && (owner_mask_on & PERM_TRANSFER)) + { + childSetEnabled("checkbox for sale", can_transfer || (!can_transfer && num_for_sale)); + // Set the checkbox to tentative if the prices of each object selected + // are not the same. + childSetTentative("checkbox for sale", is_for_sale_mixed); + childSetEnabled("sale type",num_for_sale && can_transfer && !is_sale_price_mixed); + + childSetEnabled("Next owner can:", TRUE); + childSetEnabled("checkbox next owner can modify",base_mask_on & PERM_MODIFY); + childSetEnabled("checkbox next owner can copy",base_mask_on & PERM_COPY); + childSetEnabled("checkbox next owner can transfer",next_owner_mask_on & PERM_COPY); + } + else + { + childSetEnabled("checkbox for sale",FALSE); + childSetEnabled("sale type",FALSE); + + childSetEnabled("Next owner can:",FALSE); + childSetEnabled("checkbox next owner can modify",FALSE); + childSetEnabled("checkbox next owner can copy",FALSE); + childSetEnabled("checkbox next owner can transfer",FALSE); + } + + if(valid_group_perms) + { + if((group_mask_on & PERM_COPY) && (group_mask_on & PERM_MODIFY) && (group_mask_on & PERM_MOVE)) + { + childSetValue("checkbox share with group",TRUE); + childSetTentative("checkbox share with group",FALSE); + childSetEnabled("button deed",gAgent.hasPowerInGroup(group_id, GP_OBJECT_DEED) && (owner_mask_on & PERM_TRANSFER) && !group_owned && can_transfer); + } + else if((group_mask_off & PERM_COPY) && (group_mask_off & PERM_MODIFY) && (group_mask_off & PERM_MOVE)) + { + childSetValue("checkbox share with group",FALSE); + childSetTentative("checkbox share with group",false); + childSetEnabled("button deed",false); + } + else + { + childSetValue("checkbox share with group",TRUE); + childSetTentative("checkbox share with group",true); + childSetEnabled("button deed",gAgent.hasPowerInGroup(group_id, GP_OBJECT_DEED) && (group_mask_on & PERM_MOVE) && (owner_mask_on & PERM_TRANSFER) && !group_owned && can_transfer); + } + } + + if(valid_everyone_perms) + { + // Move + if(everyone_mask_on & PERM_MOVE) + { + childSetValue("checkbox allow everyone move",TRUE); + childSetTentative("checkbox allow everyone move",false); + } + else if(everyone_mask_off & PERM_MOVE) + { + childSetValue("checkbox allow everyone move",FALSE); + childSetTentative("checkbox allow everyone move",false); + } + else + { + childSetValue("checkbox allow everyone move",TRUE); + childSetTentative("checkbox allow everyone move",true); + } + + // Copy == everyone can't copy + if(everyone_mask_on & PERM_COPY) + { + childSetValue("checkbox allow everyone copy",TRUE); + childSetTentative("checkbox allow everyone copy",!can_copy || !can_transfer); + } + else if(everyone_mask_off & PERM_COPY) + { + childSetValue("checkbox allow everyone copy",FALSE); + childSetTentative("checkbox allow everyone copy",false); + } + else + { + childSetValue("checkbox allow everyone copy",TRUE); + childSetTentative("checkbox allow everyone copy",true); + } + } + + if(valid_next_perms) + { + // Modify == next owner canot modify + if(next_owner_mask_on & PERM_MODIFY) + { + childSetValue("checkbox next owner can modify",TRUE); + childSetTentative("checkbox next owner can modify",false); + } + else if(next_owner_mask_off & PERM_MODIFY) + { + childSetValue("checkbox next owner can modify",FALSE); + childSetTentative("checkbox next owner can modify",false); + } + else + { + childSetValue("checkbox next owner can modify",TRUE); + childSetTentative("checkbox next owner can modify",true); + } + + // Copy == next owner cannot copy + if(next_owner_mask_on & PERM_COPY) + { + childSetValue("checkbox next owner can copy",TRUE); + childSetTentative("checkbox next owner can copy",!can_copy); + } + else if(next_owner_mask_off & PERM_COPY) + { + childSetValue("checkbox next owner can copy",FALSE); + childSetTentative("checkbox next owner can copy",FALSE); + } + else + { + childSetValue("checkbox next owner can copy",TRUE); + childSetTentative("checkbox next owner can copy",TRUE); + } + + // Transfer == next owner cannot transfer + if(next_owner_mask_on & PERM_TRANSFER) + { + childSetValue("checkbox next owner can transfer",TRUE); + childSetTentative("checkbox next owner can transfer",!can_transfer); + } + else if(next_owner_mask_off & PERM_TRANSFER) + { + childSetValue("checkbox next owner can transfer",FALSE); + childSetTentative("checkbox next owner can transfer",FALSE); + } + else + { + childSetValue("checkbox next owner can transfer",TRUE); + childSetTentative("checkbox next owner can transfer",TRUE); + } + } + + // reflect sale information + LLSaleInfo sale_info; + BOOL valid_sale_info = LLSelectMgr::getInstance()->selectGetSaleInfo(sale_info); + LLSaleInfo::EForSale sale_type = sale_info.getSaleType(); + + LLComboBox* combo_sale_type = getChild<LLComboBox>("sale type"); + if (valid_sale_info) + { + combo_sale_type->setValue(sale_type == LLSaleInfo::FS_NOT ? LLSaleInfo::FS_COPY : sale_type); + combo_sale_type->setTentative(FALSE); // unfortunately this doesn't do anything at the moment. + } + else + { + // default option is sell copy, determined to be safest + combo_sale_type->setValue(LLSaleInfo::FS_COPY); + combo_sale_type->setTentative(TRUE); // unfortunately this doesn't do anything at the moment. + } + + childSetValue("checkbox for sale", num_for_sale != 0); + + // HACK: There are some old objects in world that are set for sale, + // but are no-transfer. We need to let users turn for-sale off, but only + // if for-sale is set. + bool cannot_actually_sell = !can_transfer || (!can_copy && sale_type == LLSaleInfo::FS_COPY); + if (num_for_sale && has_change_sale_ability && cannot_actually_sell) + { + childSetEnabled("checkbox for sale", true); + } + + // Check search status of objects + BOOL all_volume = LLSelectMgr::getInstance()->selectionAllPCode( LL_PCODE_VOLUME ); + bool include_in_search; + bool all_include_in_search = LLSelectMgr::getInstance()->selectionGetIncludeInSearch(&include_in_search); + childSetEnabled("search_check", has_change_sale_ability && all_volume); + childSetValue("search_check", include_in_search); + childSetTentative("search_check", ! all_include_in_search); + + // Click action (touch, sit, buy) + U8 click_action = 0; + if (LLSelectMgr::getInstance()->selectionGetClickAction(&click_action)) + { + LLComboBox* ComboClickAction = getChild<LLComboBox>("clickaction"); + if(ComboClickAction) + { + ComboClickAction->setCurrentByIndex((S32)click_action); + } + } + childSetEnabled("label click action",is_perm_modify && all_volume); + childSetEnabled("clickaction",is_perm_modify && all_volume); + + if (!getIsEditing()) + { + const std::string no_item_names[]={ + "Object Name", + "Object Description", + "button set group", + "checkbox share with group", + "button deed", + "checkbox allow everyone move", + "checkbox allow everyone copy", + "checkbox for sale", + "sale type", + "Edit Cost", + "checkbox next owner can modify", + "checkbox next owner can copy", + "checkbox next owner can transfer", + "clickaction", + "search_check", + "perm_modify", + "Group Name", + }; + for(size_t t=0; t<LL_ARRAY_SIZE(no_item_names); ++t) + { + childSetEnabled(no_item_names[t],false); + } + } + updateVerbs(); +} + + +// static +void LLSidepanelTaskInfo::onClickClaim(void*) +{ + // try to claim ownership + LLSelectMgr::getInstance()->sendOwner(gAgent.getID(), gAgent.getGroupID()); +} + +// static +void LLSidepanelTaskInfo::onClickRelease(void*) +{ + // try to release ownership + LLSelectMgr::getInstance()->sendOwner(LLUUID::null, LLUUID::null); +} + +void LLSidepanelTaskInfo::onClickGroup() +{ + LLUUID owner_id; + std::string name; + BOOL owners_identical = LLSelectMgr::getInstance()->selectGetOwner(owner_id, name); + LLFloater* parent_floater = gFloaterView->getParentFloater(this); + + if(owners_identical && (owner_id == gAgent.getID())) + { + LLFloaterGroupPicker* fg = LLFloaterReg::showTypedInstance<LLFloaterGroupPicker>("group_picker", LLSD(gAgent.getID())); + if (fg) + { + fg->setSelectGroupCallback( boost::bind(&LLSidepanelTaskInfo::cbGroupID, this, _1) ); + + if (parent_floater) + { + LLRect new_rect = gFloaterView->findNeighboringPosition(parent_floater, fg); + fg->setOrigin(new_rect.mLeft, new_rect.mBottom); + parent_floater->addDependentFloater(fg); + } + } + } +} + +void LLSidepanelTaskInfo::cbGroupID(LLUUID group_id) +{ + if(mLabelGroupName) + { + mLabelGroupName->setNameID(group_id, TRUE); + } + LLSelectMgr::getInstance()->sendGroup(group_id); +} + +static bool callback_deed_to_group(const LLSD& notification, const LLSD& response) +{ + S32 option = LLNotification::getSelectedOption(notification, response); + if (0 == option) + { + LLUUID group_id; + BOOL groups_identical = LLSelectMgr::getInstance()->selectGetGroup(group_id); + if(group_id.notNull() && groups_identical && (gAgent.hasPowerInGroup(group_id, GP_OBJECT_DEED))) + { + LLSelectMgr::getInstance()->sendOwner(LLUUID::null, group_id, FALSE); +// LLViewerStats::getInstance()->incStat(LLViewerStats::ST_RELEASE_COUNT); + } + } + return false; +} + +void LLSidepanelTaskInfo::onClickDeedToGroup() +{ + LLNotifications::instance().add( "DeedObjectToGroup", LLSD(), LLSD(), callback_deed_to_group); +} + +///---------------------------------------------------------------------------- +/// Permissions checkboxes +///---------------------------------------------------------------------------- + +void LLSidepanelTaskInfo::onCommitPerm(LLCheckBoxCtrl *ctrl, U8 field, U32 perm) +{ + LLViewerObject* object = mObjectSelection->getFirstRootObject(); + if(!object) return; + + BOOL new_state = ctrl->get(); + LLSelectMgr::getInstance()->selectionSetObjectPermissions(field, new_state, perm); +} + +void LLSidepanelTaskInfo::onCommitGroupShare() +{ + LLCheckBoxCtrl *ctrl = getChild<LLCheckBoxCtrl>("checkbox share with group"); + onCommitPerm(ctrl, PERM_GROUP, PERM_MODIFY | PERM_MOVE | PERM_COPY); +} + +void LLSidepanelTaskInfo::onCommitEveryoneMove() +{ + LLCheckBoxCtrl *ctrl = getChild<LLCheckBoxCtrl>("checkbox allow everyone move"); + onCommitPerm(ctrl, PERM_EVERYONE, PERM_MOVE); +} + + +void LLSidepanelTaskInfo::onCommitEveryoneCopy() +{ + LLCheckBoxCtrl *ctrl = getChild<LLCheckBoxCtrl>("checkbox allow everyone copy"); + onCommitPerm(ctrl, PERM_EVERYONE, PERM_COPY); +} + +void LLSidepanelTaskInfo::onCommitNextOwnerModify() +{ + LLCheckBoxCtrl *ctrl = getChild<LLCheckBoxCtrl>("checkbox next owner can modify"); + onCommitPerm(ctrl, PERM_NEXT_OWNER, PERM_MODIFY); +} + +void LLSidepanelTaskInfo::onCommitNextOwnerCopy() +{ + LLCheckBoxCtrl *ctrl = getChild<LLCheckBoxCtrl>("checkbox next owner can copy"); + onCommitPerm(ctrl, PERM_NEXT_OWNER, PERM_COPY); +} + +void LLSidepanelTaskInfo::onCommitNextOwnerTransfer() +{ + LLCheckBoxCtrl *ctrl = getChild<LLCheckBoxCtrl>("checkbox next owner can transfer"); + onCommitPerm(ctrl, PERM_NEXT_OWNER, PERM_TRANSFER); +} + +void LLSidepanelTaskInfo::onCommitName() +{ + LLLineEditor* tb = getChild<LLLineEditor>("Object Name"); + LLSelectMgr::getInstance()->selectionSetObjectName(tb->getText()); +} + +void LLSidepanelTaskInfo::onCommitDesc() +{ + LLLineEditor* le = getChild<LLLineEditor>("Object Description"); + LLSelectMgr::getInstance()->selectionSetObjectDescription(le->getText()); +} + +void LLSidepanelTaskInfo::onCommitSaleInfo() +{ + setAllSaleInfo(); +} + +void LLSidepanelTaskInfo::onCommitSaleType() +{ + setAllSaleInfo(); +} + +void LLSidepanelTaskInfo::setAllSaleInfo() +{ + llinfos << "LLSidepanelTaskInfo::setAllSaleInfo()" << llendl; + LLSaleInfo::EForSale sale_type = LLSaleInfo::FS_NOT; + + LLCheckBoxCtrl *checkPurchase = getChild<LLCheckBoxCtrl>("checkbox for sale"); + + // Set the sale type if the object(s) are for sale. + if(checkPurchase && checkPurchase->get()) + { + sale_type = static_cast<LLSaleInfo::EForSale>(getChild<LLComboBox>("sale type")->getValue().asInteger()); + } + + S32 price = -1; + + LLSpinCtrl *edit_price = getChild<LLSpinCtrl>("Edit Cost"); + price = (edit_price->getTentative()) ? DEFAULT_PRICE : edit_price->getValue().asInteger(); + + // If somehow an invalid price, turn the sale off. + if (price < 0) + sale_type = LLSaleInfo::FS_NOT; + + LLSaleInfo sale_info(sale_type, price); + LLSelectMgr::getInstance()->selectionSetObjectSaleInfo(sale_info); + + // If turned off for-sale, make sure click-action buy is turned + // off as well + if (sale_type == LLSaleInfo::FS_NOT) + { + U8 click_action = 0; + LLSelectMgr::getInstance()->selectionGetClickAction(&click_action); + if (click_action == CLICK_ACTION_BUY) + { + LLSelectMgr::getInstance()->selectionSetClickAction(CLICK_ACTION_TOUCH); + } + } +} + +struct LLSelectionPayable : public LLSelectedObjectFunctor +{ + virtual bool apply(LLViewerObject* obj) + { + // can pay if you or your parent has money() event in script + LLViewerObject* parent = (LLViewerObject*)obj->getParent(); + return (obj->flagTakesMoney() + || (parent && parent->flagTakesMoney())); + } +}; + +// static +void LLSidepanelTaskInfo::onCommitClickAction(U8 click_action) +{ + if (click_action == CLICK_ACTION_BUY) + { + LLSaleInfo sale_info; + LLSelectMgr::getInstance()->selectGetSaleInfo(sale_info); + if (!sale_info.isForSale()) + { + LLNotifications::instance().add("CantSetBuyObject"); + + // Set click action back to its old value + U8 click_action = 0; + LLSelectMgr::getInstance()->selectionGetClickAction(&click_action); +// box->setCurrentByIndex((S32)click_action); + + return; + } + } + else if (click_action == CLICK_ACTION_PAY) + { + // Verify object has script with money() handler + LLSelectionPayable payable; + bool can_pay = mObjectSelection->applyToObjects(&payable); + if (!can_pay) + { + // Warn, but do it anyway. + LLNotifications::instance().add("ClickActionNotPayable"); + } + } + LLSelectMgr::getInstance()->selectionSetClickAction(click_action); +} + +// static +void LLSidepanelTaskInfo::onCommitIncludeInSearch() +{ + LLCheckBoxCtrl *ctrl = getChild<LLCheckBoxCtrl>("search_check"); + LLSelectMgr::getInstance()->selectionSetIncludeInSearch(ctrl->get()); +} + +// virtual +void LLSidepanelTaskInfo::updateVerbs() +{ + LLSidepanelInventorySubpanel::updateVerbs(); + + mOpenBtn->setVisible(!getIsEditing()); + mPayBtn->setVisible(!getIsEditing()); + mBuyBtn->setVisible(!getIsEditing()); + + mOpenBtn->setEnabled(enable_object_open()); + const LLViewerObject *obj = getFirstSelectedObject(); + mEditBtn->setEnabled(obj && obj->permModify()); +} + +void LLSidepanelTaskInfo::onOpenButtonClicked() +{ + if (enable_object_open()) + { + handle_object_open(); + } +} + +void LLSidepanelTaskInfo::onPayButtonClicked() +{ + onCommitClickAction(CLICK_ACTION_PAY); +} + +void LLSidepanelTaskInfo::onBuyButtonClicked() +{ + onCommitClickAction(CLICK_ACTION_BUY); +} + +// virtual +void LLSidepanelTaskInfo::save() +{ + onCommitGroupShare(); + onCommitEveryoneMove(); + onCommitEveryoneCopy(); + onCommitNextOwnerModify(); + onCommitNextOwnerCopy(); + onCommitNextOwnerTransfer(); + onCommitName(); + onCommitDesc(); + onCommitSaleInfo(); + onCommitSaleType(); + onCommitIncludeInSearch(); +} + +void LLSidepanelTaskInfo::setObjectSelection(LLObjectSelectionHandle selection) +{ + mObjectSelection = selection; +} + +LLSidepanelTaskInfo* LLSidepanelTaskInfo::getActivePanel() +{ + return sActivePanel; +} + +LLViewerObject* LLSidepanelTaskInfo::getFirstSelectedObject() +{ + LLSelectNode *node = mObjectSelection->getFirstRootNode(); + if (node) + { + return node->getObject(); + } + return NULL; +} + +const LLUUID& LLSidepanelTaskInfo::getSelectedUUID() +{ + const LLViewerObject* obj = getFirstSelectedObject(); + if (obj) + { + return obj->getID(); + } + return LLUUID::null; +} diff --git a/indra/newview/llsidepaneltaskinfo.h b/indra/newview/llsidepaneltaskinfo.h new file mode 100644 index 0000000000..b6dd4dfb2c --- /dev/null +++ b/indra/newview/llsidepaneltaskinfo.h @@ -0,0 +1,115 @@ +/** + * @file llsidepaneltaskinfo.h + * @brief LLSidepanelTaskInfo class header file + * + * $LicenseInfo:firstyear=2002&license=viewergpl$ + * + * Copyright (c) 2002-2009, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#ifndef LL_LLSIDEPANELTASKINFO_H +#define LL_LLSIDEPANELTASKINFO_H + +#include "llsidepanelinventorysubpanel.h" +#include "lluuid.h" +#include "llselectmgr.h" + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// Class LLSidepanelTaskInfo +// +// Panel for permissions of an object. +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +class LLNameBox; +class LLCheckBoxCtrl; +class LLViewerObject; + +class LLSidepanelTaskInfo : public LLSidepanelInventorySubpanel +{ +public: + LLSidepanelTaskInfo(); + virtual ~LLSidepanelTaskInfo(); + + /*virtual*/ BOOL postBuild(); + /*virtual*/ void setVisible(BOOL visible); + + void setObjectSelection(LLObjectSelectionHandle selection); + + const LLUUID& getSelectedUUID(); + LLViewerObject* getFirstSelectedObject(); + + static LLSidepanelTaskInfo *getActivePanel(); +protected: + /*virtual*/ void refresh(); // refresh all labels as needed + /*virtual*/ void save(); + /*virtual*/ void updateVerbs(); + + // statics + static void onClickClaim(void*); + static void onClickRelease(void*); + void onClickGroup(); + void cbGroupID(LLUUID group_id); + + void onClickDeedToGroup(); + void onCommitPerm(LLCheckBoxCtrl* ctrl, U8 field, U32 perm); + void onCommitGroupShare(); + void onCommitEveryoneMove(); + void onCommitEveryoneCopy(); + void onCommitNextOwnerModify(); + void onCommitNextOwnerCopy(); + void onCommitNextOwnerTransfer(); + void onCommitName(); + void onCommitDesc(); + void onCommitSaleInfo(); + void onCommitSaleType(); + + void onCommitClickAction(U8 click_action); + void onCommitIncludeInSearch(); + + void setAllSaleInfo(); + +private: + LLNameBox* mLabelGroupName; // group name + + LLUUID mCreatorID; + LLUUID mOwnerID; + LLUUID mLastOwnerID; + +protected: + void onOpenButtonClicked(); + void onPayButtonClicked(); + void onBuyButtonClicked(); +private: + LLButton* mOpenBtn; + LLButton* mPayBtn; + LLButton* mBuyBtn; + + LLObjectSelectionHandle mObjectSelection; + static LLSidepanelTaskInfo* sActivePanel; +}; + + +#endif // LL_LLSIDEPANELTASKINFO_H diff --git a/indra/newview/llspatialpartition.cpp b/indra/newview/llspatialpartition.cpp index 279e143851..8c5439d47e 100644 --- a/indra/newview/llspatialpartition.cpp +++ b/indra/newview/llspatialpartition.cpp @@ -2679,8 +2679,7 @@ void renderTexturePriority(LLDrawable* drawable) //LLViewerTexture* imagep = facep->getTexture(); //if (imagep) { - - //F32 vsize = LLVOVolume::getTextureVirtualSize(facep); + //F32 vsize = imagep->mMaxVirtualSize; F32 vsize = facep->getPixelArea(); diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index b23e7feda2..2c1f468f77 100644 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -60,6 +60,7 @@ #include "llfocusmgr.h" #include "llhttpsender.h" #include "lllocationhistory.h" +#include "llimageworker.h" #include "llloginflags.h" #include "llmd5.h" #include "llmemorystream.h" @@ -168,7 +169,7 @@ #include "llvoclouds.h" #include "llweb.h" #include "llworld.h" -#include "llworldmap.h" +#include "llworldmapmessage.h" #include "llxfermanager.h" #include "pipeline.h" #include "llappviewer.h" @@ -743,6 +744,7 @@ bool idle_startup() // We have at least some login information on a SLURL gFirstname = gLoginHandler.getFirstName(); gLastname = gLoginHandler.getLastName(); + LL_DEBUGS("LLStartup") << "STATE_FIRST: setting gFirstname, gLastname from gLoginHandler: '" << gFirstname << "' '" << gLastname << "'" << LL_ENDL; // Show the login screen if we don't have everything show_connect_box = @@ -753,6 +755,7 @@ bool idle_startup() LLSD cmd_line_login = gSavedSettings.getLLSD("UserLoginInfo"); gFirstname = cmd_line_login[0].asString(); gLastname = cmd_line_login[1].asString(); + LL_DEBUGS("LLStartup") << "Setting gFirstname, gLastname from gSavedSettings(\"UserLoginInfo\"): '" << gFirstname << "' '" << gLastname << "'" << LL_ENDL; LLMD5 pass((unsigned char*)cmd_line_login[2].asString().c_str()); char md5pass[33]; /* Flawfinder: ignore */ @@ -770,6 +773,7 @@ bool idle_startup() { gFirstname = gSavedSettings.getString("FirstName"); gLastname = gSavedSettings.getString("LastName"); + LL_DEBUGS("LLStartup") << "AutoLogin: setting gFirstname, gLastname from gSavedSettings(\"First|LastName\"): '" << gFirstname << "' '" << gLastname << "'" << LL_ENDL; gPassword = LLStartUp::loadPasswordFromDisk(); gSavedSettings.setBOOL("RememberPassword", TRUE); @@ -785,6 +789,7 @@ bool idle_startup() // a valid grid is selected gFirstname = gSavedSettings.getString("FirstName"); gLastname = gSavedSettings.getString("LastName"); + LL_DEBUGS("LLStartup") << "normal login: setting gFirstname, gLastname from gSavedSettings(\"First|LastName\"): '" << gFirstname << "' '" << gLastname << "'" << LL_ENDL; gPassword = LLStartUp::loadPasswordFromDisk(); show_connect_box = true; } @@ -895,8 +900,15 @@ bool idle_startup() gViewerWindow->moveProgressViewToFront(); //reset the values that could have come in from a slurl - gFirstname = gLoginHandler.getFirstName(); - gLastname = gLoginHandler.getLastName(); + // DEV-42215: Make sure they're not empty -- gFirstname and gLastname + // might already have been set from gSavedSettings, and it's too bad + // to overwrite valid values with empty strings. + if (! gLoginHandler.getFirstName().empty() && ! gLoginHandler.getLastName().empty()) + { + gFirstname = gLoginHandler.getFirstName(); + gLastname = gLoginHandler.getLastName(); + LL_DEBUGS("LLStartup") << "STATE_LOGIN_CLEANUP: setting gFirstname, gLastname from gLoginHandler: '" << gFirstname << "' '" << gLastname << "'" << LL_ENDL; + } if (show_connect_box) { @@ -1310,6 +1322,7 @@ bool idle_startup() gViewerWindow->moveProgressViewToFront(); LLError::logToFixedBuffer(gDebugView->mDebugConsolep); + // set initial visibility of debug console gDebugView->mDebugConsolep->setVisible(gSavedSettings.getBOOL("ShowDebugConsole")); } @@ -2525,9 +2538,8 @@ void register_viewer_callbacks(LLMessageSystem* msg) msg->setHandlerFunc("AvatarPickerReply", LLFloaterAvatarPicker::processAvatarPickerReply); - msg->setHandlerFunc("MapLayerReply", LLWorldMap::processMapLayerReply); - msg->setHandlerFunc("MapBlockReply", LLWorldMap::processMapBlockReply); - msg->setHandlerFunc("MapItemReply", LLWorldMap::processMapItemReply); + msg->setHandlerFunc("MapBlockReply", LLWorldMapMessage::processMapBlockReply); + msg->setHandlerFunc("MapItemReply", LLWorldMapMessage::processMapItemReply); msg->setHandlerFunc("EventInfoReply", LLPanelEvent::processEventInfoReply); msg->setHandlerFunc("PickInfoReply", &LLAvatarPropertiesProcessor::processPickInfoReply); diff --git a/indra/newview/llsurface.cpp b/indra/newview/llsurface.cpp index 5440b2c9ad..1d479bac8c 100644 --- a/indra/newview/llsurface.cpp +++ b/indra/newview/llsurface.cpp @@ -234,12 +234,7 @@ void LLSurface::createSTexture() { if (!mSTexturep) { - // Fill with dummy gray data. - - //mSTexturep = LLViewerTextureManager::getLocalTexture(sTextureSize, sTextureSize, 3, FALSE); - //mSTexturep->dontDiscard(); - //mSTexturep->setAddressMode(LLTexUnit::TAM_CLAMP); - + // Fill with dummy gray data. // GL NOT ACTIVE HERE LLPointer<LLImageRaw> raw = new LLImageRaw(sTextureSize, sTextureSize, 3); U8 *default_texture = raw->getData(); diff --git a/indra/newview/lltexlayer.cpp b/indra/newview/lltexlayer.cpp index 5d9046ac90..5d682cad3c 100644 --- a/indra/newview/lltexlayer.cpp +++ b/indra/newview/lltexlayer.cpp @@ -670,8 +670,6 @@ BOOL LLTexLayerSet::render( S32 x, S32 y, S32 width, S32 height ) LLGLDepthTest gls_depth(GL_FALSE, GL_FALSE); gGL.setColorMask(true, true); - BOOL render_morph = mAvatar->morphMaskNeedsUpdate(mBakedTexIndex); - // clear buffer area to ensure we don't pick up UI elements { gGL.flush(); @@ -691,12 +689,8 @@ BOOL LLTexLayerSet::render( S32 x, S32 y, S32 width, S32 height ) if (layer->getRenderPass() == LLTexLayer::RP_COLOR) { gGL.flush(); - success &= layer->render(x, y, width, height, render_morph); + success &= layer->render(x, y, width, height); gGL.flush(); - if (layer->isMorphValid()) - { - mAvatar->setMorphMasksValid(TRUE, mBakedTexIndex); - } } } @@ -786,12 +780,10 @@ void LLTexLayerSet::gatherMorphMaskAlpha(U8 *data, S32 width, S32 height) { memset(data, 255, width * height); - BOOL render_morph = mAvatar->morphMaskNeedsUpdate(mBakedTexIndex); - for( layer_list_t::iterator iter = mLayerList.begin(); iter != mLayerList.end(); iter++ ) { LLTexLayerInterface* layer = *iter; - layer->gatherAlphaMasks(data, mComposite->getOriginX(),mComposite->getOriginY(), width, height, render_morph); + layer->gatherAlphaMasks(data, mComposite->getOriginX(),mComposite->getOriginY(), width, height); } // Set alpha back to that of our alpha masks. @@ -863,6 +855,31 @@ void LLTexLayerSet::applyMorphMask(U8* tex_data, S32 width, S32 height, S32 num_ mAvatar->applyMorphMask(tex_data, width, height, num_components, mBakedTexIndex); } +BOOL LLTexLayerSet::isMorphValid() +{ + for( layer_list_t::iterator iter = mLayerList.begin(); iter != mLayerList.end(); iter++ ) + { + LLTexLayerInterface* layer = *iter; + if (layer && !layer->isMorphValid()) + { + return FALSE; + } + } + return TRUE; +} + +void LLTexLayerSet::invalidateMorphMasks() +{ + for( layer_list_t::iterator iter = mLayerList.begin(); iter != mLayerList.end(); iter++ ) + { + LLTexLayerInterface* layer = *iter; + if (layer) + { + layer->invalidateMorphMasks(); + } + } +} + //----------------------------------------------------------------------------- // LLTexLayerInfo @@ -1282,7 +1299,7 @@ void LLTexLayer::calculateTexLayerColor(const param_color_list_t ¶m_list, LL } } -BOOL LLTexLayer::render(S32 x, S32 y, S32 width, S32 height, BOOL render_morph) +BOOL LLTexLayer::render(S32 x, S32 y, S32 width, S32 height) { LLGLEnable color_mat(GL_COLOR_MATERIAL); gPipeline.disableLights(); @@ -1333,7 +1350,7 @@ BOOL LLTexLayer::render(S32 x, S32 y, S32 width, S32 height, BOOL render_morph) } }//*/ - renderMorphMasks(x, y, width, height, net_color, render_morph); + renderMorphMasks(x, y, width, height, net_color); alpha_mask_specified = TRUE; gGL.flush(); gGL.blendFunc(LLRender::BF_DEST_ALPHA, LLRender::BF_ONE_MINUS_DEST_ALPHA); @@ -1371,7 +1388,7 @@ BOOL LLTexLayer::render(S32 x, S32 y, S32 width, S32 height, BOOL render_morph) LLTexUnit::eTextureAddressMode old_mode = tex->getAddressMode(); - gGL.getTexUnit(0)->bind(tex); + gGL.getTexUnit(0)->bind(tex, TRUE); gGL.getTexUnit(0)->setTextureAddressMode(LLTexUnit::TAM_CLAMP); gl_rect_2d_simple_tex( width, height ); @@ -1393,7 +1410,7 @@ BOOL LLTexLayer::render(S32 x, S32 y, S32 width, S32 height, BOOL render_morph) LLViewerTexture* tex = LLTexLayerStaticImageList::getInstance()->getTexture(getInfo()->mStaticImageFileName, getInfo()->mStaticImageIsMask); if( tex ) { - gGL.getTexUnit(0)->bind(tex); + gGL.getTexUnit(0)->bind(tex, TRUE); gl_rect_2d_simple_tex( width, height ); gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); } @@ -1506,7 +1523,7 @@ BOOL LLTexLayer::blendAlphaTexture(S32 x, S32 y, S32 width, S32 height) if( tex ) { LLGLSNoAlphaTest gls_no_alpha_test; - gGL.getTexUnit(0)->bind(tex); + gGL.getTexUnit(0)->bind(tex, TRUE); gl_rect_2d_simple_tex( width, height ); gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); } @@ -1534,12 +1551,12 @@ BOOL LLTexLayer::blendAlphaTexture(S32 x, S32 y, S32 width, S32 height) return success; } -/*virtual*/ void LLTexLayer::gatherAlphaMasks(U8 *data, S32 originX, S32 originY, S32 width, S32 height, BOOL render_morph) +/*virtual*/ void LLTexLayer::gatherAlphaMasks(U8 *data, S32 originX, S32 originY, S32 width, S32 height) { - addAlphaMask(data, originX, originY, width, height, render_morph); + addAlphaMask(data, originX, originY, width, height); } -BOOL LLTexLayer::renderMorphMasks(S32 x, S32 y, S32 width, S32 height, const LLColor4 &layer_color, BOOL render_morph) +BOOL LLTexLayer::renderMorphMasks(S32 x, S32 y, S32 width, S32 height, const LLColor4 &layer_color) { BOOL success = TRUE; @@ -1578,46 +1595,38 @@ BOOL LLTexLayer::renderMorphMasks(S32 x, S32 y, S32 width, S32 height, const LLC // Accumulate the alpha component of the texture if( getInfo()->mLocalTexture != -1 ) { - LLViewerTexture* tex = mLocalTextureObject->getImage(); - if( tex && (tex->getComponents() == 4) ) - { - LLGLSNoAlphaTest gls_no_alpha_test; + LLViewerTexture* tex = mLocalTextureObject->getImage(); + if( tex && (tex->getComponents() == 4) ) + { + LLGLSNoAlphaTest gls_no_alpha_test; - LLTexUnit::eTextureAddressMode old_mode = tex->getAddressMode(); - - gGL.getTexUnit(0)->bind(tex); - gGL.getTexUnit(0)->setTextureAddressMode(LLTexUnit::TAM_CLAMP); + LLTexUnit::eTextureAddressMode old_mode = tex->getAddressMode(); + + gGL.getTexUnit(0)->bind(tex, TRUE); + gGL.getTexUnit(0)->setTextureAddressMode(LLTexUnit::TAM_CLAMP); - gl_rect_2d_simple_tex( width, height ); + gl_rect_2d_simple_tex( width, height ); - gGL.getTexUnit(0)->setTextureAddressMode(old_mode); - gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - } - else - { - success = FALSE; - } + gGL.getTexUnit(0)->setTextureAddressMode(old_mode); + gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); } + } if( !getInfo()->mStaticImageFileName.empty() ) { - LLViewerTexture* tex = LLTexLayerStaticImageList::getInstance()->getTexture(getInfo()->mStaticImageFileName, getInfo()->mStaticImageIsMask); - if( tex ) - { - if( (tex->getComponents() == 4) || - ( (tex->getComponents() == 1) && getInfo()->mStaticImageIsMask ) ) - { - LLGLSNoAlphaTest gls_no_alpha_test; - gGL.getTexUnit(0)->bind(tex); - gl_rect_2d_simple_tex( width, height ); - gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - } - } - else + LLViewerTexture* tex = LLTexLayerStaticImageList::getInstance()->getTexture(getInfo()->mStaticImageFileName, getInfo()->mStaticImageIsMask); + if( tex ) + { + if( (tex->getComponents() == 4) || + ( (tex->getComponents() == 1) && getInfo()->mStaticImageIsMask ) ) { - success = FALSE; + LLGLSNoAlphaTest gls_no_alpha_test; + gGL.getTexUnit(0)->bind(tex, TRUE); + gl_rect_2d_simple_tex( width, height ); + gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); } } + } // Draw a rectangle with the layer color to multiply the alpha by that color's alpha. // Note: we're still using gGL.blendFunc( GL_DST_ALPHA, GL_ZERO ); @@ -1634,7 +1643,7 @@ BOOL LLTexLayer::renderMorphMasks(S32 x, S32 y, S32 width, S32 height, const LLC gGL.setColorMask(true, true); - if (render_morph && mHasMorph && success) + if (hasMorph() && success) { LLCRC alpha_mask_crc; const LLUUID& uuid = getUUID(); @@ -1674,7 +1683,7 @@ BOOL LLTexLayer::renderMorphMasks(S32 x, S32 y, S32 width, S32 height, const LLC return success; } -void LLTexLayer::addAlphaMask(U8 *data, S32 originX, S32 originY, S32 width, S32 height, BOOL render_morph) +void LLTexLayer::addAlphaMask(U8 *data, S32 originX, S32 originY, S32 width, S32 height) { S32 size = width * height; U8* alphaData = getAlphaData(); @@ -1684,7 +1693,7 @@ void LLTexLayer::addAlphaMask(U8 *data, S32 originX, S32 originY, S32 width, S32 findNetColor( &net_color ); // TODO: eliminate need for layer morph mask valid flag invalidateMorphMasks(); - renderMorphMasks(originX, originY, width, height, net_color, render_morph); + renderMorphMasks(originX, originY, width, height, net_color); alphaData = getAlphaData(); } if (alphaData) @@ -1805,7 +1814,7 @@ LLTexLayer* LLTexLayerTemplate::getLayer(U32 i) return layer; } -/*virtual*/ BOOL LLTexLayerTemplate::render(S32 x, S32 y, S32 width, S32 height, BOOL render_morph) +/*virtual*/ BOOL LLTexLayerTemplate::render(S32 x, S32 y, S32 width, S32 height) { BOOL success = TRUE; updateWearableCache(); @@ -1827,7 +1836,7 @@ LLTexLayer* LLTexLayerTemplate::getLayer(U32 i) { wearable->writeToAvatar(FALSE, FALSE); layer->setLTO(lto); - success &= layer->render(x,y,width,height,render_morph); + success &= layer->render(x,y,width,height); } } @@ -1849,7 +1858,7 @@ LLTexLayer* LLTexLayerTemplate::getLayer(U32 i) return success; } -/*virtual*/ void LLTexLayerTemplate::gatherAlphaMasks(U8 *data, S32 originX, S32 originY, S32 width, S32 height, BOOL render_morph) +/*virtual*/ void LLTexLayerTemplate::gatherAlphaMasks(U8 *data, S32 originX, S32 originY, S32 width, S32 height) { U32 num_wearables = updateWearableCache(); for (U32 i = 0; i < num_wearables; i++) @@ -1857,7 +1866,7 @@ LLTexLayer* LLTexLayerTemplate::getLayer(U32 i) LLTexLayer *layer = getLayer(i); if (layer) { - layer->addAlphaMask(data, originX, originY, width, height, render_morph); + layer->addAlphaMask(data, originX, originY, width, height); } } } @@ -2034,7 +2043,7 @@ LLViewerTexture* LLTexLayerStaticImageList::getTexture(const std::string& file_n // that once an image is a mask it's always a mask. tex->setExplicitFormat( GL_ALPHA8, GL_ALPHA ); } - tex->createGLTexture(0, image_raw); + tex->createGLTexture(0, image_raw, 0, TRUE, LLViewerTexture::LOCAL); gGL.getTexUnit(0)->bind(tex); tex->setAddressMode(LLTexUnit::TAM_CLAMP); diff --git a/indra/newview/lltexlayer.h b/indra/newview/lltexlayer.h index e4a6e82ba5..cd8f27a96b 100644 --- a/indra/newview/lltexlayer.h +++ b/indra/newview/lltexlayer.h @@ -81,13 +81,14 @@ public: const LLTexLayerInfo* getInfo() const { return mInfo; } virtual BOOL setInfo(const LLTexLayerInfo *info, LLWearable* wearable ); // This sets mInfo and calls initialization functions - virtual BOOL render(S32 x, S32 y, S32 width, S32 height, BOOL render_morph) = 0; + virtual BOOL render(S32 x, S32 y, S32 width, S32 height) = 0; void requestUpdate(); LLTexLayerSet* const getTexLayerSet() const { return mTexLayerSet; } virtual void deleteCaches() = 0; void invalidateMorphMasks(); virtual void setHasMorph(BOOL newval) { mHasMorph = newval; } + BOOL hasMorph() { return mHasMorph; } BOOL isMorphValid() { return mMorphMasksValid; } const std::string& getName() const; @@ -95,7 +96,7 @@ public: const std::string& getGlobalColor() const; virtual BOOL blendAlphaTexture( S32 x, S32 y, S32 width, S32 height) = 0; - virtual void gatherAlphaMasks(U8 *data, S32 originX, S32 originY, S32 width, S32 height, BOOL render_morph) = 0; + virtual void gatherAlphaMasks(U8 *data, S32 originX, S32 originY, S32 width, S32 height) = 0; BOOL hasAlphaParams() const { return !mParamAlphaList.empty(); } BOOL isVisibilityMask() const; @@ -134,10 +135,10 @@ public: LLTexLayerTemplate(const LLTexLayerTemplate &layer); /*virtual*/ ~LLTexLayerTemplate(); - /*virtual*/ BOOL render(S32 x, S32 y, S32 width, S32 height, BOOL render_morph); + /*virtual*/ BOOL render(S32 x, S32 y, S32 width, S32 height); /*virtual*/ BOOL setInfo(const LLTexLayerInfo *info, LLWearable* wearable ); // This sets mInfo and calls initialization functions /*virtual*/ BOOL blendAlphaTexture( S32 x, S32 y, S32 width, S32 height); // Multiplies a single alpha texture against the frame buffer - /*virtual*/ void gatherAlphaMasks(U8 *data, S32 originX, S32 originY, S32 width, S32 height, BOOL render_morph); + /*virtual*/ void gatherAlphaMasks(U8 *data, S32 originX, S32 originY, S32 width, S32 height); /*virtual*/ void setHasMorph(BOOL newval); /*virtual*/ void deleteCaches(); private: @@ -162,16 +163,16 @@ public: /*virtual*/ ~LLTexLayer(); /*virtual*/ BOOL setInfo(const LLTexLayerInfo *info, LLWearable* wearable ); // This sets mInfo and calls initialization functions - /*virtual*/ BOOL render(S32 x, S32 y, S32 width, S32 height, BOOL render_morph); + /*virtual*/ BOOL render(S32 x, S32 y, S32 width, S32 height); /*virtual*/ void deleteCaches(); U8* getAlphaData(); BOOL findNetColor(LLColor4* color) const; /*virtual*/ BOOL blendAlphaTexture( S32 x, S32 y, S32 width, S32 height); // Multiplies a single alpha texture against the frame buffer - /*virtual*/ void gatherAlphaMasks(U8 *data, S32 originX, S32 originY, S32 width, S32 height, BOOL render_morph); - BOOL renderMorphMasks(S32 x, S32 y, S32 width, S32 height, const LLColor4 &layer_color, BOOL render_morph); - void addAlphaMask(U8 *data, S32 originX, S32 originY, S32 width, S32 height, BOOL render_morph); + /*virtual*/ void gatherAlphaMasks(U8 *data, S32 originX, S32 originY, S32 width, S32 height); + BOOL renderMorphMasks(S32 x, S32 y, S32 width, S32 height, const LLColor4 &layer_color); + void addAlphaMask(U8 *data, S32 originX, S32 originY, S32 width, S32 height); void setLTO(LLLocalTextureObject *lto) { mLocalTextureObject = lto; } LLLocalTextureObject* getLTO() { return mLocalTextureObject; } @@ -261,6 +262,8 @@ public: void deleteCaches(); void gatherMorphMaskAlpha(U8 *data, S32 width, S32 height); void applyMorphMask(U8* tex_data, S32 width, S32 height, S32 num_components); + BOOL isMorphValid(); + void invalidateMorphMasks(); LLTexLayerInterface* findLayerByName(const std::string& name); void cloneTemplates(LLLocalTextureObject *lto, LLVOAvatarDefines::ETextureIndex tex_index, LLWearable* wearable); diff --git a/indra/newview/lltexturecache.cpp b/indra/newview/lltexturecache.cpp index 1b249d75d1..69a2d1d7a6 100644 --- a/indra/newview/lltexturecache.cpp +++ b/indra/newview/lltexturecache.cpp @@ -43,11 +43,17 @@ // Included to allow LLTextureCache::purgeTextures() to pause watchdog timeout #include "llappviewer.h" -#define USE_LFS_READ 0 -#define USE_LFS_WRITE 0 - -// Note: first 4 bytes store file size, rest is j2c data -const S32 TEXTURE_CACHE_ENTRY_SIZE = FIRST_PACKET_SIZE; //1024; +// Cache organization: +// cache/texture.entries +// Unordered array of Entry structs +// cache/texture.cache +// First TEXTURE_CACHE_ENTRY_SIZE bytes of each texture in texture.entries in same order +// cache/textures/[0-F]/UUID.texture +// Actual texture body files + +const S32 TEXTURE_CACHE_ENTRY_SIZE = 1024; +const F32 TEXTURE_CACHE_PURGE_AMOUNT = .20f; // % amount to reduce the cache by when it exceeds its limit +const F32 TEXTURE_CACHE_LRU_SIZE = .10f; // % amount for LRU list (low overhead to regenerate) class LLTextureCacheWorker : public LLWorkerClass { @@ -309,94 +315,75 @@ void LLTextureCacheWorker::startWork(S32 param) { } +// This is where a texture is read from the cache system (header and body) +// Current assumption are: +// - the whole data are in a raw form, will be stored at mReadData +// - the size of this raw data is mDataSize and can be smaller than TEXTURE_CACHE_ENTRY_SIZE (the size of a record in the header cache) +// - the code supports offset reading but this is actually never exercised in the viewer bool LLTextureCacheRemoteWorker::doRead() { + bool done = false; + S32 idx = -1; + S32 local_size = 0; std::string local_filename; + // First state / stage : find out if the file is local if (mState == INIT) { std::string filename = mCache->getLocalFileName(mID); - local_filename = filename + ".j2c"; - local_size = LLAPRFile::size(local_filename, mCache->getLocalAPRFilePool()); - if (local_size == 0) + // Is it a JPEG2000 file? { - local_filename = filename + ".tga"; + local_filename = filename + ".j2c"; local_size = LLAPRFile::size(local_filename, mCache->getLocalAPRFilePool()); if (local_size > 0) { - mImageFormat = IMG_CODEC_TGA; - mDataSize = local_size; // Only a complete .tga file is valid + mImageFormat = IMG_CODEC_J2C; } } - if (local_size > 0) - { - mState = LOCAL; - } - else - { - mState = CACHE; - } - } - - if (mState == LOCAL) - { -#if USE_LFS_READ - if (mFileHandle == LLLFSThread::nullHandle()) + // If not, is it a jpeg file? + if (local_size == 0) { - mImageLocal = TRUE; - mImageSize = local_size; - if (!mDataSize || mDataSize + mOffset > local_size) + local_filename = filename + ".jpg"; + local_size = LLAPRFile::size(local_filename, mCache->getLocalAPRFilePool()); + if (local_size > 0) { - mDataSize = local_size - mOffset; + mImageFormat = IMG_CODEC_JPEG; + mDataSize = local_size; // Only a complete .jpg file is valid } - if (mDataSize <= 0) - { - // no more data to read - mDataSize = 0; - return true; - } - mReadData = new U8[mDataSize]; - mBytesRead = -1; - mBytesToRead = mDataSize; - setPriority(LLWorkerThread::PRIORITY_LOW | mPriority); - mFileHandle = LLLFSThread::sLocal->read(local_filename, mReadData, mOffset, mDataSize, - new ReadResponder(mCache, mRequestHandle)); - return false; } - else + // Hmm... What about a targa file? (used for UI texture mostly) + if (local_size == 0) { - if (mBytesRead >= 0) - { - if (mBytesRead != mBytesToRead) - { -// llwarns << "Error reading file from local cache: " << local_filename -// << " Bytes: " << mDataSize << " Offset: " << mOffset -// << " / " << mDataSize << llendl; - mDataSize = 0; // failed - delete[] mReadData; - mReadData = NULL; - } - return true; - } - else + local_filename = filename + ".tga"; + local_size = LLAPRFile::size(local_filename, mCache->getLocalAPRFilePool()); + if (local_size > 0) { - return false; + mImageFormat = IMG_CODEC_TGA; + mDataSize = local_size; // Only a complete .tga file is valid } } -#else + // Determine the next stage: if we found a file, then LOCAL else CACHE + mState = (local_size > 0 ? LOCAL : CACHE); + } + + // Second state / stage : if the file is local, load it and leave + if (!done && (mState == LOCAL)) + { + llassert(local_size != 0); // we're assuming there is a non empty local file here... if (!mDataSize || mDataSize > local_size) { mDataSize = local_size; } + // Allocate read buffer mReadData = new U8[mDataSize]; S32 bytes_read = LLAPRFile::readEx(local_filename, mReadData, mOffset, mDataSize, mCache->getLocalAPRFilePool()); if (bytes_read != mDataSize) { -// llwarns << "Error reading file from local cache: " << local_filename -// << " Bytes: " << mDataSize << " Offset: " << mOffset -// << " / " << mDataSize << llendl; + llwarns << "Error reading file from local cache: " << local_filename + << " Bytes: " << mDataSize << " Offset: " << mOffset + << " / " << mDataSize << llendl; mDataSize = 0; delete[] mReadData; mReadData = NULL; @@ -406,405 +393,275 @@ bool LLTextureCacheRemoteWorker::doRead() mImageSize = local_size; mImageLocal = TRUE; } - return true; -#endif + // We're done... + done = true; } - S32 idx = -1; - - if (mState == CACHE) + // Second state / stage : identify the cache or not... + if (!done && (mState == CACHE)) { - llassert_always(mImageSize == 0); - idx = mCache->getHeaderCacheEntry(mID, false, &mImageSize); - if (idx >= 0 && mImageSize > mOffset) + idx = mCache->getHeaderCacheEntry(mID, mImageSize); + if (idx < 0) { - llassert_always(mImageSize > 0); - if (!mDataSize || mDataSize > mImageSize) - { - mDataSize = mImageSize; - } - mState = mOffset < TEXTURE_CACHE_ENTRY_SIZE ? HEADER : BODY; + // The texture is *not* cached. We're done here... + mDataSize = 0; // no data + done = true; } else { - mDataSize = 0; // no data - return true; + // If the read offset is bigger than the header cache, we read directly from the body + // Note that currently, we *never* read with offset from the cache, so the result is *always* HEADER + mState = mOffset < TEXTURE_CACHE_ENTRY_SIZE ? HEADER : BODY; } } - if (mState == HEADER) + // Third state / stage : read data from the header cache (texture.entries) file + if (!done && (mState == HEADER)) { -#if USE_LFS_READ - if (mFileHandle == LLLFSThread::nullHandle()) - { - llassert_always(idx >= 0); - llassert_always(mOffset < TEXTURE_CACHE_ENTRY_SIZE); - S32 offset = idx * TEXTURE_CACHE_ENTRY_SIZE + mOffset; - S32 size = TEXTURE_CACHE_ENTRY_SIZE - mOffset; - llassert_always(mReadData == NULL); - mReadData = new U8[size]; - mBytesRead = -1; - mBytesToRead = size; - setPriority(LLWorkerThread::PRIORITY_LOW | mPriority); - mFileHandle = LLLFSThread::sLocal->read(mCache->mHeaderDataFileName, - mReadData, offset, mBytesToRead, - new ReadResponder(mCache, mRequestHandle)); - return false; - } - else - { - if (mBytesRead >= 0) - { - if (mBytesRead != mBytesToRead) - { -// llwarns << "LLTextureCacheWorker: " << mID -// << " incorrect number of bytes read from header: " << mBytesRead -// << " != " << mBytesToRead << llendl; - mDataSize = -1; // failed - return true; - } - if (mDataSize <= TEXTURE_CACHE_ENTRY_SIZE) - { - return true; // done - } - else - { - mFileHandle = LLLFSThread::nullHandle(); - mState = BODY; - } - } - else - { - return false; - } - } -#else - llassert_always(idx >= 0); + llassert_always(idx >= 0); // we need an entry here or reading the header makes no sense llassert_always(mOffset < TEXTURE_CACHE_ENTRY_SIZE); S32 offset = idx * TEXTURE_CACHE_ENTRY_SIZE + mOffset; + // Compute the size we need to read (in bytes) S32 size = TEXTURE_CACHE_ENTRY_SIZE - mOffset; + size = llmin(size, mDataSize); + // Allocate the read buffer mReadData = new U8[size]; S32 bytes_read = LLAPRFile::readEx(mCache->mHeaderDataFileName, mReadData, offset, size, mCache->getLocalAPRFilePool()); if (bytes_read != size) { -// llwarns << "LLTextureCacheWorker: " << mID -// << " incorrect number of bytes read from header: " << bytes_read -// << " / " << size << llendl; + llwarns << "LLTextureCacheWorker: " << mID + << " incorrect number of bytes read from header: " << bytes_read + << " / " << size << llendl; + delete[] mReadData; + mReadData = NULL; mDataSize = -1; // failed - return true; + done = true; } - if (mDataSize <= TEXTURE_CACHE_ENTRY_SIZE) + // If we already read all we expected, we're actually done + if (mDataSize <= bytes_read) { - return true; // done + done = true; } else { mState = BODY; } -#endif } - if (mState == BODY) + // Fourth state / stage : read the rest of the data from the UUID based cached file + if (!done && (mState == BODY)) { -#if USE_LFS_READ - if (mFileHandle == LLLFSThread::nullHandle()) - { - std::string filename = mCache->getTextureFileName(mID); - S32 filesize = LLAPRFile::size(filename, mCache->getLocalAPRFilePool()); - if (filesize > mOffset) - { - S32 datasize = TEXTURE_CACHE_ENTRY_SIZE + filesize; - mDataSize = llmin(datasize, mDataSize); - S32 data_offset = TEXTURE_CACHE_ENTRY_SIZE - mOffset; - data_offset = llmax(data_offset, 0); - S32 file_size = mDataSize - data_offset; - S32 file_offset = mOffset - TEXTURE_CACHE_ENTRY_SIZE; - file_offset = llmax(file_offset, 0); - - llassert_always(mDataSize > 0); - U8* data = new U8[mDataSize]; - if (data_offset > 0) - { - llassert_always(mReadData); - llassert_always(data_offset <= mDataSize); - memcpy(data, mReadData, data_offset); - delete[] mReadData; - mReadData = NULL; - } - llassert_always(mReadData == NULL); - mReadData = data; - - mBytesRead = -1; - mBytesToRead = file_size; - setPriority(LLWorkerThread::PRIORITY_LOW | mPriority); - llassert_always(data_offset + mBytesToRead <= mDataSize); - mFileHandle = LLLFSThread::sLocal->read(filename, - mReadData + data_offset, file_offset, mBytesToRead, - new ReadResponder(mCache, mRequestHandle)); - return false; - } - else - { - mDataSize = TEXTURE_CACHE_ENTRY_SIZE; - return true; // done - } - } - else - { - if (mBytesRead >= 0) - { - if (mBytesRead != mBytesToRead) - { -// llwarns << "LLTextureCacheWorker: " << mID -// << " incorrect number of bytes read from body: " << mBytesRead -// << " != " << mBytesToRead << llendl; - mDataSize = -1; // failed - } - return true; - } - else - { - return false; - } - } -#else std::string filename = mCache->getTextureFileName(mID); S32 filesize = LLAPRFile::size(filename, mCache->getLocalAPRFilePool()); - S32 bytes_read = 0; - if (filesize > mOffset) + + if (filesize && (filesize + TEXTURE_CACHE_ENTRY_SIZE) > mOffset) { - S32 datasize = TEXTURE_CACHE_ENTRY_SIZE + filesize; - mDataSize = llmin(datasize, mDataSize); - S32 data_offset = TEXTURE_CACHE_ENTRY_SIZE - mOffset; - data_offset = llmax(data_offset, 0); - S32 file_size = mDataSize - data_offset; - S32 file_offset = mOffset - TEXTURE_CACHE_ENTRY_SIZE; - file_offset = llmax(file_offset, 0); + S32 max_datasize = TEXTURE_CACHE_ENTRY_SIZE + filesize - mOffset; + mDataSize = llmin(max_datasize, mDataSize); + + S32 data_offset, file_size, file_offset; + // Reserve the whole data buffer first U8* data = new U8[mDataSize]; - if (data_offset > 0) + + // Set the data file pointers taking the read offset into account. 2 cases: + if (mOffset < TEXTURE_CACHE_ENTRY_SIZE) { + // Offset within the header record. That means we read something from the header cache. + // Note: most common case is (mOffset = 0), so this is the "normal" code path. + data_offset = TEXTURE_CACHE_ENTRY_SIZE - mOffset; // i.e. TEXTURE_CACHE_ENTRY_SIZE if mOffset nul (common case) + file_offset = 0; + file_size = mDataSize - data_offset; + // Copy the raw data we've been holding from the header cache into the new sized buffer llassert_always(mReadData); memcpy(data, mReadData, data_offset); delete[] mReadData; + mReadData = NULL; + } + else + { + // Offset bigger than the header record. That means we haven't read anything yet. + data_offset = 0; + file_offset = mOffset - TEXTURE_CACHE_ENTRY_SIZE; + file_size = mDataSize; + // No data from header cache to copy in that case, we skipped it all } + + // Now use that buffer as the object read buffer + llassert_always(mReadData == NULL); mReadData = data; - bytes_read = LLAPRFile::readEx(filename, + + // Read the data at last + S32 bytes_read = LLAPRFile::readEx(filename, mReadData + data_offset, file_offset, file_size, mCache->getLocalAPRFilePool()); if (bytes_read != file_size) { -// llwarns << "LLTextureCacheWorker: " << mID -// << " incorrect number of bytes read from body: " << bytes_read -// << " / " << file_size << llendl; + llwarns << "LLTextureCacheWorker: " << mID + << " incorrect number of bytes read from body: " << bytes_read + << " / " << file_size << llendl; + delete[] mReadData; + mReadData = NULL; mDataSize = -1; // failed - return true; + done = true; } } else { - mDataSize = TEXTURE_CACHE_ENTRY_SIZE; - } - - return true; -#endif + // No body, we're done. + mDataSize = llmax(TEXTURE_CACHE_ENTRY_SIZE - mOffset, 0); + lldebugs << "No body file for: " << filename << llendl; + } + // Nothing else to do at that point... + done = true; } - - return false; + + // Clean up and exit + return done; } +// This is where *everything* about a texture is written down in the cache system (entry map, header and body) +// Current assumption are: +// - the whole data are in a raw form, starting at mWriteData +// - the size of this raw data is mDataSize and can be smaller than TEXTURE_CACHE_ENTRY_SIZE (the size of a record in the header cache) +// - the code *does not* support offset writing so there are no difference between buffer addresses and start of data bool LLTextureCacheRemoteWorker::doWrite() { + bool done = false; S32 idx = -1; - // No LOCAL state for write() - + // First state / stage : check that what we're trying to cache is in an OK shape if (mState == INIT) { + llassert_always(mOffset == 0); // We currently do not support write offsets + llassert_always(mDataSize > 0); // Things will go badly wrong if mDataSize is nul or negative... + mState = CACHE; + } + + // No LOCAL state for write(): because it doesn't make much sense to cache a local file... + + // Second state / stage : set an entry in the headers entry (texture.entries) file + if (!done && (mState == CACHE)) + { + bool alreadyCached = false; S32 cur_imagesize = 0; - S32 offset = mOffset; - idx = mCache->getHeaderCacheEntry(mID, false, &cur_imagesize); - if (idx >= 0 && cur_imagesize > 0) + // Checks if this image is already in the entry list + idx = mCache->getHeaderCacheEntry(mID, cur_imagesize); + if (idx >= 0 && (cur_imagesize >= 0)) { - offset = TEXTURE_CACHE_ENTRY_SIZE; // don't re-write header + alreadyCached = true; // already there and non empty } - idx = mCache->getHeaderCacheEntry(mID, true, &mImageSize); // touch entry - if (idx >= 0) + idx = mCache->setHeaderCacheEntry(mID, mImageSize); // create or touch the entry + if (idx < 0) { - if(cur_imagesize > 0 && mImageSize != cur_imagesize) - { -// llwarns << "Header cache entry size: " << cur_imagesize << " != mImageSize: " << mImageSize << llendl; - offset = 0; // re-write header - } - mState = offset < TEXTURE_CACHE_ENTRY_SIZE ? HEADER : BODY; + llwarns << "LLTextureCacheWorker: " << mID + << " Unable to create header entry for writing!" << llendl; + mDataSize = -1; // failed + done = true; } else { - mDataSize = -1; // failed - return true; + if (cur_imagesize > 0 && (mImageSize != cur_imagesize)) + { + alreadyCached = false; // re-write the header if the size changed in all cases + } + if (alreadyCached && (mDataSize <= TEXTURE_CACHE_ENTRY_SIZE)) + { + // Small texture already cached case: we're done with writing + done = true; + } + else + { + // If the texture has already been cached, we don't resave the header and go directly to the body part + mState = alreadyCached ? BODY : HEADER; + } } } - - if (mState == HEADER) + + // Third stage / state : write the header record in the header file (texture.cache) + if (!done && (mState == HEADER)) { -#if USE_LFS_WRITE - if (mFileHandle == LLLFSThread::nullHandle()) + llassert_always(idx >= 0); // we need an entry here or storing the header makes no sense + S32 offset = idx * TEXTURE_CACHE_ENTRY_SIZE; // skip to the correct spot in the header file + S32 size = TEXTURE_CACHE_ENTRY_SIZE; // record size is fixed for the header + S32 bytes_written; + + if (mDataSize < TEXTURE_CACHE_ENTRY_SIZE) { - llassert_always(idx >= 0); - llassert_always(mOffset < TEXTURE_CACHE_ENTRY_SIZE); - S32 offset = idx * TEXTURE_CACHE_ENTRY_SIZE + mOffset; - S32 size = TEXTURE_CACHE_ENTRY_SIZE - mOffset; - mBytesRead = -1; - mBytesToRead = size; - setPriority(LLWorkerThread::PRIORITY_LOW | mPriority); - mFileHandle = LLLFSThread::sLocal->write(mCache->mHeaderDataFileName, - mWriteData, offset, mBytesToRead, - new WriteResponder(mCache, mRequestHandle)); - return false; + // We need to write a full record in the header cache so, if the amount of data is smaller + // than a record, we need to transfer the data to a buffer padded with 0 and write that + U8* padBuffer = new U8[TEXTURE_CACHE_ENTRY_SIZE]; + memset(padBuffer, 0, TEXTURE_CACHE_ENTRY_SIZE); // Init with zeros + memcpy(padBuffer, mWriteData, mDataSize); // Copy the write buffer + bytes_written = LLAPRFile::writeEx(mCache->mHeaderDataFileName, padBuffer, offset, size, mCache->getLocalAPRFilePool()); + delete [] padBuffer; } else { - if (mBytesRead >= 0) - { - if (mBytesRead != mBytesToRead) - { -// llwarns << "LLTextureCacheWorker: " << mID -// << " incorrect number of bytes written to header: " << mBytesRead -// << " != " << mBytesToRead << llendl; - mDataSize = -1; // failed - return true; - } - if (mDataSize <= mBytesToRead) - { - return true; // done - } - else - { - mFileHandle = LLLFSThread::nullHandle(); - mState = BODY; - } - } - else - { - return false; - } + // Write the header record (== first TEXTURE_CACHE_ENTRY_SIZE bytes of the raw file) in the header file + bytes_written = LLAPRFile::writeEx(mCache->mHeaderDataFileName, mWriteData, offset, size, mCache->getLocalAPRFilePool()); } -#else - llassert_always(idx >= 0); - llassert_always(mOffset < TEXTURE_CACHE_ENTRY_SIZE); - S32 offset = idx * TEXTURE_CACHE_ENTRY_SIZE + mOffset; - S32 size = TEXTURE_CACHE_ENTRY_SIZE - mOffset; - S32 bytes_written = LLAPRFile::writeEx(mCache->mHeaderDataFileName, mWriteData, offset, size, mCache->getLocalAPRFilePool()); if (bytes_written <= 0) { -// llwarns << "LLTextureCacheWorker: missing entry: " << mID << llendl; + llwarns << "LLTextureCacheWorker: " << mID + << " Unable to write header entry!" << llendl; mDataSize = -1; // failed - return true; + done = true; } - if (mDataSize <= size) + // If we wrote everything (may be more with padding) in the header cache, + // we're done so we don't have a body to store + if (mDataSize <= bytes_written) { - return true; // done + done = true; } else { mState = BODY; } -#endif } - if (mState == BODY) + // Fourth stage / state : write the body file, i.e. the rest of the texture in a "UUID" file name + if (!done && (mState == BODY)) { -#if USE_LFS_WRITE - if (mFileHandle == LLLFSThread::nullHandle()) + llassert(mDataSize > TEXTURE_CACHE_ENTRY_SIZE); // wouldn't make sense to be here otherwise... + S32 file_size = mDataSize - TEXTURE_CACHE_ENTRY_SIZE; + if ((file_size > 0) && mCache->updateTextureEntryList(mID, file_size)) { - S32 data_offset = TEXTURE_CACHE_ENTRY_SIZE - mOffset; - data_offset = llmax(data_offset, 0); - S32 file_size = mDataSize - data_offset; - S32 file_offset = mOffset - TEXTURE_CACHE_ENTRY_SIZE; - file_offset = llmax(file_offset, 0); - if (file_size > 0 && mCache->appendToTextureEntryList(mID, file_size)) - { - std::string filename = mCache->getTextureFileName(mID); - mBytesRead = -1; - mBytesToRead = file_size; - setPriority(LLWorkerThread::PRIORITY_LOW | mPriority); - mFileHandle = LLLFSThread::sLocal->write(filename, - mWriteData + data_offset, file_offset, mBytesToRead, - new WriteResponder(mCache, mRequestHandle)); - return false; - } - else - { - mDataSize = 0; // no data written - return true; // done - } - } - else - { - if (mBytesRead >= 0) - { - if (mBytesRead != mBytesToRead) - { -// llwarns << "LLTextureCacheWorker: " << mID -// << " incorrect number of bytes written to body: " << mBytesRead -// << " != " << mBytesToRead << llendl; - mDataSize = -1; // failed - } - return true; - } - else - { - return false; - } - } -#else - S32 data_offset = TEXTURE_CACHE_ENTRY_SIZE - mOffset; - data_offset = llmax(data_offset, 0); - S32 file_size = mDataSize - data_offset; - S32 file_offset = mOffset - TEXTURE_CACHE_ENTRY_SIZE; - file_offset = llmax(file_offset, 0); - S32 bytes_written = 0; - if (file_size > 0 && mCache->appendToTextureEntryList(mID, file_size)) - { - std::string filename = mCache->getTextureFileName(mID); - - bytes_written = LLAPRFile::writeEx(filename, - mWriteData + data_offset, - file_offset, file_size, - mCache->getLocalAPRFilePool()); + // build the cache file name from the UUID + std::string filename = mCache->getTextureFileName(mID); +// llinfos << "Writing Body: " << filename << " Bytes: " << file_offset+file_size << llendl; + S32 bytes_written = LLAPRFile::writeEx( filename, + mWriteData + TEXTURE_CACHE_ENTRY_SIZE, + 0, file_size, + mCache->getLocalAPRFilePool()); if (bytes_written <= 0) { + llwarns << "LLTextureCacheWorker: " << mID + << " incorrect number of bytes written to body: " << bytes_written + << " / " << file_size << llendl; mDataSize = -1; // failed + done = true; } } else { mDataSize = 0; // no data written } - - return true; -#endif + // Nothing else to do at that point... + done = true; } - - return false; + + // Clean up and exit + return done; } //virtual bool LLTextureCacheWorker::doWork(S32 param) { -// *TODO reenable disabled apr_pool usage disabled due to maint-render-9 merge breakage -brad - //allocate a new local apr_pool -// LLAPRPool pool ; - - //save the current mFileAPRPool to avoid breaking anything. -// apr_pool_t* old_pool = mCache->getFileAPRPool() ; - //make mFileAPRPool to point to the local one -// mCache->setFileAPRPool(pool.getAPRPool()) ; - bool res = false; if (param == 0) // read { @@ -818,10 +675,6 @@ bool LLTextureCacheWorker::doWork(S32 param) { llassert_always(0); } - - //set mFileAPRPool back, the local one will be released automatically. -// mCache->setFileAPRPool(old_pool) ; - return res; } @@ -887,6 +740,7 @@ LLTextureCache::LLTextureCache(bool threaded) mWorkersMutex(NULL), mHeaderMutex(NULL), mListMutex(NULL), + mHeaderAPRFile(NULL), mReadOnly(FALSE), mTexturesSizeTotal(0), mDoPurge(FALSE) @@ -926,6 +780,9 @@ S32 LLTextureCache::update(U32 max_time_ms) } } + unlockWorkers(); + + // call 'completed' with workers list unlocked (may call readComplete() or writeComplete() for (responder_list_t::iterator iter1 = completed_list.begin(); iter1 != completed_list.end(); ++iter1) { @@ -934,8 +791,6 @@ S32 LLTextureCache::update(U32 max_time_ms) responder->completed(success); } - unlockWorkers(); - return res; } @@ -954,33 +809,48 @@ std::string LLTextureCache::getTextureFileName(const LLUUID& id) { std::string idstr = id.asString(); std::string delem = gDirUtilp->getDirDelimiter(); - std::string filename = mTexturesDirName + delem + idstr[0] + delem + idstr; + std::string filename = mTexturesDirName + delem + idstr[0] + delem + idstr + ".texture"; return filename; } -bool LLTextureCache::appendToTextureEntryList(const LLUUID& id, S32 bodysize) +bool LLTextureCache::updateTextureEntryList(const LLUUID& id, S32 bodysize) { bool res = false; bool purge = false; - // Append UUID to end of texture entries { LLMutexLock lock(&mHeaderMutex); - size_map_t::iterator iter = mTexturesSizeMap.find(id); - if (iter == mTexturesSizeMap.end() || iter->second < bodysize) + size_map_t::iterator iter1 = mTexturesSizeMap.find(id); + if (iter1 == mTexturesSizeMap.end() || iter1->second < bodysize) { llassert_always(bodysize > 0); - Entry* entry = new Entry(id, bodysize, time(NULL)); - LLAPRFile::writeEx(mTexturesDirEntriesFileName, - (U8*)entry, -1, 1*sizeof(Entry), - getLocalAPRFilePool()); - delete entry; - if (iter != mTexturesSizeMap.end()) + S32 oldbodysize = 0; + if (iter1 != mTexturesSizeMap.end()) + { + oldbodysize = iter1->second; + } + + Entry entry; + S32 idx = openAndReadEntry(id, entry, false); + if (idx < 0) + { + // TODO: change to llwarns + llerrs << "Failed to open entry: " << id << llendl; + removeFromCache(id); + return false; + } + else if (oldbodysize != entry.mBodySize) { - mTexturesSizeTotal -= iter->second; + // TODO: change to llwarns + llerrs << "Entry mismatch in mTextureSizeMap / mHeaderIDMap" + << " idx=" << idx << " oldsize=" << oldbodysize << " entrysize=" << entry.mBodySize << llendl; } + entry.mBodySize = bodysize; + writeEntryAndClose(idx, entry); + + mTexturesSizeTotal -= oldbodysize; mTexturesSizeTotal += bodysize; - mTexturesSizeMap[id] = bodysize; + if (mTexturesSizeTotal > sCacheMaxTexturesSize) { purge = true; @@ -995,11 +865,59 @@ bool LLTextureCache::appendToTextureEntryList(const LLUUID& id, S32 bodysize) return res; } +//debug +BOOL LLTextureCache::isInCache(const LLUUID& id) +{ + LLMutexLock lock(&mHeaderMutex); + id_map_t::const_iterator iter = mHeaderIDMap.find(id); + + return (iter != mHeaderIDMap.end()) ; +} + +//debug +BOOL LLTextureCache::isInLocal(const LLUUID& id) +{ + S32 local_size = 0; + std::string local_filename; + + std::string filename = getLocalFileName(id); + // Is it a JPEG2000 file? + { + local_filename = filename + ".j2c"; + local_size = LLAPRFile::size(local_filename, getLocalAPRFilePool()); + if (local_size > 0) + { + return TRUE ; + } + } + + // If not, is it a jpeg file? + { + local_filename = filename + ".jpg"; + local_size = LLAPRFile::size(local_filename, getLocalAPRFilePool()); + if (local_size > 0) + { + return TRUE ; + } + } + + // Hmm... What about a targa file? (used for UI texture mostly) + { + local_filename = filename + ".tga"; + local_size = LLAPRFile::size(local_filename, getLocalAPRFilePool()); + if (local_size > 0) + { + return TRUE ; + } + } + + return FALSE ; +} ////////////////////////////////////////////////////////////////////////////// //static const S32 MAX_REASONABLE_FILE_SIZE = 512*1024*1024; // 512 MB -F32 LLTextureCache::sHeaderCacheVersion = 1.0f; +F32 LLTextureCache::sHeaderCacheVersion = 1.3f; U32 LLTextureCache::sCacheMaxEntries = MAX_REASONABLE_FILE_SIZE / TEXTURE_CACHE_ENTRY_SIZE; S64 LLTextureCache::sCacheMaxTexturesSize = 0; // no limit const char* entries_filename = "texture.entries"; @@ -1012,7 +930,6 @@ void LLTextureCache::setDirNames(ELLPath location) mHeaderEntriesFileName = gDirUtilp->getExpandedFilename(location, entries_filename); mHeaderDataFileName = gDirUtilp->getExpandedFilename(location, cache_filename); mTexturesDirName = gDirUtilp->getExpandedFilename(location, textures_dirname); - mTexturesDirEntriesFileName = mTexturesDirName + delem + entries_filename; } void LLTextureCache::purgeCache(ELLPath location) @@ -1020,7 +937,7 @@ void LLTextureCache::purgeCache(ELLPath location) if (!mReadOnly) { setDirNames(location); - + llassert_always(mHeaderAPRFile == NULL); LLAPRFile::remove(mHeaderEntriesFileName, getLocalAPRFilePool()); LLAPRFile::remove(mHeaderDataFileName, getLocalAPRFilePool()); } @@ -1063,83 +980,317 @@ S64 LLTextureCache::initCache(ELLPath location, S64 max_size, BOOL read_only) return max_size; // unused cache space } -struct lru_data +//---------------------------------------------------------------------------- +// mHeaderMutex must be locked for the following functions! + +LLAPRFile* LLTextureCache::openHeaderEntriesFile(bool readonly, S32 offset) +{ + llassert_always(mHeaderAPRFile == NULL); + apr_int32_t flags = readonly ? APR_READ|APR_BINARY : APR_READ|APR_WRITE|APR_BINARY; + mHeaderAPRFile = new LLAPRFile(mHeaderEntriesFileName, flags, getLocalAPRFilePool()); + mHeaderAPRFile->seek(APR_SET, offset); + return mHeaderAPRFile; +} + +void LLTextureCache::closeHeaderEntriesFile() +{ + llassert_always(mHeaderAPRFile != NULL); + delete mHeaderAPRFile; + mHeaderAPRFile = NULL; +} + +void LLTextureCache::readEntriesHeader() +{ + // mHeaderEntriesInfo initializes to default values so safe not to read it + llassert_always(mHeaderAPRFile == NULL); + if (LLAPRFile::isExist(mHeaderEntriesFileName, getLocalAPRFilePool())) + { + LLAPRFile::readEx(mHeaderEntriesFileName, (U8*)&mHeaderEntriesInfo, 0, sizeof(EntriesInfo), + getLocalAPRFilePool()); + } +} + +void LLTextureCache::writeEntriesHeader() { - lru_data(U32 t, S32 i, const LLUUID& id) { time=t; index=i; uuid=id; } - U32 time; - S32 index; - LLUUID uuid; - struct Compare - { - // lhs < rhs - typedef const lru_data* lru_data_ptr; - bool operator()(const lru_data_ptr& a, const lru_data_ptr& b) const + llassert_always(mHeaderAPRFile == NULL); + if (!mReadOnly) + { + LLAPRFile::writeEx(mHeaderEntriesFileName, (U8*)&mHeaderEntriesInfo, 0, sizeof(EntriesInfo), + getLocalAPRFilePool()); + } +} + +static S32 mHeaderEntriesMaxWriteIdx = 0; + +S32 LLTextureCache::openAndReadEntry(const LLUUID& id, Entry& entry, bool create) +{ + S32 idx = -1; + + id_map_t::iterator iter1 = mHeaderIDMap.find(id); + if (iter1 != mHeaderIDMap.end()) + { + idx = iter1->second; + } + + if (idx < 0) + { + if (create && !mReadOnly) { - if(a->time == b->time) - return (a->index < b->index); + if (mHeaderEntriesInfo.mEntries < sCacheMaxEntries) + { + // Add an entry to the end of the list + idx = mHeaderEntriesInfo.mEntries++; + + } + else if (!mFreeList.empty()) + { + idx = *(mFreeList.begin()); + mFreeList.erase(mFreeList.begin()); + } else - return (a->time >= b->time); + { + // Look for a still valid entry in the LRU + for (std::set<LLUUID>::iterator iter2 = mLRU.begin(); iter2 != mLRU.end();) + { + std::set<LLUUID>::iterator curiter2 = iter2++; + LLUUID oldid = *curiter2; + // Erase entry from LRU regardless + mLRU.erase(curiter2); + // Look up entry and use it if it is valid + id_map_t::iterator iter3 = mHeaderIDMap.find(oldid); + if (iter3 != mHeaderIDMap.end() && iter3->second >= 0) + { + idx = iter3->second; + mHeaderIDMap.erase(oldid); + mTexturesSizeMap.erase(oldid); + break; + } + } + // if (idx < 0) at this point, we will rebuild the LRU + // and retry if called from setHeaderCacheEntry(), + // otherwise this shouldn't happen and will trigger an error + } + if (idx >= 0) + { + // Set the header index + mHeaderIDMap[id] = idx; + llassert_always(mTexturesSizeMap.erase(id) == 0); + // Initialize the entry (will get written later) + entry.init(id, time(NULL)); + // Update Header + writeEntriesHeader(); + // Write Entry + S32 offset = sizeof(EntriesInfo) + idx * sizeof(Entry); + LLAPRFile* aprfile = openHeaderEntriesFile(false, offset); + S32 bytes_written = aprfile->write((void*)&entry, (S32)sizeof(Entry)); + llassert_always(bytes_written == sizeof(Entry)); + mHeaderEntriesMaxWriteIdx = llmax(mHeaderEntriesMaxWriteIdx, idx); + closeHeaderEntriesFile(); + } } - }; -}; + } + else + { + // Remove this entry from the LRU if it exists + mLRU.erase(id); + // Read the entry + S32 offset = sizeof(EntriesInfo) + idx * sizeof(Entry); + LLAPRFile* aprfile = openHeaderEntriesFile(true, offset); + S32 bytes_read = aprfile->read((void*)&entry, (S32)sizeof(Entry)); + llassert_always(bytes_read == sizeof(Entry)); + llassert_always(entry.mImageSize == 0 || entry.mImageSize == -1 || entry.mImageSize > entry.mBodySize); + closeHeaderEntriesFile(); + } + return idx; +} + +void LLTextureCache::writeEntryAndClose(S32 idx, Entry& entry) +{ + if (idx >= 0) + { + if (!mReadOnly) + { + entry.mTime = time(NULL); + llassert_always(entry.mImageSize == 0 || entry.mImageSize == -1 || entry.mImageSize > entry.mBodySize); + if (entry.mBodySize > 0) + { + mTexturesSizeMap[entry.mID] = entry.mBodySize; + } +// llinfos << "Updating TE: " << idx << ": " << id << " Size: " << entry.mBodySize << " Time: " << entry.mTime << llendl; + S32 offset = sizeof(EntriesInfo) + idx * sizeof(Entry); + LLAPRFile* aprfile = openHeaderEntriesFile(false, offset); + S32 bytes_written = aprfile->write((void*)&entry, (S32)sizeof(Entry)); + llassert_always(bytes_written == sizeof(Entry)); + mHeaderEntriesMaxWriteIdx = llmax(mHeaderEntriesMaxWriteIdx, idx); + closeHeaderEntriesFile(); + } + } +} + +U32 LLTextureCache::openAndReadEntries(std::vector<Entry>& entries) +{ + U32 num_entries = mHeaderEntriesInfo.mEntries; + + mHeaderIDMap.clear(); + mTexturesSizeMap.clear(); + mFreeList.clear(); + mTexturesSizeTotal = 0; + + LLAPRFile* aprfile = openHeaderEntriesFile(false, (S32)sizeof(EntriesInfo)); + for (U32 idx=0; idx<num_entries; idx++) + { + Entry entry; + S32 bytes_read = aprfile->read((void*)(&entry), (S32)sizeof(Entry)); + if (bytes_read < sizeof(Entry)) + { + llwarns << "Corrupted header entries, failed at " << idx << " / " << num_entries << llendl; + closeHeaderEntriesFile(); + purgeAllTextures(false); + return 0; + } + entries.push_back(entry); +// llinfos << "ENTRY: " << entry.mTime << " TEX: " << entry.mID << " IDX: " << idx << " Size: " << entry.mImageSize << llendl; + if (entry.mImageSize < 0) + { + mFreeList.insert(idx); + } + else + { + mHeaderIDMap[entry.mID] = idx; + if (entry.mBodySize > 0) + { + mTexturesSizeMap[entry.mID] = entry.mBodySize; + mTexturesSizeTotal += entry.mBodySize; + } + llassert_always(entry.mImageSize == 0 || entry.mImageSize > entry.mBodySize); + } + } + closeHeaderEntriesFile(); + return num_entries; +} + +void LLTextureCache::writeEntriesAndClose(const std::vector<Entry>& entries) +{ + S32 num_entries = entries.size(); + llassert_always(num_entries == mHeaderEntriesInfo.mEntries); + + if (!mReadOnly) + { + LLAPRFile* aprfile = openHeaderEntriesFile(false, (S32)sizeof(EntriesInfo)); + for (S32 idx=0; idx<num_entries; idx++) + { + S32 bytes_written = aprfile->write((void*)(&entries[idx]), (S32)sizeof(Entry)); + llassert_always(bytes_written == sizeof(Entry)); + } + mHeaderEntriesMaxWriteIdx = llmax(mHeaderEntriesMaxWriteIdx, num_entries-1); + closeHeaderEntriesFile(); + } +} + +//---------------------------------------------------------------------------- // Called from either the main thread or the worker thread void LLTextureCache::readHeaderCache() { LLMutexLock lock(&mHeaderMutex); - mHeaderEntriesInfo.mVersion = 0.f; - mHeaderEntriesInfo.mEntries = 0; - if (LLAPRFile::isExist(mHeaderEntriesFileName, getLocalAPRFilePool())) - { - LLAPRFile::readEx(mHeaderEntriesFileName, - (U8*)&mHeaderEntriesInfo, 0, sizeof(EntriesInfo), - getLocalAPRFilePool()); - } + + mLRU.clear(); // always clear the LRU + + readEntriesHeader(); + if (mHeaderEntriesInfo.mVersion != sHeaderCacheVersion) { if (!mReadOnly) { - // Info with 0 entries - mHeaderEntriesInfo.mVersion = sHeaderCacheVersion; - - LLAPRFile::writeEx(mHeaderEntriesFileName, - (U8*)&mHeaderEntriesInfo, 0, sizeof(EntriesInfo), - getLocalAPRFilePool()); + purgeAllTextures(false); } } else { - S32 num_entries = mHeaderEntriesInfo.mEntries; + std::vector<Entry> entries; + U32 num_entries = openAndReadEntries(entries); if (num_entries) { - Entry* entries = new Entry[num_entries]; + U32 empty_entries = 0; + typedef std::pair<U32, LLUUID> lru_data_t; + std::set<lru_data_t> lru; + std::vector<LLUUID> purge_list; + for (U32 i=0; i<num_entries; i++) + { + Entry& entry = entries[i]; + const LLUUID& id = entry.mID; + if (entry.mImageSize < 0) + { + // This will be in the Free List, don't put it in the LRY + ++empty_entries; + } + else + { + lru.insert(std::make_pair(entry.mTime, id)); + if (entry.mBodySize > 0) + { + if (entry.mBodySize > entry.mImageSize) + { + // Shouldn't happen, failsafe only + llwarns << "Bad entry: " << i << ": " << id << ": BodySize: " << entry.mBodySize << llendl; + purge_list.push_back(id); + } + } + } + } + if (num_entries > sCacheMaxEntries) + { + // Special case: cache size was reduced, need to remove entries + // Note: After we prune entries, we will call this again and create the LRU + U32 entries_to_purge = (num_entries-empty_entries) - sCacheMaxEntries; + if (entries_to_purge > 0) + { + for (std::set<lru_data_t>::iterator iter = lru.begin(); iter != lru.end(); ++iter) + { + purge_list.push_back(iter->second); + if (--entries_to_purge <= 0) + break; + } + } + } + else { - LLAPRFile::readEx(mHeaderEntriesFileName, - (U8*)entries, sizeof(EntriesInfo), num_entries*sizeof(Entry), - getLocalAPRFilePool()); + S32 lru_entries = (S32)((F32)sCacheMaxEntries * TEXTURE_CACHE_LRU_SIZE); + for (std::set<lru_data_t>::iterator iter = lru.begin(); iter != lru.end(); ++iter) + { + mLRU.insert(iter->second); +// llinfos << "LRU: " << iter->first << " : " << iter->second << llendl; + if (--lru_entries <= 0) + break; + } } - typedef std::set<lru_data*, lru_data::Compare> lru_set_t; - lru_set_t lru; - for (S32 i=0; i<num_entries; i++) + + if (purge_list.size() > 0) { - if (entries[i].mSize >= 0) // -1 indicates erased entry, skip + for (std::vector<LLUUID>::iterator iter = purge_list.begin(); iter != purge_list.end(); ++iter) { - const LLUUID& id = entries[i].mID; - lru.insert(new lru_data(entries[i].mTime, i, id)); - mHeaderIDMap[id] = i; + removeFromCache(*iter); } + // If we removed any entries, we need to rebuild the entries list, + // write the header, and call this again + std::vector<Entry> new_entries; + for (U32 i=0; i<num_entries; i++) + { + const Entry& entry = entries[i]; + if (entry.mImageSize >=0) + { + new_entries.push_back(entry); + } + } + llassert_always(new_entries.size() <= sCacheMaxEntries); + mHeaderEntriesInfo.mEntries = new_entries.size(); + writeEntriesAndClose(new_entries); + readHeaderCache(); // repeat with new entries file } - mLRU.clear(); - S32 lru_entries = sCacheMaxEntries / 10; - for (lru_set_t::iterator iter = lru.begin(); iter != lru.end(); ++iter) + else { - lru_data* data = *iter; - mLRU[data->index] = data->uuid; - if (--lru_entries <= 0) - break; + writeEntriesAndClose(entries); } - for_each(lru.begin(), lru.end(), DeletePointer()); - delete[] entries; } } } @@ -1162,13 +1313,21 @@ void LLTextureCache::purgeAllTextures(bool purge_directories) LLFile::rmdir(dirname); } } - LLAPRFile::remove(mTexturesDirEntriesFileName, getLocalAPRFilePool()); if (purge_directories) { LLFile::rmdir(mTexturesDirName); } } + mHeaderIDMap.clear(); mTexturesSizeMap.clear(); + mTexturesSizeTotal = 0; + mFreeList.clear(); + mTexturesSizeTotal = 0; + + // Info with 0 entries + mHeaderEntriesInfo.mVersion = sHeaderCacheVersion; + mHeaderEntriesInfo.mEntries = 0; + writeEntriesHeader(); } void LLTextureCache::purgeTextures(bool validate) @@ -1182,51 +1341,37 @@ void LLTextureCache::purgeTextures(bool validate) LLAppViewer::instance()->pauseMainloopTimeout(); LLMutexLock lock(&mHeaderMutex); - - S32 filesize = LLAPRFile::size(mTexturesDirEntriesFileName, getLocalAPRFilePool()); - S32 num_entries = filesize / sizeof(Entry); - if (num_entries * (S32)sizeof(Entry) != filesize) - { - LL_WARNS("TextureCache") << "Bad cache file: " << mTexturesDirEntriesFileName << " Purging." << LL_ENDL; - purgeAllTextures(false); - return; - } - if (num_entries == 0) + + llinfos << "TEXTURE CACHE: Purging." << llendl; + + // Read the entries list + std::vector<Entry> entries; + U32 num_entries = openAndReadEntries(entries); + if (!num_entries) { - return; // nothing to do + writeEntriesAndClose(entries); + return; // nothing to purge } - Entry* entries = new Entry[num_entries]; - S32 bytes_read = LLAPRFile::readEx(mTexturesDirEntriesFileName, - (U8*)entries, 0, num_entries*sizeof(Entry), - getLocalAPRFilePool()); - if (bytes_read != filesize) - { - LL_WARNS("TextureCache") << "Bad cache file (2): " << mTexturesDirEntriesFileName << " Purging." << LL_ENDL; - purgeAllTextures(false); - return; - } - - LL_DEBUGS("TextureCache") << "TEXTURE CACHE: Reading Entries..." << LL_ENDL; - - std::map<LLUUID, S32> entry_idx_map; - S64 total_size = 0; - for (S32 idx=0; idx<num_entries; idx++) - { - const LLUUID& id = entries[idx].mID; - LL_DEBUGS("TextureCache") << "Entry: " << id << " Size: " << entries[idx].mSize << " Time: " << entries[idx].mTime << LL_ENDL; - std::map<LLUUID, S32>::iterator iter = entry_idx_map.find(id); - if (iter != entry_idx_map.end()) + // Use mTexturesSizeMap to collect UUIDs of textures with bodies + typedef std::set<std::pair<U32,S32> > time_idx_set_t; + std::set<std::pair<U32,S32> > time_idx_set; + for (size_map_t::iterator iter1 = mTexturesSizeMap.begin(); + iter1 != mTexturesSizeMap.end(); ++iter1) + { + if (iter1->second > 0) { - // Newer entry replacing older entry - S32 pidx = iter->second; - total_size -= entries[pidx].mSize; - entries[pidx].mSize = 0; // flag: skip older entry + id_map_t::iterator iter2 = mHeaderIDMap.find(iter1->first); + if (iter2 != mHeaderIDMap.end()) + { + S32 idx = iter2->second; + time_idx_set.insert(std::make_pair(entries[idx].mTime, idx)); +// llinfos << "TIME: " << entries[idx].mTime << " TEX: " << entries[idx].mID << " IDX: " << idx << " Size: " << entries[idx].mImageSize << llendl; + } } - entry_idx_map[id] = idx; - total_size += entries[idx].mSize; } - + + // Validate 1/256th of the files on startup U32 validate_idx = 0; if (validate) { @@ -1235,19 +1380,17 @@ void LLTextureCache::purgeTextures(bool validate) gSavedSettings.setU32("CacheValidateCounter", next_idx); LL_DEBUGS("TextureCache") << "TEXTURE CACHE: Validating: " << validate_idx << LL_ENDL; } - - S64 min_cache_size = (sCacheMaxTexturesSize * 9) / 10; + + S64 cache_size = mTexturesSizeTotal; + S64 purged_cache_size = (sCacheMaxTexturesSize * (S64)((1.f-TEXTURE_CACHE_PURGE_AMOUNT)*100)) / 100; S32 purge_count = 0; - S32 next_idx = 0; - for (S32 idx=0; idx<num_entries; idx++) + for (time_idx_set_t::iterator iter = time_idx_set.begin(); + iter != time_idx_set.end(); ++iter) { - if (entries[idx].mSize == 0) - { - continue; - } + S32 idx = iter->second; bool purge_entry = false; std::string filename = getTextureFileName(entries[idx].mID); - if (total_size >= min_cache_size) + if (cache_size >= purged_cache_size) { purge_entry = true; } @@ -1257,60 +1400,44 @@ void LLTextureCache::purgeTextures(bool validate) U32 uuididx = entries[idx].mID.mData[0]; if (uuididx == validate_idx) { - LL_DEBUGS("TextureCache") << "Validating: " << filename << "Size: " << entries[idx].mSize << LL_ENDL; + LL_DEBUGS("TextureCache") << "Validating: " << filename << "Size: " << entries[idx].mBodySize << LL_ENDL; S32 bodysize = LLAPRFile::size(filename, getLocalAPRFilePool()); - if (bodysize != entries[idx].mSize) + if (bodysize != entries[idx].mBodySize) { - LL_WARNS("TextureCache") << "TEXTURE CACHE BODY HAS BAD SIZE: " << bodysize << " != " << entries[idx].mSize + LL_WARNS("TextureCache") << "TEXTURE CACHE BODY HAS BAD SIZE: " << bodysize << " != " << entries[idx].mBodySize << filename << LL_ENDL; purge_entry = true; } } } + else + { + break; + } + if (purge_entry) { purge_count++; LL_DEBUGS("TextureCache") << "PURGING: " << filename << LL_ENDL; LLAPRFile::remove(filename, getLocalAPRFilePool()); - total_size -= entries[idx].mSize; - entries[idx].mSize = 0; - } - else - { - if (next_idx != idx) - { - entries[next_idx] = entries[idx]; - } - ++next_idx; + cache_size -= entries[idx].mBodySize; + mTexturesSizeTotal -= entries[idx].mBodySize; + entries[idx].mBodySize = 0; + mTexturesSizeMap.erase(entries[idx].mID); } } - num_entries = next_idx; LL_DEBUGS("TextureCache") << "TEXTURE CACHE: Writing Entries: " << num_entries << LL_ENDL; - - LLAPRFile::remove(mTexturesDirEntriesFileName, getLocalAPRFilePool()); - LLAPRFile::writeEx(mTexturesDirEntriesFileName, - (U8*)&entries[0], 0, num_entries*sizeof(Entry), - getLocalAPRFilePool()); - - mTexturesSizeTotal = 0; - mTexturesSizeMap.clear(); - for (S32 idx=0; idx<num_entries; idx++) - { - mTexturesSizeMap[entries[idx].mID] = entries[idx].mSize; - mTexturesSizeTotal += entries[idx].mSize; - } - llassert(mTexturesSizeTotal == total_size); - - delete[] entries; + writeEntriesAndClose(entries); + // *FIX:Mani - watchdog back on. LLAppViewer::instance()->resumeMainloopTimeout(); LL_INFOS("TextureCache") << "TEXTURE CACHE:" << " PURGED: " << purge_count << " ENTRIES: " << num_entries - << " CACHE SIZE: " << total_size / 1024*1024 << " MB" + << " CACHE SIZE: " << mTexturesSizeTotal / 1024*1024 << " MB" << llendl; } @@ -1340,78 +1467,39 @@ LLTextureCacheWorker* LLTextureCache::getWriter(handle_t handle) } ////////////////////////////////////////////////////////////////////////////// - // Called from work thread -S32 LLTextureCache::getHeaderCacheEntry(const LLUUID& id, bool touch, S32* imagesize) -{ - bool retry = false; - S32 idx = -1; +// Reads imagesize from the header, updates timestamp +S32 LLTextureCache::getHeaderCacheEntry(const LLUUID& id, S32& imagesize) +{ + LLMutexLock lock(&mHeaderMutex); + Entry entry; + S32 idx = openAndReadEntry(id, entry, false); + if (idx >= 0) { - LLMutexLock lock(&mHeaderMutex); - id_map_t::iterator iter = mHeaderIDMap.find(id); - if (iter != mHeaderIDMap.end()) - { - idx = iter->second; - } - else if (touch && !mReadOnly) - { - if (mHeaderEntriesInfo.mEntries < sCacheMaxEntries) - { - // Add an entry - idx = mHeaderEntriesInfo.mEntries++; - mHeaderIDMap[id] = idx; - // Update Info - LLAPRFile::writeEx(mHeaderEntriesFileName, - (U8*)&mHeaderEntriesInfo, 0, sizeof(EntriesInfo), - getLocalAPRFilePool()); - } - else if (!mLRU.empty()) - { - idx = mLRU.begin()->first; // will be erased below - const LLUUID& oldid = mLRU.begin()->second; - mHeaderIDMap.erase(oldid); - mTexturesSizeMap.erase(oldid); - mHeaderIDMap[id] = idx; - } - else - { - idx = -1; - retry = true; - } - } - if (idx >= 0) - { - if (touch && !mReadOnly) - { - // Update the lru entry - mLRU.erase(idx); - llassert_always(imagesize && *imagesize > 0); - Entry* entry = new Entry(id, *imagesize, time(NULL)); - S32 offset = sizeof(EntriesInfo) + idx * sizeof(Entry); - LLAPRFile::writeEx(mHeaderEntriesFileName, - (U8*)entry, offset, sizeof(Entry), - getLocalAPRFilePool()); - delete entry; - } - else if (imagesize) - { - // Get the image size - Entry entry; - S32 offset = sizeof(EntriesInfo) + idx * sizeof(Entry); + imagesize = entry.mImageSize; + writeEntryAndClose(idx, entry); // updates time + } + return idx; +} - LLAPRFile::readEx(mHeaderEntriesFileName, - (U8*)&entry, offset, sizeof(Entry), - getLocalAPRFilePool()); - *imagesize = entry.mSize; - } - } +// Writes imagesize to the header, updates timestamp +S32 LLTextureCache::setHeaderCacheEntry(const LLUUID& id, S32 imagesize) +{ + LLMutexLock lock(&mHeaderMutex); + llassert_always(imagesize >= 0); + Entry entry; + S32 idx = openAndReadEntry(id, entry, true); + if (idx >= 0) + { + entry.mImageSize = imagesize; + writeEntryAndClose(idx, entry); } - if (retry) + else // retry { - readHeaderCache(); // updates the lru + readHeaderCache(); // We couldn't write an entry, so refresh the LRU llassert_always(!mLRU.empty() || mHeaderEntriesInfo.mEntries < sCacheMaxEntries); - idx = getHeaderCacheEntry(id, touch, imagesize); // assert above ensures no inf. recursion + idx = setHeaderCacheEntry(id, imagesize); // assert above ensures no inf. recursion } return idx; } @@ -1427,8 +1515,8 @@ LLTextureCache::handle_t LLTextureCache::readFromCache(const std::string& filena // so let the thread handle it LLMutexLock lock(&mWorkersMutex); LLTextureCacheWorker* worker = new LLTextureCacheLocalFileWorker(this, priority, filename, id, - NULL, size, offset, 0, - responder); + NULL, size, offset, 0, + responder); handle_t handle = worker->read(); mReaders[handle] = worker; return handle; @@ -1441,8 +1529,8 @@ LLTextureCache::handle_t LLTextureCache::readFromCache(const LLUUID& id, U32 pri // so let the thread handle it LLMutexLock lock(&mWorkersMutex); LLTextureCacheWorker* worker = new LLTextureCacheRemoteWorker(this, priority, id, - NULL, size, offset, 0, - responder); + NULL, size, offset, + 0, responder); handle_t handle = worker->read(); mReaders[handle] = worker; return handle; @@ -1453,7 +1541,7 @@ bool LLTextureCache::readComplete(handle_t handle, bool abort) { lockWorkers(); handle_map_t::iterator iter = mReaders.find(handle); - llassert_always(iter != mReaders.end()); + llassert_always(iter != mReaders.end() || abort); LLTextureCacheWorker* worker = iter->second; bool res = worker->complete(); if (res || abort) @@ -1487,19 +1575,13 @@ LLTextureCache::handle_t LLTextureCache::writeToCache(const LLUUID& id, U32 prio purgeTextures(false); mDoPurge = FALSE; } - if (datasize >= TEXTURE_CACHE_ENTRY_SIZE) - { - LLMutexLock lock(&mWorkersMutex); - llassert_always(imagesize > 0); - LLTextureCacheWorker* worker = new LLTextureCacheRemoteWorker(this, priority, id, - data, datasize, 0, - imagesize, responder); - handle_t handle = worker->write(); - mWriters[handle] = worker; - return handle; - } - delete responder; - return LLWorkerThread::nullHandle(); + LLMutexLock lock(&mWorkersMutex); + LLTextureCacheWorker* worker = new LLTextureCacheRemoteWorker(this, priority, id, + data, datasize, 0, + imagesize, responder); + handle_t handle = worker->write(); + mWriters[handle] = worker; + return handle; } bool LLTextureCache::writeComplete(handle_t handle, bool abort) @@ -1542,25 +1624,17 @@ void LLTextureCache::addCompleted(Responder* responder, bool success) bool LLTextureCache::removeHeaderCacheEntry(const LLUUID& id) { - if (mReadOnly) - { - return false; - } - LLMutexLock lock(&mHeaderMutex); - id_map_t::iterator iter = mHeaderIDMap.find(id); - if (iter != mHeaderIDMap.end()) + if (!mReadOnly) { - S32 idx = iter->second; + LLMutexLock lock(&mHeaderMutex); + Entry entry; + S32 idx = openAndReadEntry(id, entry, false); if (idx >= 0) { - Entry* entry = new Entry(id, -1, time(NULL)); - S32 offset = sizeof(EntriesInfo) + idx * sizeof(Entry); - - LLAPRFile::writeEx(mHeaderEntriesFileName, - (U8*)entry, offset, sizeof(Entry), - getLocalAPRFilePool()); - delete entry; - mLRU[idx] = id; + entry.mImageSize = -1; + entry.mBodySize = 0; + writeEntryAndClose(idx, entry); + mFreeList.insert(idx); mHeaderIDMap.erase(id); mTexturesSizeMap.erase(id); return true; diff --git a/indra/newview/lltexturecache.h b/indra/newview/lltexturecache.h index 68b1458e9a..bc9c988648 100644 --- a/indra/newview/lltexturecache.h +++ b/indra/newview/lltexturecache.h @@ -48,6 +48,27 @@ class LLTextureCache : public LLWorkerThread friend class LLTextureCacheRemoteWorker; friend class LLTextureCacheLocalFileWorker; +private: + // Entries + struct EntriesInfo + { + EntriesInfo() : mVersion(0.f), mEntries(0) {} + F32 mVersion; + U32 mEntries; + }; + struct Entry + { + Entry() {} + Entry(const LLUUID& id, S32 imagesize, S32 bodysize, U32 time) : + mID(id), mImageSize(imagesize), mBodySize(bodysize), mTime(time) {} + void init(const LLUUID& id, U32 time) { mID = id, mImageSize = 0; mBodySize = 0; mTime = time; } + LLUUID mID; // 16 bytes + S32 mImageSize; // total size of image if known + S32 mBodySize; // size of body file in body cache + U32 mTime; // seconds since 1/1/1970 + }; + + public: class Responder : public LLResponder @@ -106,10 +127,16 @@ public: // debug S32 getNumReads() { return mReaders.size(); } S32 getNumWrites() { return mWriters.size(); } + S64 getUsage() { return mTexturesSizeTotal; } + S64 getMaxUsage() { return sCacheMaxTexturesSize; } + U32 getEntries() { return mHeaderEntriesInfo.mEntries; } + U32 getMaxEntries() { return sCacheMaxEntries; }; + BOOL isInCache(const LLUUID& id) ; + BOOL isInLocal(const LLUUID& id) ; protected: // Accessed by LLTextureCacheWorker - bool appendToTextureEntryList(const LLUUID& id, S32 size); + bool updateTextureEntryList(const LLUUID& id, S32 size); std::string getLocalFileName(const LLUUID& id); std::string getTextureFileName(const LLUUID& id); void addCompleted(Responder* responder, bool success); @@ -122,7 +149,16 @@ private: void readHeaderCache(); void purgeAllTextures(bool purge_directories); void purgeTextures(bool validate); - S32 getHeaderCacheEntry(const LLUUID& id, bool touch, S32* imagesize = NULL); + LLAPRFile* openHeaderEntriesFile(bool readonly, S32 offset); + void closeHeaderEntriesFile(); + void readEntriesHeader(); + void writeEntriesHeader(); + S32 openAndReadEntry(const LLUUID& id, Entry& entry, bool create); + void writeEntryAndClose(S32 idx, Entry& entry); + U32 openAndReadEntries(std::vector<Entry>& entries); + void writeEntriesAndClose(const std::vector<Entry>& entries); + S32 getHeaderCacheEntry(const LLUUID& id, S32& imagesize); + S32 setHeaderCacheEntry(const LLUUID& id, S32 imagesize); bool removeHeaderCacheEntry(const LLUUID& id); void lockHeaders() { mHeaderMutex.lock(); } void unlockHeaders() { mHeaderMutex.unlock(); } @@ -132,6 +168,7 @@ private: LLMutex mWorkersMutex; LLMutex mHeaderMutex; LLMutex mListMutex; + LLAPRFile* mHeaderAPRFile; typedef std::map<handle_t, LLTextureCacheWorker*> handle_map_t; handle_map_t mReaders; @@ -145,42 +182,28 @@ private: BOOL mReadOnly; - // Entries - struct EntriesInfo - { - F32 mVersion; - U32 mEntries; - }; - struct Entry - { - Entry() {} - Entry(const LLUUID& id, S32 size, U32 time) : mID(id), mSize(size), mTime(time) {} - LLUUID mID; // 128 bits - S32 mSize; // total size of image if known (NOT size cached) - U32 mTime; // seconds since 1/1/1970 - }; - // HEADERS (Include first mip) std::string mHeaderEntriesFileName; std::string mHeaderDataFileName; EntriesInfo mHeaderEntriesInfo; - typedef std::map<S32,LLUUID> index_map_t; - index_map_t mLRU; // index, id; stored as a map for fast removal + std::set<S32> mFreeList; // deleted entries + std::set<LLUUID> mLRU; typedef std::map<LLUUID,S32> id_map_t; id_map_t mHeaderIDMap; // BODIES (TEXTURES minus headers) std::string mTexturesDirName; - std::string mTexturesDirEntriesFileName; typedef std::map<LLUUID,S32> size_map_t; size_map_t mTexturesSizeMap; S64 mTexturesSizeTotal; LLAtomic32<BOOL> mDoPurge; - + // Statics static F32 sHeaderCacheVersion; static U32 sCacheMaxEntries; static S64 sCacheMaxTexturesSize; }; +extern const S32 TEXTURE_CACHE_ENTRY_SIZE; + #endif // LL_LLTEXTURECACHE_H diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp index 88fc7f98c0..c918f98895 100644 --- a/indra/newview/lltexturefetch.cpp +++ b/indra/newview/lltexturefetch.cpp @@ -32,108 +32,35 @@ #include "llviewerprecompiledheaders.h" +#include <iostream> + #include "llstl.h" #include "lltexturefetch.h" #include "llcurl.h" +#include "lldir.h" #include "llhttpclient.h" +#include "llhttpstatuscodes.h" #include "llimage.h" #include "llimageworker.h" #include "llworkerthread.h" #include "llagent.h" #include "lltexturecache.h" +#include "llviewercontrol.h" #include "llviewertexturelist.h" #include "llviewertexture.h" #include "llviewerregion.h" +#include "llworld.h" ////////////////////////////////////////////////////////////////////////////// -//static class LLTextureFetchWorker : public LLWorkerClass { -friend class LLTextureFetch; - -private: -#if 0 - class URLResponder : public LLHTTPClient::Responder - { - public: - URLResponder(LLTextureFetch* fetcher, const LLUUID& id) - : mFetcher(fetcher), mID(id) - { - } - ~URLResponder() - { - } - virtual void error(U32 status, const std::string& reason) - { - mFetcher->lockQueue(); - LLTextureFetchWorker* worker = mFetcher->getWorker(mID); - if (worker) - { -// llwarns << "LLTextureFetchWorker::URLResponder::error " << status << ": " << reason << llendl; - worker->callbackURLReceived(LLSD(), false); - } - mFetcher->unlockQueue(); - } - virtual void result(const LLSD& content) - { - mFetcher->lockQueue(); - LLTextureFetchWorker* worker = mFetcher->getWorker(mID); - if (worker) - { - worker->callbackURLReceived(content, true); - } - mFetcher->unlockQueue(); - } - private: - LLTextureFetch* mFetcher; - LLUUID mID; - }; - - class HTTPGetResponder : public LLHTTPClient::Responder - { - public: - HTTPGetResponder(LLTextureFetch* fetcher, const LLUUID& id) - : mFetcher(fetcher), mID(id) - { - } - ~HTTPGetResponder() - { - } - virtual void completed(U32 status, const std::stringstream& content) - { - mFetcher->lockQueue(); - LLTextureFetchWorker* worker = mFetcher->getWorker(mID); - if (worker) - { - const std::string& cstr = content.str(); - if (200 <= status && status < 300) - { - if (203 == status) // partial information (i.e. last block) - { - worker->callbackHttpGet((U8*)cstr.c_str(), cstr.length(), true); - } - else - { - worker->callbackHttpGet((U8*)cstr.c_str(), cstr.length(), false); - } - } - else - { -// llinfos << "LLTextureFetchWorker::HTTPGetResponder::error " << status << ": " << cstr << llendl; - worker->callbackHttpGet(NULL, -1, true); - } - } - mFetcher->unlockQueue(); - } - private: - LLTextureFetch* mFetcher; - LLUUID mID; - }; -#endif + friend class LLTextureFetch; + friend class HTTPGetResponder; +private: class CacheReadResponder : public LLTextureCache::ReadResponder { public: @@ -179,20 +106,20 @@ private: LLUUID mID; }; - class DecodeResponder : public LLResponder + class DecodeResponder : public LLImageDecodeThread::Responder { public: DecodeResponder(LLTextureFetch* fetcher, const LLUUID& id, LLTextureFetchWorker* worker) : mFetcher(fetcher), mID(id), mWorker(worker) { } - virtual void completed(bool success) + virtual void completed(bool success, LLImageRaw* raw, LLImageRaw* aux) { mFetcher->lockQueue(); LLTextureFetchWorker* worker = mFetcher->getWorker(mID); if (worker) { - worker->callbackDecoded(success); + worker->callbackDecoded(success, raw, aux); } mFetcher->unlockQueue(); } @@ -227,37 +154,45 @@ public: ~LLTextureFetchWorker(); void relese() { --mActiveCount; } + void callbackHttpGet(const LLChannelDescriptors& channels, + const LLIOPipe::buffer_ptr_t& buffer, + bool last_block, bool success); + void callbackCacheRead(bool success, LLImageFormatted* image, + S32 imagesize, BOOL islocal); + void callbackCacheWrite(bool success); + void callbackDecoded(bool success, LLImageRaw* raw, LLImageRaw* aux); + + void setGetStatus(U32 status, const std::string& reason) + { + mGetStatus = status; + mGetReason = reason; + } + protected: LLTextureFetchWorker(LLTextureFetch* fetcher, const LLUUID& id, const LLHost& host, F32 priority, S32 discard, S32 size); + LLTextureFetchWorker(LLTextureFetch* fetcher, const std::string& url, const LLUUID& id, const LLHost& host, + F32 priority, S32 discard, S32 size); private: /*virtual*/ void startWork(S32 param); // called from addWork() (MAIN THREAD) /*virtual*/ void endWork(S32 param, bool aborted); // called from doWork() (MAIN THREAD) - virtual std::string getName() { return LLStringUtil::null; } void resetFormattedData(); void setImagePriority(F32 priority); void setDesiredDiscard(S32 discard, S32 size); bool insertPacket(S32 index, U8* data, S32 size); void clearPackets(); + void setupPacketData(); U32 calcWorkPriority(); void removeFromCache(); bool processSimulatorPackets(); - bool decodeImage(); bool writeToCacheComplete(); - void lockWorkData() { mWorkMutex.lock(); } - void unlockWorkData() { mWorkMutex.unlock(); } + void lockWorkMutex() { mWorkMutex.lock(); } + void unlockWorkMutex() { mWorkMutex.unlock(); } - void callbackURLReceived(const LLSD& data, bool success); - void callbackHttpGet(U8* data, S32 data_size, bool last_block); - void callbackCacheRead(bool success, LLImageFormatted* image, - S32 imagesize, BOOL islocal); - void callbackCacheWrite(bool success); - void callbackDecoded(bool success); - private: enum e_state // mState { @@ -268,8 +203,8 @@ private: CACHE_POST, LOAD_FROM_NETWORK, LOAD_FROM_SIMULATOR, - LOAD_FROM_HTTP_GET_URL, - LOAD_FROM_HTTP_GET_DATA, + SEND_HTTP_REQ, + WAIT_HTTP_REQ, DECODE_IMAGE, DECODE_IMAGE_UPDATE, WRITE_TO_CACHE, @@ -280,19 +215,17 @@ private: { UNSENT = 0, QUEUED = 1, - SENT_SIM = 2, - SENT_URL = 3, - SENT_HTTP = 4 + SENT_SIM = 2 }; static const char* sStateDescs[]; e_state mState; LLTextureFetch* mFetcher; - LLImageWorker* mImageWorker; LLPointer<LLImageFormatted> mFormattedImage; LLPointer<LLImageRaw> mRawImage; LLPointer<LLImageRaw> mAuxImage; LLUUID mID; LLHost mHost; + std::string mUrl; U8 mType; F32 mImagePriority; U32 mWorkPriority; @@ -314,15 +247,18 @@ private: S32 mCachedSize; BOOL mLoaded; e_request_state mSentRequest; + handle_t mDecodeHandle; BOOL mDecoded; BOOL mWritten; BOOL mNeedsAux; BOOL mHaveAllData; BOOL mInLocalCache; + S32 mHTTPFailCount; S32 mRetryAttempt; - std::string mURL; S32 mActiveCount; - + U32 mGetStatus; + std::string mGetReason; + // Work Data LLMutex mWorkMutex; struct PacketData @@ -340,25 +276,79 @@ private: U8 mImageCodec; }; -class LLTextureFetchLocalFileWorker : public LLTextureFetchWorker -{ -friend class LLTextureFetch; - -protected: - LLTextureFetchLocalFileWorker(LLTextureFetch* fetcher, const std::string& filename, const LLUUID& id, const LLHost& host, - F32 priority, S32 discard, S32 size) - : LLTextureFetchWorker(fetcher, id, host, priority, discard, size), - mFileName(filename) - {} +////////////////////////////////////////////////////////////////////////////// -private: - /*virtual*/ std::string getName() { return mFileName; } +class HTTPGetResponder : public LLCurl::Responder +{ + LOG_CLASS(HTTPGetResponder); +public: + HTTPGetResponder(LLTextureFetch* fetcher, const LLUUID& id, U64 startTime, S32 requestedSize, U32 offset) + : mFetcher(fetcher), mID(id), mStartTime(startTime), mRequestedSize(requestedSize), mOffset(offset) + { + } + ~HTTPGetResponder() + { + } + virtual void completedRaw(U32 status, const std::string& reason, + const LLChannelDescriptors& channels, + const LLIOPipe::buffer_ptr_t& buffer) + { + if ((gSavedSettings.getBOOL("LogTextureDownloadsToViewerLog")) || (gSavedSettings.getBOOL("LogTextureDownloadsToSimulator"))) + { + mFetcher->mTextureInfo.setRequestStartTime(mID, mStartTime); + U64 timeNow = LLTimer::getTotalTime(); + mFetcher->mTextureInfo.setRequestType(mID, LLTextureInfoDetails::REQUEST_TYPE_HTTP); + mFetcher->mTextureInfo.setRequestSize(mID, mRequestedSize); + mFetcher->mTextureInfo.setRequestOffset(mID, mOffset); + mFetcher->mTextureInfo.setRequestCompleteTimeAndLog(mID, timeNow); + } + lldebugs << "HTTP COMPLETE: " << mID << llendl; + mFetcher->lockQueue(); + LLTextureFetchWorker* worker = mFetcher->getWorker(mID); + if (worker) + { + bool success = false; + bool partial = false; + if (200 <= status && status < 300) + { + success = true; + if (203 == status) // partial information (i.e. last block) + { + partial = true; + } + } + else + { + worker->setGetStatus(status, reason); +// llwarns << status << ": " << reason << llendl; + } + if (!success) + { + worker->setGetStatus(status, reason); +// llwarns << "CURL GET FAILED, status:" << status << " reason:" << reason << llendl; + } + mFetcher->removeFromHTTPQueue(mID); + worker->callbackHttpGet(channels, buffer, partial, success); + } + else + { + mFetcher->removeFromHTTPQueue(mID); + llwarns << "Worker not found: " << mID << llendl; + } + mFetcher->unlockQueue(); + } + private: - std::string mFileName; + LLTextureFetch* mFetcher; + LLUUID mID; + U64 mStartTime; + S32 mRequestedSize; + U32 mOffset; }; +////////////////////////////////////////////////////////////////////////////// //static const char* LLTextureFetchWorker::sStateDescs[] = { @@ -368,8 +358,8 @@ const char* LLTextureFetchWorker::sStateDescs[] = { "CACHE_POST", "LOAD_FROM_NETWORK", "LOAD_FROM_SIMULATOR", - "LOAD_FROM_HTTP_URL", - "LOAD_FROM_HTTP_DATA", + "SEND_HTTP_REQ", + "WAIT_HTTP_REQ", "DECODE_IMAGE", "DECODE_IMAGE_UPDATE", "WRITE_TO_CACHE", @@ -380,6 +370,7 @@ const char* LLTextureFetchWorker::sStateDescs[] = { // called from MAIN THREAD LLTextureFetchWorker::LLTextureFetchWorker(LLTextureFetch* fetcher, + const std::string& url, // Optional URL const LLUUID& id, // Image UUID const LLHost& host, // Simulator host F32 priority, // Priority @@ -388,9 +379,9 @@ LLTextureFetchWorker::LLTextureFetchWorker(LLTextureFetch* fetcher, : LLWorkerClass(fetcher, "TextureFetch"), mState(INIT), mFetcher(fetcher), - mImageWorker(NULL), mID(id), mHost(host), + mUrl(url), mImagePriority(priority), mWorkPriority(0), mRequestedPriority(0.f), @@ -404,18 +395,21 @@ LLTextureFetchWorker::LLTextureFetchWorker(LLTextureFetch* fetcher, mBuffer(NULL), mBufferSize(0), mRequestedSize(0), - mDesiredSize(FIRST_PACKET_SIZE), + mDesiredSize(TEXTURE_CACHE_ENTRY_SIZE), mFileSize(0), mCachedSize(0), mLoaded(FALSE), mSentRequest(UNSENT), + mDecodeHandle(0), mDecoded(FALSE), mWritten(FALSE), mNeedsAux(FALSE), mHaveAllData(FALSE), mInLocalCache(FALSE), + mHTTPFailCount(0), mRetryAttempt(0), mActiveCount(0), + mGetStatus(0), mWorkMutex(NULL), mFirstPacket(0), mLastPacket(-1), @@ -440,7 +434,7 @@ LLTextureFetchWorker::~LLTextureFetchWorker() // << " Requested=" << mRequestedDiscard // << " Desired=" << mDesiredDiscard << llendl; llassert_always(!haveWork()); - lockWorkData(); + lockWorkMutex(); if (mCacheReadHandle != LLTextureCache::nullHandle()) { mFetcher->mTextureCache->readComplete(mCacheReadHandle, true); @@ -449,13 +443,9 @@ LLTextureFetchWorker::~LLTextureFetchWorker() { mFetcher->mTextureCache->writeComplete(mCacheWriteHandle, true); } - if (mImageWorker) - { - mImageWorker->scheduleDelete(); - } mFormattedImage = NULL; clearPackets(); - unlockWorkData(); + unlockWorkMutex(); } void LLTextureFetchWorker::clearPackets() @@ -467,6 +457,38 @@ void LLTextureFetchWorker::clearPackets() mFirstPacket = 0; } +void LLTextureFetchWorker::setupPacketData() +{ + S32 data_size = 0; + if (mFormattedImage.notNull()) + { + data_size = mFormattedImage->getDataSize(); + } + if (data_size > 0) + { + // Only used for simulator requests + mFirstPacket = (data_size - FIRST_PACKET_SIZE) / MAX_IMG_PACKET_SIZE + 1; + if (FIRST_PACKET_SIZE + (mFirstPacket-1) * MAX_IMG_PACKET_SIZE != data_size) + { + llwarns << "Bad CACHED TEXTURE size: " << data_size << " removing." << llendl; + removeFromCache(); + resetFormattedData(); + clearPackets(); + } + else if (mFileSize > 0) + { + mLastPacket = mFirstPacket-1; + mTotalPackets = (mFileSize - FIRST_PACKET_SIZE + MAX_IMG_PACKET_SIZE-1) / MAX_IMG_PACKET_SIZE + 1; + } + else + { + // This file was cached using HTTP so we have to refetch the first packet + resetFormattedData(); + clearPackets(); + } + } +} + U32 LLTextureFetchWorker::calcWorkPriority() { // llassert_always(mImagePriority >= 0 && mImagePriority <= LLViewerTexture::maxDecodePriority()); @@ -538,7 +560,6 @@ void LLTextureFetchWorker::resetFormattedData() // Called from MAIN thread void LLTextureFetchWorker::startWork(S32 param) { - llassert(mImageWorker == NULL); llassert(mFormattedImage.isNull()); } @@ -549,6 +570,14 @@ bool LLTextureFetchWorker::doWork(S32 param) { LLMutexLock lock(&mWorkMutex); + if ((mFetcher->isQuitting() || getFlags(LLWorkerClass::WCF_DELETE_REQUESTED))) + { + if (mState < WRITE_TO_CACHE) + { + return true; // abort + } + } + if (mFetcher->mDebugPause) { return false; // debug: don't do any work @@ -563,16 +592,9 @@ bool LLTextureFetchWorker::doWork(S32 param) mFetchTimer.reset(); } - if (mImagePriority <= 0.0f) - { - if (mState < WRITE_TO_CACHE) - { - return true; // cancel request - } - } - if (mState == INIT) { + mRawImage = NULL ; mRequestedDiscard = -1; mLoadedDiscard = -1; mDecodedDiscard = -1; @@ -590,8 +612,9 @@ bool LLTextureFetchWorker::doWork(S32 param) clearPackets(); // TODO: Shouldn't be necessary mCacheReadHandle = LLTextureCache::nullHandle(); mCacheWriteHandle = LLTextureCache::nullHandle(); - mURL.clear(); mState = LOAD_FROM_TEXTURE_CACHE; + LL_DEBUGS("Texture") << mID << ": Priority: " << llformat("%8.0f",mImagePriority) + << " Desired Discard: " << mDesiredDiscard << " Desired Size: " << mDesiredSize << LL_ENDL; // fall through } @@ -612,16 +635,27 @@ bool LLTextureFetchWorker::doWork(S32 param) setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); // Set priority first since Responder may change it CacheReadResponder* responder = new CacheReadResponder(mFetcher, mID, mFormattedImage); - if (getName().empty()) + if (mUrl.compare(0, 7, "file://") == 0) + { + // read file from local disk + std::string filename = mUrl.substr(7, std::string::npos); + mCacheReadHandle = mFetcher->mTextureCache->readFromCache(filename, mID, cache_priority, + offset, size, responder); + } + else if (mUrl.empty()) { mCacheReadHandle = mFetcher->mTextureCache->readFromCache(mID, cache_priority, offset, size, responder); } else { - // read file from local disk - mCacheReadHandle = mFetcher->mTextureCache->readFromCache(getName(), mID, cache_priority, - offset, size, responder); + if (!(mUrl.compare(0, 7, "http://") == 0)) + { + // *TODO:?remove this warning + llwarns << "Unknown URL Type: " << mUrl << llendl; + } + setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); + mState = SEND_HTTP_REQ; } } @@ -647,75 +681,101 @@ bool LLTextureFetchWorker::doWork(S32 param) if (mState == CACHE_POST) { - mDesiredSize = llmax(mDesiredSize, FIRST_PACKET_SIZE); + mDesiredSize = llmax(mDesiredSize, TEXTURE_CACHE_ENTRY_SIZE); mCachedSize = mFormattedImage.notNull() ? mFormattedImage->getDataSize() : 0; // Successfully loaded if ((mCachedSize >= mDesiredSize) || mHaveAllData) { // we have enough data, decode it llassert_always(mFormattedImage->getDataSize() > 0); + mLoadedDiscard = mDesiredDiscard; mState = DECODE_IMAGE; + LL_DEBUGS("Texture") << mID << ": Cached. Bytes: " << mFormattedImage->getDataSize() + << " Size: " << llformat("%dx%d",mFormattedImage->getWidth(),mFormattedImage->getHeight()) + << " Desired Discard: " << mDesiredDiscard << " Desired Size: " << mDesiredSize << LL_ENDL; // fall through } else { - if (!getName().empty()) + if (mUrl.compare(0, 7, "file://") == 0) { // failed to load local file, we're done. return true; } // need more data - mState = LOAD_FROM_NETWORK; + else + { + LL_DEBUGS("Texture") << mID << ": Not in Cache" << LL_ENDL; + mState = LOAD_FROM_NETWORK; + } // fall through } } if (mState == LOAD_FROM_NETWORK) { - if (mSentRequest == UNSENT) + bool get_url = gSavedSettings.getBOOL("ImagePipelineUseHTTP"); + if (!mUrl.empty()) get_url = false; +// if (mHost != LLHost::invalid) get_url = false; + if ( get_url ) { - if (mFormattedImage.isNull()) - { - mFormattedImage = new LLImageJ2C; - } - // Add this to the network queue and sit here. - // LLTextureFetch::update() will send off a request which will change our state - S32 data_size = mFormattedImage->getDataSize(); - if (data_size > 0) + LLViewerRegion* region = NULL; + if (mHost == LLHost::invalid) + region = gAgent.getRegion(); + else + region = LLWorld::getInstance()->getRegion(mHost); + + if (region) { - // Only used for simulator requests - mFirstPacket = (data_size - FIRST_PACKET_SIZE) / MAX_IMG_PACKET_SIZE + 1; - if (FIRST_PACKET_SIZE + (mFirstPacket-1) * MAX_IMG_PACKET_SIZE != data_size) - { -// llwarns << "Bad CACHED TEXTURE size: " << data_size << " removing." << llendl; - removeFromCache(); - resetFormattedData(); - clearPackets(); - } - else + std::string http_url = region->getCapability("GetTexture"); + if (!http_url.empty()) { - mLastPacket = mFirstPacket-1; - mTotalPackets = (mFileSize - FIRST_PACKET_SIZE + MAX_IMG_PACKET_SIZE-1) / MAX_IMG_PACKET_SIZE + 1; + mUrl = http_url + "/?texture_id=" + mID.asString().c_str(); } } + else + { + llwarns << "Region not found for host: " << mHost << llendl; + } + } + if (!mUrl.empty()) + { + mState = LLTextureFetchWorker::SEND_HTTP_REQ; + setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); + // don't return, fall through to next state + } + else if (mSentRequest == UNSENT) + { + // Add this to the network queue and sit here. + // LLTextureFetch::update() will send off a request which will change our state mRequestedSize = mDesiredSize; mRequestedDiscard = mDesiredDiscard; mSentRequest = QUEUED; - mFetcher->lockQueue(); mFetcher->addToNetworkQueue(this); - mFetcher->unlockQueue(); setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); + return false; + } + else + { + // Shouldn't need to do anything here + //llassert_always(mFetcher->mNetworkQueue.find(mID) != mFetcher->mNetworkQueue.end()); + // Make certain this is in the network queue + //mFetcher->addToNetworkQueue(this); + //setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); + return false; } - return false; } if (mState == LOAD_FROM_SIMULATOR) { + if (mFormattedImage.isNull()) + { + mFormattedImage = new LLImageJ2C; + } if (processSimulatorPackets()) { - mFetcher->lockQueue(); - mFetcher->removeFromNetworkQueue(this); - mFetcher->unlockQueue(); + LL_DEBUGS("Texture") << mID << ": Loaded from Sim. Bytes: " << mFormattedImage->getDataSize() << LL_ENDL; + mFetcher->removeFromNetworkQueue(this, false); if (mFormattedImage.isNull() || !mFormattedImage->getDataSize()) { // processSimulatorPackets() failed @@ -727,108 +787,99 @@ bool LLTextureFetchWorker::doWork(S32 param) } else { + mFetcher->addToNetworkQueue(this); // failsafe setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); } return false; } -#if 0 - if (mState == LOAD_FROM_HTTP_GET_URL) - { - if (!mSentRequest) - { - mSentRequest = TRUE; - mLoaded = FALSE; - std::string url; - LLViewerRegion* region = gAgent.getRegion(); - if (region) + if (mState == SEND_HTTP_REQ) + { + { + const S32 HTTP_QUEUE_MAX_SIZE = 32; + // *TODO: Integrate this with llviewerthrottle + // Note: LLViewerThrottle uses dynamic throttling which makes sense for UDP, + // but probably not for Textures. + // Set the throttle to the entire bandwidth, assuming UDP packets will get priority + // when they are needed + F32 max_bandwidth = mFetcher->mMaxBandwidth; + if ((mFetcher->getHTTPQueueSize() >= HTTP_QUEUE_MAX_SIZE) || + (mFetcher->getTextureBandwidth() > max_bandwidth)) { - url = region->getCapability("RequestTextureDownload"); - } - if (!url.empty()) - { - LLSD sd; - sd = mID.asString(); - setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); - LLHTTPClient::post(url, sd, new URLResponder(mFetcher, mID)); + // Make normal priority and return (i.e. wait until there is room in the queue) + setPriority(LLWorkerThread::PRIORITY_NORMAL | mWorkPriority); return false; } - else - { -// llwarns << mID << ": HTTP get url failed, requesting from simulator" << llendl; - mSentRequest = FALSE; - mState = LOAD_FROM_SIMULATOR; - return false; - } - } - else - { - if (mLoaded) + + mFetcher->removeFromNetworkQueue(this, false); + + S32 cur_size = 0; + if (mFormattedImage.notNull()) { - if (!mURL.empty()) - { - mState = LOAD_FROM_HTTP_GET_DATA; - mSentRequest = FALSE; // reset - mLoaded = FALSE; // reset - } - else - { -// llwarns << mID << ": HTTP get url is empty, requesting from simulator" << llendl; - mSentRequest = FALSE; - mState = LOAD_FROM_SIMULATOR; - return false; - } + cur_size = mFormattedImage->getDataSize(); // amount of data we already have } - } - // fall through - } - - if (mState == LOAD_FROM_HTTP_GET_DATA) - { - if (!mSentRequest) - { - mSentRequest = TRUE; - S32 cur_size = mFormattedImage->getDataSize(); // amount of data we already have mRequestedSize = mDesiredSize; mRequestedDiscard = mDesiredDiscard; -#if 1 // *TODO: LLCurl::getByteRange is broken (ignores range) - cur_size = 0; - mFormattedImage->deleteData(); -#endif mRequestedSize -= cur_size; - // F32 priority = mImagePriority / (F32)LLViewerTexture::maxDecodePriority(); // 0-1 S32 offset = cur_size; mBufferSize = cur_size; // This will get modified by callbackHttpGet() - std::string url; - if (mURL.empty()) + + bool res = false; + if (!mUrl.empty()) { - //url = "http://asset.agni/0000002f-38ae-0e17-8e72-712e58964e9c.texture"; - std::stringstream urlstr; - urlstr << "http://asset.agni/" << mID.asString() << ".texture"; - url = urlstr.str(); + mLoaded = FALSE; + mGetStatus = 0; + mGetReason.clear(); + lldebugs << "HTTP GET: " << mID << " Offset: " << offset + << " Bytes: " << mRequestedSize + << " Bandwidth(kbps): " << mFetcher->getTextureBandwidth() << "/" << max_bandwidth + << llendl; + setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); + mState = WAIT_HTTP_REQ; + + mFetcher->addToHTTPQueue(mID); + // Will call callbackHttpGet when curl request completes + std::vector<std::string> headers; + headers.push_back("Accept: image/x-j2c"); + res = mFetcher->mCurlGetRequest->getByteRange(mUrl, headers, offset, mRequestedSize, + new HTTPGetResponder(mFetcher, mID, LLTimer::getTotalTime(), mRequestedSize, offset)); } - else + if (!res) { - url = mURL; + llwarns << "HTTP GET request failed for " << mID << llendl; + resetFormattedData(); + ++mHTTPFailCount; + return true; // failed } - mLoaded = FALSE; -// llinfos << "HTTP GET: " << mID << " Offset: " << offset << " Bytes: " << mRequestedSize << llendl; - setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); - LLCurl::getByteRange(url, offset, mRequestedSize, - new HTTPGetResponder(mFetcher, mID)); // *TODO: use mWorkPriority - return false; // not done + // fall through } - + } + + if (mState == WAIT_HTTP_REQ) + { if (mLoaded) { - S32 cur_size = mFormattedImage->getDataSize(); + S32 cur_size = mFormattedImage.notNull() ? mFormattedImage->getDataSize() : 0; if (mRequestedSize < 0) { -// llwarns << "http get failed for: " << mID << llendl; + const S32 HTTP_MAX_RETRY_COUNT = 3; + S32 max_attempts = (mGetStatus == HTTP_NOT_FOUND) ? 1 : HTTP_MAX_RETRY_COUNT + 1; + llinfos << "HTTP GET failed for: " << mUrl + << " Status: " << mGetStatus << " Reason: '" << mGetReason << "'" + << " Attempt:" << mHTTPFailCount+1 << "/" << max_attempts << llendl; if (cur_size == 0) { - resetFormattedData(); - return true; // failed + ++mHTTPFailCount; + if (mHTTPFailCount >= max_attempts) + { + resetFormattedData(); + return true; // failed + } + else + { + mState = SEND_HTTP_REQ; + return false; // retry + } } else { @@ -836,6 +887,18 @@ bool LLTextureFetchWorker::doWork(S32 param) return false; // use what we have } } + + if (mFormattedImage.isNull()) + { + // For now, create formatted image based on extension + std::string extension = gDirUtilp->getExtension(mUrl); + mFormattedImage = LLImageFormatted::createFromType(LLImageBase::getCodecFromExtension(extension)); + if (mFormattedImage.isNull()) + { + mFormattedImage = new LLImageJ2C; // default + } + } + llassert_always(mBufferSize == cur_size + mRequestedSize); if (mHaveAllData) { @@ -854,43 +917,51 @@ bool LLTextureFetchWorker::doWork(S32 param) mBuffer = NULL; mBufferSize = 0; mLoadedDiscard = mRequestedDiscard; - setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); mState = DECODE_IMAGE; + setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); + return false; + } + else + { + setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); return false; } - - // NOTE: Priority gets updated when the http get completes (in callbackHTTPGet()) - setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); - return false; } -#endif if (mState == DECODE_IMAGE) { - llassert_always(mFormattedImage->getDataSize() > 0); + if (mFormattedImage->getDataSize() <= 0) + { + llerrs << "Decode entered with invalid mFormattedImage. ID = " << mID << llendl; + } setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); // Set priority first since Responder may change it mRawImage = NULL; mAuxImage = NULL; - llassert_always(mImageWorker == NULL); llassert_always(mFormattedImage.notNull()); + llassert_always(mLoadedDiscard >= 0); S32 discard = mHaveAllData ? 0 : mLoadedDiscard; U32 image_priority = LLWorkerThread::PRIORITY_NORMAL | mWorkPriority; mDecoded = FALSE; mState = DECODE_IMAGE_UPDATE; - mImageWorker = new LLImageWorker(mFormattedImage, image_priority, discard, new DecodeResponder(mFetcher, mID, this)); - // fall though (need to call requestDecodedData() to start work) + LL_DEBUGS("Texture") << mID << ": Decoding. Bytes: " << mFormattedImage->getDataSize() << " Discard: " << discard + << " All Data: " << mHaveAllData << LL_ENDL; + mDecodeHandle = mFetcher->mImageDecodeThread->decodeImage(mFormattedImage, image_priority, discard, mNeedsAux, + new DecodeResponder(mFetcher, mID, this)); + // fall though } if (mState == DECODE_IMAGE_UPDATE) { - if (decodeImage()) + if (mDecoded) { if (mDecodedDiscard < 0) { + LL_DEBUGS("Texture") << mID << ": Failed to Decode." << LL_ENDL; if (mCachedSize > 0 && !mInLocalCache && mRetryAttempt == 0) { // Cache file should be deleted, try again // llwarns << mID << ": Decode of cached file failed (removed), retrying" << llendl; + llassert_always(mDecodeHandle == 0); mFormattedImage = NULL; ++mRetryAttempt; setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); @@ -905,6 +976,9 @@ bool LLTextureFetchWorker::doWork(S32 param) } else { + llassert_always(mRawImage.notNull()); + LL_DEBUGS("Texture") << mID << ": Decoded. Discard: " << mDecodedDiscard + << " Raw Image: " << llformat("%dx%d",mRawImage->getWidth(),mRawImage->getHeight()) << LL_ENDL; setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); mState = WRITE_TO_CACHE; } @@ -918,9 +992,10 @@ bool LLTextureFetchWorker::doWork(S32 param) if (mState == WRITE_TO_CACHE) { - if (mInLocalCache || !mFileSize || mSentRequest == UNSENT) + if (mInLocalCache || mSentRequest == UNSENT || mFormattedImage.isNull()) { - // If we're in a local cache or we didn't actually receive any new data, skip + // If we're in a local cache or we didn't actually receive any new data, + // or we failed to load anything, skip mState = DONE; return false; } @@ -979,10 +1054,10 @@ bool LLTextureFetchWorker::doWork(S32 param) // Called from MAIN thread void LLTextureFetchWorker::endWork(S32 param, bool aborted) { - if (mImageWorker) + if (mDecodeHandle != 0) { - mImageWorker->scheduleDelete(); - mImageWorker = NULL; + mFetcher->mImageDecodeThread->abortRequest(mDecodeHandle, false); + mDecodeHandle = 0; } mFormattedImage = NULL; } @@ -1035,7 +1110,7 @@ bool LLTextureFetchWorker::deleteOK() if ((haveWork() && // not ok to delete from these states - ((mState >= LOAD_FROM_HTTP_GET_URL && mState <= LOAD_FROM_HTTP_GET_DATA) || + ((mState >= SEND_HTTP_REQ && mState <= WAIT_HTTP_REQ) || (mState >= WRITE_TO_CACHE && mState <= WAIT_ON_WRITE)))) { delete_ok = false; @@ -1044,7 +1119,6 @@ bool LLTextureFetchWorker::deleteOK() return delete_ok; } - void LLTextureFetchWorker::removeFromCache() { if (!mInLocalCache) @@ -1061,6 +1135,7 @@ bool LLTextureFetchWorker::processSimulatorPackets() if (mFormattedImage.isNull() || mRequestedSize < 0) { // not sure how we got here, but not a valid state, abort! + llassert_always(mDecodeHandle == 0); mFormattedImage = NULL; return true; } @@ -1074,6 +1149,12 @@ bool LLTextureFetchWorker::processSimulatorPackets() buffer_size += mPackets[i]->mSize; } bool have_all_data = mLastPacket >= mTotalPackets-1; + if (mRequestedSize <= 0) + { + // We received a packed but haven't requested anything yet (edge case) + // Return true (we're "done") since we didn't request anything + return true; + } if (buffer_size >= mRequestedSize || have_all_data) { /// We have enough (or all) data @@ -1109,50 +1190,36 @@ bool LLTextureFetchWorker::processSimulatorPackets() ////////////////////////////////////////////////////////////////////////////// -void LLTextureFetchWorker::callbackURLReceived(const LLSD& data, bool success) +void LLTextureFetchWorker::callbackHttpGet(const LLChannelDescriptors& channels, + const LLIOPipe::buffer_ptr_t& buffer, + bool last_block, bool success) { -#if 0 LLMutexLock lock(&mWorkMutex); - if (!mSentRequest || mState != LOAD_FROM_HTTP_GET_URL) - { - llwarns << "callbackURLReceived for unrequested fetch worker, req=" - << mSentRequest << " state= " << mState << llendl; - return; - } - if (success) - { - mURL = data.asString(); - } - mLoaded = TRUE; - setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); -#endif -} - -////////////////////////////////////////////////////////////////////////////// -void LLTextureFetchWorker::callbackHttpGet(U8* data, S32 data_size, bool last_block) -{ -#if 0 - LLMutexLock lock(&mWorkMutex); - if (!mSentRequest || mState != LOAD_FROM_HTTP_GET_DATA) + if (mState != WAIT_HTTP_REQ) { - llwarns << "callbackHttpGet for unrequested fetch worker, req=" - << mSentRequest << " state= " << mState << llendl; + llwarns << "callbackHttpGet for unrequested fetch worker: " << mID + << " req=" << mSentRequest << " state= " << mState << llendl; return; } -// llinfos << "HTTP RECEIVED: " << mID.asString() << " Bytes: " << data_size << llendl; if (mLoaded) { llwarns << "Duplicate callback for " << mID.asString() << llendl; return; // ignore duplicate callback } - if (data_size >= 0) + if (success) { + // get length of stream: + S32 data_size = buffer->countAfter(channels.in(), NULL); + + gTextureList.sTextureBits += data_size * 8; // Approximate - does not include header bits + + //llinfos << "HTTP RECEIVED: " << mID.asString() << " Bytes: " << data_size << llendl; if (data_size > 0) { + // *TODO: set the formatted image data here directly to avoid the copy mBuffer = new U8[data_size]; - // *TODO: set the formatted image data here - memcpy(mBuffer, data, data_size); + buffer->readAfter(channels.in(), NULL, mBuffer, data_size); mBufferSize += data_size; if (data_size < mRequestedSize || last_block == true) { @@ -1160,10 +1227,11 @@ void LLTextureFetchWorker::callbackHttpGet(U8* data, S32 data_size, bool last_bl } else if (data_size > mRequestedSize) { - // *TODO: This will happen until we fix LLCurl::getByteRange() -// llinfos << "HUH?" << llendl; + // *TODO: This shouldn't be happening any more + llwarns << "data_size = " << data_size << " > requested: " << mRequestedSize << llendl; mHaveAllData = TRUE; - mFormattedImage->deleteData(); + llassert_always(mDecodeHandle == 0); + mFormattedImage = NULL; // discard any previous data we had mBufferSize = data_size; } } @@ -1181,7 +1249,6 @@ void LLTextureFetchWorker::callbackHttpGet(U8* data, S32 data_size, bool last_bl } mLoaded = TRUE; setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); -#endif } ////////////////////////////////////////////////////////////////////////////// @@ -1197,7 +1264,7 @@ void LLTextureFetchWorker::callbackCacheRead(bool success, LLImageFormatted* ima } if (success) { - llassert_always(imagesize > 0); + llassert_always(imagesize >= 0); mFileSize = imagesize; mFormattedImage = image; mImageCodec = image->getCodec(); @@ -1225,65 +1292,40 @@ void LLTextureFetchWorker::callbackCacheWrite(bool success) ////////////////////////////////////////////////////////////////////////////// -void LLTextureFetchWorker::callbackDecoded(bool success) +void LLTextureFetchWorker::callbackDecoded(bool success, LLImageRaw* raw, LLImageRaw* aux) { + LLMutexLock lock(&mWorkMutex); + if (mDecodeHandle == 0) + { + return; // aborted, ignore + } if (mState != DECODE_IMAGE_UPDATE) { // llwarns << "Decode callback for " << mID << " with state = " << mState << llendl; + mDecodeHandle = 0; return; } -// llinfos << mID << " : DECODE COMPLETE " << llendl; - setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); -} - -////////////////////////////////////////////////////////////////////////////// - -bool LLTextureFetchWorker::decodeImage() -{ - if(!mImageWorker) - { - //LLTextureFetchWorker is aborted, skip image decoding. - return true ; - } - - bool res = true; - if (mRawImage.isNull()) - { - res = false; - if (mImageWorker->requestDecodedData(mRawImage, -1)) - { - res = true; -// llinfos << mID << " : BASE DECODE FINISHED" << llendl; - } - } - if (res && - (mRawImage.notNull() && mRawImage->getDataSize() > 0) && - (mNeedsAux && mAuxImage.isNull())) + llassert_always(mFormattedImage.notNull()); + + mDecodeHandle = 0; + if (success) { - res = false; - if (mImageWorker->requestDecodedAuxData(mAuxImage, 4, -1)) - { - res = true; -// llinfos << mID << " : AUX DECODE FINISHED" << llendl; - } + llassert_always(raw); + mRawImage = raw; + mAuxImage = aux; + mDecodedDiscard = mFormattedImage->getDiscardLevel(); + LL_DEBUGS("Texture") << mID << ": Decode Finished. Discard: " << mDecodedDiscard + << " Raw Image: " << llformat("%dx%d",mRawImage->getWidth(),mRawImage->getHeight()) << LL_ENDL; } - if (res) + else { - if ((mRawImage.notNull() && mRawImage->getDataSize() > 0) && - (!mNeedsAux || (mAuxImage.notNull() && mAuxImage->getDataSize() > 0))) - { - mDecodedDiscard = mFormattedImage->getDiscardLevel(); -// llinfos << mID << " : DECODE FINISHED. DISCARD: " << mDecodedDiscard << llendl; - } - else - { -// llwarns << "DECODE FAILED: " << mID << " Discard: " << (S32)mFormattedImage->getDiscardLevel() << llendl; - removeFromCache(); - } - mImageWorker->scheduleDelete(); - mImageWorker = NULL; + llwarns << "DECODE FAILED: " << mID << " Discard: " << (S32)mFormattedImage->getDiscardLevel() << llendl; + removeFromCache(); + mDecodedDiscard = -1; // Redundant, here for clarity and paranoia } - return res; + mDecoded = TRUE; +// llinfos << mID << " : DECODE COMPLETE " << llendl; + setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); } ////////////////////////////////////////////////////////////////////////////// @@ -1314,15 +1356,21 @@ bool LLTextureFetchWorker::writeToCacheComplete() ////////////////////////////////////////////////////////////////////////////// // public -LLTextureFetch::LLTextureFetch(LLTextureCache* cache, bool threaded) +LLTextureFetch::LLTextureFetch(LLTextureCache* cache, LLImageDecodeThread* imagedecodethread, bool threaded) : LLWorkerThread("TextureFetch", threaded), mDebugCount(0), mDebugPause(FALSE), mPacketCount(0), mBadPacketCount(0), mQueueMutex(getAPRPool()), - mTextureCache(cache) + mNetworkQueueMutex(getAPRPool()), + mTextureCache(cache), + mImageDecodeThread(imagedecodethread), + mTextureBandwidth(0), + mCurlGetRequest(NULL) { + mMaxBandwidth = gSavedSettings.getF32("ThrottleBandwidthKBPS"); + mTextureInfo.setUpLogging(gSavedSettings.getBOOL("LogTextureDownloadsToViewerLog"), gSavedSettings.getBOOL("LogTextureDownloadsToSimulator"), gSavedSettings.getU32("TextureLoggingThreshold")); } LLTextureFetch::~LLTextureFetch() @@ -1330,13 +1378,7 @@ LLTextureFetch::~LLTextureFetch() // ~LLQueuedThread() called here } -bool LLTextureFetch::createRequest(const LLUUID& id, const LLHost& host, F32 priority, - S32 w, S32 h, S32 c, S32 desired_discard, bool needs_aux) -{ - return createRequest(LLStringUtil::null, id, host, priority, w, h, c, desired_discard, needs_aux); -} - -bool LLTextureFetch::createRequest(const std::string& filename, const LLUUID& id, const LLHost& host, F32 priority, +bool LLTextureFetch::createRequest(const std::string& url, const LLUUID& id, const LLHost& host, F32 priority, S32 w, S32 h, S32 c, S32 desired_discard, bool needs_aux) { if (mDebugPause) @@ -1361,7 +1403,14 @@ bool LLTextureFetch::createRequest(const std::string& filename, const LLUUID& id } S32 desired_size; - if (desired_discard == 0) + std::string exten = gDirUtilp->getExtension(url); + if (!url.empty() && (!exten.empty() && LLImageBase::getCodecFromExtension(exten) != IMG_CODEC_J2C)) + { + // Only do partial requests for J2C at the moment + //llinfos << "Merov : LLTextureFetch::createRequest(), blocking fetch on " << url << llendl; + desired_size = MAX_IMAGE_DATA_SIZE; + } + else if (desired_discard == 0) { // if we want the entire image, and we know its size, then get it all // (calcDataSizeJ2C() below makes assumptions about how the image @@ -1378,7 +1427,7 @@ bool LLTextureFetch::createRequest(const std::string& filename, const LLUUID& id } else { - desired_size = FIRST_PACKET_SIZE; + desired_size = TEXTURE_CACHE_ENTRY_SIZE; desired_discard = MAX_DISCARD_LEVEL; } @@ -1389,10 +1438,10 @@ bool LLTextureFetch::createRequest(const std::string& filename, const LLUUID& id { return false; // need to wait for previous aborted request to complete } - worker->lockWorkData(); + worker->lockWorkMutex(); worker->setImagePriority(priority); worker->setDesiredDiscard(desired_discard, desired_size); - worker->unlockWorkData(); + worker->unlockWorkMutex(); if (!worker->haveWork()) { worker->mState = LLTextureFetchWorker::INIT; @@ -1401,16 +1450,7 @@ bool LLTextureFetch::createRequest(const std::string& filename, const LLUUID& id } else { - if (filename.empty()) - { - // do remote fetch - worker = new LLTextureFetchWorker(this, id, host, priority, desired_discard, desired_size); - } - else - { - // do local file fetch - worker = new LLTextureFetchLocalFileWorker(this, filename, id, host, priority, desired_discard, desired_size); - } + worker = new LLTextureFetchWorker(this, url, id, host, priority, desired_discard, desired_size); mRequestMap[id] = worker; } worker->mActiveCount++; @@ -1430,10 +1470,9 @@ void LLTextureFetch::deleteRequest(const LLUUID& id, bool cancel) } // protected - -// call lockQueue() first! void LLTextureFetch::addToNetworkQueue(LLTextureFetchWorker* worker) { + LLMutexLock lock(&mNetworkQueueMutex); if (mRequestMap.find(worker->mID) != mRequestMap.end()) { // only add to the queue if in the request map @@ -1447,10 +1486,27 @@ void LLTextureFetch::addToNetworkQueue(LLTextureFetchWorker* worker) } } -// call lockQueue() first! -void LLTextureFetch::removeFromNetworkQueue(LLTextureFetchWorker* worker) +void LLTextureFetch::removeFromNetworkQueue(LLTextureFetchWorker* worker, bool cancel) { - mNetworkQueue.erase(worker->mID); + LLMutexLock lock(&mNetworkQueueMutex); + size_t erased = mNetworkQueue.erase(worker->mID); + if (cancel && erased > 0) + { + mCancelQueue[worker->mHost].insert(worker->mID); + } +} + +// protected +void LLTextureFetch::addToHTTPQueue(const LLUUID& id) +{ + LLMutexLock lock(&mNetworkQueueMutex); + mHTTPTextureQueue.insert(id); +} + +void LLTextureFetch::removeFromHTTPQueue(const LLUUID& id) +{ + LLMutexLock lock(&mNetworkQueueMutex); + mHTTPTextureQueue.erase(id); } // call lockQueue() first! @@ -1458,11 +1514,7 @@ void LLTextureFetch::removeRequest(LLTextureFetchWorker* worker, bool cancel) { size_t erased_1 = mRequestMap.erase(worker->mID); llassert_always(erased_1 > 0) ; - size_t erased = mNetworkQueue.erase(worker->mID); - if (cancel && erased > 0) - { - mCancelQueue[worker->mHost].insert(worker->mID); - } + removeFromNetworkQueue(worker, cancel); llassert_always(!(worker->getFlags(LLWorkerClass::WCF_DELETE_REQUESTED))) ; worker->scheduleDelete(); @@ -1504,24 +1556,27 @@ bool LLTextureFetch::getRequestFinished(const LLUUID& id, S32& discard_level, } else if (worker->checkWork()) { + worker->lockWorkMutex(); discard_level = worker->mDecodedDiscard; - raw = worker->mRawImage; worker->mRawImage = NULL; - aux = worker->mAuxImage; worker->mAuxImage = NULL; + raw = worker->mRawImage; + aux = worker->mAuxImage; res = true; + LL_DEBUGS("Texture") << id << ": Request Finished. State: " << worker->mState << " Discard: " << discard_level << LL_ENDL; + worker->unlockWorkMutex(); } else { - worker->lockWorkData(); + worker->lockWorkMutex(); if ((worker->mDecodedDiscard >= 0) && (worker->mDecodedDiscard < discard_level || discard_level < 0) && (worker->mState >= LLTextureFetchWorker::WAIT_ON_WRITE)) { // Not finished, but data is ready discard_level = worker->mDecodedDiscard; - if (worker->mRawImage) raw = worker->mRawImage; - if (worker->mAuxImage) aux = worker->mAuxImage; + raw = worker->mRawImage; + aux = worker->mAuxImage; } - worker->unlockWorkData(); + worker->unlockWorkMutex(); } } else @@ -1538,9 +1593,9 @@ bool LLTextureFetch::updateRequestPriority(const LLUUID& id, F32 priority) LLTextureFetchWorker* worker = getWorker(id); if (worker) { - worker->lockWorkData(); + worker->lockWorkMutex(); worker->setImagePriority(priority); - worker->unlockWorkData(); + worker->unlockWorkMutex(); res = true; } return res; @@ -1548,40 +1603,106 @@ bool LLTextureFetch::updateRequestPriority(const LLUUID& id, F32 priority) ////////////////////////////////////////////////////////////////////////////// +// MAIN THREAD //virtual S32 LLTextureFetch::update(U32 max_time_ms) { S32 res; + + mMaxBandwidth = gSavedSettings.getF32("ThrottleBandwidthKBPS"); + res = LLWorkerThread::update(max_time_ms); - const F32 REQUEST_TIME = 1.f; - - // Periodically, gather the list of textures that need data from the network - // And send the requests out to the simulators - if (mNetworkTimer.getElapsedTimeF32() >= REQUEST_TIME) + if (!mDebugPause) { - mNetworkTimer.reset(); sendRequestListToSimulators(); } return res; } +// WORKER THREAD +void LLTextureFetch::startThread() +{ + // Construct mCurlGetRequest from Worker Thread + mCurlGetRequest = new LLCurlRequest(); +} + +// WORKER THREAD +void LLTextureFetch::endThread() +{ + // Destroy mCurlGetRequest from Worker Thread + delete mCurlGetRequest; + mCurlGetRequest = NULL; +} + +// WORKER THREAD +void LLTextureFetch::threadedUpdate() +{ + llassert_always(mCurlGetRequest); + + // Limit update frequency + const F32 PROCESS_TIME = 0.05f; + static LLFrameTimer process_timer; + if (process_timer.getElapsedTimeF32() < PROCESS_TIME) + { + return; + } + process_timer.reset(); + + // Update Curl on same thread as mCurlGetRequest was constructed + S32 processed = mCurlGetRequest->process(); + if (processed > 0) + { + lldebugs << "processed: " << processed << " messages." << llendl; + } + +#if 0 + const F32 INFO_TIME = 1.0f; + static LLFrameTimer info_timer; + if (info_timer.getElapsedTimeF32() >= INFO_TIME) + { + S32 q = mCurlGetRequest->getQueued(); + if (q > 0) + { + llinfos << "Queued gets: " << q << llendl; + info_timer.reset(); + } + } +#endif + +} + ////////////////////////////////////////////////////////////////////////////// void LLTextureFetch::sendRequestListToSimulators() { + // All requests + const F32 REQUEST_DELTA_TIME = 0.10f; // 10 fps + + // Sim requests const S32 IMAGES_PER_REQUEST = 50; - const F32 LAZY_FLUSH_TIMEOUT = 15.f; // 10.0f // temp + const F32 SIM_LAZY_FLUSH_TIMEOUT = 10.0f; // temp const F32 MIN_REQUEST_TIME = 1.0f; const F32 MIN_DELTA_PRIORITY = 1000.f; - LLMutexLock lock(&mQueueMutex); + // Periodically, gather the list of textures that need data from the network + // And send the requests out to the simulators + static LLFrameTimer timer; + if (timer.getElapsedTimeF32() < REQUEST_DELTA_TIME) + { + return; + } + timer.reset(); + LLMutexLock lock(&mQueueMutex); + // Send requests typedef std::set<LLTextureFetchWorker*,LLTextureFetchWorker::Compare> request_list_t; typedef std::map< LLHost, request_list_t > work_request_map_t; work_request_map_t requests; + { + LLMutexLock lock2(&mNetworkQueueMutex); for (queue_t::iterator iter = mNetworkQueue.begin(); iter != mNetworkQueue.end(); ) { queue_t::iterator curiter = iter++; @@ -1591,65 +1712,65 @@ void LLTextureFetch::sendRequestListToSimulators() mNetworkQueue.erase(curiter); continue; // paranoia } + if ((req->mState != LLTextureFetchWorker::LOAD_FROM_NETWORK) && + (req->mState != LLTextureFetchWorker::LOAD_FROM_SIMULATOR)) + { + // We already received our URL, remove from the queue + llwarns << "Worker: " << req->mID << " in mNetworkQueue but in wrong state: " << req->mState << llendl; + mNetworkQueue.erase(curiter); + continue; + } if (req->mID == mDebugID) { mDebugCount++; // for setting breakpoints } - if (req->mTotalPackets > 0 && req->mLastPacket >= req->mTotalPackets-1) + if (req->mSentRequest == LLTextureFetchWorker::SENT_SIM && + req->mTotalPackets > 0 && + req->mLastPacket >= req->mTotalPackets-1) { // We have all the packets... make sure this is high priority // req->setPriority(LLWorkerThread::PRIORITY_HIGH | req->mWorkPriority); continue; } F32 elapsed = req->mRequestedTimer.getElapsedTimeF32(); - F32 delta_priority = llabs(req->mRequestedPriority - req->mImagePriority); - if ((req->mSimRequestedDiscard != req->mDesiredDiscard) || - (delta_priority > MIN_DELTA_PRIORITY && elapsed >= MIN_REQUEST_TIME) || - (elapsed >= LAZY_FLUSH_TIMEOUT)) { - requests[req->mHost].insert(req); + F32 delta_priority = llabs(req->mRequestedPriority - req->mImagePriority); + if ((req->mSimRequestedDiscard != req->mDesiredDiscard) || + (delta_priority > MIN_DELTA_PRIORITY && elapsed >= MIN_REQUEST_TIME) || + (elapsed >= SIM_LAZY_FLUSH_TIMEOUT)) + { + requests[req->mHost].insert(req); + } } } - - std::string http_url; -#if 0 - if (gSavedSettings.getBOOL("ImagePipelineUseHTTP")) - { - LLViewerRegion* region = gAgent.getRegion(); - if (region) - { - http_url = region->getCapability("RequestTextureDownload"); - } } -#endif - + for (work_request_map_t::iterator iter1 = requests.begin(); iter1 != requests.end(); ++iter1) { - bool use_http = http_url.empty() ? false : true; LLHost host = iter1->first; // invalid host = use agent host if (host == LLHost::invalid) { host = gAgent.getRegionHost(); } - else - { - use_http = false; - } - if (use_http) + S32 sim_request_count = 0; + + for (request_list_t::iterator iter2 = iter1->second.begin(); + iter2 != iter1->second.end(); ++iter2) { - } - else - { - S32 request_count = 0; - for (request_list_t::iterator iter2 = iter1->second.begin(); - iter2 != iter1->second.end(); ++iter2) + LLTextureFetchWorker* req = *iter2; + if (gMessageSystem) { - LLTextureFetchWorker* req = *iter2; - req->mSentRequest = LLTextureFetchWorker::SENT_SIM; - if (0 == request_count) + if (req->mSentRequest != LLTextureFetchWorker::SENT_SIM) + { + // Initialize packet data based on data read from cache + req->lockWorkMutex(); + req->setupPacketData(); + req->unlockWorkMutex(); + } + if (0 == sim_request_count) { gMessageSystem->newMessageFast(_PREHASH_RequestImage); gMessageSystem->nextBlockFast(_PREHASH_AgentData); @@ -1666,30 +1787,42 @@ void LLTextureFetch::sendRequestListToSimulators() // llinfos << "IMAGE REQUEST: " << req->mID << " Discard: " << req->mDesiredDiscard // << " Packet: " << packet << " Priority: " << req->mImagePriority << llendl; - req->lockWorkData(); + if ((gSavedSettings.getBOOL("LogTextureDownloadsToViewerLog")) || (gSavedSettings.getBOOL("LogTextureDownloadsToSimulator"))) + { + mTextureInfo.setRequestStartTime(req->mID, LLTimer::getTotalTime()); + mTextureInfo.setRequestOffset(req->mID, 0); + mTextureInfo.setRequestSize(req->mID, 0); + mTextureInfo.setRequestType(req->mID, LLTextureInfoDetails::REQUEST_TYPE_UDP); + } + + req->lockWorkMutex(); + req->mSentRequest = LLTextureFetchWorker::SENT_SIM; req->mSimRequestedDiscard = req->mDesiredDiscard; req->mRequestedPriority = req->mImagePriority; req->mRequestedTimer.reset(); - req->unlockWorkData(); - request_count++; - if (request_count >= IMAGES_PER_REQUEST) + req->unlockWorkMutex(); + sim_request_count++; + if (sim_request_count >= IMAGES_PER_REQUEST) { -// llinfos << "REQUESTING " << request_count << " IMAGES FROM HOST: " << host.getIPString() << llendl; +// llinfos << "REQUESTING " << sim_request_count << " IMAGES FROM HOST: " << host.getIPString() << llendl; + gMessageSystem->sendSemiReliable(host, NULL, NULL); - request_count = 0; + sim_request_count = 0; } } - if (request_count > 0 && request_count < IMAGES_PER_REQUEST) - { -// llinfos << "REQUESTING " << request_count << " IMAGES FROM HOST: " << host.getIPString() << llendl; - gMessageSystem->sendSemiReliable(host, NULL, NULL); - request_count = 0; - } + } + if (gMessageSystem && sim_request_count > 0 && sim_request_count < IMAGES_PER_REQUEST) + { +// llinfos << "REQUESTING " << sim_request_count << " IMAGES FROM HOST: " << host.getIPString() << llendl; + gMessageSystem->sendSemiReliable(host, NULL, NULL); + sim_request_count = 0; } } // Send cancelations - if (!mCancelQueue.empty()) + { + LLMutexLock lock2(&mNetworkQueueMutex); + if (gMessageSystem && !mCancelQueue.empty()) { for (cancel_queue_t::iterator iter1 = mCancelQueue.begin(); iter1 != mCancelQueue.end(); ++iter1) @@ -1732,6 +1865,7 @@ void LLTextureFetch::sendRequestListToSimulators() } mCancelQueue.clear(); } + } } ////////////////////////////////////////////////////////////////////////////// @@ -1808,7 +1942,7 @@ bool LLTextureFetch::receiveImageHeader(const LLHost& host, const LLUUID& id, U8 return false; } - worker->lockWorkData(); + worker->lockWorkMutex(); // Copy header data into image object worker->mImageCodec = codec; @@ -1819,7 +1953,7 @@ bool LLTextureFetch::receiveImageHeader(const LLHost& host, const LLUUID& id, U8 res = worker->insertPacket(0, data, data_size); worker->setPriority(LLWorkerThread::PRIORITY_HIGH | worker->mWorkPriority); worker->mState = LLTextureFetchWorker::LOAD_FROM_SIMULATOR; - worker->unlockWorkData(); + worker->unlockWorkMutex(); return res; } @@ -1853,7 +1987,7 @@ bool LLTextureFetch::receiveImagePacket(const LLHost& host, const LLUUID& id, U1 return false; } - worker->lockWorkData(); + worker->lockWorkMutex(); res = worker->insertPacket(packet_num, data, data_size); @@ -1866,12 +2000,20 @@ bool LLTextureFetch::receiveImagePacket(const LLHost& host, const LLUUID& id, U1 else { // llwarns << "receiveImagePacket " << packet_num << "/" << worker->mLastPacket << " for worker: " << id -// << " in state: " << LLTextureFetchWorker::sStateDescs[worker->mState] << llendl; - removeFromNetworkQueue(worker); // failsafe - mCancelQueue[host].insert(id); +// << " in state: " << LLTextureFetchWorker::sStateDescs[worker->mState] << llendl; + removeFromNetworkQueue(worker, true); // failsafe } - - worker->unlockWorkData(); + + if(packet_num >= (worker->mTotalPackets - 1)) + { + if ((gSavedSettings.getBOOL("LogTextureDownloadsToViewerLog")) || (gSavedSettings.getBOOL("LogTextureDownloadsToSimulator"))) + { + U64 timeNow = LLTimer::getTotalTime(); + mTextureInfo.setRequestSize(id, worker->mFileSize); + mTextureInfo.setRequestCompleteTimeAndLog(id, timeNow); + } + } + worker->unlockWorkMutex(); return res; } @@ -1885,9 +2027,9 @@ BOOL LLTextureFetch::isFromLocalCache(const LLUUID& id) LLTextureFetchWorker* worker = getWorker(id); if (worker) { - worker->lockWorkData(); + worker->lockWorkMutex() ; from_cache = worker->mInLocalCache ; - worker->unlockWorkData(); + worker->unlockWorkMutex() ; } return from_cache ; @@ -1907,7 +2049,7 @@ S32 LLTextureFetch::getFetchState(const LLUUID& id, F32& data_progress_p, F32& r LLTextureFetchWorker* worker = getWorker(id); if (worker && worker->haveWork()) { - worker->lockWorkData(); + worker->lockWorkMutex(); state = worker->mState; fetch_dtime = worker->mFetchTimer.getElapsedTimeF32(); request_dtime = worker->mRequestedTimer.getElapsedTimeF32(); @@ -1924,7 +2066,7 @@ S32 LLTextureFetch::getFetchState(const LLUUID& id, F32& data_progress_p, F32& r data_progress = (F32)worker->mFormattedImage->getDataSize() / (F32)worker->mFileSize; } } - if (state >= LLTextureFetchWorker::LOAD_FROM_NETWORK && state <= LLTextureFetchWorker::LOAD_FROM_HTTP_GET_DATA) + if (state >= LLTextureFetchWorker::LOAD_FROM_NETWORK && state <= LLTextureFetchWorker::WAIT_HTTP_REQ) { requested_priority = worker->mRequestedPriority; } @@ -1933,7 +2075,7 @@ S32 LLTextureFetch::getFetchState(const LLUUID& id, F32& data_progress_p, F32& r requested_priority = worker->mImagePriority; } fetch_priority = worker->getPriority(); - worker->unlockWorkData(); + worker->unlockWorkMutex(); } data_progress_p = data_progress; requested_priority_p = requested_priority; @@ -1959,5 +2101,3 @@ void LLTextureFetch::dump() } } - -////////////////////////////////////////////////////////////////////////////// diff --git a/indra/newview/lltexturefetch.h b/indra/newview/lltexturefetch.h index 97719a9468..373e38a83c 100644 --- a/indra/newview/lltexturefetch.h +++ b/indra/newview/lltexturefetch.h @@ -37,26 +37,29 @@ #include "llimage.h" #include "lluuid.h" #include "llworkerthread.h" +#include "llcurl.h" +#include "lltextureinfo.h" class LLViewerTexture; class LLTextureFetchWorker; +class HTTPGetResponder; class LLTextureCache; +class LLImageDecodeThread; class LLHost; // Interface class class LLTextureFetch : public LLWorkerThread { friend class LLTextureFetchWorker; + friend class HTTPGetResponder; public: - LLTextureFetch(LLTextureCache* cache, bool threaded); + LLTextureFetch(LLTextureCache* cache, LLImageDecodeThread* imagedecodethread, bool threaded); ~LLTextureFetch(); /*virtual*/ S32 update(U32 max_time_ms); - bool createRequest(const LLUUID& id, const LLHost& host, F32 priority, - S32 w, S32 h, S32 c, S32 discard, bool needs_aux); - bool createRequest(const std::string& filename, const LLUUID& id, const LLHost& host, F32 priority, + bool createRequest(const std::string& url, const LLUUID& id, const LLHost& host, F32 priority, S32 w, S32 h, S32 c, S32 discard, bool needs_aux); void deleteRequest(const LLUUID& id, bool cancel); bool getRequestFinished(const LLUUID& id, S32& discard_level, @@ -66,25 +69,39 @@ public: bool receiveImageHeader(const LLHost& host, const LLUUID& id, U8 codec, U16 packets, U32 totalbytes, U16 data_size, U8* data); bool receiveImagePacket(const LLHost& host, const LLUUID& id, U16 packet_num, U16 data_size, U8* data); + void setTextureBandwidth(F32 bandwidth) { mTextureBandwidth = bandwidth; } + F32 getTextureBandwidth() { return mTextureBandwidth; } + // Debug BOOL isFromLocalCache(const LLUUID& id); S32 getFetchState(const LLUUID& id, F32& decode_progress_p, F32& requested_priority_p, U32& fetch_priority_p, F32& fetch_dtime_p, F32& request_dtime_p); void dump(); S32 getNumRequests() { return mRequestMap.size(); } + S32 getNumHTTPRequests() { return mHTTPTextureQueue.size(); } // Public for access by callbacks void lockQueue() { mQueueMutex.lock(); } void unlockQueue() { mQueueMutex.unlock(); } LLTextureFetchWorker* getWorker(const LLUUID& id); + + LLTextureInfo* getTextureInfo() { return &mTextureInfo; } protected: void addToNetworkQueue(LLTextureFetchWorker* worker); - void removeFromNetworkQueue(LLTextureFetchWorker* worker); + void removeFromNetworkQueue(LLTextureFetchWorker* worker, bool cancel); + void addToHTTPQueue(const LLUUID& id); + void removeFromHTTPQueue(const LLUUID& id); + S32 getHTTPQueueSize() { return (S32)mHTTPTextureQueue.size(); } void removeRequest(LLTextureFetchWorker* worker, bool cancel); + // Called from worker thread (during doWork) + void processCurlRequests(); private: void sendRequestListToSimulators(); + /*virtual*/ void startThread(void); + /*virtual*/ void endThread(void); + /*virtual*/ void threadedUpdate(void); public: LLUUID mDebugID; @@ -95,8 +112,11 @@ public: private: LLMutex mQueueMutex; + LLMutex mNetworkQueueMutex; LLTextureCache* mTextureCache; + LLImageDecodeThread* mImageDecodeThread; + LLCurlRequest* mCurlGetRequest; // Map of all requests by UUID typedef std::map<LLUUID,LLTextureFetchWorker*> map_t; @@ -105,10 +125,13 @@ private: // Set of requests that require network data typedef std::set<LLUUID> queue_t; queue_t mNetworkQueue; + queue_t mHTTPTextureQueue; typedef std::map<LLHost,std::set<LLUUID> > cancel_queue_t; cancel_queue_t mCancelQueue; - - LLFrameTimer mNetworkTimer; + F32 mTextureBandwidth; + F32 mMaxBandwidth; + LLTextureInfo mTextureInfo; }; #endif // LL_LLTEXTUREFETCH_H + diff --git a/indra/newview/lltextureinfo.cpp b/indra/newview/lltextureinfo.cpp new file mode 100644 index 0000000000..672a36a8bd --- /dev/null +++ b/indra/newview/lltextureinfo.cpp @@ -0,0 +1,290 @@ +/** + * @file lltextureinfo.cpp + * @brief Object which handles local texture info + * + * $LicenseInfo:firstyear=2000&license=viewergpl$ + * + * Copyright (c) 2000-2009, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "lltextureinfo.h" +#include "lltexturestats.h" +#include "llviewercontrol.h" + +LLTextureInfo::LLTextureInfo() : + mLogTextureDownloadsToViewerLog(false), + mLogTextureDownloadsToSimulator(false), + mTotalBytes(0), + mTotalMilliseconds(0), + mTextureDownloadsStarted(0), + mTextureDownloadsCompleted(0), + mTextureDownloadProtocol("NONE"), + mTextureLogThreshold(100 * 1024), + mCurrentStatsBundleStartTime(0) +{ + mTextures.clear(); +} + +void LLTextureInfo::setUpLogging(bool writeToViewerLog, bool sendToSim, U32 textureLogThreshold) +{ + mLogTextureDownloadsToViewerLog = writeToViewerLog; + mLogTextureDownloadsToSimulator = sendToSim; + mTextureLogThreshold = textureLogThreshold; +} + +LLTextureInfo::~LLTextureInfo() +{ + std::map<LLUUID, LLTextureInfoDetails *>::iterator iterator; + for (iterator = mTextures.begin(); iterator != mTextures.end(); iterator++) + { + LLTextureInfoDetails *info = (*iterator).second; + delete info; + } + + mTextures.clear(); +} + +void LLTextureInfo::addRequest(const LLUUID& id) +{ + LLTextureInfoDetails *info = new LLTextureInfoDetails(); + mTextures[id] = info; +} + +U32 LLTextureInfo::getTextureInfoMapSize() +{ + return mTextures.size(); +} + +bool LLTextureInfo::has(const LLUUID& id) +{ + std::map<LLUUID, LLTextureInfoDetails *>::iterator iterator = mTextures.find(id); + if (iterator == mTextures.end()) + { + return false; + } + else + { + return true; + } +} + +void LLTextureInfo::setRequestStartTime(const LLUUID& id, U64 startTime) +{ + if (!has(id)) + { + addRequest(id); + } + mTextures[id]->mStartTime = startTime; + mTextureDownloadsStarted++; +} + +void LLTextureInfo::setRequestSize(const LLUUID& id, U32 size) +{ + if (!has(id)) + { + addRequest(id); + } + mTextures[id]->mSize = size; +} + +void LLTextureInfo::setRequestOffset(const LLUUID& id, U32 offset) +{ + if (!has(id)) + { + addRequest(id); + } + mTextures[id]->mOffset = offset; +} + +void LLTextureInfo::setRequestType(const LLUUID& id, LLTextureInfoDetails::LLRequestType type) +{ + if (!has(id)) + { + addRequest(id); + } + mTextures[id]->mType = type; +} + +void LLTextureInfo::setRequestCompleteTimeAndLog(const LLUUID& id, U64 completeTime) +{ + if (!has(id)) + { + addRequest(id); + } + mTextures[id]->mCompleteTime = completeTime; + + std::string protocol = "NONE"; + switch(mTextures[id]->mType) + { + case LLTextureInfoDetails::REQUEST_TYPE_HTTP: + protocol = "HTTP"; + break; + + case LLTextureInfoDetails::REQUEST_TYPE_UDP: + protocol = "UDP"; + break; + + case LLTextureInfoDetails::REQUEST_TYPE_NONE: + default: + break; + } + + if (mLogTextureDownloadsToViewerLog) + { + llinfos << "texture=" << id + << " start=" << mTextures[id]->mStartTime + << " end=" << mTextures[id]->mCompleteTime + << " size=" << mTextures[id]->mSize + << " offset=" << mTextures[id]->mOffset + << " length_in_ms=" << (mTextures[id]->mCompleteTime - mTextures[id]->mStartTime) / 1000 + << " protocol=" << protocol + << llendl; + } + + if(mLogTextureDownloadsToSimulator) + { + S32 texture_stats_upload_threshold = mTextureLogThreshold; + mTotalBytes += mTextures[id]->mSize; + mTotalMilliseconds += mTextures[id]->mCompleteTime - mTextures[id]->mStartTime; + mTextureDownloadsCompleted++; + mTextureDownloadProtocol = protocol; + if (mTotalBytes >= texture_stats_upload_threshold) + { + LLSD texture_data; + std::stringstream startTime; + startTime << mCurrentStatsBundleStartTime; + texture_data["start_time"] = startTime.str(); + std::stringstream endTime; + endTime << completeTime; + texture_data["end_time"] = endTime.str(); + texture_data["averages"] = getAverages(); + send_texture_stats_to_sim(texture_data); + resetTextureStatistics(); + } + } + + mTextures.erase(id); +} + +LLSD LLTextureInfo::getAverages() +{ + LLSD averagedTextureData; + S32 averageDownloadRate; + if(mTotalMilliseconds == 0) + { + averageDownloadRate = 0; + } + else + { + averageDownloadRate = (mTotalBytes * 8) / mTotalMilliseconds; + } + + averagedTextureData["bits_per_second"] = averageDownloadRate; + averagedTextureData["bytes_downloaded"] = mTotalBytes; + averagedTextureData["texture_downloads_started"] = mTextureDownloadsStarted; + averagedTextureData["texture_downloads_completed"] = mTextureDownloadsCompleted; + averagedTextureData["transport"] = mTextureDownloadProtocol; + + return averagedTextureData; +} + +void LLTextureInfo::resetTextureStatistics() +{ + mTotalMilliseconds = 0; + mTotalBytes = 0; + mTextureDownloadsStarted = 0; + mTextureDownloadsCompleted = 0; + mTextureDownloadProtocol = "NONE"; + mCurrentStatsBundleStartTime = LLTimer::getTotalTime(); +} + +U32 LLTextureInfo::getRequestStartTime(const LLUUID& id) +{ + if (!has(id)) + { + return 0; + } + else + { + std::map<LLUUID, LLTextureInfoDetails *>::iterator iterator = mTextures.find(id); + return (*iterator).second->mStartTime; + } +} + +U32 LLTextureInfo::getRequestSize(const LLUUID& id) +{ + if (!has(id)) + { + return 0; + } + else + { + std::map<LLUUID, LLTextureInfoDetails *>::iterator iterator = mTextures.find(id); + return (*iterator).second->mSize; + } +} + +U32 LLTextureInfo::getRequestOffset(const LLUUID& id) +{ + if (!has(id)) + { + return 0; + } + else + { + std::map<LLUUID, LLTextureInfoDetails *>::iterator iterator = mTextures.find(id); + return (*iterator).second->mOffset; + } +} + +LLTextureInfoDetails::LLRequestType LLTextureInfo::getRequestType(const LLUUID& id) +{ + if (!has(id)) + { + return LLTextureInfoDetails::REQUEST_TYPE_NONE; + } + else + { + std::map<LLUUID, LLTextureInfoDetails *>::iterator iterator = mTextures.find(id); + return (*iterator).second->mType; + } +} + +U32 LLTextureInfo::getRequestCompleteTime(const LLUUID& id) +{ + if (!has(id)) + { + return 0; + } + else + { + std::map<LLUUID, LLTextureInfoDetails *>::iterator iterator = mTextures.find(id); + return (*iterator).second->mCompleteTime; + } +} + diff --git a/indra/newview/lltextureinfo.h b/indra/newview/lltextureinfo.h new file mode 100644 index 0000000000..71b0ea431f --- /dev/null +++ b/indra/newview/lltextureinfo.h @@ -0,0 +1,80 @@ +/** + * @file lltextureinfo.h + * @brief Object for managing texture information. + * + * $LicenseInfo:firstyear=2000&license=viewergpl$ + * + * Copyright (c) 2000-2009, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#ifndef LL_LLTEXTUREINFO_H +#define LL_LLTEXTUREINFO_H + +#include "lluuid.h" +#include "lltextureinfodetails.h" +#include <map> + +class LLTextureInfo +{ +public: + LLTextureInfo(); + ~LLTextureInfo(); + + void setUpLogging(bool writeToViewerLog, bool sendToSim, U32 textureLogThreshold); + bool has(const LLUUID& id); + void setRequestStartTime(const LLUUID& id, U64 startTime); + void setRequestSize(const LLUUID& id, U32 size); + void setRequestOffset(const LLUUID& id, U32 offset); + void setRequestType(const LLUUID& id, LLTextureInfoDetails::LLRequestType type); + void setRequestCompleteTimeAndLog(const LLUUID& id, U64 completeTime); + U32 getRequestStartTime(const LLUUID& id); + U32 getRequestSize(const LLUUID& id); + U32 getRequestOffset(const LLUUID& id); + LLTextureInfoDetails::LLRequestType getRequestType(const LLUUID& id); + U32 getRequestCompleteTime(const LLUUID& id); + void resetTextureStatistics(); + U32 getTextureInfoMapSize(); + LLSD getAverages(); + +private: + void addRequest(const LLUUID& id); + + std::map<LLUUID, LLTextureInfoDetails *> mTextures; + + LLSD mAverages; + + bool mLogTextureDownloadsToViewerLog; + bool mLogTextureDownloadsToSimulator; + S32 mTotalBytes; + S32 mTotalMilliseconds; + S32 mTextureDownloadsStarted; + S32 mTextureDownloadsCompleted; + std::string mTextureDownloadProtocol; + U32 mTextureLogThreshold; // in bytes + U64 mCurrentStatsBundleStartTime; +}; + +#endif // LL_LLTEXTUREINFO_H diff --git a/indra/newview/lltextureinfodetails.cpp b/indra/newview/lltextureinfodetails.cpp new file mode 100644 index 0000000000..f6ef47a2ee --- /dev/null +++ b/indra/newview/lltextureinfodetails.cpp @@ -0,0 +1,40 @@ +/** + * @file lltextureinfodetails.cpp + * @brief Object which handles details of any individual texture + * + * $LicenseInfo:firstyear=2000&license=viewergpl$ + * + * Copyright (c) 2000-2009, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "lltextureinfodetails.h" + +LLTextureInfoDetails::LLTextureInfoDetails() : mStartTime(0), mCompleteTime(0), mSize(0), mType(REQUEST_TYPE_NONE), mOffset(0) +{ +} + diff --git a/indra/newview/lltextureinfodetails.h b/indra/newview/lltextureinfodetails.h new file mode 100644 index 0000000000..091fa01a3d --- /dev/null +++ b/indra/newview/lltextureinfodetails.h @@ -0,0 +1,58 @@ +/** + * @file lltextureinfo.h + * @brief Object for managing texture information. + * + * $LicenseInfo:firstyear=2000&license=viewergpl$ + * + * Copyright (c) 2000-2009, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#ifndef LL_LLTEXTUREINFODETAILS_H +#define LL_LLTEXTUREINFODETAILS_H + +#include "lluuid.h" + +class LLTextureInfoDetails +{ +public: + enum LLRequestType + { + REQUEST_TYPE_NONE, + REQUEST_TYPE_HTTP, + REQUEST_TYPE_UDP + }; + + U32 mStartTime; + U32 mCompleteTime; + U32 mOffset; + U32 mSize; + LLRequestType mType; + + LLTextureInfoDetails(); +}; + +#endif // LL_LLTEXTUREINFODETAILS_H + diff --git a/indra/newview/lltexturestats.cpp b/indra/newview/lltexturestats.cpp new file mode 100644 index 0000000000..c91bfd4df2 --- /dev/null +++ b/indra/newview/lltexturestats.cpp @@ -0,0 +1,61 @@ +/** + * @file lltexturerstats.cpp + * @brief texture stats helper methods + * + * $LicenseInfo:firstyear=2002&license=viewergpl$ + * + * Copyright (c) 2002-2009, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "pipeline.h" +#include "llagent.h" +#include "lltexturefetch.h" +#include "lltexturestats.h" +#include "lltexturestatsuploader.h" +#include "llviewerregion.h" + +void send_texture_stats_to_sim(const LLSD &texture_stats) +{ + LLSD texture_stats_report; + // Only send stats if the agent is connected to a region. + if (!gAgent.getRegion() || gNoRender) + { + return; + } + + LLUUID agent_id = gAgent.getID(); + texture_stats_report["agent_id"] = agent_id; + texture_stats_report["region_id"] = gAgent.getRegion()->getRegionID(); + texture_stats_report["stats_data"] = texture_stats; + + std::string texture_cap_url = gAgent.getRegion()->getCapability("TextureStats"); + LLTextureStatsUploader tsu; + llinfos << "uploading texture stats data to simulator" << llendl; + tsu.uploadStatsToSimulator(texture_cap_url, texture_stats); +} + diff --git a/indra/newview/lltexturestats.h b/indra/newview/lltexturestats.h new file mode 100644 index 0000000000..2deb377dfd --- /dev/null +++ b/indra/newview/lltexturestats.h @@ -0,0 +1,41 @@ +/** + * @file lltexturestats.h + * @brief texture stats utilities + * + * $LicenseInfo:firstyear=2009&license=viewergpl$ + * + * Copyright (c) 2009, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#ifndef LL_LLTEXTURESTATS_H +#define LL_LLTEXTURESTATS_H + +#include "llappviewer.h" + +// utility functions to capture data on texture download speeds and send to simulator periodically +void send_texture_stats_to_sim(const LLSD &texture_stats); + +#endif // LL_LLTEXTURESTATS_H diff --git a/indra/newview/lltexturestatsuploader.cpp b/indra/newview/lltexturestatsuploader.cpp new file mode 100644 index 0000000000..e0358e1fca --- /dev/null +++ b/indra/newview/lltexturestatsuploader.cpp @@ -0,0 +1,59 @@ +/** + * @file lltexturerstats.cpp + * @brief texture stats upload class + * + * $LicenseInfo:firstyear=2002&license=viewergpl$ + * + * Copyright (c) 2002-2009, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "lltexturestatsuploader.h" + +LLTextureStatsUploader::LLTextureStatsUploader() +{ +} + +LLTextureStatsUploader::~LLTextureStatsUploader() +{ +} + +void LLTextureStatsUploader::uploadStatsToSimulator(const std::string texture_cap_url, const LLSD &texture_stats) +{ + if ( texture_cap_url != "" ) + { + LLHTTPClient::post(texture_cap_url, texture_stats, NULL); + } + else + { + llinfos << "Not sending texture stats: " + << texture_stats + << " as there is no cap url." + << llendl; + } +} + diff --git a/indra/newview/lltexturestatsuploader.h b/indra/newview/lltexturestatsuploader.h new file mode 100644 index 0000000000..f6cc8be8fe --- /dev/null +++ b/indra/newview/lltexturestatsuploader.h @@ -0,0 +1,48 @@ +/** + * @file lltexturestatsuploader.h + * @brief Class to send the texture stats to the simulatore + * + * $LicenseInfo:firstyear=2009&license=viewergpl$ + * + * Copyright (c) 2009, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#ifndef LL_LLTEXTURESTATSUPLOADER_H +#define LL_LLTEXTURESTATSUPLOADER_H + +#include "llappviewer.h" + +// utility functions to capture data on texture download speeds and send to simulator periodically + +class LLTextureStatsUploader +{ +public: + LLTextureStatsUploader(); + ~LLTextureStatsUploader(); + void uploadStatsToSimulator(const std::string texture_cap_url, const LLSD &texture_stats); +}; + +#endif // LL_LLTEXTURESTATSUPLOADER_H diff --git a/indra/newview/lltextureview.cpp b/indra/newview/lltextureview.cpp index dafa4f25ca..b6e20608eb 100644 --- a/indra/newview/lltextureview.cpp +++ b/indra/newview/lltextureview.cpp @@ -44,19 +44,21 @@ #include "llrender.h" #include "lltooltip.h" +#include "llappviewer.h" #include "llselectmgr.h" #include "lltexlayer.h" #include "lltexturecache.h" #include "lltexturefetch.h" +#include "llviewercontrol.h" #include "llviewerobject.h" #include "llviewertexture.h" #include "llviewertexturelist.h" -#include "llappviewer.h" - +#include "llvovolume.h" extern F32 texmem_lower_bound_scale; LLTextureView *gTextureView = NULL; LLTextureSizeView *gTextureSizeView = NULL; +LLTextureSizeView *gTextureCategoryView = NULL; //static std::set<LLViewerFetchedTexture*> LLTextureView::sDebugImages; @@ -230,10 +232,10 @@ void LLTextureBar::draw() { "DSK", LLColor4::blue }, // CACHE_POST { "NET", LLColor4::green }, // LOAD_FROM_NETWORK { "SIM", LLColor4::green }, // LOAD_FROM_SIMULATOR - { "URL", LLColor4::green2 },// LOAD_FROM_HTTP_GET_URL - { "HTP", LLColor4::green }, // LOAD_FROM_HTTP_GET_DATA + { "REQ", LLColor4::yellow },// SEND_HTTP_REQ + { "HTP", LLColor4::green }, // WAIT_HTTP_REQ { "DEC", LLColor4::yellow },// DECODE_IMAGE - { "DEC", LLColor4::yellow },// DECODE_IMAGE_UPDATE + { "DEC", LLColor4::green }, // DECODE_IMAGE_UPDATE { "WRT", LLColor4::purple },// WRITE_TO_CACHE { "WRT", LLColor4::orange },// WAIT_ON_WRITE { "END", LLColor4::red }, // DONE @@ -261,7 +263,7 @@ void LLTextureBar::draw() // Draw the progress bar. S32 bar_width = 100; - S32 bar_left = 280; + S32 bar_left = 260; left = bar_left; right = left + bar_width; @@ -286,30 +288,31 @@ void LLTextureBar::draw() S32 pip_x = title_x3 + pip_space/2; // Draw the packet pip + const F32 pip_max_time = 5.f; F32 last_event = mImagep->mLastPacketTimer.getElapsedTimeF32(); - if (last_event < 1.f) + if (last_event < pip_max_time) { clr = LLColor4::white; } else { last_event = mImagep->mRequestDeltaTime; - if (last_event < 1.f) + if (last_event < pip_max_time) { clr = LLColor4::green; } else { last_event = mImagep->mFetchDeltaTime; - if (last_event < 1.f) + if (last_event < pip_max_time) { clr = LLColor4::yellow; } } } - if (last_event < 1.f) + if (last_event < pip_max_time) { - clr.setAlpha(1.f - last_event); + clr.setAlpha(1.f - last_event/pip_max_time); gGL.color4fv(clr.mV); gl_rect_2d(pip_x, top, pip_x + pip_width, bottom); } @@ -406,89 +409,113 @@ void LLGLTexMemBar::draw() S32 total_mem = BYTES_TO_MEGA_BYTES(LLViewerTexture::sTotalTextureMemoryInBytes); S32 max_total_mem = LLViewerTexture::sMaxTotalTextureMemInMegaBytes; F32 discard_bias = LLViewerTexture::sDesiredDiscardBias; + F32 cache_usage = (F32)BYTES_TO_MEGA_BYTES(LLAppViewer::getTextureCache()->getUsage()) ; + F32 cache_max_usage = (F32)BYTES_TO_MEGA_BYTES(LLAppViewer::getTextureCache()->getMaxUsage()) ; S32 line_height = (S32)(LLFontGL::getFontMonospace()->getLineHeight() + .5f); S32 h_offset = (S32)((texture_bar_height + 2.5f) * mTextureView->mNumTextureBars + 2.5f); //---------------------------------------------------------------------------- LLGLSUIDefault gls_ui; - F32 text_color[] = {1.f, 1.f, 1.f, 0.75f}; + LLColor4 text_color(1.f, 1.f, 1.f, 0.75f); + LLColor4 color; std::string text; - text = llformat("GL Tot: %d/%d MB Bound: %d/%d MB Discard Bias: %.2f", + text = llformat("GL Tot: %d/%d MB Bound: %d/%d MB Raw Tot: %d MB Bias: %.2f Cache: %.1f/%.1f MB", total_mem, max_total_mem, bound_mem, max_bound_mem, - discard_bias); + LLImageRaw::sGlobalRawMemory >> 20, discard_bias, + cache_usage, cache_max_usage); + //, cache_entries, cache_max_entries LLFontGL::getFontMonospace()->renderUTF8(text, 0, 0, h_offset + line_height*3, - text_color, LLFontGL::LEFT, LLFontGL::TOP); + text_color, LLFontGL::LEFT, LLFontGL::TOP); //---------------------------------------------------------------------------- - S32 bar_left = 380; +#if 0 + S32 bar_left = 400; S32 bar_width = 200; S32 top = line_height*3 - 2 + h_offset; S32 bottom = top - 6; S32 left = bar_left; S32 right = left + bar_width; - - F32 bar_scale = (F32)bar_width / (max_bound_mem * 1.5f); + F32 bar_scale; gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - - gGL.color4f(0.5f, 0.5f, 0.5f, 0.75f); - gl_rect_2d(left, top, right, bottom); - + // GL Mem Bar + left = bar_left; - right = left + llfloor(bound_mem * bar_scale); - if (bound_mem < llfloor(max_bound_mem * texmem_lower_bound_scale)) - { - gGL.color4f(0.f, 1.f, 0.f, 0.75f); - } - else if (bound_mem < max_bound_mem) - { - gGL.color4f(1.f, 1.f, 0.f, 0.75f); - } - else - { - gGL.color4f(1.f, 0.f, 0.f, 0.75f); - } + text = "GL"; + LLFontGL::getFontMonospace()->renderUTF8(text, 0, left, line_height*3, + text_color, LLFontGL::LEFT, LLFontGL::TOP); + + left = bar_left+20; + right = left + bar_width; + + gGL.color4f(0.5f, 0.5f, 0.5f, 0.75f); // grey gl_rect_2d(left, top, right, bottom); bar_scale = (F32)bar_width / (max_total_mem * 1.5f); + right = left + llfloor(total_mem * bar_scale); + right = llclamp(right, bar_left, bar_left + bar_width); - top = bottom - 2; - bottom = top - 6; + color = (total_mem < llfloor(max_total_mem * texmem_lower_bound_scale)) ? LLColor4::green : + (total_mem < max_total_mem) ? LLColor4::yellow : LLColor4::red; + color[VALPHA] = .75f; + glColor4fv(color.mV); + + gl_rect_2d(left, top, right, bottom); // red/yellow/green + + // + bar_left += bar_width + bar_space; + //top = bottom - 2; bottom = top - 6; + + // Bound Mem Bar + left = bar_left; - right = left + llfloor(total_mem * bar_scale); - if (total_mem < llfloor(max_total_mem * texmem_lower_bound_scale)) - { - gGL.color4f(0.f, 1.f, 0.f, 0.75f); - } - else if (total_mem < max_total_mem) - { - gGL.color4f(1.f, 1.f, 0.f, 0.75f); - } - else - { - gGL.color4f(1.f, 0.f, 0.f, 0.75f); - } + text = "GL"; + LLFontGL::getFontMonospace()->renderUTF8(text, 0, left, line_height*3, + text_color, LLFontGL::LEFT, LLFontGL::TOP); + left = bar_left + 20; + right = left + bar_width; + + gGL.color4f(0.5f, 0.5f, 0.5f, 0.75f); gl_rect_2d(left, top, right, bottom); + color = (bound_mem < llfloor(max_bound_mem * texmem_lower_bound_scale)) ? LLColor4::green : + (bound_mem < max_bound_mem) ? LLColor4::yellow : LLColor4::red; + color[VALPHA] = .75f; + glColor4fv(color.mV); + + gl_rect_2d(left, top, right, bottom); +#else + S32 left = 0 ; +#endif //---------------------------------------------------------------------------- - text = llformat("Textures: Count: %d Fetch: %d(%d) Pkts:%d(%d) Cache R/W: %d/%d LFS:%d IW:%d(%d) RAW:%d mRaw:%d mAux:%d CB:%d", + text = llformat("Textures: %d Fetch: %d(%d) Pkts:%d(%d) Cache R/W: %d/%d LFS:%d IW:%d RAW:%d HTP:%d", gTextureList.getNumImages(), LLAppViewer::getTextureFetch()->getNumRequests(), LLAppViewer::getTextureFetch()->getNumDeletes(), LLAppViewer::getTextureFetch()->mPacketCount, LLAppViewer::getTextureFetch()->mBadPacketCount, LLAppViewer::getTextureCache()->getNumReads(), LLAppViewer::getTextureCache()->getNumWrites(), LLLFSThread::sLocal->getPending(), - LLImageWorker::sCount, LLImageWorker::getWorkerThread()->getNumDeletes(), - LLImageRaw::sRawImageCount, LLViewerFetchedTexture::sRawCount, LLViewerFetchedTexture::sAuxCount, - gTextureList.mCallbackList.size()); + LLAppViewer::getImageDecodeThread()->getPending(), + LLImageRaw::sRawImageCount, + LLAppViewer::getTextureFetch()->getNumHTTPRequests()); LLFontGL::getFontMonospace()->renderUTF8(text, 0, 0, h_offset + line_height*2, text_color, LLFontGL::LEFT, LLFontGL::TOP); + + + left = 550; + F32 bandwidth = LLAppViewer::getTextureFetch()->getTextureBandwidth(); + F32 max_bandwidth = gSavedSettings.getF32("ThrottleBandwidthKBPS"); + color = bandwidth > max_bandwidth ? LLColor4::red : bandwidth > max_bandwidth*.75f ? LLColor4::yellow : text_color; + color[VALPHA] = text_color[VALPHA]; + text = llformat("BW:%.0f/%.0f",bandwidth, max_bandwidth); + LLFontGL::getFontMonospace()->renderUTF8(text, 0, left, line_height*2, + color, LLFontGL::LEFT, LLFontGL::TOP); S32 dx1 = 0; if (LLAppViewer::getTextureFetch()->mDebugPause) @@ -555,7 +582,7 @@ public: void setTop(S32 loaded, S32 bound, F32 scale) {mTopLoaded = loaded ; mTopBound = bound; mScale = scale ;} void draw(); - BOOL handleHover(S32 x, S32 y, MASK mask) ; + BOOL handleHover(S32 x, S32 y, MASK mask, BOOL set_pick_size) ; private: S32 mIndex ; @@ -568,19 +595,16 @@ private: F32 mScale ; }; -BOOL LLGLTexSizeBar::handleHover(S32 x, S32 y, MASK mask) +BOOL LLGLTexSizeBar::handleHover(S32 x, S32 y, MASK mask, BOOL set_pick_size) { -#if !LL_RELEASE_FOR_DOWNLOAD if(y > mBottom && (y < mBottom + (S32)(mTopLoaded * mScale) || y < mBottom + (S32)(mTopBound * mScale))) { - LLImageGL::setCurTexSizebar(mIndex); + LLImageGL::setCurTexSizebar(mIndex, set_pick_size); } -#endif return TRUE ; } void LLGLTexSizeBar::draw() { -#if !LL_RELEASE_FOR_DOWNLOAD LLGLSUIDefault gls_ui; if(LLImageGL::sCurTexSizeBar == mIndex) @@ -601,7 +625,6 @@ void LLGLTexSizeBar::draw() F32 bound_color[] = {1.0f, 1.0f, 0.0f, 0.75f}; gl_rect_2d(mLeft, mBottom + (S32)(mTopLoaded * mScale), (mLeft + mRight) / 2, mBottom, loaded_color) ; gl_rect_2d((mLeft + mRight) / 2, mBottom + (S32)(mTopBound * mScale), mRight, mBottom, bound_color) ; -#endif } //////////////////////////////////////////////////////////////////////////// @@ -675,7 +698,13 @@ void LLTextureView::draw() << "\t" << cur_discard << llendl; } - + + if (imagep->getID() == LLAppViewer::getTextureFetch()->mDebugID) + { + static S32 debug_count = 0; + ++debug_count; // for breakpoints + } + #if 0 if (imagep->getDontDiscard()) { @@ -889,8 +918,7 @@ BOOL LLTextureView::handleKey(KEY key, MASK mask, BOOL called_from_parent) } //----------------------------------------------------------------- -LLTextureSizeView::LLTextureSizeView(const LLTextureSizeView::Params& p) - : LLView(p) +LLTextureSizeView::LLTextureSizeView(const LLTextureSizeView::Params& p) : LLContainerView(p) { setVisible(FALSE) ; @@ -910,7 +938,31 @@ LLTextureSizeView::~LLTextureSizeView() } void LLTextureSizeView::draw() { -#if !LL_RELEASE_FOR_DOWNLOAD + if(mType == TEXTURE_MEM_OVER_SIZE) + { + drawTextureSizeGraph(); + } + else + { + drawTextureCategoryGraph() ; + } + + LLView::draw(); +} + +BOOL LLTextureSizeView::handleHover(S32 x, S32 y, MASK mask) +{ + if(x > mTextureSizeBarRect.mLeft && x < mTextureSizeBarRect.mRight) + { + mTextureSizeBar[(x - mTextureSizeBarRect.mLeft) / mTextureSizeBarWidth]->handleHover(x, y, mask, (mType == TEXTURE_MEM_OVER_SIZE)) ; + } + + return TRUE ; +} + +//draw real-time texture mem bar over size +void LLTextureSizeView::drawTextureSizeGraph() +{ if(mTextureSizeBar.size() == 0) { S32 line_height = (S32)(LLFontGL::getFontMonospace()->getLineHeight() + .5f); @@ -931,29 +983,16 @@ void LLTextureSizeView::draw() mTextureSizeBar[i]->draw() ; } LLImageGL::resetCurTexSizebar(); - - LLView::draw(); -#endif -} - -BOOL LLTextureSizeView::handleHover(S32 x, S32 y, MASK mask) -{ - if(x > mTextureSizeBarRect.mLeft && x < mTextureSizeBarRect.mRight) - { - mTextureSizeBar[(x - mTextureSizeBarRect.mLeft) / mTextureSizeBarWidth]->handleHover(x, y, mask) ; - } - - return TRUE ; } //draw background of texture size bar graph F32 LLTextureSizeView::drawTextureSizeDistributionGraph() { + //scale F32 scale = 1.0f ; -#if !LL_RELEASE_FOR_DOWNLOAD + LLGLSUIDefault gls_ui; - //scale { S32 count = 0 ; for(U32 i = 0 ; i < LLImageGL::sTextureLoadedCounter.size() ; i++) @@ -1043,8 +1082,137 @@ F32 LLTextureSizeView::drawTextureSizeDistributionGraph() text = llformat("Texture Size Distribution") ; LLFontGL::getFontMonospace()->renderUTF8(text, 0, left + 250, top + line_height * 3, text_color, LLFontGL::LEFT, LLFontGL::TOP); - -#endif return scale ; } +//draw real-time texture mem bar over category +void LLTextureSizeView::drawTextureCategoryGraph() +{ + if(mTextureSizeBar.size() == 0) + { + S32 line_height = (S32)(LLFontGL::getFontMonospace()->getLineHeight() + .5f); + mTextureSizeBar.resize(LLViewerTexture::getTotalNumOfCategories()) ; + mTextureSizeBarRect.set(700, line_height * 2 + 400, 700 + mTextureSizeBar.size() * mTextureSizeBarWidth, line_height * 2) ; + + for(U32 i = 0 ; i < mTextureSizeBar.size() ; i++) + { + mTextureSizeBar[i] = new LLGLTexSizeBar(i, mTextureSizeBarRect.mLeft + i * mTextureSizeBarWidth , + line_height * 2, mTextureSizeBarRect.mLeft + (i + 1) * mTextureSizeBarWidth, line_height) ; + } + } + + F32 size_bar_scale = drawTextureCategoryDistributionGraph() ; + for(U32 i = 0 ; i < mTextureSizeBar.size() ; i++) + { + U32 k = LLViewerTexture::getIndexFromCategory(i) ; + mTextureSizeBar[i]->setTop(LLImageGL::sTextureMemByCategory[k] >> 20, LLImageGL::sTextureMemByCategoryBound[k] >> 20, size_bar_scale) ; + mTextureSizeBar[i]->draw() ; + } + LLImageGL::resetCurTexSizebar(); +} + +//draw background for TEXTURE_MEM_OVER_CATEGORY +F32 LLTextureSizeView::drawTextureCategoryDistributionGraph() +{ + //scale + F32 scale = 4.0f ; + + LLGLSUIDefault gls_ui; + + { + S32 count = 0 ; + for(U32 i = 0 ; i < LLImageGL::sTextureMemByCategory.size() ; i++) + { + S32 tmp = LLImageGL::sTextureMemByCategory[i] >> 20 ; + if(tmp > count) + { + count = tmp ; + } + } + if(count > mTextureSizeBarRect.getHeight() * 0.25f) + { + scale = (F32)mTextureSizeBarRect.getHeight() * 0.25f / count ; + } + } + + S32 line_height = (S32)(LLFontGL::getFontMonospace()->getLineHeight() + .5f); + S32 left = mTextureSizeBarRect.mLeft ; + S32 bottom = mTextureSizeBarRect.mBottom ; + S32 right = mTextureSizeBarRect.mRight ; + S32 top = mTextureSizeBarRect.mTop ; + + gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); + + //background rect + gl_rect_2d(left - 25, top + 30, right + 100, bottom - 25, LLColor4(0.0f, 0.0f, 0.0f, 0.25f)) ; + + //-------------------------------------------------- + gGL.color4f(1.0f, 0.5f, 0.5f, 0.75f); + gl_line_2d(left, bottom, right, bottom) ; //x axis + gl_line_2d(left, bottom, left, top) ; //y axis + + //ruler + //-------------------------------------------------- + gGL.color4f(1.0f, 0.5f, 0.5f, 0.5f); + for(S32 i = bottom + 50 ; i <= top ; i += 50) + { + gl_line_2d(left, i, right, i) ; + } + + //texts + //-------------------------------------------------- + F32 text_color[] = {1.f, 1.f, 1.f, 0.75f}; + std::string text; + + //------- + //x axis: size label + static char category[LLViewerTexture::MAX_GL_IMAGE_CATEGORY][4] = + {"Non", "Bak", "Av", "Cld", "Scp", "Hi", "Trn", "Slt", "Hud", "Bsf", "UI", "Pvw", "Map", "Mvs", "Slf", "Loc", "Scr", "Dyn", "Mdi", "ALT", "Oth" } ; + + text = llformat("%s", category[0]) ; + LLFontGL::getFontMonospace()->renderUTF8(text, 0, left + 12, bottom - line_height / 2, + text_color, LLFontGL::LEFT, LLFontGL::TOP); + for(U32 i = 1 ; i < mTextureSizeBar.size() ; i++) + { + text = llformat("%s", category[i]) ; + LLFontGL::getFontMonospace()->renderUTF8(text, 0, left + i * mTextureSizeBarWidth + 12, bottom - line_height / 2, + text_color, LLFontGL::LEFT, LLFontGL::TOP); + } + //------- + + //y axis: number label + for(S32 i = bottom + 50 ; i <= top ; i += 50) + { + text = llformat("%d", (S32)((i - bottom) / scale)) ; + LLFontGL::getFontMonospace()->renderUTF8(text, 0, left - 20, i + line_height / 2 , + text_color, LLFontGL::LEFT, LLFontGL::TOP); + LLFontGL::getFontMonospace()->renderUTF8(text, 0, right + 5, i + line_height / 2 , + text_color, LLFontGL::LEFT, LLFontGL::TOP); + } + + text = llformat("MB") ; + LLFontGL::getFontMonospace()->renderUTF8(text, 0, left - 20, top + line_height * 2 , + text_color, LLFontGL::LEFT, LLFontGL::TOP); + //-------------------------------------------------- + F32 loaded_color[] = {1.0f, 0.0f, 0.0f, 0.75f}; + gl_rect_2d(left + 70, top + line_height * 2, left + 90, top + line_height, loaded_color) ; + text = llformat("Loaded") ; + LLFontGL::getFontMonospace()->renderUTF8(text, 0, left + 100, top + line_height * 2, + loaded_color, + LLFontGL::LEFT, LLFontGL::TOP); + + F32 bound_color[] = {1.0f, 1.0f, 0.0f, 0.75f}; + gl_rect_2d(left + 170, top + line_height * 2, left + 190, top + line_height, bound_color) ; + text = llformat("Bound") ; + LLFontGL::getFontMonospace()->renderUTF8(text, 0, left + 200, top + line_height * 2, + bound_color, LLFontGL::LEFT, LLFontGL::TOP); + + //-------------------------------------------------- + + //title + text = llformat("Texture Category Distribution") ; + LLFontGL::getFontMonospace()->renderUTF8(text, 0, left + 250, top + line_height * 3, + text_color, LLFontGL::LEFT, LLFontGL::TOP); + + return scale ; +} diff --git a/indra/newview/lltextureview.h b/indra/newview/lltextureview.h index e917c0235e..435a55df83 100644 --- a/indra/newview/lltextureview.h +++ b/indra/newview/lltextureview.h @@ -79,24 +79,41 @@ public: }; class LLGLTexSizeBar; - -class LLTextureSizeView : public LLView +class LLTextureSizeView : public LLContainerView { -public: +protected: LLTextureSizeView(const Params&); + friend class LLUICtrlFactory; +public: ~LLTextureSizeView(); /*virtual*/ void draw(); /*virtual*/ BOOL handleHover(S32 x, S32 y, MASK mask) ; + void setType(S32 type) {mType = type ;} + enum + { + TEXTURE_MEM_OVER_SIZE, + TEXTURE_MEM_OVER_CATEGORY + }; private: + //draw background for TEXTURE_MEM_OVER_SIZE F32 drawTextureSizeDistributionGraph() ; - + //draw real-time texture mem bar over size + void drawTextureSizeGraph(); + + //draw background for TEXTURE_MEM_OVER_CATEGORY + F32 drawTextureCategoryDistributionGraph() ; + //draw real-time texture mem bar over category + void drawTextureCategoryGraph(); + private: std::vector<LLGLTexSizeBar*> mTextureSizeBar ; LLRect mTextureSizeBarRect ; - S32 mTextureSizeBarWidth ; + S32 mTextureSizeBarWidth ; + S32 mType ; }; extern LLTextureView *gTextureView; extern LLTextureSizeView *gTextureSizeView; +extern LLTextureSizeView *gTextureCategoryView; #endif // LL_TEXTURE_VIEW_H diff --git a/indra/newview/lltoolpie.cpp b/indra/newview/lltoolpie.cpp index 24017202cc..93da32b115 100644 --- a/indra/newview/lltoolpie.cpp +++ b/indra/newview/lltoolpie.cpp @@ -494,6 +494,8 @@ BOOL LLToolPie::handleHover(S32 x, S32 y, MASK mask) } else if (handleMediaHover(mHoverPick)) { + // *NOTE: If you think the hover glow conflicts with the media outline, you + // could disable it here. show_highlight = true; // cursor set by media object lldebugst(LLERR_USER_INPUT) << "hover handled by LLToolPie (inactive)" << llendl; diff --git a/indra/newview/llurldispatcher.cpp b/indra/newview/llurldispatcher.cpp index 841902f683..a3daca6fa4 100644 --- a/indra/newview/llurldispatcher.cpp +++ b/indra/newview/llurldispatcher.cpp @@ -46,7 +46,7 @@ #include "llstartup.h" // gStartupState #include "llurlsimstring.h" #include "llweb.h" -#include "llworldmap.h" +#include "llworldmapmessage.h" // library includes #include "llsd.h" @@ -201,7 +201,7 @@ bool LLURLDispatcherImpl::dispatchRegion(const std::string& url, bool right_mous //if(url_displayp) url_displayp->setName(region_name); // Request a region handle by name - LLWorldMap::getInstance()->sendNamedRegionRequest(region_name, + LLWorldMapMessage::getInstance()->sendNamedRegionRequest(region_name, LLURLDispatcherImpl::regionNameCallback, url, false); // don't teleport @@ -240,7 +240,7 @@ void LLURLDispatcherImpl::regionNameCallback(U64 region_handle, const std::strin LLVector3d global_pos = from_region_handle(region_handle) + LLVector3d(local_pos); U64 new_region_handle = to_region_handle(global_pos); - LLWorldMap::getInstance()->sendHandleRegionRequest(new_region_handle, + LLWorldMapMessage::getInstance()->sendHandleRegionRequest(new_region_handle, LLURLDispatcherImpl::regionHandleCallback, url, teleport); } @@ -335,7 +335,7 @@ public: { url += tokens[i].asString() + "/"; } - LLWorldMap::getInstance()->sendNamedRegionRequest(region_name, + LLWorldMapMessage::getInstance()->sendNamedRegionRequest(region_name, LLURLDispatcherImpl::regionHandleCallback, url, true); // teleport diff --git a/indra/newview/llviewercamera.cpp b/indra/newview/llviewercamera.cpp index f65baea6ca..b5709fa102 100644 --- a/indra/newview/llviewercamera.cpp +++ b/indra/newview/llviewercamera.cpp @@ -109,10 +109,13 @@ LLViewerCamera::LLViewerCamera() : LLCamera() { calcProjection(getFar()); mCameraFOVDefault = DEFAULT_FIELD_OF_VIEW; + mCosHalfCameraFOV = cosf(mCameraFOVDefault * 0.5f); mPixelMeterRatio = 0.f; mScreenPixelArea = 0; mZoomFactor = 1.f; mZoomSubregion = 1; + mAverageSpeed = 0.f; + mAverageAngularSpeed = 0.f; gSavedSettings.getControl("CameraAngle")->getCommitSignal()->connect(boost::bind(&LLViewerCamera::updateCameraAngle, this, _2)); } @@ -151,15 +154,22 @@ void LLViewerCamera::updateCameraLocation(const LLVector3 ¢er, setOriginAndLookAt(origin, up_direction, point_of_interest); - F32 dpos = (center - last_position).magVec(); + mVelocityDir = center - last_position ; + F32 dpos = mVelocityDir.normVec() ; LLQuaternion rotation; rotation.shortestArc(last_axis, getAtAxis()); F32 x, y, z; F32 drot; rotation.getAngleAxis(&drot, &x, &y, &z); + mVelocityStat.addValue(dpos); mAngularVelocityStat.addValue(drot); + + mAverageSpeed = mVelocityStat.getMeanPerSec() ; + mAverageAngularSpeed = mAngularVelocityStat.getMeanPerSec() ; + mCosHalfCameraFOV = cosf(0.5f * getView() * llmax(1.0f, getAspect())); + // update pixel meter ratio using default fov, not modified one mPixelMeterRatio = getViewHeightInPixels()/ (2.f*tanf(mCameraFOVDefault*0.5)); // update screen pixel area @@ -818,10 +828,12 @@ BOOL LLViewerCamera::areVertsVisible(LLViewerObject* volumep, BOOL all_verts) LLCamera::setView(vertical_fov_rads); // call base implementation } -void LLViewerCamera::setDefaultFOV(F32 vertical_fov_rads) { +void LLViewerCamera::setDefaultFOV(F32 vertical_fov_rads) +{ vertical_fov_rads = llclamp(vertical_fov_rads, getMinView(), getMaxView()); setView(vertical_fov_rads); mCameraFOVDefault = vertical_fov_rads; + mCosHalfCameraFOV = cosf(mCameraFOVDefault * 0.5f); } diff --git a/indra/newview/llviewercamera.h b/indra/newview/llviewercamera.h index 90b77f771f..2b8a0892bf 100644 --- a/indra/newview/llviewercamera.h +++ b/indra/newview/llviewercamera.h @@ -91,17 +91,20 @@ public: BOOL projectPosAgentToScreen(const LLVector3 &pos_agent, LLCoordGL &out_point, const BOOL clamp = TRUE) const; BOOL projectPosAgentToScreenEdge(const LLVector3 &pos_agent, LLCoordGL &out_point) const; - + const LLVector3* getVelocityDir() const {return &mVelocityDir;} LLStat *getVelocityStat() { return &mVelocityStat; } LLStat *getAngularVelocityStat() { return &mAngularVelocityStat; } + F32 getCosHalfFov() {return mCosHalfCameraFOV;} + F32 getAverageSpeed() {return mAverageSpeed ;} + F32 getAverageAngularSpeed() {return mAverageAngularSpeed;} void getPixelVectors(const LLVector3 &pos_agent, LLVector3 &up, LLVector3 &right); LLVector3 roundToPixel(const LLVector3 &pos_agent); // Sets the current matrix /* virtual */ void setView(F32 vertical_fov_rads); - // Sets the current matrix AND remembers result as default view - void setDefaultFOV(F32 vertical_fov_rads); + + void setDefaultFOV(F32 fov) ; F32 getDefaultFOV() { return mCameraFOVDefault; } BOOL cameraUnderWater() const; @@ -120,9 +123,14 @@ protected: LLStat mVelocityStat; LLStat mAngularVelocityStat; + LLVector3 mVelocityDir ; + F32 mAverageSpeed ; + F32 mAverageAngularSpeed ; + mutable LLMatrix4 mProjectionMatrix; // Cache of perspective matrix mutable LLMatrix4 mModelviewMatrix; F32 mCameraFOVDefault; + F32 mCosHalfCameraFOV; LLVector3 mLastPointOfInterest; F32 mPixelMeterRatio; // Divide by distance from camera to get pixels per meter at that distance. S32 mScreenPixelArea; // Pixel area of entire window diff --git a/indra/newview/llviewercontrol.cpp b/indra/newview/llviewercontrol.cpp index 35226a1632..5e23a7e114 100644 --- a/indra/newview/llviewercontrol.cpp +++ b/indra/newview/llviewercontrol.cpp @@ -90,7 +90,7 @@ std::string gCurrentVersion; extern BOOL gResizeScreenTexture; extern BOOL gDebugGL; - +extern BOOL gAuditTexture; //////////////////////////////////////////////////////////////////////////// // Listeners @@ -378,6 +378,12 @@ static bool handleRenderUseImpostorsChanged(const LLSD& newvalue) return true; } +static bool handleAuditTextureChanged(const LLSD& newvalue) +{ + gAuditTexture = newvalue.asBoolean(); + return true; +} + static bool handleRenderDebugGLChanged(const LLSD& newvalue) { gDebugGL = newvalue.asBoolean() || gDebugSession; @@ -587,6 +593,7 @@ void settings_setup_listeners() gSavedSettings.getControl("RenderDeferredShadow")->getSignal()->connect(boost::bind(&handleSetShaderChanged, _2)); gSavedSettings.getControl("RenderDeferredGI")->getSignal()->connect(boost::bind(&handleSetShaderChanged, _2)); gSavedSettings.getControl("TextureMemory")->getSignal()->connect(boost::bind(&handleVideoMemoryChanged, _2)); + gSavedSettings.getControl("AuditTexture")->getSignal()->connect(boost::bind(&handleAuditTextureChanged, _2)); gSavedSettings.getControl("ChatFontSize")->getSignal()->connect(boost::bind(&handleChatFontSizeChanged, _2)); gSavedSettings.getControl("ChatPersistTime")->getSignal()->connect(boost::bind(&handleChatPersistTimeChanged, _2)); gSavedSettings.getControl("ConsoleMaxLines")->getSignal()->connect(boost::bind(&handleConsoleMaxLinesChanged, _2)); diff --git a/indra/newview/llviewerdisplay.cpp b/indra/newview/llviewerdisplay.cpp index a6a72e9666..e0bb8fedeb 100644 --- a/indra/newview/llviewerdisplay.cpp +++ b/indra/newview/llviewerdisplay.cpp @@ -712,7 +712,8 @@ void display(BOOL rebuild, F32 zoom_factor, int subfield, BOOL for_snapshot) gBumpImageList.updateImages(); // must be called before gTextureList version so that it's textures are thrown out first. - const F32 max_image_decode_time = llmin(0.005f, 0.005f*10.f*gFrameIntervalSeconds); // 50 ms/second decode time (no more than 5ms/frame) + F32 max_image_decode_time = 0.050f*gFrameIntervalSeconds; // 50 ms/second decode time + max_image_decode_time = llclamp(max_image_decode_time, 0.001f, 0.005f ); // min 1ms/frame, max 5ms/frame) gTextureList.updateImages(max_image_decode_time); //remove dead textures from GL diff --git a/indra/newview/llviewermediafocus.cpp b/indra/newview/llviewermediafocus.cpp index b2a0fa4b1c..70a7d835a3 100644 --- a/indra/newview/llviewermediafocus.cpp +++ b/indra/newview/llviewermediafocus.cpp @@ -499,8 +499,14 @@ void LLViewerMediaFocus::focusZoomOnMedia(LLUUID media_id) S32 face = obj->getFaceIndexWithMediaImpl(impl, -1); // We don't have a proper pick normal here, and finding a face's real normal is... complicated. - // For now, use +z to look at the top of the object. - LLVector3 normal(0.0f, 0.0f, 1.0f); + LLVector3 normal = obj->getApproximateFaceNormal(face); + if(normal.isNull()) + { + // If that didn't work, use the inverse of the camera "look at" axis, which should keep the camera pointed in the same direction. +// llinfos << "approximate face normal invalid, using camera direction." << llendl; + normal = LLViewerCamera::getInstance()->getAtAxis(); + normal *= (F32)-1.0f; + } // Attempt to focus/zoom on that face. setFocusFace(obj, face, impl, normal); diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp index 55b1fa6b49..07d073c3a9 100644 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -610,6 +610,14 @@ class LLAdvancedToggleConsole : public view_listener_t { toggle_visibility( (void*)((LLView*)gDebugView->mDebugConsolep) ); } + else if (gTextureSizeView && "texture size" == console_type) + { + toggle_visibility( (void*)gTextureSizeView ); + } + else if (gTextureCategoryView && "texture category" == console_type) + { + toggle_visibility( (void*)gTextureCategoryView ); + } else if ("fast timers" == console_type) { toggle_visibility( (void*)gDebugView->mFastTimerView ); @@ -637,6 +645,14 @@ class LLAdvancedCheckConsole : public view_listener_t { new_value = get_visibility( (void*)((LLView*)gDebugView->mDebugConsolep) ); } + else if (gTextureSizeView && "texture size" == console_type) + { + new_value = get_visibility( (void*)gTextureSizeView ); + } + else if (gTextureCategoryView && "texture category" == console_type) + { + new_value = get_visibility( (void*)gTextureCategoryView ); + } else if ("fast timers" == console_type) { new_value = get_visibility( (void*)gDebugView->mFastTimerView ); @@ -1161,28 +1177,6 @@ class LLAdvancedCheckWireframe : public view_listener_t }; ////////////////////// -// DISABLE TEXTURES // -////////////////////// - -class LLAdvancedToggleDisableTextures : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - LLViewerTexture::sDontLoadVolumeTextures = !LLViewerTexture::sDontLoadVolumeTextures; - return true; - } -}; - -class LLAdvancedCheckDisableTextures : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - bool new_value = LLViewerTexture::sDontLoadVolumeTextures; // <-- make this using LLCacheControl - return new_value; - } -}; - -////////////////////// // TEXTURE ATLAS // ////////////////////// @@ -1885,7 +1879,7 @@ class LLAdvancedRebakeTextures : public view_listener_t }; -#ifndef LL_RELEASE_FOR_DOWNLOAD +#if 1 //ndef LL_RELEASE_FOR_DOWNLOAD /////////////////////////// // DEBUG AVATAR TEXTURES // /////////////////////////// @@ -2523,24 +2517,12 @@ class LLObjectEnableTouch : public view_listener_t // label.assign("Touch"); // } //} -/* -bool handle_object_open() -{ - LLViewerObject* obj = LLSelectMgr::getInstance()->getSelection()->getPrimaryObject(); - if(!obj) return true; - LLFloaterOpenObject::show(); - return true; +void handle_object_open() +{ + LLFloaterReg::showInstance("openobject"); } -class LLObjectOpen : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - return handle_object_open(); - } -}; -*/ bool enable_object_open() { // Look for contents in root object, which is all the LLFloaterOpenObject @@ -2648,8 +2630,22 @@ void handle_object_edit() // Could be first use LLFirstUse::useBuild(); return; - } + +void handle_object_inspect() +{ + LLObjectSelectionHandle selection = LLSelectMgr::getInstance()->getSelection(); + LLViewerObject* selected_objectp = selection->getFirstRootObject(); + if (selected_objectp) + { + LLSD key; + key["task"] = "task"; + LLSideTray::getInstance()->showPanel("sidepanel_inventory", key); + } + + LLFloaterReg::showInstance("inspect", LLSD()); +} + //--------------------------------------------------------------------------- // Land pie menu //--------------------------------------------------------------------------- @@ -3484,9 +3480,8 @@ void set_god_level(U8 god_level) gAgent.setGodLevel( god_level ); LLViewerParcelMgr::getInstance()->notifyObservers(); - // God mode changes sim visibility - LLWorldMap::getInstance()->reset(); - LLWorldMap::getInstance()->setCurrentLayer(0); + // God mode changes region visibility + LLWorldMap::getInstance()->reloadItems(true); // inventory in items may change in god mode gObjectList.dirtyAllObjectInventory(); @@ -5198,7 +5193,7 @@ void show_debug_menus() gMenuBarView->setItemEnabled("Develop", qamode); // Server ('Admin') menu hidden when not in godmode. - const bool show_server_menu = debug && (gAgent.getGodLevel() > GOD_NOT); + const bool show_server_menu = debug && (gAgent.getGodLevel() > GOD_NOT || gAgent.getAdminOverride()); gMenuBarView->setItemVisible("Admin", show_server_menu); gMenuBarView->setItemEnabled("Admin", show_server_menu); } @@ -7782,8 +7777,6 @@ void initialize_menus() view_listener_t::addMenu(new LLAdvancedSelectedTextureInfo(), "Advanced.SelectedTextureInfo"); view_listener_t::addMenu(new LLAdvancedToggleWireframe(), "Advanced.ToggleWireframe"); view_listener_t::addMenu(new LLAdvancedCheckWireframe(), "Advanced.CheckWireframe"); - view_listener_t::addMenu(new LLAdvancedToggleDisableTextures(), "Advanced.ToggleDisableTextures"); - view_listener_t::addMenu(new LLAdvancedCheckDisableTextures(), "Advanced.CheckDisableTextures"); view_listener_t::addMenu(new LLAdvancedToggleTextureAtlas(), "Advanced.ToggleTextureAtlas"); view_listener_t::addMenu(new LLAdvancedCheckTextureAtlas(), "Advanced.CheckTextureAtlas"); view_listener_t::addMenu(new LLAdvancedEnableObjectObjectOcclusion(), "Advanced.EnableObjectObjectOcclusion"); @@ -7967,6 +7960,8 @@ void initialize_menus() commit.add("Object.Buy", boost::bind(&handle_buy)); commit.add("Object.Edit", boost::bind(&handle_object_edit)); + commit.add("Object.Inspect", boost::bind(&handle_object_inspect)); + commit.add("Object.Open", boost::bind(&handle_object_open)); commit.add("Object.Take", boost::bind(&handle_take)); diff --git a/indra/newview/llviewermenu.h b/indra/newview/llviewermenu.h index b65878b5e6..db4eb3be9d 100644 --- a/indra/newview/llviewermenu.h +++ b/indra/newview/llviewermenu.h @@ -94,6 +94,7 @@ void handle_sit_down(void*); void handle_object_build(void*); void handle_object_touch(); bool enable_object_open(); +void handle_object_open(); // Buy either contents or object itself void handle_buy(); diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp index d8e6c52c8c..ea1097c477 100644 --- a/indra/newview/llviewermessage.cpp +++ b/indra/newview/llviewermessage.cpp @@ -2914,46 +2914,37 @@ void process_agent_movement_complete(LLMessageSystem* msg, void**) if (!gLastVersionChannel.empty()) { - LLSD payload; - payload["message"] = version_channel; - LLNotifications::instance().add("ServerVersionChanged", LLSD(), payload, server_version_changed_callback); - } - - gLastVersionChannel = version_channel; -} - -bool server_version_changed_callback(const LLSD& notification, const LLSD& response) -{ - if(notification["payload"]["message"].asString() =="") - return false; - std::string url ="http://wiki.secondlife.com/wiki/Release_Notes/"; - //parse the msg string - std::string server_version = notification["payload"]["message"].asString(); - std::vector<std::string> s_vect; - boost::algorithm::split(s_vect, server_version, isspace); - for(U32 i = 0; i < s_vect.size(); i++) - { - if (i != (s_vect.size() - 1)) - { - if(i != (s_vect.size() - 2)) + // work out the URL for this server's Release Notes + std::string url ="http://wiki.secondlife.com/wiki/Release_Notes/"; + std::string server_version = version_channel; + std::vector<std::string> s_vect; + boost::algorithm::split(s_vect, server_version, isspace); + for(U32 i = 0; i < s_vect.size(); i++) + { + if (i != (s_vect.size() - 1)) { - url += s_vect[i] + "_"; + if(i != (s_vect.size() - 2)) + { + url += s_vect[i] + "_"; + } + else + { + url += s_vect[i] + "/"; + } } else { - url += s_vect[i] + "/"; + url += s_vect[i].substr(0,4); } } - else - { - url += s_vect[i].substr(0,4); - } + + LLSD args; + args["URL"] = url; + LLNotifications::instance().add("ServerVersionChanged", args); } - - LLWeb::loadURL(url); - return false; -} + gLastVersionChannel = version_channel; +} void process_crossed_region(LLMessageSystem* msg, void**) { diff --git a/indra/newview/llviewermessage.h b/indra/newview/llviewermessage.h index c15e5df675..e24da2013d 100644 --- a/indra/newview/llviewermessage.h +++ b/indra/newview/llviewermessage.h @@ -132,7 +132,6 @@ void container_inventory_arrived(LLViewerObject* object, // agent movement void send_complete_agent_movement(const LLHost& sim_host); void process_agent_movement_complete(LLMessageSystem* msg, void**); -bool server_version_changed_callback(const LLSD& notification, const LLSD& response); void process_crossed_region(LLMessageSystem* msg, void**); void process_teleport_start(LLMessageSystem* msg, void**); void process_teleport_progress(LLMessageSystem* msg, void**); diff --git a/indra/newview/llviewerobject.cpp b/indra/newview/llviewerobject.cpp index 26411ce152..e491e11960 100644 --- a/indra/newview/llviewerobject.cpp +++ b/indra/newview/llviewerobject.cpp @@ -2906,7 +2906,7 @@ F32 LLViewerObject::getMidScale() const } -void LLViewerObject::updateTextures(LLAgent &agent) +void LLViewerObject::updateTextures() { } diff --git a/indra/newview/llviewerobject.h b/indra/newview/llviewerobject.h index b8ae31118c..01b213a87d 100644 --- a/indra/newview/llviewerobject.h +++ b/indra/newview/llviewerobject.h @@ -199,7 +199,7 @@ public: S32 getNumFaces() const { return mNumFaces; } // Graphical stuff for objects - maybe broken out into render class later? - virtual void updateTextures(LLAgent &agent); + virtual void updateTextures(); virtual void boostTexturePriority(BOOL boost_children = TRUE); // When you just want to boost priority of this object virtual LLDrawable* createDrawable(LLPipeline *pipeline); diff --git a/indra/newview/llviewerobjectlist.cpp b/indra/newview/llviewerobjectlist.cpp index 2927ca5292..96828ee1b6 100644 --- a/indra/newview/llviewerobjectlist.cpp +++ b/indra/newview/llviewerobjectlist.cpp @@ -642,7 +642,7 @@ void LLViewerObjectList::updateApparentAngles(LLAgent &agent) // Update distance & gpw objectp->setPixelAreaAndAngle(agent); // Also sets the approx. pixel area - objectp->updateTextures(agent); // Update the image levels of textures for this object. + objectp->updateTextures(); // Update the image levels of textures for this object. } } @@ -1074,6 +1074,7 @@ void LLViewerObjectList::renderObjectsForMap(LLNetMap &netmap) LLColor4 group_own_below_water_color = LLUIColorTable::instance().getColor( "NetMapGroupOwnBelowWater" ); + F32 max_radius = gSavedSettings.getF32("MiniMapPrimMaxRadius"); for (S32 i = 0; i < mMapObjects.count(); i++) { @@ -1089,6 +1090,11 @@ void LLViewerObjectList::renderObjectsForMap(LLNetMap &netmap) F32 approx_radius = (scale.mV[VX] + scale.mV[VY]) * 0.5f * 0.5f * 1.3f; // 1.3 is a fudge + // Limit the size of megaprims so they don't blot out everything on the minimap. + // Attempting to draw very large megaprims also causes client lag. + // See DEV-17370 and DEV-29869/SNOW-79 for details. + approx_radius = llmin(approx_radius, max_radius); + LLColor4U color = above_water_color; if( objectp->permYouOwner() ) { diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp index 7ea55b49e8..d1c9840a97 100644 --- a/indra/newview/llviewerregion.cpp +++ b/indra/newview/llviewerregion.cpp @@ -1427,11 +1427,11 @@ void LLViewerRegion::setSeedCapability(const std::string& url) capabilityNames.append("EstateChangeInfo"); capabilityNames.append("EventQueueGet"); capabilityNames.append("FetchInventory"); - capabilityNames.append("WebFetchInventoryDescendents"); capabilityNames.append("ObjectMedia"); capabilityNames.append("ObjectMediaNavigate"); capabilityNames.append("FetchLib"); capabilityNames.append("FetchLibDescendents"); + capabilityNames.append("GetTexture"); capabilityNames.append("GroupProposalBallot"); capabilityNames.append("HomeLocation"); capabilityNames.append("MapLayer"); @@ -1452,6 +1452,7 @@ void LLViewerRegion::setSeedCapability(const std::string& url) capabilityNames.append("SendUserReportWithScreenshot"); capabilityNames.append("ServerReleaseNotes"); capabilityNames.append("StartGroupProposal"); + capabilityNames.append("TextureStats"); capabilityNames.append("UntrustedSimulatorMessage"); capabilityNames.append("UpdateAgentInformation"); capabilityNames.append("UpdateAgentLanguage"); @@ -1464,6 +1465,7 @@ void LLViewerRegion::setSeedCapability(const std::string& url) capabilityNames.append("UploadBakedTexture"); capabilityNames.append("ViewerStartAuction"); capabilityNames.append("ViewerStats"); + capabilityNames.append("WebFetchInventoryDescendents"); // Please add new capabilities alphabetically to reduce // merge conflicts. diff --git a/indra/newview/llviewerstats.cpp b/indra/newview/llviewerstats.cpp index 6e07d8f246..caa94dba38 100644 --- a/indra/newview/llviewerstats.cpp +++ b/indra/newview/llviewerstats.cpp @@ -560,12 +560,18 @@ extern U32 gVisCompared; extern U32 gVisTested; std::map<S32,LLFrameTimer> gDebugTimers; +std::map<S32,std::string> gDebugTimerLabel; + +void init_statistics() +{ + // Label debug timers + gDebugTimerLabel[0] = "Texture"; +} void update_statistics(U32 frame_count) { gTotalWorldBytes += gVLManager.getTotalBytes(); gTotalObjectBytes += gObjectBits / 8; - gTotalTextureBytes += gTextureList.mTextureBits / 8; // make sure we have a valid time delta for this frame if (gFrameIntervalSeconds > 0.f) @@ -617,7 +623,6 @@ void update_statistics(U32 frame_count) F32 layer_bits = (F32)(gVLManager.getLandBits() + gVLManager.getWindBits() + gVLManager.getCloudBits()); LLViewerStats::getInstance()->mLayersKBitStat.addValue(layer_bits/1024.f); LLViewerStats::getInstance()->mObjectKBitStat.addValue(gObjectBits/1024.f); - LLViewerStats::getInstance()->mTextureKBitStat.addValue(gTextureList.mTextureBits/1024.f); LLViewerStats::getInstance()->mVFSPendingOperations.addValue(LLVFile::getVFSThread()->getPending()); LLViewerStats::getInstance()->mAssetKBitStat.addValue(gTransferManager.getTransferBitsIn(LLTCT_ASSET)/1024.f); gTransferManager.resetTransferBitsIn(LLTCT_ASSET); @@ -631,8 +636,6 @@ void update_statistics(U32 frame_count) gDebugTimers[0].unpause(); } - LLViewerStats::getInstance()->mTexturePacketsStat.addValue(gTextureList.mTexturePackets); - { static F32 visible_avatar_frames = 0.f; static F32 avg_visible_avatars = 0; @@ -652,8 +655,20 @@ void update_statistics(U32 frame_count) gObjectBits = 0; // gDecodedBits = 0; - gTextureList.mTextureBits = 0; - gTextureList.mTexturePackets = 0; + // Only update texture stats ones per second so that they are less noisy + { + static const F32 texture_stats_freq = 1.f; + static LLFrameTimer texture_stats_timer; + if (texture_stats_timer.getElapsedTimeF32() >= texture_stats_freq) + { + LLViewerStats::getInstance()->mTextureKBitStat.addValue(LLViewerTextureList::sTextureBits/1024.f); + LLViewerStats::getInstance()->mTexturePacketsStat.addValue(LLViewerTextureList::sTexturePackets); + gTotalTextureBytes += LLViewerTextureList::sTextureBits / 8; + LLViewerTextureList::sTextureBits = 0; + LLViewerTextureList::sTexturePackets = 0; + texture_stats_timer.reset(); + } + } } @@ -826,3 +841,4 @@ void send_stats() LLViewerStats::getInstance()->addToMessage(body); LLHTTPClient::post(url, body, new ViewerStatsResponder()); } + diff --git a/indra/newview/llviewerstats.h b/indra/newview/llviewerstats.h index ba89fbf02a..13d73000d2 100644 --- a/indra/newview/llviewerstats.h +++ b/indra/newview/llviewerstats.h @@ -34,6 +34,7 @@ #define LL_LLVIEWERSTATS_H #include "llstat.h" +#include "lltextureinfo.h" class LLViewerStats : public LLSingleton<LLViewerStats> { @@ -205,10 +206,13 @@ private: static const F32 SEND_STATS_PERIOD = 300.0f; // The following are from (older?) statistics code found in appviewer. +void init_statistics(); void reset_statistics(); void output_statistics(void*); void update_statistics(U32 frame_count); void send_stats(); extern std::map<S32,LLFrameTimer> gDebugTimers; +extern std::map<S32,std::string> gDebugTimerLabel; + #endif // LL_LLVIEWERSTATS_H diff --git a/indra/newview/llviewertexture.cpp b/indra/newview/llviewertexture.cpp index 6b8c8c01d4..9923c9ac74 100644 --- a/indra/newview/llviewertexture.cpp +++ b/indra/newview/llviewertexture.cpp @@ -60,6 +60,8 @@ #include "llviewercontrol.h" #include "pipeline.h" #include "llappviewer.h" +#include "llface.h" +#include "llviewercamera.h" #include "lltextureatlas.h" #include "lltextureatlasmanager.h" #include "lltextureentry.h" @@ -88,7 +90,15 @@ S32 LLViewerTexture::sTotalTextureMemoryInBytes = 0; S32 LLViewerTexture::sMaxBoundTextureMemInMegaBytes = 0; S32 LLViewerTexture::sMaxTotalTextureMemInMegaBytes = 0; S32 LLViewerTexture::sMaxDesiredTextureMemInBytes = 0 ; -BOOL LLViewerTexture::sDontLoadVolumeTextures = FALSE; +S8 LLViewerTexture::sCameraMovingDiscardBias = 0 ; +S32 LLViewerTexture::sMaxSculptRez = 128 ; //max sculpt image size +const S32 MAX_CACHED_RAW_IMAGE_AREA = 64 * 64 ; +const S32 MAX_CACHED_RAW_SCULPT_IMAGE_AREA = LLViewerTexture::sMaxSculptRez * LLViewerTexture::sMaxSculptRez ; +const S32 MAX_CACHED_RAW_TERRAIN_IMAGE_AREA = 128 * 128 ; +S32 LLViewerTexture::sMinLargeImageSize = 65536 ; //256 * 256. +S32 LLViewerTexture::sMaxSmallImageSize = MAX_CACHED_RAW_IMAGE_AREA ; +BOOL LLViewerTexture::sFreezeImageScalingDown = FALSE ; +F32 LLViewerTexture::sCurrentTime = 0.0f ; BOOL LLViewerTexture::sUseTextureAtlas = FALSE ; const F32 desired_discard_bias_min = -2.0f; // -max number of levels to improve image quality by @@ -162,6 +172,7 @@ LLPointer<LLViewerTexture> LLViewerTextureManager::getLocalTexture(BOOL usemipma if(generate_gl_tex) { tex->generateGLTexture() ; + tex->setCategory(LLViewerTexture::LOCAL) ; } return tex ; } @@ -171,12 +182,14 @@ LLPointer<LLViewerTexture> LLViewerTextureManager::getLocalTexture(const LLUUID& if(generate_gl_tex) { tex->generateGLTexture() ; + tex->setCategory(LLViewerTexture::LOCAL) ; } return tex ; } LLPointer<LLViewerTexture> LLViewerTextureManager::getLocalTexture(const LLImageRaw* raw, BOOL usemipmaps) { LLPointer<LLViewerTexture> tex = new LLViewerTexture(raw, usemipmaps) ; + tex->setCategory(LLViewerTexture::LOCAL) ; return tex ; } LLPointer<LLViewerTexture> LLViewerTextureManager::getLocalTexture(const U32 width, const U32 height, const U8 components, BOOL usemipmaps, BOOL generate_gl_tex) @@ -185,6 +198,7 @@ LLPointer<LLViewerTexture> LLViewerTextureManager::getLocalTexture(const U32 wid if(generate_gl_tex) { tex->generateGLTexture() ; + tex->setCategory(LLViewerTexture::LOCAL) ; } return tex ; } @@ -215,6 +229,19 @@ LLViewerFetchedTexture* LLViewerTextureManager::getFetchedTextureFromFile( return gTextureList.getImageFromFile(filename, usemipmaps, boost_priority, texture_type, internal_format, primary_format, force_id) ; } +//static +LLViewerFetchedTexture* LLViewerTextureManager::getFetchedTextureFromUrl(const std::string& url, + BOOL usemipmaps, + S32 boost_priority, + S8 texture_type, + LLGLint internal_format, + LLGLenum primary_format, + const LLUUID& force_id + ) +{ + return gTextureList.getImageFromUrl(url, usemipmaps, boost_priority, texture_type, internal_format, primary_format, force_id) ; +} + LLViewerFetchedTexture* LLViewerTextureManager::getFetchedTextureFromHost(const LLUUID& image_id, LLHost host) { return gTextureList.getImageFromHost(image_id, host) ; @@ -256,11 +283,12 @@ void LLViewerTextureManager::init() } imagep->createGLTexture(0, image_raw); image_raw = NULL; - LLViewerFetchedTexture::sDefaultImagep->dontDiscard(); #else LLViewerFetchedTexture::sDefaultImagep = LLViewerTextureManager::getFetchedTexture(IMG_DEFAULT, TRUE, LLViewerTexture::BOOST_UI); #endif - + LLViewerFetchedTexture::sDefaultImagep->dontDiscard(); + LLViewerFetchedTexture::sDefaultImagep->setCategory(LLViewerTexture::OTHER) ; + LLViewerFetchedTexture::sSmokeImagep = LLViewerTextureManager::getFetchedTexture(IMG_SMOKE, TRUE, LLViewerTexture::BOOST_UI); LLViewerFetchedTexture::sSmokeImagep->setNoDelete() ; @@ -284,6 +312,8 @@ void LLViewerTextureManager::cleanup() LLViewerFetchedTexture::sWhiteImagep = NULL; LLViewerMediaTexture::cleanup() ; + + LLViewerTexture::cleanupClass() ; } //---------------------------------------------------------------------------------------------- @@ -294,6 +324,11 @@ void LLViewerTextureManager::cleanup() void LLViewerTexture::initClass() { LLImageGL::sDefaultGLTexture = LLViewerFetchedTexture::sDefaultImagep->getGLTexture() ; + + if(gAuditTexture) + { + LLImageGL::setHighlightTexture(LLViewerTexture::OTHER) ; + } } // static @@ -301,6 +336,25 @@ void LLViewerTexture::cleanupClass() { } +// static +S32 LLViewerTexture::getTotalNumOfCategories() +{ + return MAX_GL_IMAGE_CATEGORY - (BOOST_HIGH - BOOST_SCULPTED) + 2 ; +} + +// static +//index starts from zero. +S32 LLViewerTexture::getIndexFromCategory(S32 category) +{ + return (category < BOOST_HIGH) ? category : category - (BOOST_HIGH - BOOST_SCULPTED) + 1 ; +} + +//static +S32 LLViewerTexture::getCategoryFromIndex(S32 index) +{ + return (index < BOOST_HIGH) ? index : index + (BOOST_HIGH - BOOST_SCULPTED) - 1 ; +} + // tuning params const F32 discard_bias_delta = .05f; const F32 discard_delta_time = 0.5f; @@ -312,6 +366,8 @@ F32 texmem_middle_bound_scale = 0.925f; //static void LLViewerTexture::updateClass(const F32 velocity, const F32 angular_velocity) { + sCurrentTime = gFrameTimeSeconds ; + if(LLViewerTextureManager::sTesterp) { LLViewerTextureManager::sTesterp->update() ; @@ -352,6 +408,13 @@ void LLViewerTexture::updateClass(const F32 velocity, const F32 angular_velocity } sDesiredDiscardBias = llclamp(sDesiredDiscardBias, desired_discard_bias_min, desired_discard_bias_max); LLViewerTexture::sUseTextureAtlas = gSavedSettings.getBOOL("EnableTextureAtlas") ; + + F32 camera_moving_speed = LLViewerCamera::getInstance()->getAverageSpeed() ; + F32 camera_angular_speed = LLViewerCamera::getInstance()->getAverageAngularSpeed(); + sCameraMovingDiscardBias = (S8)llmax(0.2f * camera_moving_speed, 2.0f * camera_angular_speed - 1) ; + + LLViewerTexture::sFreezeImageScalingDown = (BYTES_TO_MEGA_BYTES(sBoundTextureMemoryInBytes) < 0.75f * sMaxBoundTextureMemInMegaBytes * texmem_middle_bound_scale) && + (BYTES_TO_MEGA_BYTES(sTotalTextureMemoryInBytes) < 0.75f * sMaxTotalTextureMemInMegaBytes * texmem_middle_bound_scale) ; } //end of static functions @@ -417,7 +480,9 @@ void LLViewerTexture::init(bool firstinit) mTextureState = NO_DELETE ; mDontDiscard = FALSE; mMaxVirtualSize = 0.f; + mNeedsGLTexture = FALSE ; mNeedsResetMaxVirtualSize = FALSE ; + mAdditionalDecodePriority = 0.f ; mParcelMedia = NULL ; } @@ -459,11 +524,15 @@ void LLViewerTexture::setBoostLevel(S32 level) { setNoDelete() ; } + if(gAuditTexture) + { + setCategory(mBoostLevel); + } } } -bool LLViewerTexture::bindDefaultImage(S32 stage) const +bool LLViewerTexture::bindDefaultImage(S32 stage) { if (stage < 0) return false; @@ -482,6 +551,10 @@ bool LLViewerTexture::bindDefaultImage(S32 stage) const llwarns << "LLViewerTexture::bindDefaultImage failed." << llendl; } stop_glerror(); + + //check if there is cached raw image and switch to it if possible + switchToCachedImage() ; + if(LLViewerTextureManager::sTesterp) { LLViewerTextureManager::sTesterp->updateGrayTextureBinding() ; @@ -500,24 +573,32 @@ void LLViewerTexture::forceImmediateUpdate() { } -void LLViewerTexture::addTextureStats(F32 virtual_size) const +void LLViewerTexture::addTextureStats(F32 virtual_size, BOOL needs_gltexture) const { - if (virtual_size > mMaxVirtualSize) + if(needs_gltexture) { - mMaxVirtualSize = virtual_size; + mNeedsGLTexture = TRUE ; } -} -void LLViewerTexture::resetTextureStats(BOOL zero) -{ - if (zero) + if(mNeedsResetMaxVirtualSize) { - mMaxVirtualSize = 0.0f; + //flag to reset the values because the old values are used. + mNeedsResetMaxVirtualSize = FALSE ; + mMaxVirtualSize = virtual_size; + mAdditionalDecodePriority = 0.f ; + mNeedsGLTexture = needs_gltexture ; } - else + else if (virtual_size > mMaxVirtualSize) { - mMaxVirtualSize -= mMaxVirtualSize * .10f; // decay by 5%/update - } + mMaxVirtualSize = virtual_size; + } +} + +void LLViewerTexture::resetTextureStats() +{ + mMaxVirtualSize = 0.0f; + mAdditionalDecodePriority = 0.f ; + mNeedsResetMaxVirtualSize = FALSE ; } //virtual @@ -544,6 +625,12 @@ void LLViewerTexture::removeFace(LLFace* facep) mFaceList.remove(facep) ; } +//virtual +void LLViewerTexture::switchToCachedImage() +{ + //nothing here. +} + void LLViewerTexture::forceActive() { mTextureState = ACTIVE ; @@ -588,11 +675,11 @@ BOOL LLViewerTexture::createGLTexture() return mGLTexturep->createGLTexture() ; } -BOOL LLViewerTexture::createGLTexture(S32 discard_level, const LLImageRaw* imageraw, S32 usename) +BOOL LLViewerTexture::createGLTexture(S32 discard_level, const LLImageRaw* imageraw, S32 usename, BOOL to_create, S32 category) { llassert_always(mGLTexturep.notNull()) ; - BOOL ret = mGLTexturep->createGLTexture(discard_level, imageraw, usename) ; + BOOL ret = mGLTexturep->createGLTexture(discard_level, imageraw, usename, to_create, category) ; if(ret) { @@ -704,6 +791,13 @@ void LLViewerTexture::setGLTextureCreated (bool initialized) mGLTexturep->setGLTextureCreated (initialized) ; } +void LLViewerTexture::setCategory(S32 category) +{ + llassert_always(mGLTexturep.notNull()) ; + + mGLTexturep->setCategory(category) ; +} + LLTexUnit::eTextureAddressMode LLViewerTexture::getAddressMode(void) const { llassert_always(mGLTexturep.notNull()) ; @@ -752,18 +846,18 @@ BOOL LLViewerTexture::getMissed() const return mGLTexturep->getMissed() ; } -BOOL LLViewerTexture::isValidForSculpt(S32 discard_level, S32 image_width, S32 image_height, S32 ncomponents) +BOOL LLViewerTexture::isJustBound() const { llassert_always(mGLTexturep.notNull()) ; - return mGLTexturep->isValidForSculpt(discard_level, image_width, image_height, ncomponents) ; + return mGLTexturep->isJustBound() ; } -BOOL LLViewerTexture::readBackRaw(S32 discard_level, LLImageRaw* imageraw, bool compressed_ok) const +void LLViewerTexture::forceUpdateBindStats(void) const { llassert_always(mGLTexturep.notNull()) ; - return mGLTexturep->readBackRaw(discard_level, imageraw, compressed_ok) ; + return mGLTexturep->forceUpdateBindStats() ; } U32 LLViewerTexture::getTexelsInAtlas() const @@ -803,6 +897,11 @@ void LLViewerTexture::destroyGLTexture() } } +BOOL LLViewerTexture::isLargeImage() +{ + return mFullWidth * mFullHeight > LLViewerTexture::sMinLargeImageSize ; +} + //virtual void LLViewerTexture::updateBindStatsForTester() { @@ -823,11 +922,12 @@ void LLViewerTexture::updateBindStatsForTester() //static F32 LLViewerFetchedTexture::maxDecodePriority() { - return 2000000.f; + return 6000000.f; } -LLViewerFetchedTexture::LLViewerFetchedTexture(const LLUUID& id, BOOL usemipmaps) - : LLViewerTexture(id, usemipmaps) +LLViewerFetchedTexture::LLViewerFetchedTexture(const LLUUID& id, const LLHost& host, BOOL usemipmaps) + : LLViewerTexture(id, usemipmaps), + mTargetHost(host) { init(TRUE) ; generateGLTexture() ; @@ -839,9 +939,9 @@ LLViewerFetchedTexture::LLViewerFetchedTexture(const LLImageRaw* raw, BOOL usemi init(TRUE) ; } -LLViewerFetchedTexture::LLViewerFetchedTexture(const std::string& full_path, const LLUUID& id, BOOL usemipmaps) +LLViewerFetchedTexture::LLViewerFetchedTexture(const std::string& url, const LLUUID& id, BOOL usemipmaps) : LLViewerTexture(id, usemipmaps), - mLocalFileName(full_path) + mUrl(url) { init(TRUE) ; generateGLTexture() ; @@ -886,10 +986,18 @@ void LLViewerFetchedTexture::init(bool firstinit) mFetchPriority = 0; mDownloadProgress = 0.f; mFetchDeltaTime = 999999.f; - mDecodeFrame = 0; - mVisibleFrame = 0; mForSculpt = FALSE ; mIsFetched = FALSE ; + + mCachedRawImage = NULL ; + mCachedRawDiscardLevel = -1 ; + mCachedRawImageReady = FALSE ; + + mSavedRawImage = NULL ; + mForceToSaveRawImage = FALSE ; + mSavedRawDiscardLevel = -1 ; + mDesiredSavedRawDiscardLevel = -1 ; + mLastReferencedSavedRawImageTime = 0.0f ; } LLViewerFetchedTexture::~LLViewerFetchedTexture() @@ -926,11 +1034,25 @@ void LLViewerFetchedTexture::cleanup() // Clean up image data destroyRawImage(); + mCachedRawImage = NULL ; + mCachedRawDiscardLevel = -1 ; + mCachedRawImageReady = FALSE ; + mSavedRawImage = NULL ; } void LLViewerFetchedTexture::setForSculpt() { mForSculpt = TRUE ; + if(isForSculptOnly() && !getBoundRecently()) + { + destroyGLTexture() ; //sculpt image does not need gl texture. + } + checkCachedRawSculptImage() ; +} + +BOOL LLViewerFetchedTexture::isForSculptOnly() const +{ + return mForSculpt && !mNeedsGLTexture ; } BOOL LLViewerFetchedTexture::isDeleted() @@ -965,17 +1087,37 @@ void LLViewerFetchedTexture::setInactive() } } +BOOL LLViewerFetchedTexture::isFullyLoaded() const +{ + // Unfortunately, the boolean "mFullyLoaded" is never updated correctly so we use that logic + // to check if the texture is there and completely downloaded + return (mFullWidth != 0) && (mFullHeight != 0) && !mIsFetching && !mHasFetcher; +} + + // virtual void LLViewerFetchedTexture::dump() { LLViewerTexture::dump(); - llinfos << "LLViewerFetchedTexture" - << " mIsMissingAsset " << (S32)mIsMissingAsset - << " mFullWidth " << mFullWidth - << " mFullHeight " << mFullHeight - << " mOrigWidth" << mOrigWidth - << " mOrigHeight" << mOrigHeight + llinfos << "Dump : " << mID + << ", mIsMissingAsset = " << (S32)mIsMissingAsset + << ", mFullWidth = " << (S32)mFullWidth + << ", mFullHeight = " << (S32)mFullHeight + << ", mOrigWidth = " << (S32)mOrigWidth + << ", mOrigHeight = " << (S32)mOrigHeight + << llendl; + llinfos << " : " + << " mFullyLoaded = " << (S32)mFullyLoaded + << ", mFetchState = " << (S32)mFetchState + << ", mFetchPriority = " << (S32)mFetchPriority + << ", mDownloadProgress = " << (F32)mDownloadProgress + << llendl; + llinfos << " : " + << " mHasFetcher = " << (S32)mHasFetcher + << ", mIsFetching = " << (S32)mIsFetching + << ", mIsFetched = " << (S32)mIsFetched + << ", mBoostLevel = " << (S32)mBoostLevel << llendl; } @@ -996,6 +1138,75 @@ void LLViewerFetchedTexture::destroyTexture() mFullyLoaded = FALSE ; } +// +//do not change the discard level of the loaded texture image. +BOOL LLViewerFetchedTexture::keepReuestedDiscardLevel() +{ + if (!mLoadedCallbackList.empty()) + { + return TRUE ; + } + + return FALSE ; +} + +void LLViewerFetchedTexture::addToCreateTexture() +{ + if(isForSculptOnly()) + { + //just update some variables, not to create a real GL texture. + createGLTexture(mRawDiscardLevel, mRawImage, 0, FALSE) ; + mNeedsCreateTexture = FALSE ; + destroyRawImage(); + } + else + { +#if 1 + // + //if mRequestedDiscardLevel > mDesiredDiscardLevel, we assume the required image res keep going up, + //so do not scale down the over qualified image. + //Note: scaling down image is expensensive. Do it only when very necessary. + // + if(mRequestedDiscardLevel <= mDesiredDiscardLevel && !keepReuestedDiscardLevel()) + { + S32 w = mFullWidth >> mRawDiscardLevel; + S32 h = mFullHeight >> mRawDiscardLevel; + + //if big image, do not load extra data + //scale it down to size >= LLViewerTexture::sMinLargeImageSize + if(w * h > LLViewerTexture::sMinLargeImageSize) + { + S32 d_level = llmin(mRequestedDiscardLevel, (S32)mDesiredDiscardLevel) - mRawDiscardLevel ; + + if(d_level > 0) + { + S32 i = 0 ; + while((d_level > 0) && ((w >> i) * (h >> i) > LLViewerTexture::sMinLargeImageSize)) + { + i++; + d_level--; + } + if(i > 0) + { + mRawDiscardLevel += i ; + if(mRawDiscardLevel >= getDiscardLevel() && getDiscardLevel() > 0) + { + mNeedsCreateTexture = FALSE ; + destroyRawImage(); + return ; + } + mRawImage->scale(w >> i, h >> i) ; + } + } + } + } +#endif + mNeedsCreateTexture = TRUE; + gTextureList.mCreateTextureList.insert(this); + } + return ; +} + // ONLY called from LLViewerTextureList BOOL LLViewerFetchedTexture::createTexture(S32 usename/*= 0*/) { @@ -1017,7 +1228,7 @@ BOOL LLViewerFetchedTexture::createTexture(S32 usename/*= 0*/) if (!gNoRender) { // store original size only for locally-sourced images - if (!mLocalFileName.empty()) + if (mUrl.compare(0, 7, "file://") == 0) { mOrigWidth = mRawImage->getWidth(); mOrigHeight = mRawImage->getHeight(); @@ -1063,7 +1274,7 @@ BOOL LLViewerFetchedTexture::createTexture(S32 usename/*= 0*/) if(!(res = insertToAtlas())) { - res = mGLTexturep->createGLTexture(mRawDiscardLevel, mRawImage, usename); + res = mGLTexturep->createGLTexture(mRawDiscardLevel, mRawImage, usename, TRUE, mBoostLevel); resetFaceAtlas() ; } setActive() ; @@ -1117,7 +1328,15 @@ void LLViewerFetchedTexture::processTextureStats() return ; } - if(!mFullWidth || !mFullHeight) + updateVirtualSize() ; + + static LLCachedControl<bool> textures_fullres(gSavedSettings,"TextureLoadFullRes"); + + if (textures_fullres) + { + mDesiredDiscardLevel = 0; + } + else if(!mFullWidth || !mFullHeight) { mDesiredDiscardLevel = getMaxDiscardLevel() ; } @@ -1149,16 +1368,6 @@ void LLViewerFetchedTexture::processTextureStats() } } -//texture does not have any data, so we don't know the size of the image, treat it like 32 * 32. -F32 LLViewerFetchedTexture::calcDecodePriorityForUnknownTexture(F32 pixel_priority) -{ - F32 desired = (F32)(log(32.0/pixel_priority) / log_2); - S32 ddiscard = MAX_DISCARD_LEVEL - (S32)desired + 1; - ddiscard = llclamp(ddiscard, 1, 9); - - return ddiscard*100000.f; -} - F32 LLViewerFetchedTexture::calcDecodePriority() { #ifndef LL_RELEASE_FOR_DOWNLOAD @@ -1177,22 +1386,28 @@ F32 LLViewerFetchedTexture::calcDecodePriority() { return mDecodePriority; // no change while waiting to create } - - F32 priority; - S32 cur_discard = getDiscardLevel(); - bool have_all_data = (cur_discard >= 0 && (cur_discard <= mDesiredDiscardLevel)); - F32 pixel_priority = fsqrtf(mMaxVirtualSize); - const S32 MIN_NOT_VISIBLE_FRAMES = 30; // NOTE: this function is not called every frame - mDecodeFrame++; - if (pixel_priority > 0.f) + if(mForceToSaveRawImage) { - mVisibleFrame = mDecodeFrame; + return maxDecodePriority() ; } + S32 cur_discard = getDiscardLevel(); + bool have_all_data = (cur_discard >= 0 && (cur_discard <= mDesiredDiscardLevel)); + F32 pixel_priority = fsqrtf(mMaxVirtualSize); + + F32 priority; if (mIsMissingAsset) { priority = 0.0f; } + else if(mDesiredDiscardLevel >= cur_discard && cur_discard > -1) + { + priority = -1.0f ; + } + else if(mCachedRawDiscardLevel > -1 && mDesiredDiscardLevel >= mCachedRawDiscardLevel) + { + priority = -1.0f; + } else if (mDesiredDiscardLevel > getMaxDiscardLevel()) { // Don't decode anything we don't need @@ -1210,11 +1425,6 @@ F32 LLViewerFetchedTexture::calcDecodePriority() // Always want high boosted images priority = 1.f; } - else if (mVisibleFrame == 0 || (mDecodeFrame - mVisibleFrame > MIN_NOT_VISIBLE_FRAMES)) - { - // Don't decode anything that isn't visible unless it's important - priority = -2.0f; - } else { // Leave the priority as-is @@ -1223,7 +1433,13 @@ F32 LLViewerFetchedTexture::calcDecodePriority() } else if (cur_discard < 0) { - priority = calcDecodePriorityForUnknownTexture(pixel_priority) ; + //texture does not have any data, so we don't know the size of the image, treat it like 32 * 32. + // priority range = 100,000 - 500,000 + static const F64 log_2 = log(2.0); + F32 desired = (F32)(log(32.0/pixel_priority) / log_2); + S32 ddiscard = MAX_DISCARD_LEVEL - (S32)desired; + ddiscard = llclamp(ddiscard, 0, 4); + priority = (ddiscard+1)*100000.f; } else if ((mMinDiscardLevel > 0) && (cur_discard <= mMinDiscardLevel)) { @@ -1236,29 +1452,47 @@ F32 LLViewerFetchedTexture::calcDecodePriority() } else { - // priority range = 100000-400000 - S32 ddiscard = cur_discard - mDesiredDiscardLevel; + // priority range = 100,000 - 500,000 + S32 desired_discard = mDesiredDiscardLevel; if (getDontDiscard()) { - ddiscard+=2; + desired_discard -= 2; + } + else if (!isJustBound() && mCachedRawImageReady && !mBoostLevel) + { + // We haven't rendered this in the last half second, and we have a cached raw image, leave the desired discard as-is + desired_discard = cur_discard; } else if (mGLTexturep.notNull() && !mGLTexturep->getBoundRecently() && mBoostLevel == LLViewerTexture::BOOST_NONE) { - ddiscard-=2; + // We haven't rendered this in a while, de-prioritize it + desired_discard += 2; } + S32 ddiscard = cur_discard - desired_discard; ddiscard = llclamp(ddiscard, 0, 4); - priority = ddiscard*100000.f; + priority = (ddiscard+1)*100000.f; } + + // Priority Formula: + // BOOST_HIGH + ADDITIONAL PRI + DELTA DISCARD + BOOST LEVEL + PIXELS + // [10,000,000] + [1-9,000,000] + [1-400,000] + [1-20,000] + [0-999] if (priority > 0.0f) { - pixel_priority = llclamp(pixel_priority, 0.0f, priority-1.f); // priority range = 100000-900000 + pixel_priority = llclamp(pixel_priority, 0.0f, 999.f); + + priority = pixel_priority + 1000.f * mBoostLevel; + if ( mBoostLevel > BOOST_HIGH) { - priority = 1000000.f + pixel_priority + 1000.f * (mBoostLevel - LLViewerTexture::BOOST_NONE); + priority += 10000000.f; } - else + + if(mAdditionalDecodePriority > 0.0f) { - priority += 0.f + pixel_priority + 1000.f * (mBoostLevel - LLViewerTexture::BOOST_NONE); + // 1-9 + S32 additional_priority = (S32)(1.0f + mAdditionalDecodePriority*8.0f + .5f); // round + // priority range += 0-9,000,000 + priority += 1000000.f * (F32)additional_priority; } } return priority; @@ -1271,6 +1505,36 @@ void LLViewerFetchedTexture::setDecodePriority(F32 priority) mDecodePriority = priority; } +void LLViewerFetchedTexture::setAdditionalDecodePriority(F32 priority) +{ + priority = llclamp(priority, 0.f, 1.f); + if(mAdditionalDecodePriority < priority) + { + mAdditionalDecodePriority = priority; + } +} + +void LLViewerFetchedTexture::updateVirtualSize() +{ + if(mNeedsResetMaxVirtualSize) + { + addTextureStats(0.f, FALSE) ;//reset + } + if(mFaceList.size() > 0) + { + for(std::list<LLFace*>::iterator iter = mFaceList.begin(); iter != mFaceList.end(); ++iter) + { + LLFace* facep = *iter ; + if(facep->getDrawable()->isRecentlyVisible()) + { + addTextureStats(facep->getVirtualSize()) ; + setAdditionalDecodePriority(facep->getImportanceToCamera()) ; + } + } + } + mNeedsResetMaxVirtualSize = TRUE ; +} + bool LLViewerFetchedTexture::updateFetch() { mFetchState = 0; @@ -1310,6 +1574,15 @@ bool LLViewerFetchedTexture::updateFetch() { // Sets mRawDiscardLevel, mRawImage, mAuxRawImage S32 fetch_discard = current_discard; + + if(mForceToSaveRawImage) + { + if(fetch_discard >= 0) + { + fetch_discard = llmax(fetch_discard, mSavedRawDiscardLevel) ; + } + } + if (mRawImage.notNull()) sRawCount--; if (mAuxRawImage.notNull()) sAuxCount--; bool finished = LLAppViewer::getTextureFetch()->getRequestFinished(getID(), fetch_discard, mRawImage, mAuxRawImage); @@ -1349,11 +1622,24 @@ bool LLViewerFetchedTexture::updateFetch() (*iter)->dirtyTexture() ; } } - mIsRawImageValid = TRUE; - gTextureList.mCreateTextureList.insert(this); - mNeedsCreateTexture = TRUE; mFullWidth = mRawImage->getWidth() << mRawDiscardLevel; mFullHeight = mRawImage->getHeight() << mRawDiscardLevel; + + if(mFullWidth > MAX_IMAGE_SIZE || mFullHeight > MAX_IMAGE_SIZE) + { + //discard all oversized textures. + destroyRawImage(); + setIsMissingAsset(); + mRawDiscardLevel = INVALID_DISCARD_LEVEL ; + mIsFetching = FALSE ; + } + else + { + mIsRawImageValid = TRUE; + addToCreateTexture() ; + } + + return TRUE ; } else { @@ -1376,13 +1662,13 @@ bool LLViewerFetchedTexture::updateFetch() } else { - llwarns << mID << ": Setting min discard to " << current_discard << llendl; + //llwarns << mID << ": Setting min discard to " << current_discard << llendl; mMinDiscardLevel = current_discard; desired_discard = current_discard; } destroyRawImage(); } - else if (mRawImage.isNull()) + else if (mRawImage.notNull()) { // We have data, but our fetch failed to return raw data // *TODO: FIgure out why this is happening and fix it @@ -1391,12 +1677,29 @@ bool LLViewerFetchedTexture::updateFetch() } else { +// // Useful debugging code for undesired deprioritization of textures. +// if (decode_priority <= 0.0f && desired_discard >= 0 && desired_discard < current_discard) +// { +// llinfos << "Calling updateRequestPriority() with decode_priority = 0.0f" << llendl; +// calcDecodePriority(); +// } LLAppViewer::getTextureFetch()->updateRequestPriority(mID, decode_priority); } } - bool make_request = true; - + if (!mDontDiscard) + { + if (mBoostLevel == 0) + { + desired_discard = llmax(desired_discard, current_discard-1); + } + else + { + desired_discard = llmax(desired_discard, current_discard-2); + } + } + + bool make_request = true; if (decode_priority <= 0) { make_request = false; @@ -1409,6 +1712,10 @@ bool LLViewerFetchedTexture::updateFetch() { make_request = false; } + else if (!isJustBound() && mCachedRawImageReady) + { + make_request = false; + } else { if (mIsFetching) @@ -1436,33 +1743,12 @@ bool LLViewerFetchedTexture::updateFetch() h = mGLTexturep->getHeight(0); c = mComponents; } - if (!mDontDiscard) - { - if (mBoostLevel == LLViewerTexture::BOOST_NONE) - { - desired_discard = llmax(desired_discard, current_discard-1); - } - else - { - desired_discard = llmax(desired_discard, current_discard-2); - } - } - + // bypass texturefetch directly by pulling from LLTextureCache bool fetch_request_created = false; - if (mLocalFileName.empty()) - { - fetch_request_created = LLAppViewer::getTextureFetch()->createRequest(getID(), getTargetHost(), decode_priority, - w, h, c, desired_discard, - needsAux()); - } - else - { - fetch_request_created = LLAppViewer::getTextureFetch()->createRequest(mLocalFileName, getID(),getTargetHost(), decode_priority, - w, h, c, desired_discard, - needsAux()); - } - + fetch_request_created = LLAppViewer::getTextureFetch()->createRequest(mUrl, getID(),getTargetHost(), decode_priority, + w, h, c, desired_discard, needsAux()); + if (fetch_request_created) { mHasFetcher = TRUE; @@ -1492,9 +1778,82 @@ bool LLViewerFetchedTexture::updateFetch() return mIsFetching ? true : false; } +// +//force to fetch a new raw image for this texture +// +BOOL LLViewerFetchedTexture::forceFetch() +{ + if(!mForceToSaveRawImage) + { + return false ; + } + if(mDesiredSavedRawDiscardLevel < getDiscardLevel()) + { + //no need to force fetching. normal fetching flow will do the work. + //return false ; + } + if (mNeedsCreateTexture) + { + // We may be fetching still (e.g. waiting on write) + // but don't check until we've processed the raw data we have + //return false; + } + if(mIsFetching) + { + return false ; + } + if (mIsMissingAsset) + { + mForceToSaveRawImage = false ; + llassert_always(!mHasFetcher); + return false; // skip + } + if (!mLoadedCallbackList.empty() && mRawImage.notNull()) + { + return false; // process any raw image data in callbacks before replacing + } + if(mRawImage.notNull() && mRawDiscardLevel <= mDesiredSavedRawDiscardLevel) + { + return false ; // mRawImage is enough + } + + S32 desired_discard = mDesiredSavedRawDiscardLevel ; + S32 current_discard = getDiscardLevel(); + + bool fetch_request_created = false; + S32 w=0, h=0, c=0; + if (current_discard >= 0) + { + w = getWidth(0); + h = getHeight(0); + c = getComponents(); + } + fetch_request_created = LLAppViewer::getTextureFetch()->createRequest(mUrl, getID(),getTargetHost(), maxDecodePriority(), + w, h, c, desired_discard, needsAux()); + + if (fetch_request_created) + { + mHasFetcher = TRUE; + mIsFetching = TRUE; + mRequestedDiscardLevel = desired_discard ; + + mFetchState = LLAppViewer::getTextureFetch()->getFetchState(mID, mDownloadProgress, mRequestedDownloadPriority, + mFetchPriority, mFetchDeltaTime, mRequestDeltaTime); + } + + return mIsFetching ? true : false; +} + void LLViewerFetchedTexture::setIsMissingAsset() { - llwarns << mLocalFileName << " " << mID << ": Marking image as missing" << llendl; + if (mUrl.empty()) + { + llwarns << mID << ": Marking image as missing" << llendl; + } + else + { + llwarns << mUrl << ": Marking image as missing" << llendl; + } if (mHasFetcher) { LLAppViewer::getTextureFetch()->deleteRequest(getID(), true); @@ -1516,7 +1875,13 @@ void LLViewerFetchedTexture::setLoadedCallback( loaded_callback_func loaded_call { // Put in list to call this->doLoadedCallbacks() periodically gTextureList.mCallbackList.insert(this); + mLoadedCallbackDesiredDiscardLevel = (S8)discard_level; + } + else + { + mLoadedCallbackDesiredDiscardLevel = llmin(mLoadedCallbackDesiredDiscardLevel, (S8)discard_level) ; } + LLLoadedCallbackEntry* entryp = new LLLoadedCallbackEntry(loaded_callback, discard_level, keep_imageraw, userdata); mLoadedCallbackList.push_back(entryp); mNeedsAux |= needs_aux; @@ -1645,7 +2010,7 @@ bool LLViewerFetchedTexture::doLoadedCallbacks() // We have GL data. destroyRawImage(); - readBackRawImage(gl_discard); + reloadRawImage(mLoadedCallbackDesiredDiscardLevel); llassert_always(mRawImage.notNull()); llassert_always(!mNeedsAux || mAuxRawImage.notNull()); } @@ -1754,8 +2119,7 @@ void LLViewerFetchedTexture::forceImmediateUpdate() return ; } -// Was in LLImageGL -LLImageRaw* LLViewerFetchedTexture::readBackRawImage(S8 discard_level) +LLImageRaw* LLViewerFetchedTexture::reloadRawImage(S8 discard_level) { llassert_always(mGLTexturep.notNull()) ; llassert_always(discard_level >= 0); @@ -1765,25 +2129,216 @@ LLImageRaw* LLViewerFetchedTexture::readBackRawImage(S8 discard_level) llerrs << "called with existing mRawImage" << llendl; mRawImage = NULL; } - mRawImage = new LLImageRaw(mGLTexturep->getWidth(discard_level), mGLTexturep->getHeight(discard_level), mComponents); - sRawCount++; - mRawDiscardLevel = discard_level; - mGLTexturep->readBackRaw(mRawDiscardLevel, mRawImage, false); - mIsRawImageValid = TRUE; + + if(mSavedRawDiscardLevel >= 0 && mSavedRawDiscardLevel <= discard_level) + { + mRawImage = new LLImageRaw(getWidth(discard_level), getHeight(discard_level), getComponents()) ; + mRawImage->copy(getSavedRawImage()) ; + mRawDiscardLevel = discard_level ; + } + else + { + //force to fetch raw image again if cached raw image is not good enough. + if(mCachedRawDiscardLevel > discard_level) + { + mRawImage = mCachedRawImage ; + mRawDiscardLevel = mCachedRawDiscardLevel; + + forceToSaveRawImage(discard_level) ; + } + else //cached raw image is good enough, copy it. + { + mRawImage = new LLImageRaw(getWidth(discard_level), getHeight(discard_level), getComponents()) ; + mRawImage->copy(mCachedRawImage) ; + mRawDiscardLevel = discard_level ; + } + } + mIsRawImageValid = TRUE ; + sRawCount++; return mRawImage; } void LLViewerFetchedTexture::destroyRawImage() -{ - if (mRawImage.notNull()) sRawCount--; +{ if (mAuxRawImage.notNull()) sAuxCount--; + + if (mRawImage.notNull()) + { + sRawCount--; + setCachedRawImage() ; + + if(mForceToSaveRawImage) + { + saveRawImage() ; + } + } + mRawImage = NULL; mAuxRawImage = NULL; mIsRawImageValid = FALSE; mRawDiscardLevel = INVALID_DISCARD_LEVEL; + + if(mForceToSaveRawImage) + { + forceFetch() ; + } +} + +//use the mCachedRawImage to (re)generate the gl texture. +//virtual +void LLViewerFetchedTexture::switchToCachedImage() +{ + if(mCachedRawImage.notNull()) + { + mRawImage = mCachedRawImage ; + + if (getComponents() != mRawImage->getComponents()) + { + // We've changed the number of components, so we need to move any + // objects using this pool to a different pool. + mComponents = mRawImage->getComponents(); + mGLTexturep->setComponents(mComponents) ; + gTextureList.dirtyImage(this); + } + + mIsRawImageValid = TRUE; + mRawDiscardLevel = mCachedRawDiscardLevel ; + gTextureList.mCreateTextureList.insert(this); + mNeedsCreateTexture = TRUE; + } +} + +void LLViewerFetchedTexture::setCachedRawImage() +{ + if(mRawImage == mCachedRawImage) + { + return ; + } + if(!mIsRawImageValid) + { + return ; + } + + if(mCachedRawImageReady) + { + return ; + } + + if(mCachedRawDiscardLevel < 0 || mCachedRawDiscardLevel > mRawDiscardLevel) + { + S32 i = 0 ; + S32 w = mRawImage->getWidth() ; + S32 h = mRawImage->getHeight() ; + + S32 max_size = MAX_CACHED_RAW_IMAGE_AREA ; + if(LLViewerTexture::BOOST_TERRAIN == mBoostLevel) + { + max_size = MAX_CACHED_RAW_TERRAIN_IMAGE_AREA ; + } + if(mForSculpt) + { + max_size = MAX_CACHED_RAW_SCULPT_IMAGE_AREA ; + mCachedRawImageReady = !mRawDiscardLevel ; + } + else + { + mCachedRawImageReady = (!mRawDiscardLevel || ((w * h) >= max_size)) ; + } + + while(((w >> i) * (h >> i)) > max_size) + { + ++i ; + } + + if(i) + { + if(!(w >> i) || !(h >> i)) + { + --i ; + } + //if(mForSculpt) + //{ + // mRawImage->scaleDownWithoutBlending(w >> i, h >> i) ; + //} + //else + { + mRawImage->scale(w >> i, h >> i) ; + } + } + mCachedRawImage = mRawImage ; + mCachedRawDiscardLevel = mRawDiscardLevel + i ; + } +} + +void LLViewerFetchedTexture::checkCachedRawSculptImage() +{ + if(mCachedRawImageReady && mCachedRawDiscardLevel > 0) + { + if(getDiscardLevel() != 0) + { + mCachedRawImageReady = FALSE ; + } + else if(isForSculptOnly()) + { + resetTextureStats() ; //do not update this image any more. + } + } +} + +void LLViewerFetchedTexture::saveRawImage() +{ + if(mRawImage.isNull() || mSavedRawDiscardLevel == mRawDiscardLevel) + { + return ; + } + + mSavedRawDiscardLevel = mRawDiscardLevel ; + mSavedRawImage = new LLImageRaw(mRawImage->getData(), mRawImage->getWidth(), mRawImage->getHeight(), mRawImage->getComponents()) ; + + if(mSavedRawDiscardLevel <= mDesiredSavedRawDiscardLevel) + { + mForceToSaveRawImage = FALSE ; + } + + mLastReferencedSavedRawImageTime = sCurrentTime ; } +void LLViewerFetchedTexture::forceToSaveRawImage(S32 desired_discard) +{ + if(!mForceToSaveRawImage && (mDesiredSavedRawDiscardLevel < 0 || mDesiredSavedRawDiscardLevel > desired_discard)) + { + mForceToSaveRawImage = TRUE ; + mDesiredSavedRawDiscardLevel = desired_discard ; + + forceFetch() ; + } +} +void LLViewerFetchedTexture::destroySavedRawImage() +{ + mSavedRawImage = NULL ; + mForceToSaveRawImage = FALSE ; + mSavedRawDiscardLevel = -1 ; + mDesiredSavedRawDiscardLevel = -1 ; + mLastReferencedSavedRawImageTime = 0.0f ; +} + +LLImageRaw* LLViewerFetchedTexture::getSavedRawImage() +{ + mLastReferencedSavedRawImageTime = sCurrentTime ; + + return mSavedRawImage ; +} + +BOOL LLViewerFetchedTexture::hasSavedRawImage() const +{ + return mSavedRawImage.notNull() ; +} + +F32 LLViewerFetchedTexture::getElapsedLastReferencedSavedRawImageTime() const +{ + return mLastReferencedSavedRawImageTime - sCurrentTime ; +} //---------------------------------------------------------------------------------------------- //atlasing //---------------------------------------------------------------------------------------------- @@ -1981,14 +2536,14 @@ BOOL LLViewerFetchedTexture::insertToAtlas() //---------------------------------------------------------------------------------------------- //start of LLViewerLODTexture //---------------------------------------------------------------------------------------------- -LLViewerLODTexture::LLViewerLODTexture(const LLUUID& id, BOOL usemipmaps) - : LLViewerFetchedTexture(id, usemipmaps) +LLViewerLODTexture::LLViewerLODTexture(const LLUUID& id, const LLHost& host, BOOL usemipmaps) + : LLViewerFetchedTexture(id, host, usemipmaps) { init(TRUE) ; } -LLViewerLODTexture::LLViewerLODTexture(const std::string& full_path, const LLUUID& id, BOOL usemipmaps) - : LLViewerFetchedTexture(full_path, id, usemipmaps) +LLViewerLODTexture::LLViewerLODTexture(const std::string& url, const LLUUID& id, BOOL usemipmaps) + : LLViewerFetchedTexture(url, id, usemipmaps) { init(TRUE) ; } @@ -2006,12 +2561,25 @@ S8 LLViewerLODTexture::getType() const return LLViewerTexture::LOD_TEXTURE ; } +BOOL LLViewerLODTexture::isUpdateFrozen() +{ + return LLViewerTexture::sFreezeImageScalingDown && !getDiscardLevel() ; +} + // This is gauranteed to get called periodically for every texture //virtual void LLViewerLODTexture::processTextureStats() { + updateVirtualSize() ; + + static LLCachedControl<bool> textures_fullres(gSavedSettings,"TextureLoadFullRes"); + + if (textures_fullres) + { + mDesiredDiscardLevel = 0; + } // Generate the request priority and render priority - if (mDontDiscard || !mUseMipMaps) + else if (mDontDiscard || !mUseMipMaps) { mDesiredDiscardLevel = 0; if (mFullWidth > MAX_IMAGE_SIZE_DEFAULT || mFullHeight > MAX_IMAGE_SIZE_DEFAULT) @@ -2040,13 +2608,7 @@ void LLViewerLODTexture::processTextureStats() // If we know the output width and height, we can force the discard // level to the correct value, and thus not decode more texture // data than we need to. - /*if (mBoostLevel == LLViewerTexture::BOOST_UI || - mBoostLevel == LLViewerTexture::BOOST_PREVIEW || - mBoostLevel == LLViewerTexture::BOOST_AVATAR_SELF) // what about AVATAR_BAKED_SELF? - { - discard_level = 0; // full res - } - else*/ if (mKnownDrawWidth && mKnownDrawHeight) + if (mKnownDrawWidth && mKnownDrawHeight) { S32 draw_texels = mKnownDrawWidth * mKnownDrawHeight; @@ -2057,6 +2619,12 @@ void LLViewerLODTexture::processTextureStats() } else { + if(isLargeImage() && !isJustBound() && mAdditionalDecodePriority < 1.0f) + { + //if is a big image and not being used recently, nor close to the view point, do not load hi-res data. + mMaxVirtualSize = llmin(mMaxVirtualSize, (F32)LLViewerTexture::sMinLargeImageSize) ; + } + if ((mCalculatedDiscardLevel >= 0.f) && (llabs(mMaxVirtualSize - mDiscardVirtualSize) < mMaxVirtualSize*.20f)) { @@ -2073,13 +2641,11 @@ void LLViewerLODTexture::processTextureStats() } if (mBoostLevel < LLViewerTexture::BOOST_HIGH) { - static const F32 discard_bias = -.5f; // Must be < 1 or highest discard will never load! - discard_level += discard_bias; discard_level += sDesiredDiscardBias; discard_level *= sDesiredDiscardScale; // scale + discard_level += sCameraMovingDiscardBias ; } discard_level = floorf(discard_level); -// discard_level -= (gTextureList.mVideoMemorySetting>>1); // more video ram = higher detail F32 min_discard = 0.f; if (mFullWidth > MAX_IMAGE_SIZE_DEFAULT || mFullHeight > MAX_IMAGE_SIZE_DEFAULT) @@ -2098,46 +2664,39 @@ void LLViewerLODTexture::processTextureStats() // proper action if we don't. // - BOOL increase_discard = FALSE; S32 current_discard = getDiscardLevel(); if ((sDesiredDiscardBias > 0.0f) && (current_discard >= 0 && mDesiredDiscardLevel >= current_discard)) { - if ( BYTES_TO_MEGA_BYTES(sBoundTextureMemoryInBytes) > sMaxBoundTextureMemInMegaBytes * texmem_middle_bound_scale) + // Limit the amount of GL memory bound each frame + if ( BYTES_TO_MEGA_BYTES(sBoundTextureMemoryInBytes) > sMaxBoundTextureMemInMegaBytes * texmem_middle_bound_scale && + (!getBoundRecently() || mDesiredDiscardLevel >= mCachedRawDiscardLevel)) { - // Limit the amount of GL memory bound each frame - if (mDesiredDiscardLevel > current_discard) - { - increase_discard = TRUE; - } + scaleDown() ; } - if ( BYTES_TO_MEGA_BYTES(sTotalTextureMemoryInBytes) > sMaxTotalTextureMemInMegaBytes*texmem_middle_bound_scale) + // Only allow GL to have 2x the video card memory + else if ( BYTES_TO_MEGA_BYTES(sTotalTextureMemoryInBytes) > sMaxTotalTextureMemInMegaBytes*texmem_middle_bound_scale && + (!getBoundRecently() || mDesiredDiscardLevel >= mCachedRawDiscardLevel)) { - // Only allow GL to have 2x the video card memory - if (!mGLTexturep->getBoundRecently()) - { - increase_discard = TRUE; - } - } - if (increase_discard) - { - // llinfos << "DISCARDED: " << mID << " Discard: " << current_discard << llendl; - sBoundTextureMemoryInBytes -= mGLTexturep->mTextureMemory; - sTotalTextureMemoryInBytes -= mGLTexturep->mTextureMemory; - // Increase the discard level (reduce the texture res) - S32 new_discard = current_discard+1; - mGLTexturep->setDiscardLevel(new_discard); - sBoundTextureMemoryInBytes += mGLTexturep->mTextureMemory; - sTotalTextureMemoryInBytes += mGLTexturep->mTextureMemory; - if(LLViewerTextureManager::sTesterp) - { - LLViewerTextureManager::sTesterp->setStablizingTime() ; - } + scaleDown() ; + } } } } +void LLViewerLODTexture::scaleDown() +{ + if(hasGLTexture() && mCachedRawDiscardLevel > getDiscardLevel()) + { + switchToCachedImage() ; + + if(LLViewerTextureManager::sTesterp) + { + LLViewerTextureManager::sTesterp->setStablizingTime() ; + } + } +} //---------------------------------------------------------------------------------------------- //end of LLViewerLODTexture //---------------------------------------------------------------------------------------------- @@ -2226,6 +2785,8 @@ LLViewerMediaTexture::LLViewerMediaTexture(const LLUUID& id, BOOL usemipmaps, LL setMediaImpl() ; + setCategory(LLViewerTexture::MEDIA) ; + LLViewerTexture* tex = gTextureList.findImage(mID) ; if(tex) //this media is a parcel media for tex. { @@ -2770,7 +3331,7 @@ void LLTexturePipelineTester::updateTextureLoadingStats(const LLViewerFetchedTex mTotalBytesLoadedForLargeImage += data_size ; } - if(imagep->isForSculpt()) + if(imagep->forSculpt()) { mTotalBytesLoadedForSculpties += data_size ; diff --git a/indra/newview/llviewertexture.h b/indra/newview/llviewertexture.h index ff8f14e879..bde87d1dd5 100644 --- a/indra/newview/llviewertexture.h +++ b/indra/newview/llviewertexture.h @@ -123,10 +123,23 @@ public: BOOST_UI = 16, BOOST_PREVIEW = 17, BOOST_MAP = 18, - BOOST_AVATAR_SELF = 19, // needed for baking avatar - BOOST_MAX_LEVEL + BOOST_MAP_VISIBLE = 19, + BOOST_AVATAR_SELF = 20, // needed for baking avatar + BOOST_MAX_LEVEL, + + //other texture Categories + LOCAL = BOOST_MAX_LEVEL, + AVATAR_SCRATCH_TEX, + DYNAMIC_TEX, + MEDIA, + ATLAS, + OTHER, + MAX_GL_IMAGE_CATEGORY }; - + static S32 getTotalNumOfCategories() ; + static S32 getIndexFromCategory(S32 category) ; + static S32 getCategoryFromIndex(S32 index) ; + typedef std::list<LLFace*> ll_face_list_t ; protected: @@ -147,7 +160,7 @@ public: virtual BOOL isMissingAsset()const ; virtual void dump(); // debug info to llinfos - /*virtual*/ bool bindDefaultImage(const S32 stage = 0) const ; + /*virtual*/ bool bindDefaultImage(const S32 stage = 0) ; /*virtual*/ void forceImmediateUpdate() ; const LLUUID& getID() const { return mID; } @@ -155,9 +168,9 @@ public: void setBoostLevel(S32 level); S32 getBoostLevel() { return mBoostLevel; } - //maxVirtualSize of the texture - void addTextureStats(F32 virtual_size) const ; - void resetTextureStats(BOOL zero = FALSE); + void addTextureStats(F32 virtual_size, BOOL needs_gltexture = TRUE) const; + void resetTextureStats(); + virtual F32 getMaxVirtualSize() ; LLFrameTimer* getLastReferencedTimer() {return &mLastReferencedTimer ;} @@ -182,7 +195,7 @@ public: BOOL hasGLTexture() const ; LLGLuint getTexName() const ; BOOL createGLTexture() ; - BOOL createGLTexture(S32 discard_level, const LLImageRaw* imageraw, S32 usename = 0); + BOOL createGLTexture(S32 discard_level, const LLImageRaw* imageraw, S32 usename = 0, BOOL to_create = TRUE, S32 category = LLViewerTexture::OTHER); void setFilteringOption(LLTexUnit::eTextureFilterOptions option); void setExplicitFormat(LLGLint internal_format, LLGLenum primary_format, LLGLenum type_format = 0, BOOL swap_bytes = FALSE); @@ -190,7 +203,8 @@ public: BOOL setSubImage(const LLImageRaw* imageraw, S32 x_pos, S32 y_pos, S32 width, S32 height); BOOL setSubImage(const U8* datap, S32 data_width, S32 data_height, S32 x_pos, S32 y_pos, S32 width, S32 height); void setGLTextureCreated (bool initialized); - + void setCategory(S32 category) ; + LLTexUnit::eTextureAddressMode getAddressMode(void) const ; S32 getMaxDiscardLevel() const; S32 getDiscardLevel() const; @@ -203,8 +217,8 @@ public: BOOL getMask(const LLVector2 &tc); F32 getTimePassedSinceLastBound(); BOOL getMissed() const ; - BOOL isValidForSculpt(S32 discard_level, S32 image_width, S32 image_height, S32 ncomponents) ; - BOOL readBackRaw(S32 discard_level, LLImageRaw* imageraw, bool compressed_ok) const; + BOOL isJustBound()const ; + void forceUpdateBindStats(void) const; U32 getTexelsInAtlas() const ; U32 getTexelsInGLTexture() const ; @@ -222,6 +236,8 @@ public: BOOL getDontDiscard() const { return mDontDiscard; } //----------------- + BOOL isLargeImage() ; + void setParcelMedia(LLViewerMediaTexture* media) {mParcelMedia = media;} BOOL hasParcelMedia() const { return mParcelMedia != NULL;} LLViewerMediaTexture* getParcelMedia() const { return mParcelMedia;} @@ -234,6 +250,7 @@ protected: private: //note: do not make this function public. /*virtual*/ LLImageGL* getGLTexture() const ; + virtual void switchToCachedImage(); protected: LLUUID mID; @@ -243,7 +260,9 @@ protected: BOOL mUseMipMaps ; S8 mComponents; mutable F32 mMaxVirtualSize; // The largest virtual size of the image, in pixels - how much data to we need? + mutable S8 mNeedsGLTexture; mutable BOOL mNeedsResetMaxVirtualSize ; + mutable F32 mAdditionalDecodePriority; // priority add to mDecodePriority. LLFrameTimer mLastReferencedTimer; ll_face_list_t mFaceList ; //reverse pointer pointing to the faces using this image as texture @@ -279,7 +298,12 @@ public: static S32 sMaxBoundTextureMemInMegaBytes; static S32 sMaxTotalTextureMemInMegaBytes; static S32 sMaxDesiredTextureMemInBytes ; - static BOOL sDontLoadVolumeTextures; + static S8 sCameraMovingDiscardBias; + static S32 sMaxSculptRez ; + static S32 sMinLargeImageSize ; + static S32 sMaxSmallImageSize ; + static BOOL sFreezeImageScalingDown ;//do not scale down image res if set. + static F32 sCurrentTime ; static BOOL sUseTextureAtlas ; static LLPointer<LLViewerTexture> sNullImagep; // Null texture for non-textured objects. @@ -299,9 +323,9 @@ class LLViewerFetchedTexture : public LLViewerTexture protected: /*virtual*/ ~LLViewerFetchedTexture(); public: - LLViewerFetchedTexture(const LLUUID& id, BOOL usemipmaps = TRUE); + LLViewerFetchedTexture(const LLUUID& id, const LLHost& host = LLHost::invalid, BOOL usemipmaps = TRUE); LLViewerFetchedTexture(const LLImageRaw* raw, BOOL usemipmaps); - LLViewerFetchedTexture(const std::string& full_path, const LLUUID& id, BOOL usemipmaps = TRUE); + LLViewerFetchedTexture(const std::string& url, const LLUUID& id, BOOL usemipmaps = TRUE); public: static F32 maxDecodePriority(); @@ -337,6 +361,8 @@ public: bool hasCallbacks() { return mLoadedCallbackList.empty() ? false : true; } bool doLoadedCallbacks(); + void addToCreateTexture(); + // ONLY call from LLViewerTextureList BOOL createTexture(S32 usename = 0); void destroyTexture() ; @@ -355,7 +381,11 @@ public: // the priority list, and cause horrible things to happen. void setDecodePriority(F32 priority = -1.0f); F32 getDecodePriority() const { return mDecodePriority; }; + + void setAdditionalDecodePriority(F32 priority) ; + void updateVirtualSize() ; + // setDesiredDiscardLevel is only used by LLViewerTextureList void setDesiredDiscardLevel(S32 discard) { mDesiredDiscardLevel = discard; } S32 getDesiredDiscardLevel() { return mDesiredDiscardLevel; } @@ -379,15 +409,15 @@ public: BOOL isInImageList() const {return mInImageList ;} void setInImageList(BOOL flag) {mInImageList = flag ;} - const std::string& getLocalFileName() const {return mLocalFileName ;} LLFrameTimer* getLastPacketTimer() {return &mLastPacketTimer;} U32 getFetchPriority() const { return mFetchPriority ;} F32 getDownloadProgress() const {return mDownloadProgress ;} - LLImageRaw* readBackRawImage(S8 discard_level) ; + LLImageRaw* reloadRawImage(S8 discard_level) ; void destroyRawImage(); + const std::string& getUrl() const {return mUrl;} //--------------- BOOL isDeleted() ; BOOL isInactive() ; @@ -398,13 +428,35 @@ public: //--------------- void setForSculpt(); - BOOL isForSculpt() const {return mForSculpt;} + BOOL forSculpt() const {return mForSculpt;} + BOOL isForSculptOnly() const; + + //raw image management + void checkCachedRawSculptImage() ; + LLImageRaw* getRawImage()const { return mRawImage ;} + S32 getRawImageLevel() const {return mRawDiscardLevel;} + LLImageRaw* getCachedRawImage() const { return mCachedRawImage ;} + S32 getCachedRawImageLevel() const {return mCachedRawDiscardLevel;} + BOOL isCachedRawImageReady() const {return mCachedRawImageReady ;} + BOOL isRawImageValid()const { return mIsRawImageValid ; } + void forceToSaveRawImage(S32 desired_discard = 0) ; + void destroySavedRawImage() ; + LLImageRaw* getSavedRawImage() ; + BOOL hasSavedRawImage() const ; + F32 getElapsedLastReferencedSavedRawImageTime() const ; + BOOL isFullyLoaded() const; + +protected: + /*virtual*/ void switchToCachedImage(); private: void init(bool firstinit) ; void cleanup() ; - F32 calcDecodePriorityForUnknownTexture(F32 pixel_priority) ; + void saveRawImage() ; + BOOL forceFetch() ; + void setCachedRawImage() ; + BOOL keepReuestedDiscardLevel(); //for atlas void resetFaceAtlas() ; @@ -425,11 +477,8 @@ protected: S32 mKnownDrawWidth; S32 mKnownDrawHeight; BOOL mKnownDrawSizeChanged ; - - S8 mDesiredDiscardLevel; // The discard level we'd LIKE to have - if we have it and there's space - S8 mMinDesiredDiscardLevel; // The minimum discard level we'd like to have - S32 mMinDiscardLevel; - + std::string mUrl; + S32 mRequestedDiscardLevel; F32 mRequestedDownloadPriority; S32 mFetchState; @@ -437,8 +486,10 @@ protected: F32 mDownloadProgress; F32 mFetchDeltaTime; F32 mRequestDeltaTime; - S32 mDecodeFrame; - S32 mVisibleFrame; // decode frame where image was last visible + F32 mDecodePriority; // The priority for decoding this image. + S32 mMinDiscardLevel; + S8 mDesiredDiscardLevel; // The discard level we'd LIKE to have - if we have it and there's space + S8 mMinDesiredDiscardLevel; // The minimum discard level we'd like to have S8 mNeedsAux; // We need to decode the auxiliary channels S8 mDecodingAux; // Are we decoding high components @@ -446,10 +497,10 @@ protected: S8 mHasFetcher; // We've made a fecth request S8 mIsFetching; // Fetch request is active - mutable S8 mIsMissingAsset; // True if we know that there is no image asset with this image id in the database. + mutable S8 mIsMissingAsset; // True if we know that there is no image asset with this image id in the database. - F32 mDecodePriority; // The priority for decoding this image. typedef std::list<LLLoadedCallbackEntry*> callback_list_t; + S8 mLoadedCallbackDesiredDiscardLevel; callback_list_t mLoadedCallbackList; LLPointer<LLImageRaw> mRawImage; @@ -459,6 +510,19 @@ protected: // doing if you use it for anything else! - djs LLPointer<LLImageRaw> mAuxRawImage; + //keep a copy of mRawImage for some special purposes + //when mForceToSaveRawImage is set. + BOOL mForceToSaveRawImage ; + LLPointer<LLImageRaw> mSavedRawImage; + S32 mSavedRawDiscardLevel; + S32 mDesiredSavedRawDiscardLevel; + F32 mLastReferencedSavedRawImageTime ; + + //a small version of the copy of the raw image (<= 64 * 64) + LLPointer<LLImageRaw> mCachedRawImage; + S32 mCachedRawDiscardLevel; + BOOL mCachedRawImageReady; //the rez of the mCachedRawImage reaches the upper limit. + LLHost mTargetHost; // if LLHost::invalid, just request from agent's simulator // Timers @@ -487,15 +551,17 @@ protected: /*virtual*/ ~LLViewerLODTexture(){} public: - LLViewerLODTexture(const LLUUID& id, BOOL usemipmaps = TRUE); - LLViewerLODTexture(const std::string& full_path, const LLUUID& id, BOOL usemipmaps = TRUE); + LLViewerLODTexture(const LLUUID& id, const LLHost& host = LLHost::invalid, BOOL usemipmaps = TRUE); + LLViewerLODTexture(const std::string& url, const LLUUID& id, BOOL usemipmaps = TRUE); /*virtual*/ S8 getType() const; // Process image stats to determine priority/quality requirements. /*virtual*/ void processTextureStats(); - + BOOL isUpdateFrozen() ; + private: void init(bool firstinit) ; + void scaleDown() ; private: @@ -611,7 +677,16 @@ public: static LLViewerFetchedTexture* getFetchedTextureFromFile(const std::string& filename, BOOL usemipmap = TRUE, - S32 boost_priority = LLViewerTexture::BOOST_NONE, // Get the requested level immediately upon creation. + S32 boost_priority = LLViewerTexture::BOOST_NONE, + S8 texture_type = LLViewerTexture::FETCHED_TEXTURE, + LLGLint internal_format = 0, + LLGLenum primary_format = 0, + const LLUUID& force_id = LLUUID::null + ); + + static LLViewerFetchedTexture* getFetchedTextureFromUrl(const std::string& url, + BOOL usemipmap = TRUE, + S32 boost_priority = LLViewerTexture::BOOST_NONE, S8 texture_type = LLViewerTexture::FETCHED_TEXTURE, LLGLint internal_format = 0, LLGLenum primary_format = 0, diff --git a/indra/newview/llviewertexturelist.cpp b/indra/newview/llviewertexturelist.cpp index d2be1ac9b5..081b7cc483 100644 --- a/indra/newview/llviewertexturelist.cpp +++ b/indra/newview/llviewertexturelist.cpp @@ -44,6 +44,7 @@ #include "llimagetga.h" #include "llimagejpeg.h" #include "llimagepng.h" +#include "llimageworker.h" #include "llsdserialize.h" #include "llsys.h" @@ -68,10 +69,15 @@ void (*LLViewerTextureList::sUUIDCallback)(void **, const LLUUID&) = NULL; -const S32 IMAGES_PER_REQUEST = 42; -const S32 IMAGES_MIN_UPDATES = 4; // Always update the highest N images each frame -const S32 IMAGES_MAX_PACKET_UPDATES = 1; // Only send N packets of IMAGES_PER_REQUEST in a frame -const F32 RESEND_IMAGE_REQUEST_TIME = 15.f; // seconds +U32 LLViewerTextureList::sTextureBits = 0; +U32 LLViewerTextureList::sTexturePackets = 0; +S32 LLViewerTextureList::sNumImages = 0; +LLStat LLViewerTextureList::sNumImagesStat(32, TRUE); +LLStat LLViewerTextureList::sNumRawImagesStat(32, TRUE); +LLStat LLViewerTextureList::sGLTexMemStat(32, TRUE); +LLStat LLViewerTextureList::sGLBoundMemStat(32, TRUE); +LLStat LLViewerTextureList::sRawMemStat(32, TRUE); +LLStat LLViewerTextureList::sFormattedMemStat(32, TRUE); LLViewerTextureList gTextureList; static LLFastTimer::DeclareTimer FTM_PROCESS_IMAGES("Process Images"); @@ -88,7 +94,7 @@ LLViewerTextureList::LLViewerTextureList() void LLViewerTextureList::init() { - mNumImages = 0; + sNumImages = 0; mMaxResidentTexMemInMegaBytes = 0; mMaxTotalTextureMemInMegaBytes = 0 ; if (gNoRender) @@ -231,6 +237,10 @@ void LLViewerTextureList::shutdown() { continue; // avoid UI, baked, and other special images } + if(!image->getBoundRecently()) + { + continue ; + } S32 desired = image->getDesiredDiscardLevel(); if (desired >= 0 && desired < MAX_DISCARD_LEVEL) { @@ -321,17 +331,30 @@ LLViewerFetchedTexture* LLViewerTextureList::getImageFromFile(const std::string& LLGLenum primary_format, const LLUUID& force_id) { - if (gNoRender) + std::string full_path = gDirUtilp->findSkinnedFilename("textures", filename); + if (full_path.empty()) { - // Never mind that this ignores image_set_id; - // getImage() will handle that later. + llwarns << "Failed to find local image file: " << filename << llendl; return LLViewerTextureManager::getFetchedTexture(IMG_DEFAULT, TRUE, LLViewerTexture::BOOST_UI); } - std::string full_path = gDirUtilp->findSkinnedFilename("textures", filename); - if (full_path.empty()) + std::string url = "file://" + full_path; + + return getImageFromUrl(url, usemipmaps, boost_priority, texture_type, internal_format, primary_format, force_id); +} + +LLViewerFetchedTexture* LLViewerTextureList::getImageFromUrl(const std::string& url, + BOOL usemipmaps, + S32 boost_priority, + S8 texture_type, + LLGLint internal_format, + LLGLenum primary_format, + const LLUUID& force_id) +{ + if (gNoRender) { - llwarns << "Failed to find local image file: " << filename << llendl; + // Never mind that this ignores image_set_id; + // getImage() will handle that later. return LLViewerTextureManager::getFetchedTexture(IMG_DEFAULT, TRUE, LLViewerTexture::BOOST_UI); } @@ -343,7 +366,7 @@ LLViewerFetchedTexture* LLViewerTextureList::getImageFromFile(const std::string& } else { - new_id.generate(full_path); + new_id.generate(url); } LLPointer<LLViewerFetchedTexture> imagep = findImage(new_id); @@ -353,10 +376,10 @@ LLViewerFetchedTexture* LLViewerTextureList::getImageFromFile(const std::string& switch(texture_type) { case LLViewerTexture::FETCHED_TEXTURE: - imagep = new LLViewerFetchedTexture(full_path, new_id, usemipmaps); + imagep = new LLViewerFetchedTexture(url, new_id, usemipmaps); break ; case LLViewerTexture::LOD_TEXTURE: - imagep = new LLViewerLODTexture(full_path, new_id, usemipmaps); + imagep = new LLViewerLODTexture(url, new_id, usemipmaps); break ; default: llerrs << "Invalid texture type " << texture_type << llendl ; @@ -428,18 +451,15 @@ LLViewerFetchedTexture* LLViewerTextureList::createImage(const LLUUID &image_id, switch(texture_type) { case LLViewerTexture::FETCHED_TEXTURE: - imagep = new LLViewerFetchedTexture(image_id, usemipmaps); + imagep = new LLViewerFetchedTexture(image_id, request_from_host, usemipmaps); break ; case LLViewerTexture::LOD_TEXTURE: - imagep = new LLViewerLODTexture(image_id, usemipmaps); + imagep = new LLViewerLODTexture(image_id, request_from_host, usemipmaps); break ; default: llerrs << "Invalid texture type " << texture_type << llendl ; } - // Might want to request from host other than where the agent is. JC - imagep->setTargetHost(request_from_host); - if (internal_format && primary_format) { imagep->setExplicitFormat(internal_format, primary_format); @@ -517,7 +537,7 @@ void LLViewerTextureList::addImage(LLViewerFetchedTexture *new_image) { llwarns << "Image with ID " << image_id << " already in list" << llendl; } - mNumImages++; + sNumImages++; addImageToList(new_image); mUUIDMap[image_id] = new_image; @@ -534,7 +554,7 @@ void LLViewerTextureList::deleteImage(LLViewerFetchedTexture *image) } llverify(mUUIDMap.erase(image->getID()) == 1); - mNumImages--; + sNumImages--; removeImageFromList(image); } } @@ -554,7 +574,9 @@ static LLFastTimer::DeclareTimer FTM_IMAGE_MARK_DIRTY("Dirty Images"); void LLViewerTextureList::updateImages(F32 max_time) { - LLViewerStats::getInstance()->mNumImagesStat.addValue(mNumImages); + LLAppViewer::getTextureFetch()->setTextureBandwidth(LLViewerStats::getInstance()->mTextureKBitStat.getMeanPerSec()); + + LLViewerStats::getInstance()->mNumImagesStat.addValue(sNumImages); LLViewerStats::getInstance()->mNumRawImagesStat.addValue(LLImageRaw::sRawImageCount); LLViewerStats::getInstance()->mGLTexMemStat.addValue((F32)BYTES_TO_MEGA_BYTES(LLImageGL::sGlobalTextureMemoryInBytes)); LLViewerStats::getInstance()->mGLBoundMemStat.addValue((F32)BYTES_TO_MEGA_BYTES(LLImageGL::sBoundTextureMemoryInBytes)); @@ -562,10 +584,13 @@ void LLViewerTextureList::updateImages(F32 max_time) LLViewerStats::getInstance()->mFormattedMemStat.addValue((F32)BYTES_TO_MEGA_BYTES(LLImageFormatted::sGlobalFormattedMemory)); updateImagesDecodePriorities(); + + F32 total_max_time = max_time; max_time -= updateImagesFetchTextures(max_time); - max_time = llmin(llmax(max_time, 0.001f*10.f*gFrameIntervalSeconds), 0.001f); + + max_time = llmax(max_time, total_max_time*.25f); // at least 25% of max_time max_time -= updateImagesCreateTextures(max_time); - max_time = llmin(llmax(max_time, 0.001f*10.f*gFrameIntervalSeconds), 0.001f); + if (!mDirtyTextureList.empty()) { LLFastTimer t(FTM_IMAGE_MARK_DIRTY); @@ -578,7 +603,7 @@ void LLViewerTextureList::updateImages(F32 max_time) { //trigger loaded callbacks on local textures immediately LLViewerFetchedTexture* image = *iter++; - if (!image->getLocalFileName().empty()) + if (!image->getUrl().empty()) { // Do stuff to handle callbacks, update priorities, etc. didone = image->doLoadedCallbacks(); @@ -636,6 +661,14 @@ void LLViewerTextureList::updateImagesDecodePriorities() } else { + if(imagep->hasSavedRawImage()) + { + if(imagep->getElapsedLastReferencedSavedRawImageTime() > MAX_INACTIVE_TIME) + { + imagep->destroySavedRawImage() ; + } + } + if(imagep->isDeleted()) { continue ; @@ -766,74 +799,76 @@ F32 LLViewerTextureList::updateImagesFetchTextures(F32 max_time) const size_t max_update_count = llmin((S32) (1024*10.f*gFrameIntervalSeconds)+1, 256); // 32 high priority entries - std::set<LLViewerFetchedTexture*> entries; + typedef std::vector<LLViewerFetchedTexture*> entries_list_t; + entries_list_t entries; size_t update_counter = llmin(max_priority_count, mImageList.size()); image_priority_list_t::iterator iter1 = mImageList.begin(); while(update_counter > 0) { - // added extra granularity and verbosity for crash logging during 1.19.1 RC. -Brad - if(iter1 == mImageList.end()) - { - llerrs << "DEV-12002: update_counter not calculated correctly!" << llendl; - return 0.f; - } - - LLPointer<LLViewerFetchedTexture> const & ptr = *iter1; - - LLViewerFetchedTexture * img = ptr.get(); - - // added extra granularity and verbosity for crash logging during 1.19.1 RC. -Brad - if(img == NULL) - { - llwarns << "DEV-12002: image is NULL!" << llendl; - } - - entries.insert(img); - + entries.push_back(*iter1); + ++iter1; update_counter--; } // 256 cycled entries - update_counter = llmin(max_update_count, mUUIDMap.size()); - uuid_map_t::iterator iter2 = mUUIDMap.upper_bound(mLastFetchUUID); - while(update_counter > 0) + update_counter = llmin(max_update_count, mUUIDMap.size()); + if(update_counter > 0) { - if (iter2 == mUUIDMap.end()) + uuid_map_t::iterator iter2 = mUUIDMap.upper_bound(mLastFetchUUID); + uuid_map_t::iterator iter2p = iter2; + while(update_counter > 0) { - iter2 = mUUIDMap.begin(); + if (iter2 == mUUIDMap.end()) + { + iter2 = mUUIDMap.begin(); + } + entries.push_back(iter2->second); + iter2p = iter2++; + update_counter--; } - mLastFetchUUID = iter2->first; - entries.insert(iter2->second); - ++iter2; - update_counter--; + + mLastFetchUUID = iter2p->first; } + S32 fetch_count = 0; S32 min_count = max_priority_count + max_update_count/4; - for (std::set<LLViewerFetchedTexture*>::iterator iter3 = entries.begin(); + for (entries_list_t::iterator iter3 = entries.begin(); iter3 != entries.end(); ) { LLPointer<LLViewerFetchedTexture> imagep = *iter3++; - imagep->updateFetch(); + bool fetching = imagep->updateFetch(); + if (fetching) + { + fetch_count++; + } if (min_count <= 0 && image_op_timer.getElapsedTimeF32() > max_time) { break; } min_count--; } + if (fetch_count == 0) + { + gDebugTimers[0].pause(); + } + else + { + gDebugTimers[0].unpause(); + } return image_op_timer.getElapsedTimeF32(); } void LLViewerTextureList::updateImagesUpdateStats() { - if (mUpdateStats) + if (mUpdateStats && mForceResetTextureStats) { for (image_priority_list_t::iterator iter = mImageList.begin(); iter != mImageList.end(); ) { LLViewerFetchedTexture* imagep = *iter++; - imagep->resetTextureStats(mForceResetTextureStats); + imagep->resetTextureStats(); } mUpdateStats = FALSE; mForceResetTextureStats = FALSE; @@ -1019,6 +1054,9 @@ LLPointer<LLImageJ2C> LLViewerTextureList::convertToUploadFile(LLPointer<LLImage return compressedImage; } +const S32 MIN_VIDEO_RAM = 32; +const S32 MAX_VIDEO_RAM = 512; // 512MB max for performance reasons. + // Returns min setting for TextureMemory (in MB) S32 LLViewerTextureList::getMinVideoRamSetting() { @@ -1137,13 +1175,13 @@ void LLViewerTextureList::receiveImageHeader(LLMessageSystem *msg, void **user_d if (msg->getReceiveCompressedSize()) { - gTextureList.mTextureBits += msg->getReceiveCompressedSize() * 8; + gTextureList.sTextureBits += msg->getReceiveCompressedSize() * 8; } else { - gTextureList.mTextureBits += msg->getReceiveSize() * 8; + gTextureList.sTextureBits += msg->getReceiveSize() * 8; } - gTextureList.mTexturePackets++; + gTextureList.sTexturePackets++; U8 codec; U16 packets; @@ -1202,13 +1240,13 @@ void LLViewerTextureList::receiveImagePacket(LLMessageSystem *msg, void **user_d if (msg->getReceiveCompressedSize()) { - gTextureList.mTextureBits += msg->getReceiveCompressedSize() * 8; + gTextureList.sTextureBits += msg->getReceiveCompressedSize() * 8; } else { - gTextureList.mTextureBits += msg->getReceiveSize() * 8; + gTextureList.sTextureBits += msg->getReceiveSize() * 8; } - gTextureList.mTexturePackets++; + gTextureList.sTexturePackets++; //llprintline("Start decode, image header..."); msg->getUUIDFast(_PREHASH_ImageID, _PREHASH_ID, id); @@ -1401,7 +1439,7 @@ void LLUIImageList::onUIImageLoaded( BOOL success, LLViewerFetchedTexture *src_v // for images grabbed from local files, apply clipping rectangle to restore original dimensions // from power-of-2 gl image - if (success && imagep.notNull() && src_vi && !src_vi->getLocalFileName().empty()) + if (success && imagep.notNull() && src_vi && (src_vi->getUrl().compare(0, 7, "file://")==0)) { F32 clip_x = (F32)src_vi->getOriginalWidth() / (F32)src_vi->getFullWidth(); F32 clip_y = (F32)src_vi->getOriginalHeight() / (F32)src_vi->getFullHeight(); @@ -1458,6 +1496,11 @@ bool LLUIImageList::initFromFile() llwarns << "Unable to parse UI image list file " << base_file_path << llendl; return false; } + if (!root->hasAttribute("version")) + { + llwarns << "No valid version number in UI image list file " << base_file_path << llendl; + return false; + } std::vector<std::string> paths; // path to current selected skin diff --git a/indra/newview/llviewertexturelist.h b/indra/newview/llviewertexturelist.h index 6948db70f8..3c9c81a689 100644 --- a/indra/newview/llviewertexturelist.h +++ b/indra/newview/llviewertexturelist.h @@ -53,7 +53,6 @@ const BOOL GL_TEXTURE_NO = FALSE; const BOOL IMMEDIATE_YES = TRUE; const BOOL IMMEDIATE_NO = FALSE; -class LLImageJ2C; class LLMessageSystem; class LLTextureView; @@ -146,6 +145,15 @@ private: LLGLenum primary_format = 0, const LLUUID& force_id = LLUUID::null ); + + LLViewerFetchedTexture* getImageFromUrl(const std::string& url, + BOOL usemipmap = TRUE, + BOOL level_immediate = FALSE, // Get the requested level immediately upon creation. + S8 texture_type = LLViewerTexture::FETCHED_TEXTURE, + LLGLint internal_format = 0, + LLGLenum primary_format = 0, + const LLUUID& force_id = LLUUID::null + ); LLViewerFetchedTexture* createImage(const LLUUID &image_id, BOOL usemipmap = TRUE, @@ -190,11 +198,18 @@ private: LLFrameTimer mForceDecodeTimer; public: - U32 mTextureBits; - U32 mTexturePackets; + static U32 sTextureBits; + static U32 sTexturePackets; + + static LLStat sNumImagesStat; + static LLStat sNumRawImagesStat; + static LLStat sGLTexMemStat; + static LLStat sGLBoundMemStat; + static LLStat sRawMemStat; + static LLStat sFormattedMemStat; private: - S32 mNumImages; + static S32 sNumImages; static void (*sUUIDCallback)(void**, const LLUUID &); }; diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp index 9b3dbcd2f6..fc09c946af 100644 --- a/indra/newview/llviewerwindow.cpp +++ b/indra/newview/llviewerwindow.cpp @@ -121,6 +121,7 @@ #include "llhudview.h" #include "llimagebmp.h" #include "llimagej2c.h" +#include "llimageworker.h" #include "llkeyboard.h" #include "lllineeditor.h" #include "llmenugl.h" @@ -160,7 +161,6 @@ #include "lltoolselectland.h" #include "lltrans.h" #include "lluictrlfactory.h" -#include "lluploaddialog.h" #include "llurldispatcher.h" // SLURL from other app instance #include "llvieweraudio.h" #include "llviewercamera.h" @@ -192,6 +192,7 @@ #include "llbottomtray.h" #include "llnearbychatbar.h" #include "llagentui.h" +#include "llwearablelist.h" #include "llnotificationmanager.h" @@ -330,7 +331,9 @@ public: S32 hours = (S32)(time / (60*60)); S32 mins = (S32)((time - hours*(60*60)) / 60); S32 secs = (S32)((time - hours*(60*60) - mins*60)); - addText(xpos, ypos, llformat(" Debug %d: %d:%02d:%02d", idx, hours,mins,secs)); ypos += y_inc2; + std::string label = gDebugTimerLabel[idx]; + if (label.empty()) label = llformat("Debug: %d", idx); + addText(xpos, ypos, llformat(" %s: %d:%02d:%02d", label.c_str(), hours,mins,secs)); ypos += y_inc2; } F32 time = gFrameTimeSeconds; @@ -1304,6 +1307,7 @@ LLViewerWindow::LLViewerWindow( // Init the image list. Must happen after GL is initialized and before the images that // LLViewerWindow needs are requested. + LLImageGL::initClass(LLViewerTexture::MAX_GL_IMAGE_CATEGORY) ; gTextureList.init(); LLViewerTextureManager::init() ; gBumpImageList.init(); @@ -1670,6 +1674,8 @@ void LLViewerWindow::shutdownGL() gSky.cleanup(); stop_glerror(); + LLWearableList::instance().cleanup() ; + gTextureList.shutdown(); stop_glerror(); @@ -1683,7 +1689,10 @@ void LLViewerWindow::shutdownGL() stop_glerror(); LLViewerTextureManager::cleanup() ; - + LLImageGL::cleanupClass() ; + + llinfos << "All texturs and llimagegl images are destroyed!" << llendl ; + llinfos << "Cleaning up select manager" << llendl; LLSelectMgr::getInstance()->cleanup(); diff --git a/indra/newview/llviewerwindow.h b/indra/newview/llviewerwindow.h index d7c403739e..c2906b1718 100644 --- a/indra/newview/llviewerwindow.h +++ b/indra/newview/llviewerwindow.h @@ -324,7 +324,7 @@ public: } ESnapshotType; BOOL saveSnapshot(const std::string& filename, S32 image_width, S32 image_height, BOOL show_ui = TRUE, BOOL do_rebuild = FALSE, ESnapshotType type = SNAPSHOT_TYPE_COLOR); BOOL rawSnapshot(LLImageRaw *raw, S32 image_width, S32 image_height, BOOL keep_window_aspect = TRUE, BOOL is_texture = FALSE, - BOOL show_ui = TRUE, BOOL do_rebuild = FALSE, ESnapshotType type = SNAPSHOT_TYPE_COLOR, S32 max_size = MAX_IMAGE_SIZE ); + BOOL show_ui = TRUE, BOOL do_rebuild = FALSE, ESnapshotType type = SNAPSHOT_TYPE_COLOR, S32 max_size = MAX_SNAPSHOT_IMAGE_SIZE ); BOOL thumbnailSnapshot(LLImageRaw *raw, S32 preview_width, S32 preview_height, BOOL show_ui, BOOL do_rebuild, ESnapshotType type) ; BOOL isSnapshotLocSet() const { return ! sSnapshotDir.empty(); } void resetSnapshotLoc() const { sSnapshotDir.clear(); } diff --git a/indra/newview/llvlcomposition.cpp b/indra/newview/llvlcomposition.cpp index d124cbcdce..999701ece1 100644 --- a/indra/newview/llvlcomposition.cpp +++ b/indra/newview/llvlcomposition.cpp @@ -279,7 +279,6 @@ BOOL LLVLComposition::generateTexture(const F32 x, const F32 y, if (mRawImages[i].isNull()) { // Read back a raw image for this discard level, if it exists - mRawImages[i] = new LLImageRaw; S32 min_dim = llmin(mDetailTextures[i]->getFullWidth(), mDetailTextures[i]->getFullHeight()); S32 ddiscard = 0; while (min_dim > BASE_SIZE && ddiscard < MAX_DISCARD_LEVEL) @@ -287,12 +286,18 @@ BOOL LLVLComposition::generateTexture(const F32 x, const F32 y, ddiscard++; min_dim /= 2; } - if (!mDetailTextures[i]->readBackRaw(ddiscard, mRawImages[i], false)) + + mDetailTextures[i]->reloadRawImage(ddiscard) ; + if(mDetailTextures[i]->getRawImageLevel() != ddiscard)//raw iamge is not ready, will enter here again later. { - llwarns << "Unable to read raw data for terrain detail texture: " << mDetailTextures[i]->getID() << llendl; - mRawImages[i] = NULL; + mDetailTextures[i]->destroyRawImage() ; + lldebugs << "cached raw data for terrain detail texture is not ready yet: " << mDetailTextures[i]->getID() << llendl; return FALSE; } + + mRawImages[i] = mDetailTextures[i]->getRawImage() ; + mDetailTextures[i]->destroyRawImage() ; + if (mDetailTextures[i]->getWidth(ddiscard) != BASE_SIZE || mDetailTextures[i]->getHeight(ddiscard) != BASE_SIZE || mDetailTextures[i]->getComponents() != 3) diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp index 60ab6f8075..a6f20922d9 100644 --- a/indra/newview/llvoavatar.cpp +++ b/indra/newview/llvoavatar.cpp @@ -689,7 +689,6 @@ LLVOAvatar::LLVOAvatar(const LLUUID& id, mBakedTextureDatas[i].mIsUsed = false; mBakedTextureDatas[i].mMaskTexName = 0; mBakedTextureDatas[i].mTextureIndex = LLVOAvatarDictionary::bakedToLocalTextureIndex((EBakedTextureIndex)i); - mBakedTextureDatas[i].mMorphMasksValid = FALSE; } mDirtyMesh = TRUE; // Dirty geometry, need to regenerate. @@ -999,7 +998,7 @@ void LLVOAvatar::resetImpostors() // static void LLVOAvatar::deleteCachedImages(bool clearAll) -{ +{ if (LLTexLayerSet::sHasCaches) { lldebugs << "Deleting layer set caches" << llendl; @@ -3919,7 +3918,7 @@ U32 LLVOAvatar::renderFootShadows() LLGLDepthTest test(GL_TRUE, GL_FALSE); //render foot shadows LLGLEnable blend(GL_BLEND); - gGL.getTexUnit(0)->bind(mShadowImagep.get()); + gGL.getTexUnit(0)->bind(mShadowImagep, TRUE); glColor4fv(mShadow0Facep->getRenderColor().mV); mShadow0Facep->renderIndexed(foot_mask); glColor4fv(mShadow1Facep->getRenderColor().mV); @@ -3967,7 +3966,7 @@ U32 LLVOAvatar::renderImpostor(LLColor4U color, S32 diffuse_channel) //------------------------------------------------------------------------ // LLVOAvatar::updateTextures() //------------------------------------------------------------------------ -void LLVOAvatar::updateTextures(LLAgent &agent) +void LLVOAvatar::updateTextures() { BOOL render_avatar = TRUE; @@ -6091,28 +6090,6 @@ void LLVOAvatar::addMaskedMorph(EBakedTextureIndex index, LLPolyMorphTarget* mor } } -// invalidates morph masks for a given layer. Don't pass a parameter to invalidate all morph masks. -void LLVOAvatar::invalidateMorphMasks(LLVOAvatarDefines::EBakedTextureIndex index) -{ - setMorphMasksValid(FALSE, index); -} - -// updates morph masks to be a value for a given layer. Don't pass an argument to set value for all morph masks -void LLVOAvatar::setMorphMasksValid(BOOL new_status, LLVOAvatarDefines::EBakedTextureIndex index) -{ - if (index == BAKED_NUM_INDICES) - { - for (U8 tex = 0; tex < (U8)BAKED_NUM_INDICES; tex++) - { - mBakedTextureDatas[tex].mMorphMasksValid = new_status; - } - } - else if (index < BAKED_NUM_INDICES) - { - mBakedTextureDatas[index].mMorphMasksValid = new_status; - } -} - // returns TRUE if morph masks are present and not valid for a given baked texture, FALSE otherwise BOOL LLVOAvatar::morphMaskNeedsUpdate(LLVOAvatarDefines::EBakedTextureIndex index) { @@ -6121,9 +6098,20 @@ BOOL LLVOAvatar::morphMaskNeedsUpdate(LLVOAvatarDefines::EBakedTextureIndex inde return FALSE; } - if (!mBakedTextureDatas[index].mMaskedMorphs.empty() && !mBakedTextureDatas[index].mMorphMasksValid) + if (!mBakedTextureDatas[index].mMaskedMorphs.empty()) { - return TRUE; + if (isSelf()) + { + LLTexLayerSet *layer_set = mBakedTextureDatas[index].mTexLayerSet; + if (layer_set) + { + return !layer_set->isMorphValid(); + } + } + else + { + return FALSE; + } } return FALSE; diff --git a/indra/newview/llvoavatar.h b/indra/newview/llvoavatar.h index f7c794defe..173ad02808 100644 --- a/indra/newview/llvoavatar.h +++ b/indra/newview/llvoavatar.h @@ -125,7 +125,7 @@ public: virtual BOOL updateLOD(); BOOL updateJointLODs(); virtual BOOL isActive() const; // Whether this object needs to do an idleUpdate. - virtual void updateTextures(LLAgent &agent); + virtual void updateTextures(); virtual S32 setTETexture(const U8 te, const LLUUID& uuid); // If setting a baked texture, need to request it from a non-local sim. virtual void onShift(const LLVector3& shift_vector); virtual U32 getPartitionType() const; @@ -355,10 +355,8 @@ private: // Morph masks //-------------------------------------------------------------------- public: - void invalidateMorphMasks(LLVOAvatarDefines::EBakedTextureIndex index = LLVOAvatarDefines::BAKED_NUM_INDICES); BOOL morphMaskNeedsUpdate(LLVOAvatarDefines::EBakedTextureIndex index = LLVOAvatarDefines::BAKED_NUM_INDICES); void addMaskedMorph(LLVOAvatarDefines::EBakedTextureIndex index, LLPolyMorphTarget* morph_target, BOOL invert, std::string layer); - void setMorphMasksValid(BOOL new_status, LLVOAvatarDefines::EBakedTextureIndex index = LLVOAvatarDefines::BAKED_NUM_INDICES); void applyMorphMask(U8* tex_data, S32 width, S32 height, S32 num_components, LLVOAvatarDefines::EBakedTextureIndex index = LLVOAvatarDefines::BAKED_NUM_INDICES); //-------------------------------------------------------------------- @@ -489,7 +487,6 @@ protected: // Stores pointers to the joint meshes that this baked texture deals with std::vector< LLViewerJointMesh * > mMeshes; // std::vector<LLViewerJointMesh> mJoints[i]->mMeshParts morph_list_t mMaskedMorphs; - BOOL mMorphMasksValid; }; typedef std::vector<BakedTextureData> bakedtexturedata_vec_t; bakedtexturedata_vec_t mBakedTextureDatas; diff --git a/indra/newview/llvoavatarself.cpp b/indra/newview/llvoavatarself.cpp index 85a68fdd3f..7a83070770 100644 --- a/indra/newview/llvoavatarself.cpp +++ b/indra/newview/llvoavatarself.cpp @@ -1171,26 +1171,6 @@ void LLVOAvatarSelf::localTextureLoaded(BOOL success, LLViewerFetchedTexture *sr } // virtual -/* //unused -BOOL LLVOAvatarSelf::getLocalTextureRaw(ETextureIndex index, LLImageRaw* image_raw) const -{ - if (!isIndexLocalTexture(index)) return FALSE; - if (getLocalTextureID(index) == IMG_DEFAULT_AVATAR) return TRUE; - - const LocalTextureData *local_tex_data = getLocalTextureData(index)[0]; - if (local_tex_data->mImage->readBackRaw(-1, image_raw, false)) - { - - return TRUE; - } - - // No data loaded yet - setLocalTexture((ETextureIndex)index, getTEImage(index), FALSE); // <-- non-const, move this elsewhere - return FALSE; -} -*/ - -// virtual BOOL LLVOAvatarSelf::getLocalTextureGL(ETextureIndex type, LLViewerTexture** tex_pp, U32 index) const { *tex_pp = NULL; @@ -1387,8 +1367,8 @@ void LLVOAvatarSelf::invalidateComposite( LLTexLayerSet* layerset, BOOL set_by_u } // llinfos << "LLVOAvatar::invalidComposite() " << layerset->getBodyRegion() << llendl; - invalidateMorphMasks(layerset->getBakedTexIndex()); layerset->requestUpdate(); + layerset->invalidateMorphMasks(); if( set_by_user ) { @@ -1841,12 +1821,13 @@ void LLVOAvatarSelf::addLocalTextureStats( ETextureIndex type, LLViewerFetchedTe if (!covered_by_baked) { - if (getLocalTextureID(type, index) != IMG_DEFAULT_AVATAR) + if (getLocalTextureID(type, index) != IMG_DEFAULT_AVATAR && imagep->getDiscardLevel() != 0) { F32 desired_pixels; desired_pixels = llmin(mPixelArea, (F32)getTexImageArea()); imagep->setBoostLevel(getAvatarBoostLevel()); imagep->addTextureStats( desired_pixels / texel_area_ratio ); + imagep->forceUpdateBindStats() ; if (imagep->getDiscardLevel() < 0) { mHasGrey = TRUE; // for statistics gathering @@ -2144,6 +2125,49 @@ BOOL LLVOAvatarSelf::needsRenderBeam() // static void LLVOAvatarSelf::deleteScratchTextures() { + if(gAuditTexture) + { + S32 total_tex_size = sScratchTexBytes ; + S32 tex_size = SCRATCH_TEX_WIDTH * SCRATCH_TEX_HEIGHT ; + + if( sScratchTexNames.checkData( GL_LUMINANCE ) ) + { + LLImageGL::decTextureCounter(tex_size, 1, LLViewerTexture::AVATAR_SCRATCH_TEX) ; + total_tex_size -= tex_size ; + } + if( sScratchTexNames.checkData( GL_ALPHA ) ) + { + LLImageGL::decTextureCounter(tex_size, 1, LLViewerTexture::AVATAR_SCRATCH_TEX) ; + total_tex_size -= tex_size ; + } + if( sScratchTexNames.checkData( GL_COLOR_INDEX ) ) + { + LLImageGL::decTextureCounter(tex_size, 1, LLViewerTexture::AVATAR_SCRATCH_TEX) ; + total_tex_size -= tex_size ; + } + if( sScratchTexNames.checkData( GL_LUMINANCE_ALPHA ) ) + { + LLImageGL::decTextureCounter(tex_size, 2, LLViewerTexture::AVATAR_SCRATCH_TEX) ; + total_tex_size -= 2 * tex_size ; + } + if( sScratchTexNames.checkData( GL_RGB ) ) + { + LLImageGL::decTextureCounter(tex_size, 3, LLViewerTexture::AVATAR_SCRATCH_TEX) ; + total_tex_size -= 3 * tex_size ; + } + if( sScratchTexNames.checkData( GL_RGBA ) ) + { + LLImageGL::decTextureCounter(tex_size, 4, LLViewerTexture::AVATAR_SCRATCH_TEX) ; + total_tex_size -= 4 * tex_size ; + } + //others + while(total_tex_size > 0) + { + LLImageGL::decTextureCounter(tex_size, 4, LLViewerTexture::AVATAR_SCRATCH_TEX) ; + total_tex_size -= 4 * tex_size ; + } + } + for( LLGLuint* namep = sScratchTexNames.getFirstData(); namep; namep = sScratchTexNames.getNextData() ) @@ -2166,7 +2190,8 @@ void LLVOAvatarSelf::deleteScratchTextures() BOOL LLVOAvatarSelf::bindScratchTexture( LLGLenum format ) { U32 texture_bytes = 0; - GLuint gl_name = getScratchTexName( format, &texture_bytes ); + S32 components = 0; + GLuint gl_name = getScratchTexName( format, components, &texture_bytes ); if( gl_name ) { gGL.getTexUnit(0)->bindManual(LLTexUnit::TT_TEXTURE, gl_name); @@ -2178,12 +2203,12 @@ BOOL LLVOAvatarSelf::bindScratchTexture( LLGLenum format ) if( *last_bind_time != LLImageGL::sLastFrameTime ) { *last_bind_time = LLImageGL::sLastFrameTime; - LLImageGL::updateBoundTexMem(texture_bytes); + LLImageGL::updateBoundTexMem(texture_bytes, components, LLViewerTexture::AVATAR_SCRATCH_TEX) ; } } else { - LLImageGL::updateBoundTexMem(texture_bytes); + LLImageGL::updateBoundTexMem(texture_bytes, components, LLViewerTexture::AVATAR_SCRATCH_TEX) ; sScratchTexLastBindTime.addData( format, new F32(LLImageGL::sLastFrameTime) ); } return TRUE; @@ -2191,9 +2216,8 @@ BOOL LLVOAvatarSelf::bindScratchTexture( LLGLenum format ) return FALSE; } -LLGLuint LLVOAvatarSelf::getScratchTexName( LLGLenum format, U32* texture_bytes ) -{ - S32 components; +LLGLuint LLVOAvatarSelf::getScratchTexName( LLGLenum format, S32& components, U32* texture_bytes ) +{ GLenum internal_format; switch( format ) { @@ -2239,6 +2263,11 @@ LLGLuint LLVOAvatarSelf::getScratchTexName( LLGLenum format, U32* texture_bytes sScratchTexBytes += *texture_bytes; LLImageGL::sGlobalTextureMemoryInBytes += *texture_bytes; + + if(gAuditTexture) + { + LLImageGL::incTextureCounter(SCRATCH_TEX_WIDTH * SCRATCH_TEX_HEIGHT, components, LLViewerTexture::AVATAR_SCRATCH_TEX) ; + } return name; } diff --git a/indra/newview/llvoavatarself.h b/indra/newview/llvoavatarself.h index 6e52b33634..8a66422c44 100644 --- a/indra/newview/llvoavatarself.h +++ b/indra/newview/llvoavatarself.h @@ -241,7 +241,7 @@ public: BOOL bindScratchTexture(LLGLenum format); static void deleteScratchTextures(); protected: - LLGLuint getScratchTexName(LLGLenum format, U32* texture_bytes); + LLGLuint getScratchTexName(LLGLenum format, S32& components, U32* texture_bytes); private: static S32 sScratchTexBytes; static LLMap< LLGLenum, LLGLuint*> sScratchTexNames; diff --git a/indra/newview/llvoclouds.cpp b/indra/newview/llvoclouds.cpp index 8d3c8b6f1a..177cb16c50 100644 --- a/indra/newview/llvoclouds.cpp +++ b/indra/newview/llvoclouds.cpp @@ -101,7 +101,7 @@ void LLVOClouds::setPixelAreaAndAngle(LLAgent &agent) mPixelArea = 1500*100; } -void LLVOClouds::updateTextures(LLAgent &agent) +void LLVOClouds::updateTextures() { getTEImage(0)->addTextureStats(mPixelArea); } diff --git a/indra/newview/llvoclouds.h b/indra/newview/llvoclouds.h index 95e6b96e4e..c4a75f5b5e 100644 --- a/indra/newview/llvoclouds.h +++ b/indra/newview/llvoclouds.h @@ -65,7 +65,7 @@ public: /*virtual*/ BOOL isActive() const; // Whether this object needs to do an idleUpdate. F32 getPartSize(S32 idx); - /*virtual*/ void updateTextures(LLAgent &agent); + /*virtual*/ void updateTextures(); /*virtual*/ void setPixelAreaAndAngle(LLAgent &agent); // generate accurate apparent angle and area void updateFaceSize(S32 idx) { } diff --git a/indra/newview/llvograss.cpp b/indra/newview/llvograss.cpp index 110433a27d..f33a5cc818 100644 --- a/indra/newview/llvograss.cpp +++ b/indra/newview/llvograss.cpp @@ -322,7 +322,7 @@ void LLVOGrass::setPixelAreaAndAngle(LLAgent &agent) // BUG could speed this up by caching the relative_position and range calculations -void LLVOGrass::updateTextures(LLAgent &agent) +void LLVOGrass::updateTextures() { if (getTEImage(0)) { diff --git a/indra/newview/llvograss.h b/indra/newview/llvograss.h index 124400d356..6a6fcc31c3 100644 --- a/indra/newview/llvograss.h +++ b/indra/newview/llvograss.h @@ -72,7 +72,7 @@ public: LLStrider<U16>& indicesp); void updateFaceSize(S32 idx) { } - /*virtual*/ void updateTextures(LLAgent &agent); + /*virtual*/ void updateTextures(); /*virtual*/ BOOL updateLOD(); /*virtual*/ void setPixelAreaAndAngle(LLAgent &agent); // generate accurate apparent angle and area diff --git a/indra/newview/llvoground.cpp b/indra/newview/llvoground.cpp index ac2484ddfd..221c6b61ec 100644 --- a/indra/newview/llvoground.cpp +++ b/indra/newview/llvoground.cpp @@ -70,7 +70,7 @@ BOOL LLVOGround::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time) } -void LLVOGround::updateTextures(LLAgent &agent) +void LLVOGround::updateTextures() { } diff --git a/indra/newview/llvoground.h b/indra/newview/llvoground.h index af3fcd65d4..0ccb0834a2 100644 --- a/indra/newview/llvoground.h +++ b/indra/newview/llvoground.h @@ -51,7 +51,7 @@ public: // Graphical stuff for objects - maybe broken out into render class // later? - /*virtual*/ void updateTextures(LLAgent &agent); + /*virtual*/ void updateTextures(); /*virtual*/ LLDrawable* createDrawable(LLPipeline *pipeline); /*virtual*/ BOOL updateGeometry(LLDrawable *drawable); diff --git a/indra/newview/llvopartgroup.cpp b/indra/newview/llvopartgroup.cpp index 143cd2d9c6..9dd0b598dc 100644 --- a/indra/newview/llvopartgroup.cpp +++ b/indra/newview/llvopartgroup.cpp @@ -108,7 +108,7 @@ void LLVOPartGroup::setPixelAreaAndAngle(LLAgent &agent) } } -void LLVOPartGroup::updateTextures(LLAgent &agent) +void LLVOPartGroup::updateTextures() { // Texture stats for particles need to be updated in a different way... } diff --git a/indra/newview/llvopartgroup.h b/indra/newview/llvopartgroup.h index 3dc3292992..18583b4be9 100644 --- a/indra/newview/llvopartgroup.h +++ b/indra/newview/llvopartgroup.h @@ -61,7 +61,7 @@ public: virtual U32 getPartitionType() const; /*virtual*/ void setPixelAreaAndAngle(LLAgent &agent); - /*virtual*/ void updateTextures(LLAgent &agent); + /*virtual*/ void updateTextures(); /*virtual*/ LLDrawable* createDrawable(LLPipeline *pipeline); /*virtual*/ BOOL updateGeometry(LLDrawable *drawable); diff --git a/indra/newview/llvosky.cpp b/indra/newview/llvosky.cpp index d37deaf53d..5ff8f0d267 100644 --- a/indra/newview/llvosky.cpp +++ b/indra/newview/llvosky.cpp @@ -289,7 +289,7 @@ void LLSkyTex::create(const F32 brightness) void LLSkyTex::createGLImage(S32 which) { - mTexture[which]->createGLTexture(0, mImageRaw[which]); + mTexture[which]->createGLTexture(0, mImageRaw[which], 0, TRUE, LLViewerTexture::LOCAL); mTexture[which]->setAddressMode(LLTexUnit::TAM_CLAMP); } @@ -1180,7 +1180,7 @@ BOOL LLVOSky::updateSky() return TRUE; } -void LLVOSky::updateTextures(LLAgent &agent) +void LLVOSky::updateTextures() { if (mSunTexturep) { diff --git a/indra/newview/llvosky.h b/indra/newview/llvosky.h index 62c934fb41..ef74324e58 100644 --- a/indra/newview/llvosky.h +++ b/indra/newview/llvosky.h @@ -492,7 +492,7 @@ public: // Graphical stuff for objects - maybe broken out into render class // later? - /*virtual*/ void updateTextures(LLAgent &agent); + /*virtual*/ void updateTextures(); /*virtual*/ LLDrawable* createDrawable(LLPipeline *pipeline); /*virtual*/ BOOL updateGeometry(LLDrawable *drawable); diff --git a/indra/newview/llvosurfacepatch.cpp b/indra/newview/llvosurfacepatch.cpp index 164f0f0cad..ef7b161003 100644 --- a/indra/newview/llvosurfacepatch.cpp +++ b/indra/newview/llvosurfacepatch.cpp @@ -134,7 +134,7 @@ void LLVOSurfacePatch::setPixelAreaAndAngle(LLAgent &agent) } -void LLVOSurfacePatch::updateTextures(LLAgent &agent) +void LLVOSurfacePatch::updateTextures() { } diff --git a/indra/newview/llvosurfacepatch.h b/indra/newview/llvosurfacepatch.h index aaf4d41fa1..10a5888526 100644 --- a/indra/newview/llvosurfacepatch.h +++ b/indra/newview/llvosurfacepatch.h @@ -75,7 +75,7 @@ public: LLStrider<LLVector2> &texCoords1p, LLStrider<U16> &indicesp); - /*virtual*/ void updateTextures(LLAgent &agent); + /*virtual*/ void updateTextures(); /*virtual*/ void setPixelAreaAndAngle(LLAgent &agent); // generate accurate apparent angle and area /*virtual*/ void updateSpatialExtents(LLVector3& newMin, LLVector3& newMax); diff --git a/indra/newview/llvotextbubble.cpp b/indra/newview/llvotextbubble.cpp index f5094c025e..75beab519e 100644 --- a/indra/newview/llvotextbubble.cpp +++ b/indra/newview/llvotextbubble.cpp @@ -115,7 +115,7 @@ BOOL LLVOTextBubble::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time) } -void LLVOTextBubble::updateTextures(LLAgent &agent) +void LLVOTextBubble::updateTextures() { // Update the image levels of all textures... diff --git a/indra/newview/llvotextbubble.h b/indra/newview/llvotextbubble.h index 45d4df2a7e..7f84dbf631 100644 --- a/indra/newview/llvotextbubble.h +++ b/indra/newview/llvotextbubble.h @@ -44,7 +44,7 @@ public: /*virtual*/ BOOL isActive() const; // Whether this object needs to do an idleUpdate. /*virtual*/ BOOL idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time); - /*virtual*/ void updateTextures(LLAgent &agent); + /*virtual*/ void updateTextures(); /*virtual*/ LLDrawable* createDrawable(LLPipeline *pipeline); /*virtual*/ BOOL updateGeometry(LLDrawable *drawable); /*virtual*/ BOOL updateLOD(); diff --git a/indra/newview/llvotree.cpp b/indra/newview/llvotree.cpp index ec118d89bc..235e10f716 100644 --- a/indra/newview/llvotree.cpp +++ b/indra/newview/llvotree.cpp @@ -463,7 +463,7 @@ void LLVOTree::setPixelAreaAndAngle(LLAgent &agent) #endif } -void LLVOTree::updateTextures(LLAgent &agent) +void LLVOTree::updateTextures() { if (mTreeImagep) { diff --git a/indra/newview/llvotree.h b/indra/newview/llvotree.h index 13817fa111..feac9e0675 100644 --- a/indra/newview/llvotree.h +++ b/indra/newview/llvotree.h @@ -69,7 +69,7 @@ public: // Graphical stuff for objects - maybe broken out into render class later? /*virtual*/ void render(LLAgent &agent); /*virtual*/ void setPixelAreaAndAngle(LLAgent &agent); - /*virtual*/ void updateTextures(LLAgent &agent); + /*virtual*/ void updateTextures(); /*virtual*/ LLDrawable* createDrawable(LLPipeline *pipeline); /*virtual*/ BOOL updateGeometry(LLDrawable *drawable); diff --git a/indra/newview/llvotreenew.h b/indra/newview/llvotreenew.h index 3fec5855ef..426470101d 100644 --- a/indra/newview/llvotreenew.h +++ b/indra/newview/llvotreenew.h @@ -156,7 +156,7 @@ public: /*virtual*/ BOOL idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time); /*virtual*/ void render(LLAgent &agent); - /*virtual*/ void updateTextures(LLAgent &agent); + /*virtual*/ void updateTextures(); /*virtual*/ LLDrawable* createDrawable(LLPipeline *pipeline); /*virtual*/ BOOL updateGeometry(LLDrawable *drawable); diff --git a/indra/newview/llvovolume.cpp b/indra/newview/llvovolume.cpp index f6343b4134..021fc74648 100644 --- a/indra/newview/llvovolume.cpp +++ b/indra/newview/llvovolume.cpp @@ -53,12 +53,10 @@ #include "lldrawpoolbump.h" #include "llface.h" #include "llspatialpartition.h" - -// TEMP HACK ventrella #include "llhudmanager.h" #include "llflexibleobject.h" - #include "llsky.h" +#include "lltexturefetch.h" #include "llviewercamera.h" #include "llviewertexturelist.h" #include "llviewerregion.h" @@ -75,7 +73,6 @@ const S32 MIN_QUIET_FRAMES_COALESCE = 30; const F32 FORCE_SIMPLE_RENDER_AREA = 512.f; const F32 FORCE_CULL_AREA = 8.f; const F32 MAX_LOD_DISTANCE = 24.f; -const S32 MAX_SCULPT_REZ = 128; BOOL gAnimateTextures = TRUE; @@ -151,7 +148,6 @@ LLVOVolume::LLVOVolume(const LLUUID &id, const LLPCode pcode, LLViewerRegion *re mRelativeXformInvTrans.setIdentity(); mLOD = MIN_LOD; - mSculptLevel = -2; mTextureAnimp = NULL; mVObjRadius = LLVector3(1,1,0.5f).length(); mNumFaces = 0; @@ -564,28 +560,32 @@ BOOL LLVOVolume::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time) return TRUE; } -void LLVOVolume::updateTextures(LLAgent &agent) +void LLVOVolume::updateTextures() { const F32 TEXTURE_AREA_REFRESH_TIME = 5.f; // seconds - if (mDrawable.notNull() && mTextureUpdateTimer.getElapsedTimeF32() > TEXTURE_AREA_REFRESH_TIME) + if (mTextureUpdateTimer.getElapsedTimeF32() > TEXTURE_AREA_REFRESH_TIME) { - if (mDrawable->isVisible()) - { - updateTextures(); - } + updateTextureVirtualSize(); } } -void LLVOVolume::updateTextures() +void LLVOVolume::updateTextureVirtualSize() { // Update the pixel area of all faces + if(mDrawable.isNull() || !mDrawable->isVisible()) + { + return ; + } + if (!gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_SIMPLE)) { return; } - - if (LLViewerTexture::sDontLoadVolumeTextures || mDrawable.isNull()) // || !mDrawable->isVisible()) + + static LLCachedControl<bool> dont_load_textures(gSavedSettings,"TextureDisable"); + + if (dont_load_textures || LLAppViewer::getTextureFetch()->mDebugPause) // || !mDrawable->isVisible()) { return; } @@ -602,14 +602,15 @@ void LLVOVolume::updateTextures() LLFace* face = mDrawable->getFace(i); const LLTextureEntry *te = face->getTextureEntry(); LLViewerTexture *imagep = face->getTexture(); - if (!imagep || !te || + if (!imagep || !te || face->mExtents[0] == face->mExtents[1]) { continue; } F32 vsize; - + F32 old_size = face->getVirtualSize(); + if (isHUDAttachment()) { F32 area = (F32) LLViewerCamera::getInstance()->getScreenPixelArea(); @@ -619,12 +620,10 @@ void LLVOVolume::updateTextures() } else { - vsize = getTextureVirtualSize(face); + vsize = face->getTextureVirtualSize(); } - mPixelArea = llmax(mPixelArea, face->getPixelArea()); - - F32 old_size = face->getVirtualSize(); + mPixelArea = llmax(mPixelArea, face->getPixelArea()); if (face->mTextureMatrix != NULL) { @@ -636,7 +635,6 @@ void LLVOVolume::updateTextures() } face->setVirtualSize(vsize); - imagep->addTextureStats(vsize); if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_TEXTURE_AREA)) { if (vsize < min_vsize) min_vsize = vsize; @@ -668,32 +666,44 @@ void LLVOVolume::updateTextures() mSculptTexture = LLViewerTextureManager::getFetchedTexture(id, TRUE, LLViewerTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE); if (mSculptTexture.notNull()) { - S32 lod = llmin(mLOD, 3); - F32 lodf = ((F32)(lod + 1.0f)/4.f); - F32 tex_size = lodf * MAX_SCULPT_REZ; - mSculptTexture->addTextureStats(2.f * tex_size * tex_size); mSculptTexture->setBoostLevel(llmax((S32)mSculptTexture->getBoostLevel(), (S32)LLViewerTexture::BOOST_SCULPTED)); mSculptTexture->setForSculpt() ; - } - - S32 texture_discard = mSculptTexture->getDiscardLevel(); //try to match the texture - S32 current_discard = mSculptLevel; + + if(!mSculptTexture->isCachedRawImageReady()) + { + S32 lod = llmin(mLOD, 3); + F32 lodf = ((F32)(lod + 1.0f)/4.f); + F32 tex_size = lodf * LLViewerTexture::sMaxSculptRez ; + mSculptTexture->addTextureStats(2.f * tex_size * tex_size, FALSE); + + //if the sculpty very close to the view point, load first + { + LLVector3 lookAt = getPositionAgent() - LLViewerCamera::getInstance()->getOrigin(); + F32 dist = lookAt.normVec() ; + F32 cos_angle_to_view_dir = lookAt * LLViewerCamera::getInstance()->getXAxis() ; + mSculptTexture->setAdditionalDecodePriority(0.8f * LLFace::calcImportanceToCamera(cos_angle_to_view_dir, dist)) ; + } + } + + S32 texture_discard = mSculptTexture->getDiscardLevel(); //try to match the texture + S32 current_discard = getVolume() ? getVolume()->getSculptLevel() : -2 ; - if (texture_discard >= 0 && //texture has some data available - (texture_discard < current_discard || //texture has more data than last rebuild - current_discard < 0)) //no previous rebuild - { - gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_VOLUME, FALSE); - mSculptChanged = TRUE; - } + if (texture_discard >= 0 && //texture has some data available + (texture_discard < current_discard || //texture has more data than last rebuild + current_discard < 0)) //no previous rebuild + { + gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_VOLUME, FALSE); + mSculptChanged = TRUE; + } - if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_SCULPTED)) + if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_SCULPTED)) { setDebugText(llformat("T%d C%d V%d\n%dx%d", - texture_discard, current_discard, getVolume()->getSculptLevel(), - mSculptTexture->getHeight(), mSculptTexture->getWidth())); + texture_discard, current_discard, getVolume()->getSculptLevel(), + mSculptTexture->getHeight(), mSculptTexture->getWidth())); } + } } if (getLightTextureID().notNull()) @@ -714,10 +724,10 @@ void LLVOVolume::updateTextures() { setDebugText(llformat("%.0f:%.0f", fsqrtf(min_vsize),fsqrtf(max_vsize))); } - else if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_TEXTURE_PRIORITY)) - { - setDebugText(llformat("%.0f:%.0f", fsqrtf(min_vsize),fsqrtf(max_vsize))); - } + else if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_TEXTURE_PRIORITY)) + { + setDebugText(llformat("%.0f:%.0f", fsqrtf(min_vsize),fsqrtf(max_vsize))); + } else if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_FACE_AREA)) { setDebugText(llformat("%.0f:%.0f", fsqrtf(min_vsize),fsqrtf(max_vsize))); @@ -729,36 +739,6 @@ void LLVOVolume::updateTextures() } } -F32 LLVOVolume::getTextureVirtualSize(LLFace* face) -{ - //get area of circle around face - LLVector3 center = face->getPositionAgent(); - LLVector3 size = (face->mExtents[1] - face->mExtents[0]) * 0.5f; - - F32 face_area = LLPipeline::calcPixelArea(center, size, *LLViewerCamera::getInstance()); - - face->setPixelArea(face_area); - - if (face_area <= 0.f) - { - return 0.f; - } - - //get area of circle in texture space - LLVector2 tdim = face->mTexExtents[1] - face->mTexExtents[0]; - F32 texel_area = (tdim * 0.5f).lengthSquared()*F_PI; - if (texel_area <= 0.f) - { - // Probably animated, use default - texel_area = 1.f; - } - - //apply texel area to face area to get accurate ratio - face_area /= llclamp(texel_area, 1.f/64.f, 16.f); - - return face_area; -} - BOOL LLVOVolume::isActive() const { return !mStatic || mTextureAnimp || (mVolumeImpl && mVolumeImpl->isActive()); @@ -835,7 +815,6 @@ LLDrawable *LLVOVolume::createDrawable(LLPipeline *pipeline) return mDrawable; } - BOOL LLVOVolume::setVolume(const LLVolumeParams &volume_params, const S32 detail, bool unique_volume) { // Check if we need to change implementations @@ -890,7 +869,6 @@ BOOL LLVOVolume::setVolume(const LLVolumeParams &volume_params, const S32 detail { sculpt(); } - mSculptLevel = getVolume()->getSculptLevel(); } } else @@ -905,32 +883,22 @@ BOOL LLVOVolume::setVolume(const LLVolumeParams &volume_params, const S32 detail // sculpt replaces generate() for sculpted surfaces void LLVOVolume::sculpt() -{ - U16 sculpt_height = 0; - U16 sculpt_width = 0; - S8 sculpt_components = 0; - const U8* sculpt_data = NULL; - +{ if (mSculptTexture.notNull()) - { - S32 discard_level; - S32 desired_discard = 0; // lower discard levels have MUCH less resolution - - discard_level = desired_discard; + { + U16 sculpt_height = 0; + U16 sculpt_width = 0; + S8 sculpt_components = 0; + const U8* sculpt_data = NULL; + + S32 discard_level = mSculptTexture->getDiscardLevel() ; + LLImageRaw* raw_image = mSculptTexture->getCachedRawImage() ; S32 max_discard = mSculptTexture->getMaxDiscardLevel(); if (discard_level > max_discard) discard_level = max_discard; // clamp to the best we can do - S32 best_discard = mSculptTexture->getDiscardLevel(); - if (discard_level < best_discard) - discard_level = best_discard; // clamp to what we have - - if (best_discard == -1) - discard_level = -1; // and if we have nothing, set to nothing - - - S32 current_discard = getVolume()->getSculptLevel(); + S32 current_discard = getVolume()->getSculptLevel() ; if(current_discard < -2) { llwarns << "WARNING!!: Current discard of sculpty at " << current_discard @@ -951,18 +919,7 @@ void LLVOVolume::sculpt() if (current_discard == discard_level) // no work to do here return; - LLPointer<LLImageRaw> raw_image = new LLImageRaw(); - BOOL is_valid = mSculptTexture->readBackRaw(discard_level, raw_image, FALSE); - - sculpt_height = raw_image->getHeight(); - sculpt_width = raw_image->getWidth(); - sculpt_components = raw_image->getComponents(); - - if(is_valid) - { - is_valid = mSculptTexture->isValidForSculpt(discard_level, sculpt_width, sculpt_height, sculpt_components) ; - } - if(!is_valid) + if(!raw_image) { sculpt_width = 0; sculpt_height = 0; @@ -974,10 +931,10 @@ void LLVOVolume::sculpt() } } else - { - if (raw_image->getDataSize() < sculpt_height * sculpt_width * sculpt_components) - llerrs << "Sculpt: image data size = " << raw_image->getDataSize() - << " < " << sculpt_height << " x " << sculpt_width << " x " <<sculpt_components << llendl; + { + sculpt_height = raw_image->getHeight(); + sculpt_width = raw_image->getWidth(); + sculpt_components = raw_image->getComponents(); sculpt_data = raw_image->getData(); @@ -1013,12 +970,6 @@ BOOL LLVOVolume::calcLOD() return FALSE; } - //update face texture sizes on lod calculation - //if (mDrawable->isVisible()) - //{ - // updateTextures(); - //} - S32 cur_detail = 0; F32 radius = getVolume()->mLODScaleBias.scaledVec(getScale()).length(); @@ -1687,6 +1638,45 @@ bool LLVOVolume::hasMedia() const return result; } +LLVector3 LLVOVolume::getApproximateFaceNormal(U8 face_id) +{ + LLVector3 result = LLVector3::zero; + + LLFace* facep = mDrawable->getFace(face_id); + if(facep) + { + LLStrider<LLVector3> verticesp; + LLStrider<LLVector3> normalsp; + LLStrider<LLVector2> texCoordsp; + LLStrider<U16> indicesp; + S32 index_offset; + index_offset = facep->getGeometry(verticesp,normalsp,texCoordsp, indicesp); + + if(index_offset != -1 && (normalsp.get() != NULL)) + { + U16 count = facep->getGeomCount(); + U16 i; + + for(i=0; i < count; i++) + { + LLVector3 normal = *normalsp++; +// llinfos << "adding " << normal << llendl; + result += normal; + } + } + } + + if(!result.isNull()) + { +// llinfos << "before conversion: " << result << llendl; + result = volumeDirectionToAgent(result); + result.normalize(); +// llinfos << "after conversion: " << result << llendl; + } + + return result; +} + void LLVOVolume::requestMediaDataUpdate() { sObjectMediaClient->fetchMedia(new LLMediaDataClientObjectImpl(this)); @@ -2783,7 +2773,7 @@ BOOL LLVOVolume::lineSegmentIntersect(const LLVector3& start, const LLVector3& e if (face == -1) { start_face = 0; - end_face = volume->getNumFaces(); + end_face = volume->getNumVolumeFaces(); } else { @@ -2798,8 +2788,8 @@ BOOL LLVOVolume::lineSegmentIntersect(const LLVector3& start, const LLVector3& e if (face_hit >= 0 && mDrawable->getNumFaces() > face_hit) { - LLFace* face = mDrawable->getFace(face_hit); - + LLFace* face = mDrawable->getFace(face_hit); + if (pick_transparent || !face->getTexture() || !face->getTexture()->hasGLTexture() || face->getTexture()->getMask(face->surfaceToTexture(tc, p, n))) { v_end = p; @@ -3046,7 +3036,7 @@ void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group) LLVOVolume* vobj = drawablep->getVOVolume(); llassert_always(vobj); - vobj->updateTextures(); + vobj->updateTextureVirtualSize(); vobj->preRebuild(); drawablep->clearState(LLDrawable::HAS_ALPHA); diff --git a/indra/newview/llvovolume.h b/indra/newview/llvovolume.h index 00810b22c4..784ef16ba3 100644 --- a/indra/newview/llvovolume.h +++ b/indra/newview/llvovolume.h @@ -139,7 +139,7 @@ public: BOOL getVolumeChanged() const { return mVolumeChanged; } - F32 getTextureVirtualSize(LLFace* face); + /*virtual*/ F32 getRadius() const { return mVObjRadius; }; const LLMatrix4& getWorldMatrix(LLXformMatrix* xform) const; @@ -187,8 +187,8 @@ public: /*virtual*/ void updateFaceSize(S32 idx); /*virtual*/ BOOL updateLOD(); void updateRadius(); - /*virtual*/ void updateTextures(LLAgent &agent); - void updateTextures(); + /*virtual*/ void updateTextures(); + void updateTextureVirtualSize(); void updateFaceFlags(); void regenFaces(); @@ -263,6 +263,8 @@ public: F64 getTotalMediaInterest() const; bool hasMedia() const; + + LLVector3 getApproximateFaceNormal(U8 face_id); protected: S32 computeLODDetail(F32 distance, F32 radius); @@ -284,7 +286,6 @@ private: LLFrameTimer mTextureUpdateTimer; S32 mLOD; BOOL mLODChanged; - S32 mSculptLevel; BOOL mSculptChanged; F32 mSpotLightPriority; LLMatrix4 mRelativeXform; diff --git a/indra/newview/llvowater.cpp b/indra/newview/llvowater.cpp index 0c967f9020..e5ff62746e 100644 --- a/indra/newview/llvowater.cpp +++ b/indra/newview/llvowater.cpp @@ -100,7 +100,7 @@ void LLVOWater::setPixelAreaAndAngle(LLAgent &agent) // virtual -void LLVOWater::updateTextures(LLAgent &agent) +void LLVOWater::updateTextures() { } diff --git a/indra/newview/llvowater.h b/indra/newview/llvowater.h index 28a5633c58..3cc031e589 100644 --- a/indra/newview/llvowater.h +++ b/indra/newview/llvowater.h @@ -68,7 +68,7 @@ public: /*virtual*/ BOOL updateGeometry(LLDrawable *drawable); /*virtual*/ void updateSpatialExtents(LLVector3& newMin, LLVector3& newMax); - /*virtual*/ void updateTextures(LLAgent &agent); + /*virtual*/ void updateTextures(); /*virtual*/ void setPixelAreaAndAngle(LLAgent &agent); // generate accurate apparent angle and area virtual U32 getPartitionType() const; diff --git a/indra/newview/llwearablelist.cpp b/indra/newview/llwearablelist.cpp index da62223aac..9bde85dcaf 100644 --- a/indra/newview/llwearablelist.cpp +++ b/indra/newview/llwearablelist.cpp @@ -70,6 +70,11 @@ struct LLWearableArrivedData LLWearableList::~LLWearableList() { + llassert_always(mList.empty()) ; +} + +void LLWearableList::cleanup() +{ for_each(mList.begin(), mList.end(), DeletePairedPointer()); mList.clear(); } diff --git a/indra/newview/llwearablelist.h b/indra/newview/llwearablelist.h index e5155c66a4..cf1a9bddff 100644 --- a/indra/newview/llwearablelist.h +++ b/indra/newview/llwearablelist.h @@ -44,6 +44,7 @@ class LLWearableList : public LLSingleton<LLWearableList> public: LLWearableList() {} ~LLWearableList(); + void cleanup() ; S32 getLength() const { return mList.size(); } diff --git a/indra/newview/llworldmap.cpp b/indra/newview/llworldmap.cpp index 355d4141c3..66cb02ce99 100644 --- a/indra/newview/llworldmap.cpp +++ b/indra/newview/llworldmap.cpp @@ -34,57 +34,81 @@ #include "llworldmap.h" -#include "llregionhandle.h" +#include "llworldmapmessage.h" #include "message.h" - -#include "llagent.h" -#include "llmapresponders.h" -#include "llviewercontrol.h" -#include "llfloaterworldmap.h" #include "lltracker.h" #include "llviewertexturelist.h" -#include "llviewerregion.h" -#include "llregionflags.h" #include "lltrans.h" -const F32 REQUEST_ITEMS_TIMER = 10.f * 60.f; // 10 minutes +// Timers to temporise database requests +const F32 AGENTS_UPDATE_TIMER = 60.0; // Seconds between 2 agent requests for a region +const F32 REQUEST_ITEMS_TIMER = 10.f * 60.f; // Seconds before we consider re-requesting item data for the grid + +//--------------------------------------------------------------------------- +// LLItemInfo +//--------------------------------------------------------------------------- -// For DEV-17507, do lazy image loading in llworldmapview.cpp instead, -// limiting requests to currently visible regions and minimizing the -// number of textures being requested simultaneously. -// -// Uncomment IMMEDIATE_IMAGE_LOAD to restore the old behavior -// -//#define IMMEDIATE_IMAGE_LOAD LLItemInfo::LLItemInfo(F32 global_x, F32 global_y, const std::string& name, - LLUUID id, - S32 extra, S32 extra2) + LLUUID id) : mName(name), mToolTip(""), mPosGlobal(global_x, global_y, 40.0), mID(id), - mSelected(FALSE), - mExtra(extra), - mExtra2(extra2) + mCount(1) +// mSelected(false) +// mColor() { - mRegionHandle = to_region_handle(mPosGlobal); } -LLSimInfo::LLSimInfo() -: mHandle(0), +//--------------------------------------------------------------------------- +// LLSimInfo +//--------------------------------------------------------------------------- + +LLSimInfo::LLSimInfo(U64 handle) +: mHandle(handle), mName(), mAgentsUpdateTime(0), - mShowAgentLocations(FALSE), mAccess(0x0), mRegionFlags(0x0), - mWaterHeight(0.f), - mAlpha(-1.f) + mFirstAgentRequest(true) +// mWaterHeight(0.f) +{ +} + +void LLSimInfo::setLandForSaleImage (LLUUID image_id) { + mMapImageID = image_id; + + // Fetch the image + if (mMapImageID.notNull()) + { + mOverlayImage = LLViewerTextureManager::getFetchedTexture(mMapImageID, MIPMAP_TRUE, LLViewerTexture::BOOST_MAP, LLViewerTexture::LOD_TEXTURE); + mOverlayImage->setAddressMode(LLTexUnit::TAM_CLAMP); + } + else + { + mOverlayImage = NULL; + } } +LLPointer<LLViewerFetchedTexture> LLSimInfo::getLandForSaleImage () +{ + if (mOverlayImage.isNull() && mMapImageID.notNull()) + { + // Fetch the image if it hasn't been done yet (unlikely but...) + mOverlayImage = LLViewerTextureManager::getFetchedTexture(mMapImageID, MIPMAP_TRUE, LLViewerTexture::BOOST_MAP, LLViewerTexture::LOD_TEXTURE); + mOverlayImage->setAddressMode(LLTexUnit::TAM_CLAMP); + } + if (!mOverlayImage.isNull()) + { + // Boost the fetch level when we try to access that image + mOverlayImage->setBoostLevel(LLViewerTexture::BOOST_MAP); + } + return mOverlayImage; +} -LLVector3d LLSimInfo::getGlobalPos(LLVector3 local_pos) const +LLVector3d LLSimInfo::getGlobalPos(const LLVector3& local_pos) const { LLVector3d pos = from_region_handle(mHandle); pos.mdV[VX] += local_pos.mV[VX]; @@ -93,128 +117,184 @@ LLVector3d LLSimInfo::getGlobalPos(LLVector3 local_pos) const return pos; } +LLVector3d LLSimInfo::getGlobalOrigin() const +{ + return from_region_handle(mHandle); +} LLVector3 LLSimInfo::getLocalPos(LLVector3d global_pos) const { LLVector3d sim_origin = from_region_handle(mHandle); return LLVector3(global_pos - sim_origin); } +void LLSimInfo::clearImage() +{ + if (!mOverlayImage.isNull()) + { + mOverlayImage->setBoostLevel(0); + mOverlayImage = NULL; + } +} - -//--------------------------------------------------------------------------- -// World Map -//--------------------------------------------------------------------------- - -LLWorldMap::LLWorldMap() : - mIsTrackingUnknownLocation( FALSE ), - mInvalidLocation( FALSE ), - mIsTrackingDoubleClick( FALSE ), - mIsTrackingCommit( FALSE ), - mUnknownLocation( 0, 0, 0 ), - mRequestLandForSale(true), - mCurrentMap(0), - mMinX(U32_MAX), - mMaxX(U32_MIN), - mMinY(U32_MAX), - mMaxY(U32_MIN), - mNeighborMap(NULL), - mTelehubCoverageMap(NULL), - mNeighborMapWidth(0), - mNeighborMapHeight(0), - mSLURLRegionName(), - mSLURLRegionHandle(0), - mSLURL(), - mSLURLCallback(0), - mSLURLTeleport(false) -{ - for (S32 map=0; map<MAP_SIM_IMAGE_TYPES; ++map) +void LLSimInfo::dropImagePriority() +{ + if (!mOverlayImage.isNull()) { - mMapLoaded[map] = FALSE; - mMapBlockLoaded[map] = new BOOL[MAP_BLOCK_RES*MAP_BLOCK_RES]; - for (S32 idx=0; idx<MAP_BLOCK_RES*MAP_BLOCK_RES; ++idx) - { - mMapBlockLoaded[map][idx] = FALSE; - } + mOverlayImage->setBoostLevel(0); } } +// Update the agent count for that region +void LLSimInfo::updateAgentCount(F64 time) +{ + if ((time - mAgentsUpdateTime > AGENTS_UPDATE_TIMER) || mFirstAgentRequest) + { + LLWorldMapMessage::getInstance()->sendItemRequest(MAP_ITEM_AGENT_LOCATIONS, mHandle); + mAgentsUpdateTime = time; + mFirstAgentRequest = false; + } +} -LLWorldMap::~LLWorldMap() +// Get the total agents count +const S32 LLSimInfo::getAgentCount() const { - reset(); - for (S32 map=0; map<MAP_SIM_IMAGE_TYPES; ++map) + S32 total_agent_count = 0; + for (LLSimInfo::item_info_list_t::const_iterator it = mAgentLocations.begin(); it != mAgentLocations.end(); ++it) { - delete[] mMapBlockLoaded[map]; + total_agent_count += it->getCount(); } + return total_agent_count; } +bool LLSimInfo::isName(const std::string& name) const +{ + return (LLStringUtil::compareInsensitive(name, mName) == 0); +} -void LLWorldMap::reset() +void LLSimInfo::dump() const { - for_each(mSimInfoMap.begin(), mSimInfoMap.end(), DeletePairedPointer()); - mSimInfoMap.clear(); + U32 x_pos, y_pos; + from_region_handle(mHandle, &x_pos, &y_pos); + + LL_INFOS("World Map") << x_pos << "," << y_pos + << " " << mName + << " " << (S32)mAccess + << " " << std::hex << mRegionFlags << std::dec +// << " " << mWaterHeight + << LL_ENDL; +} + +void LLSimInfo::clearItems() +{ + mTelehubs.clear(); + mInfohubs.clear(); + mPGEvents.clear(); + mMatureEvents.clear(); + mAdultEvents.clear(); + mLandForSale.clear(); + mLandForSaleAdult.clear(); +// We persist the agent count though as it is updated on a frequent basis +// mAgentLocations.clear(); +} + +void LLSimInfo::insertAgentLocation(const LLItemInfo& item) +{ + std::string name = item.getName(); - for (S32 m=0; m<MAP_SIM_IMAGE_TYPES; ++m) + // Find the last item in the list with a different name and erase them + item_info_list_t::iterator lastiter; + for (lastiter = mAgentLocations.begin(); lastiter != mAgentLocations.end(); ++lastiter) + { + LLItemInfo& info = *lastiter; + if (info.isName(name)) + { + break; + } + } + if (lastiter != mAgentLocations.begin()) { - mMapLoaded[m] = FALSE; + mAgentLocations.erase(mAgentLocations.begin(), lastiter); } + // Now append the new location + mAgentLocations.push_back(item); +} + +//--------------------------------------------------------------------------- +// World Map +//--------------------------------------------------------------------------- + +LLWorldMap::LLWorldMap() : + mIsTrackingLocation( false ), + mIsTrackingFound( false ), + mIsInvalidLocation( false ), + mIsTrackingDoubleClick( false ), + mIsTrackingCommit( false ), + mTrackingLocation( 0, 0, 0 ), + mFirstRequest(true) +{ + //LL_INFOS("World Map") << "Creating the World Map -> LLWorldMap::LLWorldMap()" << LL_ENDL; + mMapBlockLoaded = new bool[MAP_BLOCK_RES*MAP_BLOCK_RES]; clearSimFlags(); - - eraseItems(); +} - mMinX = U32_MAX; - mMaxX = U32_MIN; - mMinY = U32_MAX; - mMaxY = U32_MIN; +LLWorldMap::~LLWorldMap() +{ + //LL_INFOS("World Map") << "Destroying the World Map -> LLWorldMap::~LLWorldMap()" << LL_ENDL; + reset(); + delete[] mMapBlockLoaded; +} - delete [] mNeighborMap; - mNeighborMap = NULL; - delete [] mTelehubCoverageMap; - mTelehubCoverageMap = NULL; - mNeighborMapWidth = 0; - mNeighborMapHeight = 0; +void LLWorldMap::reset() +{ + clearItems(true); // Clear the items lists + clearImageRefs(); // Clear the world mipmap and the land for sale tiles + clearSimFlags(); // Clear the block info flags array - for (S32 i=0; i<MAP_SIM_IMAGE_TYPES; i++) - { - mMapLayers[i].clear(); - } + // Finally, clear the region map itself + for_each(mSimInfoMap.begin(), mSimInfoMap.end(), DeletePairedPointer()); + mSimInfoMap.clear(); } -void LLWorldMap::eraseItems() +// Returns true if the items have been cleared +bool LLWorldMap::clearItems(bool force) { - if (mRequestTimer.getElapsedTimeF32() > REQUEST_ITEMS_TIMER) + bool clear = false; + if ((mRequestTimer.getElapsedTimeF32() > REQUEST_ITEMS_TIMER) || mFirstRequest || force) { mRequestTimer.reset(); - mTelehubs.clear(); - mInfohubs.clear(); - mPGEvents.clear(); - mMatureEvents.clear(); - mAdultEvents.clear(); - mLandForSale.clear(); + LLSimInfo* sim_info = NULL; + for (sim_info_map_t::iterator it = mSimInfoMap.begin(); it != mSimInfoMap.end(); ++it) + { + sim_info = it->second; + if (sim_info) + { + sim_info->clearItems(); + } + } + clear = true; + mFirstRequest = false; } -// mAgentLocationsMap.clear(); // persists -// mNumAgents.clear(); // persists + return clear; } - void LLWorldMap::clearImageRefs() { + // We clear the reference to the images we're holding. + // Images hold by the world mipmap first + mWorldMipmap.reset(); + + // Images hold by the region map + LLSimInfo* sim_info = NULL; for (sim_info_map_t::iterator it = mSimInfoMap.begin(); it != mSimInfoMap.end(); ++it) { - LLSimInfo* info = (*it).second; - if (info->mCurrentImage) - { - info->mCurrentImage->setBoostLevel(0); - info->mCurrentImage = NULL; - } - if (info->mOverlayImage) + sim_info = it->second; + if (sim_info) { - info->mOverlayImage->setBoostLevel(0); - info->mOverlayImage = NULL; + sim_info->clearImage(); } } } @@ -222,15 +302,25 @@ void LLWorldMap::clearImageRefs() // Doesn't clear the already-loaded sim infos, just re-requests them void LLWorldMap::clearSimFlags() { - for (S32 map=0; map<MAP_SIM_IMAGE_TYPES; ++map) + for (S32 idx=0; idx<MAP_BLOCK_RES*MAP_BLOCK_RES; ++idx) { - for (S32 idx=0; idx<MAP_BLOCK_RES*MAP_BLOCK_RES; ++idx) - { - mMapBlockLoaded[map][idx] = FALSE; - } + mMapBlockLoaded[idx] = false; } } +LLSimInfo* LLWorldMap::createSimInfoFromHandle(const U64 handle) +{ + LLSimInfo* sim_info = new LLSimInfo(handle); + mSimInfoMap[handle] = sim_info; + return sim_info; +} + +void LLWorldMap::equalizeBoostLevels() +{ + mWorldMipmap.equalizeBoostLevels(); + return; +} + LLSimInfo* LLWorldMap::simInfoFromPosGlobal(const LLVector3d& pos_global) { U64 handle = to_region_handle(pos_global); @@ -242,11 +332,7 @@ LLSimInfo* LLWorldMap::simInfoFromHandle(const U64 handle) sim_info_map_t::iterator it = mSimInfoMap.find(handle); if (it != mSimInfoMap.end()) { - LLSimInfo* sim_info = (*it).second; - if (sim_info) - { - return sim_info; - } + return it->second; } return NULL; } @@ -257,763 +343,273 @@ LLSimInfo* LLWorldMap::simInfoFromName(const std::string& sim_name) LLSimInfo* sim_info = NULL; if (!sim_name.empty()) { - for (sim_info_map_t::iterator it = mSimInfoMap.begin(); it != mSimInfoMap.end(); ++it) + // Iterate through the entire sim info map and compare the name + sim_info_map_t::iterator it; + for (it = mSimInfoMap.begin(); it != mSimInfoMap.end(); ++it) { - sim_info = (*it).second; - if (sim_info - && (0 == LLStringUtil::compareInsensitive(sim_name, sim_info->mName)) ) + sim_info = it->second; + if (sim_info && sim_info->isName(sim_name) ) { + // break out of loop if success break; } - sim_info = NULL; } + // If we got to the end, we haven't found the sim. Reset the ouput value to NULL. + if (it == mSimInfoMap.end()) + sim_info = NULL; } return sim_info; } bool LLWorldMap::simNameFromPosGlobal(const LLVector3d& pos_global, std::string & outSimName ) { - bool gotSimName = true; + LLSimInfo* sim_info = simInfoFromPosGlobal(pos_global); - U64 handle = to_region_handle(pos_global); - - sim_info_map_t::iterator it = mSimInfoMap.find(handle); - if (it != mSimInfoMap.end()) + if (sim_info) { - LLSimInfo* info = (*it).second; - outSimName = info->mName; + outSimName = sim_info->getName(); } else { - gotSimName = false; outSimName = "(unknown region)"; } - return gotSimName; + return (sim_info != NULL); } -void LLWorldMap::setCurrentLayer(S32 layer, bool request_layer) +void LLWorldMap::reloadItems(bool force) { - mCurrentMap = layer; - if (!mMapLoaded[layer] || request_layer) + //LL_INFOS("World Map") << "LLWorldMap::reloadItems()" << LL_ENDL; + if (clearItems(force)) { - sendMapLayerRequest(); + LLWorldMapMessage::getInstance()->sendItemRequest(MAP_ITEM_TELEHUB); + LLWorldMapMessage::getInstance()->sendItemRequest(MAP_ITEM_PG_EVENT); + LLWorldMapMessage::getInstance()->sendItemRequest(MAP_ITEM_MATURE_EVENT); + LLWorldMapMessage::getInstance()->sendItemRequest(MAP_ITEM_ADULT_EVENT); + LLWorldMapMessage::getInstance()->sendItemRequest(MAP_ITEM_LAND_FOR_SALE); } - - if (mTelehubs.size() == 0 || - mInfohubs.size() == 0) - { - // Request for telehubs - sendItemRequest(MAP_ITEM_TELEHUB); - } - - if (mPGEvents.size() == 0) - { - // Request for events - sendItemRequest(MAP_ITEM_PG_EVENT); - } - - if (mMatureEvents.size() == 0) - { - // Request for events (mature) - sendItemRequest(MAP_ITEM_MATURE_EVENT); - } - - if (mAdultEvents.size() == 0) - { - // Request for events (adult) - sendItemRequest(MAP_ITEM_ADULT_EVENT); - } - - if (mLandForSale.size() == 0) - { - // Request for Land For Sale - sendItemRequest(MAP_ITEM_LAND_FOR_SALE); - } - - if (mLandForSaleAdult.size() == 0) - { - // Request for Land For Sale - sendItemRequest(MAP_ITEM_LAND_FOR_SALE_ADULT); - } - - clearImageRefs(); - clearSimFlags(); } -void LLWorldMap::sendItemRequest(U32 type, U64 handle) -{ - LLMessageSystem* msg = gMessageSystem; - S32 layer = mCurrentMap; - - msg->newMessageFast(_PREHASH_MapItemRequest); - msg->nextBlockFast(_PREHASH_AgentData); - msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); - msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); - msg->addU32Fast(_PREHASH_Flags, layer); - msg->addU32Fast(_PREHASH_EstateID, 0); // Filled in on sim - msg->addBOOLFast(_PREHASH_Godlike, FALSE); // Filled in on sim - - msg->nextBlockFast(_PREHASH_RequestData); - msg->addU32Fast(_PREHASH_ItemType, type); - msg->addU64Fast(_PREHASH_RegionHandle, handle); // If zero, filled in on sim - - gAgent.sendReliableMessage(); -} -// public -void LLWorldMap::sendMapLayerRequest() +// static public +// Insert a region in the region map +// returns true if region inserted, false otherwise +bool LLWorldMap::insertRegion(U32 x_world, U32 y_world, std::string& name, LLUUID& image_id, U32 accesscode, U32 region_flags) { - if (!gAgent.getRegion()) return; - - LLSD body; - body["Flags"] = mCurrentMap; - std::string url = gAgent.getRegion()->getCapability( - gAgent.isGodlike() ? "MapLayerGod" : "MapLayer"); - - if (!url.empty()) + // This region doesn't exist + if (accesscode == 255) { - llinfos << "LLWorldMap::sendMapLayerRequest via capability" << llendl; - LLHTTPClient::post(url, body, new LLMapLayerResponder()); + // Checks if the track point is in it and invalidates it if it is + if (LLWorldMap::getInstance()->isTrackingInRectangle( x_world, y_world, x_world + REGION_WIDTH_UNITS, y_world + REGION_WIDTH_UNITS)) + { + LLWorldMap::getInstance()->setTrackingInvalid(); + } + // return failure to insert + return false; } else { - llinfos << "LLWorldMap::sendMapLayerRequest via message system" << llendl; - LLMessageSystem* msg = gMessageSystem; - S32 layer = mCurrentMap; - - // Request for layer - msg->newMessageFast(_PREHASH_MapLayerRequest); - msg->nextBlockFast(_PREHASH_AgentData); - msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); - msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); - msg->addU32Fast(_PREHASH_Flags, layer); - msg->addU32Fast(_PREHASH_EstateID, 0); // Filled in on sim - msg->addBOOLFast(_PREHASH_Godlike, FALSE); // Filled in on sim - gAgent.sendReliableMessage(); - - if (mRequestLandForSale) + U64 handle = to_region_handle(x_world, y_world); + //LL_INFOS("World Map") << "Map sim : " << name << ", ID : " << image_id.getString() << LL_ENDL; + // Insert the region in the region map of the world map + // Loading the LLSimInfo object with what we got and insert it in the map + LLSimInfo* siminfo = LLWorldMap::getInstance()->simInfoFromHandle(handle); + if (siminfo == NULL) { - msg->newMessageFast(_PREHASH_MapLayerRequest); - msg->nextBlockFast(_PREHASH_AgentData); - msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); - msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); - msg->addU32Fast(_PREHASH_Flags, 2); - msg->addU32Fast(_PREHASH_EstateID, 0); // Filled in on sim - msg->addBOOLFast(_PREHASH_Godlike, FALSE); // Filled in on sim - gAgent.sendReliableMessage(); + siminfo = LLWorldMap::getInstance()->createSimInfoFromHandle(handle); } + siminfo->setName(name); + siminfo->setAccess(accesscode); + siminfo->setRegionFlags(region_flags); + // siminfo->setWaterHeight((F32) water_height); + siminfo->setLandForSaleImage(image_id); + + // Handle the location tracking (for teleport, UI feedback and info display) + if (LLWorldMap::getInstance()->isTrackingInRectangle( x_world, y_world, x_world + REGION_WIDTH_UNITS, y_world + REGION_WIDTH_UNITS)) + { + if (siminfo->isDown()) + { + // We were tracking this location, but it's no available + LLWorldMap::getInstance()->setTrackingInvalid(); + } + else + { + // We were tracking this location, and it does exist and is available + LLWorldMap::getInstance()->setTrackingValid(); + } + } + // return insert region success + return true; } } -// public -void LLWorldMap::sendNamedRegionRequest(std::string region_name) -{ - LLMessageSystem* msg = gMessageSystem; - S32 layer = mCurrentMap; - - // Request for layer - msg->newMessageFast(_PREHASH_MapNameRequest); - msg->nextBlockFast(_PREHASH_AgentData); - msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); - msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); - msg->addU32Fast(_PREHASH_Flags, layer); - msg->addU32Fast(_PREHASH_EstateID, 0); // Filled in on sim - msg->addBOOLFast(_PREHASH_Godlike, FALSE); // Filled in on sim - msg->nextBlockFast(_PREHASH_NameData); - msg->addStringFast(_PREHASH_Name, region_name); - gAgent.sendReliableMessage(); -} -// public -void LLWorldMap::sendNamedRegionRequest(std::string region_name, - url_callback_t callback, - const std::string& callback_url, - bool teleport) // immediately teleport when result returned -{ - mSLURLRegionName = region_name; - mSLURLRegionHandle = 0; - mSLURL = callback_url; - mSLURLCallback = callback; - mSLURLTeleport = teleport; - - sendNamedRegionRequest(region_name); -} - -void LLWorldMap::sendHandleRegionRequest(U64 region_handle, - url_callback_t callback, - const std::string& callback_url, - bool teleport) // immediately teleport when result returned -{ - mSLURLRegionName.clear(); - mSLURLRegionHandle = region_handle; - mSLURL = callback_url; - mSLURLCallback = callback; - mSLURLTeleport = teleport; - - U32 global_x; - U32 global_y; - from_region_handle(region_handle, &global_x, &global_y); - U16 grid_x = (U16)(global_x / REGION_WIDTH_UNITS); - U16 grid_y = (U16)(global_y / REGION_WIDTH_UNITS); - - sendMapBlockRequest(grid_x, grid_y, grid_x, grid_y, true); -} - -// public -void LLWorldMap::sendMapBlockRequest(U16 min_x, U16 min_y, U16 max_x, U16 max_y, bool return_nonexistent) -{ - S32 layer = mCurrentMap; - LLMessageSystem* msg = gMessageSystem; - msg->newMessageFast(_PREHASH_MapBlockRequest); - msg->nextBlockFast(_PREHASH_AgentData); - msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); - msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); - U32 flags = layer; - flags |= (return_nonexistent ? 0x10000 : 0); - msg->addU32Fast(_PREHASH_Flags, flags); - msg->addU32Fast(_PREHASH_EstateID, 0); // Filled in on sim - msg->addBOOLFast(_PREHASH_Godlike, FALSE); // Filled in on sim - msg->nextBlockFast(_PREHASH_PositionData); - msg->addU16Fast(_PREHASH_MinX, min_x); - msg->addU16Fast(_PREHASH_MinY, min_y); - msg->addU16Fast(_PREHASH_MaxX, max_x); - msg->addU16Fast(_PREHASH_MaxY, max_y); - gAgent.sendReliableMessage(); - - if (mRequestLandForSale) - { - msg->newMessageFast(_PREHASH_MapBlockRequest); - msg->nextBlockFast(_PREHASH_AgentData); - msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); - msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); - msg->addU32Fast(_PREHASH_Flags, 2); - msg->addU32Fast(_PREHASH_EstateID, 0); // Filled in on sim - msg->addBOOLFast(_PREHASH_Godlike, FALSE); // Filled in on sim - msg->nextBlockFast(_PREHASH_PositionData); - msg->addU16Fast(_PREHASH_MinX, min_x); - msg->addU16Fast(_PREHASH_MinY, min_y); - msg->addU16Fast(_PREHASH_MaxX, max_x); - msg->addU16Fast(_PREHASH_MaxY, max_y); - gAgent.sendReliableMessage(); - } -} - -// public static -void LLWorldMap::processMapLayerReply(LLMessageSystem* msg, void**) +// static public +// Insert an item in the relevant region map +// returns true if item inserted, false otherwise +bool LLWorldMap::insertItem(U32 x_world, U32 y_world, std::string& name, LLUUID& uuid, U32 type, S32 extra, S32 extra2) { - llinfos << "LLWorldMap::processMapLayerReply from message system" << llendl; - - U32 agent_flags; - msg->getU32Fast(_PREHASH_AgentData, _PREHASH_Flags, agent_flags); - - if (agent_flags != (U32)LLWorldMap::getInstance()->mCurrentMap) - { - llwarns << "Invalid or out of date map image type returned!" << llendl; - return; - } + // Create an item record for the received object + LLItemInfo new_item((F32)x_world, (F32)y_world, name, uuid); - LLUUID image_id; - //U32 left, right, top, bottom; + // Compute a region handle based on the objects coordinates + LLVector3d pos((F32)x_world, (F32)y_world, 40.0); + U64 handle = to_region_handle(pos); - S32 num_blocks = msg->getNumberOfBlocksFast(_PREHASH_LayerData); - - LLWorldMap::getInstance()->mMapLayers[agent_flags].clear(); - - BOOL adjust = FALSE; - for (S32 block=0; block<num_blocks; ++block) + // Get the region record for that handle or NULL if we haven't browsed it yet + LLSimInfo* siminfo = LLWorldMap::getInstance()->simInfoFromHandle(handle); + if (siminfo == NULL) { - LLWorldMapLayer new_layer; - new_layer.LayerDefined = TRUE; - msg->getUUIDFast(_PREHASH_LayerData, _PREHASH_ImageID, new_layer.LayerImageID, block); - new_layer.LayerImage = LLViewerTextureManager::getFetchedTexture(new_layer.LayerImageID, MIPMAP_TRUE, LLViewerTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE); - - gGL.getTexUnit(0)->bind(new_layer.LayerImage); - new_layer.LayerImage->setAddressMode(LLTexUnit::TAM_CLAMP); - - U32 left, right, top, bottom; - msg->getU32Fast(_PREHASH_LayerData, _PREHASH_Left, left, block); - msg->getU32Fast(_PREHASH_LayerData, _PREHASH_Right, right, block); - msg->getU32Fast(_PREHASH_LayerData, _PREHASH_Top, top, block); - msg->getU32Fast(_PREHASH_LayerData, _PREHASH_Bottom, bottom, block); - - new_layer.LayerExtents.mLeft = left; - new_layer.LayerExtents.mRight = right; - new_layer.LayerExtents.mBottom = bottom; - new_layer.LayerExtents.mTop = top; - - F32 x_meters = F32(left*REGION_WIDTH_UNITS); - F32 y_meters = F32(bottom*REGION_WIDTH_UNITS); - adjust = LLWorldMap::getInstance()->extendAABB(U32(x_meters), U32(y_meters), - U32(x_meters+REGION_WIDTH_UNITS*new_layer.LayerExtents.getWidth()), - U32(y_meters+REGION_WIDTH_UNITS*new_layer.LayerExtents.getHeight())) || adjust; - - LLWorldMap::getInstance()->mMapLayers[agent_flags].push_back(new_layer); + siminfo = LLWorldMap::getInstance()->createSimInfoFromHandle(handle); } - LLWorldMap::getInstance()->mMapLoaded[agent_flags] = TRUE; - if(adjust) gFloaterWorldMap->adjustZoomSliderBounds(); -} - -// public static -void LLWorldMap::processMapBlockReply(LLMessageSystem* msg, void**) -{ - U32 agent_flags; - msg->getU32Fast(_PREHASH_AgentData, _PREHASH_Flags, agent_flags); - - if ((S32)agent_flags < 0 || agent_flags >= MAP_SIM_IMAGE_TYPES) + //LL_INFOS("World Map") << "Process item : type = " << type << LL_ENDL; + switch (type) { - llwarns << "Invalid map image type returned! " << agent_flags << llendl; - return; - } - - S32 num_blocks = msg->getNumberOfBlocksFast(_PREHASH_Data); - - bool found_null_sim = false; - - BOOL adjust = FALSE; - for (S32 block=0; block<num_blocks; ++block) - { - U16 x_regions; - U16 y_regions; - std::string name; - U8 accesscode; - U32 region_flags; - U8 water_height; - U8 agents; - LLUUID image_id; - msg->getU16Fast(_PREHASH_Data, _PREHASH_X, x_regions, block); - msg->getU16Fast(_PREHASH_Data, _PREHASH_Y, y_regions, block); - msg->getStringFast(_PREHASH_Data, _PREHASH_Name, name, block); - msg->getU8Fast(_PREHASH_Data, _PREHASH_Access, accesscode, block); - msg->getU32Fast(_PREHASH_Data, _PREHASH_RegionFlags, region_flags, block); - msg->getU8Fast(_PREHASH_Data, _PREHASH_WaterHeight, water_height, block); - msg->getU8Fast(_PREHASH_Data, _PREHASH_Agents, agents, block); - msg->getUUIDFast(_PREHASH_Data, _PREHASH_MapImageID, image_id, block); - - U32 x_meters = x_regions * REGION_WIDTH_UNITS; - U32 y_meters = y_regions * REGION_WIDTH_UNITS; - - U64 handle = to_region_handle(x_meters, y_meters); - - if (accesscode == 255) - { - // This region doesn't exist - if (LLWorldMap::getInstance()->mIsTrackingUnknownLocation && - LLWorldMap::getInstance()->mUnknownLocation.mdV[0] >= x_meters && - LLWorldMap::getInstance()->mUnknownLocation.mdV[0] < x_meters + 256 && - LLWorldMap::getInstance()->mUnknownLocation.mdV[1] >= y_meters && - LLWorldMap::getInstance()->mUnknownLocation.mdV[1] < y_meters + 256) - { - // We were tracking this location, but it doesn't exist - LLWorldMap::getInstance()->mInvalidLocation = TRUE; - } - - found_null_sim = true; - } - else + case MAP_ITEM_TELEHUB: // telehubs { - adjust = LLWorldMap::getInstance()->extendAABB(x_meters, - y_meters, - x_meters+REGION_WIDTH_UNITS, - y_meters+REGION_WIDTH_UNITS) || adjust; - -// llinfos << "Map sim " << name << " image layer " << agent_flags << " ID " << image_id.getString() << llendl; - - LLSimInfo* siminfo = new LLSimInfo(); - sim_info_map_t::iterator iter = LLWorldMap::getInstance()->mSimInfoMap.find(handle); - if (iter != LLWorldMap::getInstance()->mSimInfoMap.end()) - { - LLSimInfo* oldinfo = iter->second; - for (S32 image=0; image<MAP_SIM_IMAGE_TYPES; ++image) - { - siminfo->mMapImageID[image] = oldinfo->mMapImageID[image]; - } - delete oldinfo; - } - LLWorldMap::getInstance()->mSimInfoMap[handle] = siminfo; - - siminfo->mHandle = handle; - siminfo->mName.assign( name ); - siminfo->mAccess = accesscode; - siminfo->mRegionFlags = region_flags; - siminfo->mWaterHeight = (F32) water_height; - siminfo->mMapImageID[agent_flags] = image_id; - -#ifdef IMMEDIATE_IMAGE_LOAD - siminfo->mCurrentImage = LLViewerTextureManager::getFetchedTexture(siminfo->mMapImageID[LLWorldMap::getInstance()->mCurrentMap], - MIPMAP_TRUE, LLViewerTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE); - siminfo->mCurrentImage->setAddressMode(LLTexUnit::TAM_CLAMP); -#endif + /* Merov: we are not using the hub color anymore for display so commenting that out + // Telehub color + U32 X = x_world / REGION_WIDTH_UNITS; + U32 Y = y_world / REGION_WIDTH_UNITS; + F32 red = fmod((F32)X * 0.11f, 1.f) * 0.8f; + F32 green = fmod((F32)Y * 0.11f, 1.f) * 0.8f; + F32 blue = fmod(1.5f * (F32)(X + Y) * 0.11f, 1.f) * 0.8f; + F32 add_amt = (X % 2) ? 0.15f : -0.15f; + add_amt += (Y % 2) ? -0.15f : 0.15f; + LLColor4 color(red + add_amt, green + add_amt, blue + add_amt); + new_item.setColor(color); + */ - if (siminfo->mMapImageID[2].notNull()) + // extra2 specifies whether this is an infohub or a telehub. + if (extra2) { -#ifdef IMMEDIATE_IMAGE_LOAD - siminfo->mOverlayImage = LLViewerTextureManager::getFetchedTexture(siminfo->mMapImageID[2], MIPMAP_TRUE, LLViewerTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE); -#endif + siminfo->insertInfoHub(new_item); } else { - siminfo->mOverlayImage = NULL; - } - - if (LLWorldMap::getInstance()->mIsTrackingUnknownLocation && - LLWorldMap::getInstance()->mUnknownLocation.mdV[0] >= x_meters && - LLWorldMap::getInstance()->mUnknownLocation.mdV[0] < x_meters + 256 && - LLWorldMap::getInstance()->mUnknownLocation.mdV[1] >= y_meters && - LLWorldMap::getInstance()->mUnknownLocation.mdV[1] < y_meters + 256) - { - if (siminfo->mAccess == SIM_ACCESS_DOWN) - { - // We were tracking this location, but it doesn't exist - LLWorldMap::getInstance()->mInvalidLocation = true; - } - else - { - // We were tracking this location, and it does exist - bool is_tracking_dbl = LLWorldMap::getInstance()->mIsTrackingDoubleClick == TRUE; - gFloaterWorldMap->trackLocation(LLWorldMap::getInstance()->mUnknownLocation); - if (is_tracking_dbl) - { - LLVector3d pos_global = LLTracker::getTrackedPositionGlobal(); - gAgent.teleportViaLocation( pos_global ); - } - } + siminfo->insertTeleHub(new_item); } + break; } - - if(LLWorldMap::getInstance()->mSLURLCallback != NULL) + case MAP_ITEM_PG_EVENT: // events + case MAP_ITEM_MATURE_EVENT: + case MAP_ITEM_ADULT_EVENT: { - // Server returns definitive capitalization, SLURL might not have that. - if ((LLStringUtil::compareInsensitive(LLWorldMap::getInstance()->mSLURLRegionName, name)==0) - || (LLWorldMap::getInstance()->mSLURLRegionHandle == handle)) + std::string timeStr = "["+ LLTrans::getString ("TimeHour")+"]:[" + +LLTrans::getString ("TimeMin")+"] [" + +LLTrans::getString ("TimeAMPM")+"]"; + LLSD substitution; + substitution["datetime"] = (S32) extra; + LLStringUtil::format (timeStr, substitution); + new_item.setTooltip(timeStr); + + // HACK: store Z in extra2 + new_item.setElevation((F64)extra2); + if (type == MAP_ITEM_PG_EVENT) { - url_callback_t callback = LLWorldMap::getInstance()->mSLURLCallback; - - LLWorldMap::getInstance()->mSLURLCallback = NULL; - LLWorldMap::getInstance()->mSLURLRegionName.clear(); - LLWorldMap::getInstance()->mSLURLRegionHandle = 0; - - callback(handle, LLWorldMap::getInstance()->mSLURL, image_id, LLWorldMap::getInstance()->mSLURLTeleport); + siminfo->insertPGEvent(new_item); } - } - } - - if(adjust) gFloaterWorldMap->adjustZoomSliderBounds(); - gFloaterWorldMap->updateSims(found_null_sim); -} - -// public static -void LLWorldMap::processMapItemReply(LLMessageSystem* msg, void**) -{ - U32 type; - msg->getU32Fast(_PREHASH_RequestData, _PREHASH_ItemType, type); - - S32 num_blocks = msg->getNumberOfBlocks("Data"); - - for (S32 block=0; block<num_blocks; ++block) - { - U32 X, Y; - std::string name; - S32 extra, extra2; - LLUUID uuid; - msg->getU32Fast(_PREHASH_Data, _PREHASH_X, X, block); - msg->getU32Fast(_PREHASH_Data, _PREHASH_Y, Y, block); - msg->getStringFast(_PREHASH_Data, _PREHASH_Name, name, block); - msg->getUUIDFast(_PREHASH_Data, _PREHASH_ID, uuid, block); - msg->getS32Fast(_PREHASH_Data, _PREHASH_Extra, extra, block); - msg->getS32Fast(_PREHASH_Data, _PREHASH_Extra2, extra2, block); - - F32 world_x = (F32)X; - X /= REGION_WIDTH_UNITS; - F32 world_y = (F32)Y; - Y /= REGION_WIDTH_UNITS; - - LLItemInfo new_item(world_x, world_y, name, uuid, extra, extra2); - LLSimInfo* siminfo = LLWorldMap::getInstance()->simInfoFromHandle(new_item.mRegionHandle); - - switch (type) - { - case MAP_ITEM_TELEHUB: // telehubs + else if (type == MAP_ITEM_MATURE_EVENT) { - // Telehub color, store in extra as 4 U8's - U8 *color = (U8 *)&new_item.mExtra; - - F32 red = fmod((F32)X * 0.11f, 1.f) * 0.8f; - F32 green = fmod((F32)Y * 0.11f, 1.f) * 0.8f; - F32 blue = fmod(1.5f * (F32)(X + Y) * 0.11f, 1.f) * 0.8f; - F32 add_amt = (X % 2) ? 0.15f : -0.15f; - add_amt += (Y % 2) ? -0.15f : 0.15f; - color[0] = U8((red + add_amt) * 255); - color[1] = U8((green + add_amt) * 255); - color[2] = U8((blue + add_amt) * 255); - color[3] = 255; - - // extra2 specifies whether this is an infohub or a telehub. - if (extra2) - { - LLWorldMap::getInstance()->mInfohubs.push_back(new_item); - } - else - { - LLWorldMap::getInstance()->mTelehubs.push_back(new_item); - } - - break; + siminfo->insertMatureEvent(new_item); } - case MAP_ITEM_PG_EVENT: // events - case MAP_ITEM_MATURE_EVENT: - case MAP_ITEM_ADULT_EVENT: + else if (type == MAP_ITEM_ADULT_EVENT) { - std::string timeStr = "["+ LLTrans::getString ("TimeHour")+"]:[" - +LLTrans::getString ("TimeMin")+"] [" - +LLTrans::getString ("TimeAMPM")+"]"; - LLSD substitution; - substitution["datetime"] = (S32) extra; - LLStringUtil::format (timeStr, substitution); - new_item.mToolTip = timeStr; - - // HACK: store Z in extra2 - new_item.mPosGlobal.mdV[VZ] = (F64)extra2; - if (type == MAP_ITEM_PG_EVENT) - { - LLWorldMap::getInstance()->mPGEvents.push_back(new_item); - } - else if (type == MAP_ITEM_MATURE_EVENT) - { - LLWorldMap::getInstance()->mMatureEvents.push_back(new_item); - } - else if (type == MAP_ITEM_ADULT_EVENT) - { - LLWorldMap::getInstance()->mAdultEvents.push_back(new_item); - } - - break; + siminfo->insertAdultEvent(new_item); } - case MAP_ITEM_LAND_FOR_SALE: // land for sale - case MAP_ITEM_LAND_FOR_SALE_ADULT: // adult land for sale + break; + } + case MAP_ITEM_LAND_FOR_SALE: // land for sale + case MAP_ITEM_LAND_FOR_SALE_ADULT: // adult land for sale + { + std::string tooltip = llformat("%d sq. m. L$%d", extra, extra2); + new_item.setTooltip(tooltip); + if (type == MAP_ITEM_LAND_FOR_SALE) { - new_item.mToolTip = llformat("%d sq. m. L$%d", new_item.mExtra, new_item.mExtra2); - if (type == MAP_ITEM_LAND_FOR_SALE) - { - LLWorldMap::getInstance()->mLandForSale.push_back(new_item); - } - else if (type == MAP_ITEM_LAND_FOR_SALE_ADULT) - { - LLWorldMap::getInstance()->mLandForSaleAdult.push_back(new_item); - } - break; + siminfo->insertLandForSale(new_item); } - case MAP_ITEM_CLASSIFIED: // classifieds + else if (type == MAP_ITEM_LAND_FOR_SALE_ADULT) { - //DEPRECATED: no longer used - break; + siminfo->insertLandForSaleAdult(new_item); } - case MAP_ITEM_AGENT_LOCATIONS: // agent locations + break; + } + case MAP_ITEM_CLASSIFIED: // classifieds + { + //DEPRECATED: no longer used + break; + } + case MAP_ITEM_AGENT_LOCATIONS: // agent locations + { +// LL_INFOS("World Map") << "New Location " << new_item.mName << LL_ENDL; + if (extra > 0) { - if (!siminfo) - { - llinfos << "siminfo missing for " << new_item.mPosGlobal.mdV[0] << ", " << new_item.mPosGlobal.mdV[1] << llendl; - break; - } -// llinfos << "New Location " << new_item.mName << llendl; - - item_info_list_t& agentcounts = LLWorldMap::getInstance()->mAgentLocationsMap[new_item.mRegionHandle]; - - // Find the last item in the list with a different name and erase them - item_info_list_t::iterator lastiter; - for (lastiter = agentcounts.begin(); lastiter!=agentcounts.end(); ++lastiter) - { - const LLItemInfo& info = *lastiter; - if (info.mName == new_item.mName) - { - break; - } - } - if (lastiter != agentcounts.begin()) - { - agentcounts.erase(agentcounts.begin(), lastiter); - } - // Now append the new location - if (new_item.mExtra > 0) - { - agentcounts.push_back(new_item); - } - break; + new_item.setCount(extra); + siminfo->insertAgentLocation(new_item); } - default: - break; - }; - } -} - -void LLWorldMap::dump() -{ - for (sim_info_map_t::iterator it = mSimInfoMap.begin(); it != mSimInfoMap.end(); ++it) - { - U64 handle = (*it).first; - LLSimInfo* info = (*it).second; - - U32 x_pos, y_pos; - from_region_handle(handle, &x_pos, &y_pos); - - llinfos << x_pos << "," << y_pos - << " " << info->mName - << " " << (S32)info->mAccess - << " " << std::hex << info->mRegionFlags << std::dec - << " " << info->mWaterHeight - //<< " " << info->mTelehubName - //<< " " << info->mTelehubPosition - << llendl; - - if (info->mCurrentImage) - { - llinfos << "image discard " << (S32)info->mCurrentImage->getDiscardLevel() - << " fullwidth " << info->mCurrentImage->getFullWidth() - << " fullheight " << info->mCurrentImage->getFullHeight() - << " maxvirt " << info->mCurrentImage->getMaxVirtualSize() - //<< " maxdisc " << (S32)info->mCurrentImage->getMaxDiscardLevel() - << llendl; + break; } + default: + break; } + return true; } - -BOOL LLWorldMap::extendAABB(U32 min_x, U32 min_y, U32 max_x, U32 max_y) +bool LLWorldMap::isTrackingInRectangle(F64 x0, F64 y0, F64 x1, F64 y1) { - BOOL rv = FALSE; - if (min_x < mMinX) - { - rv = TRUE; - mMinX = min_x; - } - if (min_y < mMinY) - { - rv = TRUE; - mMinY = min_y; - } - if (max_x > mMaxX) - { - rv = TRUE; - mMaxX = max_x; - } - if (max_y > mMaxY) - { - rv = TRUE; - mMaxY = max_y; - } - lldebugs << "World map aabb: (" << mMinX << ", " << mMinY << "), (" - << mMaxX << ", " << mMaxY << ")" << llendl; - return rv; + if (!mIsTrackingLocation) + return false; + return ((mTrackingLocation[0] >= x0) && (mTrackingLocation[0] < x1) && (mTrackingLocation[1] >= y0) && (mTrackingLocation[1] < y1)); } - -U32 LLWorldMap::getWorldWidth() const +// Drop priority of all images being fetched by the map +void LLWorldMap::dropImagePriorities() { - return mMaxX - mMinX; -} - - -U32 LLWorldMap::getWorldHeight() const -{ - return mMaxY - mMinY; -} - -BOOL LLWorldMap::coveredByTelehub(LLSimInfo* infop) -{ - /*if (!mTelehubCoverageMap) + // Drop the download of tiles priority to nil + mWorldMipmap.dropBoostLevels(); + // Same for the "land for sale" tiles per region + for (sim_info_map_t::iterator it = mSimInfoMap.begin(); it != mSimInfoMap.end(); ++it) { - return FALSE; + LLSimInfo* info = it->second; + info->dropImagePriority(); } - U32 x_pos, y_pos; - from_region_handle(infop->mHandle, &x_pos, &y_pos); - x_pos /= REGION_WIDTH_UNITS; - y_pos /= REGION_WIDTH_UNITS; - - S32 index = x_pos - (mMinX / REGION_WIDTH_UNITS - 1) + (mNeighborMapWidth * (y_pos - (mMinY / REGION_WIDTH_UNITS - 1))); - return mTelehubCoverageMap[index] != 0; */ - return FALSE; } -void LLWorldMap::updateTelehubCoverage() +// Load all regions in a given rectangle (in region grid coordinates, i.e. world / 256 meters) +void LLWorldMap::updateRegions(S32 x0, S32 y0, S32 x1, S32 y1) { - /*S32 neighbor_width = getWorldWidth() / REGION_WIDTH_UNITS + 2; - S32 neighbor_height = getWorldHeight() / REGION_WIDTH_UNITS + 2; - if (neighbor_width > mNeighborMapWidth || neighbor_height > mNeighborMapHeight) - { - mNeighborMapWidth = neighbor_width; - mNeighborMapHeight = neighbor_height; - delete mNeighborMap; - delete mTelehubCoverageMap; - - mNeighborMap = new U8[mNeighborMapWidth * mNeighborMapHeight]; - mTelehubCoverageMap = new U8[mNeighborMapWidth * mNeighborMapHeight]; - } - - memset(mNeighborMap, 0, mNeighborMapWidth * mNeighborMapHeight * sizeof(U8)); - memset(mTelehubCoverageMap, 0, mNeighborMapWidth * mNeighborMapHeight * sizeof(U8)); + // Convert those boundaries to the corresponding (MAP_BLOCK_SIZE x MAP_BLOCK_SIZE) block coordinates + x0 = x0 / MAP_BLOCK_SIZE; + x1 = x1 / MAP_BLOCK_SIZE; + y0 = y0 / MAP_BLOCK_SIZE; + y1 = y1 / MAP_BLOCK_SIZE; - // leave 1 sim border - S32 min_x = (mMinX / REGION_WIDTH_UNITS) - 1; - S32 min_y = (mMinY / REGION_WIDTH_UNITS) - 1; - - std::map<U64, LLSimInfo*>::const_iterator it; - for (it = mSimInfoMap.begin(); it != mSimInfoMap.end(); ++it) + // Load the region info those blocks + for (S32 block_x = llmax(x0, 0); block_x <= llmin(x1, MAP_BLOCK_RES-1); ++block_x) { - U64 handle = (*it).first; - //LLSimInfo* info = (*it).second; - - U32 x_pos, y_pos; - from_region_handle(handle, &x_pos, &y_pos); - x_pos /= REGION_WIDTH_UNITS; - y_pos /= REGION_WIDTH_UNITS; - x_pos -= min_x; - y_pos -= min_y; - - S32 index = x_pos + (mNeighborMapWidth * y_pos); - mNeighborMap[index - 1]++; - mNeighborMap[index + 1]++; - mNeighborMap[index - mNeighborMapWidth]++; - mNeighborMap[index + mNeighborMapWidth]++; - } - - for (it = mSimInfoMap.begin(); it != mSimInfoMap.end(); ++it) - { - U64 handle = (*it).first; - LLSimInfo* info = (*it).second; - - U32 x_pos, y_pos; - from_region_handle(handle, &x_pos, &y_pos); - x_pos /= REGION_WIDTH_UNITS; - y_pos /= REGION_WIDTH_UNITS; - x_pos -= min_x; - y_pos -= min_y; - - S32 index = x_pos + (mNeighborMapWidth * y_pos); - - if (!info->mTelehubName.empty() && mNeighborMap[index]) + for (S32 block_y = llmax(y0, 0); block_y <= llmin(y1, MAP_BLOCK_RES-1); ++block_y) { - S32 x_start = llmax(0, S32(x_pos - 5)); - S32 x_span = llmin(mNeighborMapWidth - 1, (S32)(x_pos + 5)) - x_start + 1; - S32 y_start = llmax(0, (S32)y_pos - 5); - S32 y_end = llmin(mNeighborMapHeight - 1, (S32)(y_pos + 5)); - for (S32 y_index = y_start; y_index <= y_end; y_index++) + S32 offset = block_x | (block_y * MAP_BLOCK_RES); + if (!mMapBlockLoaded[offset]) { - memset(&mTelehubCoverageMap[x_start + y_index * mNeighborMapWidth], 0xff, sizeof(U8) * x_span); + //LL_INFOS("World Map") << "Loading Block (" << block_x << "," << block_y << ")" << LL_ENDL; + LLWorldMapMessage::getInstance()->sendMapBlockRequest(block_x * MAP_BLOCK_SIZE, block_y * MAP_BLOCK_SIZE, (block_x * MAP_BLOCK_SIZE) + MAP_BLOCK_SIZE - 1, (block_y * MAP_BLOCK_SIZE) + MAP_BLOCK_SIZE - 1); + mMapBlockLoaded[offset] = true; } } } +} - for (it = mSimInfoMap.begin(); it != mSimInfoMap.end(); ++it) +void LLWorldMap::dump() +{ + LL_INFOS("World Map") << "LLWorldMap::dump()" << LL_ENDL; + for (sim_info_map_t::iterator it = mSimInfoMap.begin(); it != mSimInfoMap.end(); ++it) { - U64 handle = (*it).first; - //LLSimInfo* info = (*it).second; - - U32 x_pos, y_pos; - from_region_handle(handle, &x_pos, &y_pos); - x_pos /= REGION_WIDTH_UNITS; - y_pos /= REGION_WIDTH_UNITS; - - S32 index = x_pos - min_x + (mNeighborMapWidth * (y_pos - min_y)); - mTelehubCoverageMap[index] *= mNeighborMap[index]; - }*/ + LLSimInfo* info = it->second; + if (info) + { + info->dump(); + } + } } + diff --git a/indra/newview/llworldmap.h b/indra/newview/llworldmap.h index 366de8f071..7e37727b86 100644 --- a/indra/newview/llworldmap.h +++ b/indra/newview/llworldmap.h @@ -33,203 +33,243 @@ #ifndef LL_LLWORLDMAP_H #define LL_LLWORLDMAP_H -#include <map> -#include <string> -#include <vector> +#include "llworldmipmap.h" #include <boost/function.hpp> -#include "v3math.h" #include "v3dmath.h" -#include "llframetimer.h" -#include "llmapimagetype.h" #include "lluuid.h" #include "llpointer.h" #include "llsingleton.h" +#include "llviewerregion.h" #include "llviewertexture.h" -#include "lleventinfo.h" -#include "v3color.h" - -class LLMessageSystem; - +// Description of objects like hubs, events, land for sale, people and more (TBD). +// Note: we don't store a "type" in there so we need to store instances of this class in +// well known objects (i.e. list of objects which type is "well known"). class LLItemInfo { public: - LLItemInfo(F32 global_x, F32 global_y, const std::string& name, LLUUID id, S32 extra = 0, S32 extra2 = 0); - - std::string mName; - std::string mToolTip; - LLVector3d mPosGlobal; - LLUUID mID; - BOOL mSelected; - S32 mExtra; - S32 mExtra2; - U64 mRegionHandle; -}; + LLItemInfo(F32 global_x, F32 global_y, const std::string& name, LLUUID id); + + // Setters + void setTooltip(std::string& tooltip) { mToolTip = tooltip; } + void setElevation(F64 z) { mPosGlobal.mdV[VZ] = z; } + void setCount(S32 count) { mCount = count; } +// void setSelected(bool selected) { mSelected = selected; } +// void setColor(LLColor4 color) { mColor = color; } + + // Accessors + const LLVector3d& getGlobalPosition() const { return mPosGlobal; } + const std::string& getName() const { return mName; } + const std::string& getToolTip() const { return mToolTip; } + const LLUUID& getUUID() const { return mID; } + S32 getCount() const { return mCount; } + + U64 getRegionHandle() const { return to_region_handle(mPosGlobal); } // Build the handle on the fly -// Map layers, see indra_constants.h -// 0 - Prim -// 1 - Terrain Only -// 2 - Overlay: Land For Sale + bool isName(const std::string& name) const { return (mName == name); } // True if name same as item's name +// bool isSelected() const { return mSelected; } +private: + std::string mName; // Name of the individual item + std::string mToolTip; // Tooltip : typically, something to be displayed to the user when selecting this item + LLVector3d mPosGlobal; // Global world position + LLUUID mID; // UUID of the item + S32 mCount; // Number of elements in item (e.g. people count) + // Currently not used but might prove useful one day so we comment out +// bool mSelected; // Selected or not: updated by the viewer UI, not the simulator or asset DB +// LLColor4 mColor; // Color of the item +}; + +// Info per region +// Such records are stored in a global map hold by the LLWorldMap and indexed by region handles. +// To avoid creating too many of them, they are requested in "blocks" corresponding to areas covered by the screen. +// Unfortunately, when the screen covers the whole world (zoomed out), that can translate in requesting info for +// every sim on the grid... Not good... +// To avoid this, the code implements a cut-off threshold for overlay graphics and, therefore, all LLSimInfo. +// In other words, when zooming out too much, we simply stop requesting LLSimInfo and +// LLItemInfo and just display the map tiles. +// As they are stored in different structures (LLSimInfo and LLWorldMipmap), this strategy is now workable. class LLSimInfo { public: - LLSimInfo(); + LLSimInfo(U64 handle); - LLVector3d getGlobalPos(LLVector3 local_pos) const; + // Convert local region coordinates into world coordinates + LLVector3d getGlobalPos(const LLVector3& local_pos) const; + // Get the world coordinates of the SW corner of that region + LLVector3d getGlobalOrigin() const; LLVector3 getLocalPos(LLVector3d global_pos) const; -public: - U64 mHandle; - std::string mName; - - F64 mAgentsUpdateTime; - BOOL mShowAgentLocations; // are agents visible? + void clearImage(); // Clears the reference to the Land for sale image for that region + void dropImagePriority(); // Drops the boost level of the Land for sale image for that region + void updateAgentCount(F64 time); // Send an item request for agent count on that region if time's up - U8 mAccess; - U32 mRegionFlags; - F32 mWaterHeight; + // Setters + void setName(std::string& name) { mName = name; } + void setAccess (U32 accesscode) { mAccess = accesscode; } + void setRegionFlags (U32 region_flags) { mRegionFlags = region_flags; } + void setLandForSaleImage (LLUUID image_id); +// void setWaterHeight (F32 water_height) { mWaterHeight = water_height; } - F32 mAlpha; + // Accessors + std::string getName() const { return mName; } + const std::string getFlagsString() const { return LLViewerRegion::regionFlagsToString(mRegionFlags); } + const std::string getAccessString() const { return LLViewerRegion::accessToString((U8)mAccess); } - // Image ID for the current overlay mode. - LLUUID mMapImageID[MAP_SIM_IMAGE_TYPES]; + const S32 getAgentCount() const; // Compute the total agents count + LLPointer<LLViewerFetchedTexture> getLandForSaleImage(); // Get the overlay image, fetch it if necessary - // Hold a reference to the currently displayed image. - LLPointer<LLViewerFetchedTexture> mCurrentImage; - LLPointer<LLViewerFetchedTexture> mOverlayImage; -}; + bool isName(const std::string& name) const; + bool isDown() { return (mAccess == SIM_ACCESS_DOWN); } + bool isPG() { return (mAccess <= SIM_ACCESS_PG); } -#define MAP_BLOCK_RES 256 + // Debug only + void dump() const; // Print the region info to the standard output -struct LLWorldMapLayer -{ - BOOL LayerDefined; - LLPointer<LLViewerFetchedTexture> LayerImage; - LLUUID LayerImageID; - LLRect LayerExtents; + // Items lists handling + typedef std::vector<LLItemInfo> item_info_list_t; + void clearItems(); + + void insertTeleHub(const LLItemInfo& item) { mTelehubs.push_back(item); } + void insertInfoHub(const LLItemInfo& item) { mInfohubs.push_back(item); } + void insertPGEvent(const LLItemInfo& item) { mPGEvents.push_back(item); } + void insertMatureEvent(const LLItemInfo& item) { mMatureEvents.push_back(item); } + void insertAdultEvent(const LLItemInfo& item) { mAdultEvents.push_back(item); } + void insertLandForSale(const LLItemInfo& item) { mLandForSale.push_back(item); } + void insertLandForSaleAdult(const LLItemInfo& item) { mLandForSaleAdult.push_back(item); } + void insertAgentLocation(const LLItemInfo& item); + + const LLSimInfo::item_info_list_t& getTeleHub() const { return mTelehubs; } + const LLSimInfo::item_info_list_t& getInfoHub() const { return mInfohubs; } + const LLSimInfo::item_info_list_t& getPGEvent() const { return mPGEvents; } + const LLSimInfo::item_info_list_t& getMatureEvent() const { return mMatureEvents; } + const LLSimInfo::item_info_list_t& getAdultEvent() const { return mAdultEvents; } + const LLSimInfo::item_info_list_t& getLandForSale() const { return mLandForSale; } + const LLSimInfo::item_info_list_t& getLandForSaleAdult() const { return mLandForSaleAdult; } + const LLSimInfo::item_info_list_t& getAgentLocation() const { return mAgentLocations; } - LLWorldMapLayer() : LayerDefined(FALSE) { } +private: + U64 mHandle; // This is a hash of the X and Y world coordinates of the SW corner of the sim + std::string mName; // Region name + + F64 mAgentsUpdateTime; // Time stamp giving the last time the agents information was requested for that region + bool mFirstAgentRequest; // Init agent request flag + + U32 mAccess; // Down/up and maturity rating of the region + U32 mRegionFlags; // Tell us if the siminfo has been received (if non 0) and what kind of region it is (Sandbox, allow damage) + // Currently not used but might prove useful one day so we comment out +// F32 mWaterHeight; // Water height on the region (not actively used) + + // Handling the "land for sale / land for auction" overlay image + LLUUID mMapImageID; // Image ID of the overlay image + LLPointer<LLViewerFetchedTexture> mOverlayImage; // Reference to the overlay image + + // Items for this region + // Those are data received through item requests (as opposed to block requests for the rest of the data) + item_info_list_t mTelehubs; // List of tele hubs in the region + item_info_list_t mInfohubs; // List of info hubs in the region + item_info_list_t mPGEvents; // List of PG events in the region + item_info_list_t mMatureEvents; // List of Mature events in the region + item_info_list_t mAdultEvents; // List of Adult events in the region (AO) + item_info_list_t mLandForSale; // List of Land for sales in the region + item_info_list_t mLandForSaleAdult; // List of Adult Land for sales in the region (AO) + item_info_list_t mAgentLocations; // List of agents in the region }; +// We request region data on the world by "blocks" of (MAP_BLOCK_SIZE x MAP_BLOCK_SIZE) regions +// This is to reduce the number of requests to the asset DB and get things in big "blocks" +const S32 MAP_MAX_SIZE = 2048; +const S32 MAP_BLOCK_SIZE = 4; +const S32 MAP_BLOCK_RES = (MAP_MAX_SIZE / MAP_BLOCK_SIZE); class LLWorldMap : public LLSingleton<LLWorldMap> { public: - typedef boost::function<void(U64 region_handle, const std::string& url, const LLUUID& snapshot_id, bool teleport)> - url_callback_t; - LLWorldMap(); ~LLWorldMap(); - // clears the list + // Clear all: list of region info, tiles, blocks and items void reset(); - // clear the visible items - void eraseItems(); + void clearImageRefs(); // Clears the image references + void dropImagePriorities(); // Drops the priority of the images being fetched + void reloadItems(bool force = false); // Reload the items (people, hub, etc...) - // Removes references to cached images - void clearImageRefs(); + // Region Map access + typedef std::map<U64, LLSimInfo*> sim_info_map_t; + const LLWorldMap::sim_info_map_t& getRegionMap() const { return mSimInfoMap; } + void updateRegions(S32 x0, S32 y0, S32 x1, S32 y1); // Requests region info for a rectangle of regions (in grid coordinates) - // Clears the flags indicating that we've received sim infos - // Causes a re-request of the sim info without erasing extisting info - void clearSimFlags(); + // Insert a region and items in the map global instance + // Note: x_world and y_world in world coordinates (meters) + static bool insertRegion(U32 x_world, U32 y_world, std::string& name, LLUUID& uuid, U32 accesscode, U32 region_flags); + static bool insertItem(U32 x_world, U32 y_world, std::string& name, LLUUID& uuid, U32 type, S32 extra, S32 extra2); - // Returns simulator information, or NULL if out of range + // Get info on sims (region) : note that those methods only search the range of loaded sims (the one that are being browsed) + // *not* the entire world. So a NULL return does not mean a down or unexisting region, just an out of range region. LLSimInfo* simInfoFromHandle(const U64 handle); - - // Returns simulator information, or NULL if out of range LLSimInfo* simInfoFromPosGlobal(const LLVector3d& pos_global); - - // Returns simulator information for named sim, or NULL if non-existent LLSimInfo* simInfoFromName(const std::string& sim_name); - // Gets simulator name for a global position, returns true if it was found + // Gets simulator name from a global position, returns true if found bool simNameFromPosGlobal(const LLVector3d& pos_global, std::string& outSimName ); - // Sets the current layer - void setCurrentLayer(S32 layer, bool request_layer = false); - - void sendMapLayerRequest(); - void sendMapBlockRequest(U16 min_x, U16 min_y, U16 max_x, U16 max_y, bool return_nonexistent = false); - void sendNamedRegionRequest(std::string region_name); - void sendNamedRegionRequest(std::string region_name, - url_callback_t callback, - const std::string& callback_url, - bool teleport); - void sendHandleRegionRequest(U64 region_handle, - url_callback_t callback, - const std::string& callback_url, - bool teleport); - void sendItemRequest(U32 type, U64 handle = 0); - - static void processMapLayerReply(LLMessageSystem*, void**); - static void processMapBlockReply(LLMessageSystem*, void**); - static void processMapItemReply(LLMessageSystem*, void**); - - void dump(); - - // Extend the bounding box of the list of simulators. Returns true - // if the extents changed. - BOOL extendAABB(U32 x_min, U32 y_min, U32 x_max, U32 y_max); - - // build coverage maps for telehub region visualization - void updateTelehubCoverage(); - BOOL coveredByTelehub(LLSimInfo* infop); - - // Bounds of the world, in meters - U32 getWorldWidth() const; - U32 getWorldHeight() const; -public: - // Map from region-handle to simulator info - typedef std::map<U64, LLSimInfo*> sim_info_map_t; - sim_info_map_t mSimInfoMap; + // Debug only + void dump(); // Print the world info to the standard output - BOOL mIsTrackingUnknownLocation, mInvalidLocation, mIsTrackingDoubleClick, mIsTrackingCommit; - LLVector3d mUnknownLocation; + // Track handling + void cancelTracking() { mIsTrackingLocation = false; mIsTrackingFound = false; mIsInvalidLocation = false; mIsTrackingDoubleClick = false; mIsTrackingCommit = false; } - bool mRequestLandForSale; + void setTracking(const LLVector3d& loc) { mIsTrackingLocation = true; mTrackingLocation = loc; mIsTrackingFound = false; mIsInvalidLocation = false; mIsTrackingDoubleClick = false; mIsTrackingCommit = false;} + void setTrackingInvalid() { mIsTrackingFound = true; mIsInvalidLocation = true; } + void setTrackingValid() { mIsTrackingFound = true; mIsInvalidLocation = false; } + void setTrackingDoubleClick() { mIsTrackingDoubleClick = true; } + void setTrackingCommit() { mIsTrackingCommit = true; } - typedef std::vector<LLItemInfo> item_info_list_t; - item_info_list_t mTelehubs; - item_info_list_t mInfohubs; - item_info_list_t mPGEvents; - item_info_list_t mMatureEvents; - item_info_list_t mAdultEvents; - item_info_list_t mLandForSale; - item_info_list_t mLandForSaleAdult; - - std::map<U64,S32> mNumAgents; - - typedef std::map<U64, item_info_list_t> agent_list_map_t; - agent_list_map_t mAgentLocationsMap; - - std::vector<LLWorldMapLayer> mMapLayers[MAP_SIM_IMAGE_TYPES]; - BOOL mMapLoaded[MAP_SIM_IMAGE_TYPES]; - BOOL * mMapBlockLoaded[MAP_SIM_IMAGE_TYPES]; - S32 mCurrentMap; - - // AABB of the list of simulators - U32 mMinX; - U32 mMaxX; - U32 mMinY; - U32 mMaxY; - - U8* mNeighborMap; - U8* mTelehubCoverageMap; - S32 mNeighborMapWidth; - S32 mNeighborMapHeight; + bool isTracking() { return mIsTrackingLocation; } + bool isTrackingValidLocation() { return mIsTrackingFound && !mIsInvalidLocation; } + bool isTrackingInvalidLocation() { return mIsTrackingFound && mIsInvalidLocation; } + bool isTrackingDoubleClick() { return mIsTrackingDoubleClick; } + bool isTrackingCommit() { return mIsTrackingCommit; } + bool isTrackingInRectangle(F64 x0, F64 y0, F64 x1, F64 y1); + + LLVector3d getTrackedPositionGlobal() const { return mTrackingLocation; } + + // World Mipmap delegation: currently used when drawing the mipmap + void equalizeBoostLevels(); + LLPointer<LLViewerFetchedTexture> getObjectsTile(U32 grid_x, U32 grid_y, S32 level, bool load = true) { return mWorldMipmap.getObjectsTile(grid_x, grid_y, level, load); } private: - LLTimer mRequestTimer; - - // search for named region for url processing - std::string mSLURLRegionName; - U64 mSLURLRegionHandle; - std::string mSLURL; - url_callback_t mSLURLCallback; - bool mSLURLTeleport; + bool clearItems(bool force = false); // Clears the item lists + void clearSimFlags(); // Clears the block flags indicating that we've already requested region infos + + // Create a region record corresponding to the handle, insert it in the region map and returns a pointer + LLSimInfo* createSimInfoFromHandle(const U64 handle); + + // Map from region-handle to region info + sim_info_map_t mSimInfoMap; + + // Holds the tiled mipmap of the world. This is the structure that contains the images used for rendering. + LLWorldMipmap mWorldMipmap; + + // The World is divided in "blocks" of (MAP_BLOCK_SIZE x MAP_BLOCK_SIZE) regions that get requested at once. + // This boolean table avoids "blocks" to be requested multiple times. + // Issue: Not sure this scheme is foolproof though as I've seen + // cases where a block is never retrieved and, because of this boolean being set, never re-requested + bool * mMapBlockLoaded; // Telling us if the block of regions has been requested or not + + // Track location data : used while there's nothing tracked yet by LLTracker + bool mIsTrackingLocation; // True when we're tracking a point + bool mIsTrackingFound; // True when the tracking position has been found, valid or not + bool mIsInvalidLocation; // The region is down or the location does not correspond to an existing region + bool mIsTrackingDoubleClick; // User double clicked to set the location (i.e. teleport when found please...) + bool mIsTrackingCommit; // User used the search or landmark fields to set the location + LLVector3d mTrackingLocation; // World global position being tracked + + // General grid items request timing flags (used for events,hubs and land for sale) + LLTimer mRequestTimer; + bool mFirstRequest; }; #endif diff --git a/indra/newview/llworldmapmessage.cpp b/indra/newview/llworldmapmessage.cpp new file mode 100644 index 0000000000..6a074d9697 --- /dev/null +++ b/indra/newview/llworldmapmessage.cpp @@ -0,0 +1,261 @@ +/** + * @file llworldmapmessage.cpp + * @brief Handling of the messages to the DB made by and for the world map. + * + * $LicenseInfo:firstyear=2003&license=viewergpl$ + * + * Copyright (c) 2003-2009, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "llworldmapmessage.h" + +#include "llworldmap.h" +#include "llagent.h" +#include "llfloaterworldmap.h" + +const U32 LAYER_FLAG = 2; + +//--------------------------------------------------------------------------- +// World Map Message Handling +//--------------------------------------------------------------------------- + +LLWorldMapMessage::LLWorldMapMessage() : + mSLURLRegionName(), + mSLURLRegionHandle(0), + mSLURL(), + mSLURLCallback(0), + mSLURLTeleport(false) +{ +} + +LLWorldMapMessage::~LLWorldMapMessage() +{ +} + +void LLWorldMapMessage::sendItemRequest(U32 type, U64 handle) +{ + //LL_INFOS("World Map") << "Send item request : type = " << type << LL_ENDL; + LLMessageSystem* msg = gMessageSystem; + + msg->newMessageFast(_PREHASH_MapItemRequest); + msg->nextBlockFast(_PREHASH_AgentData); + msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); + msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); + msg->addU32Fast(_PREHASH_Flags, LAYER_FLAG); + msg->addU32Fast(_PREHASH_EstateID, 0); // Filled in on sim + msg->addBOOLFast(_PREHASH_Godlike, FALSE); // Filled in on sim + + msg->nextBlockFast(_PREHASH_RequestData); + msg->addU32Fast(_PREHASH_ItemType, type); + msg->addU64Fast(_PREHASH_RegionHandle, handle); // If zero, filled in on sim + + gAgent.sendReliableMessage(); +} + +void LLWorldMapMessage::sendNamedRegionRequest(std::string region_name) +{ + //LL_INFOS("World Map") << "LLWorldMap::sendNamedRegionRequest()" << LL_ENDL; + LLMessageSystem* msg = gMessageSystem; + + // Request for region data + msg->newMessageFast(_PREHASH_MapNameRequest); + msg->nextBlockFast(_PREHASH_AgentData); + msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); + msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); + msg->addU32Fast(_PREHASH_Flags, LAYER_FLAG); + msg->addU32Fast(_PREHASH_EstateID, 0); // Filled in on sim + msg->addBOOLFast(_PREHASH_Godlike, FALSE); // Filled in on sim + msg->nextBlockFast(_PREHASH_NameData); + msg->addStringFast(_PREHASH_Name, region_name); + gAgent.sendReliableMessage(); +} + +void LLWorldMapMessage::sendNamedRegionRequest(std::string region_name, + url_callback_t callback, + const std::string& callback_url, + bool teleport) // immediately teleport when result returned +{ + //LL_INFOS("World Map") << "LLWorldMap::sendNamedRegionRequest()" << LL_ENDL; + mSLURLRegionName = region_name; + mSLURLRegionHandle = 0; + mSLURL = callback_url; + mSLURLCallback = callback; + mSLURLTeleport = teleport; + + sendNamedRegionRequest(region_name); +} + +void LLWorldMapMessage::sendHandleRegionRequest(U64 region_handle, + url_callback_t callback, + const std::string& callback_url, + bool teleport) // immediately teleport when result returned +{ + //LL_INFOS("World Map") << "LLWorldMap::sendHandleRegionRequest()" << LL_ENDL; + mSLURLRegionName.clear(); + mSLURLRegionHandle = region_handle; + mSLURL = callback_url; + mSLURLCallback = callback; + mSLURLTeleport = teleport; + + U32 global_x; + U32 global_y; + from_region_handle(region_handle, &global_x, &global_y); + U16 grid_x = (U16)(global_x / REGION_WIDTH_UNITS); + U16 grid_y = (U16)(global_y / REGION_WIDTH_UNITS); + + sendMapBlockRequest(grid_x, grid_y, grid_x, grid_y, true); +} + +void LLWorldMapMessage::sendMapBlockRequest(U16 min_x, U16 min_y, U16 max_x, U16 max_y, bool return_nonexistent) +{ + //LL_INFOS("World Map") << "LLWorldMap::sendMapBlockRequest()" << ", min = (" << min_x << ", " << min_y << "), max = (" << max_x << ", " << max_y << "), nonexistent = " << return_nonexistent << LL_ENDL; + LLMessageSystem* msg = gMessageSystem; + msg->newMessageFast(_PREHASH_MapBlockRequest); + msg->nextBlockFast(_PREHASH_AgentData); + msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); + msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); + U32 flags = LAYER_FLAG; + flags |= (return_nonexistent ? 0x10000 : 0); + msg->addU32Fast(_PREHASH_Flags, flags); + msg->addU32Fast(_PREHASH_EstateID, 0); // Filled in on sim + msg->addBOOLFast(_PREHASH_Godlike, FALSE); // Filled in on sim + msg->nextBlockFast(_PREHASH_PositionData); + msg->addU16Fast(_PREHASH_MinX, min_x); + msg->addU16Fast(_PREHASH_MinY, min_y); + msg->addU16Fast(_PREHASH_MaxX, max_x); + msg->addU16Fast(_PREHASH_MaxY, max_y); + gAgent.sendReliableMessage(); +} + +// public static +void LLWorldMapMessage::processMapBlockReply(LLMessageSystem* msg, void**) +{ + U32 agent_flags; + msg->getU32Fast(_PREHASH_AgentData, _PREHASH_Flags, agent_flags); + + // There's only one flag that we ever use here + if (agent_flags != LAYER_FLAG) + { + llwarns << "Invalid map image type returned! layer = " << agent_flags << llendl; + return; + } + + S32 num_blocks = msg->getNumberOfBlocksFast(_PREHASH_Data); + //LL_INFOS("World Map") << "LLWorldMap::processMapBlockReply(), num_blocks = " << num_blocks << LL_ENDL; + + bool found_null_sim = false; + + for (S32 block=0; block<num_blocks; ++block) + { + U16 x_regions; + U16 y_regions; + std::string name; + U8 accesscode; + U32 region_flags; +// U8 water_height; +// U8 agents; + LLUUID image_id; + msg->getU16Fast(_PREHASH_Data, _PREHASH_X, x_regions, block); + msg->getU16Fast(_PREHASH_Data, _PREHASH_Y, y_regions, block); + msg->getStringFast(_PREHASH_Data, _PREHASH_Name, name, block); + msg->getU8Fast(_PREHASH_Data, _PREHASH_Access, accesscode, block); + msg->getU32Fast(_PREHASH_Data, _PREHASH_RegionFlags, region_flags, block); +// msg->getU8Fast(_PREHASH_Data, _PREHASH_WaterHeight, water_height, block); +// msg->getU8Fast(_PREHASH_Data, _PREHASH_Agents, agents, block); + msg->getUUIDFast(_PREHASH_Data, _PREHASH_MapImageID, image_id, block); + + U32 x_world = (U32)(x_regions) * REGION_WIDTH_UNITS; + U32 y_world = (U32)(y_regions) * REGION_WIDTH_UNITS; + + // Insert that region in the world map, if failure, flag it as a "null_sim" + if (!(LLWorldMap::getInstance()->insertRegion(x_world, y_world, name, image_id, (U32)accesscode, region_flags))) + { + found_null_sim = true; + } + + // If we hit a valid tracking location, do what needs to be done app level wise + if (LLWorldMap::getInstance()->isTrackingValidLocation()) + { + LLVector3d pos_global = LLWorldMap::getInstance()->getTrackedPositionGlobal(); + if (LLWorldMap::getInstance()->isTrackingDoubleClick()) + { + // Teleport if the user double clicked + gAgent.teleportViaLocation(pos_global); + } + // Update the "real" tracker information + gFloaterWorldMap->trackLocation(pos_global); + } + + // Handle the SLURL callback if any + if(LLWorldMapMessage::getInstance()->mSLURLCallback != NULL) + { + U64 handle = to_region_handle(x_world, y_world); + // Check if we reached the requested region + if ((LLStringUtil::compareInsensitive(LLWorldMapMessage::getInstance()->mSLURLRegionName, name)==0) + || (LLWorldMapMessage::getInstance()->mSLURLRegionHandle == handle)) + { + url_callback_t callback = LLWorldMapMessage::getInstance()->mSLURLCallback; + + LLWorldMapMessage::getInstance()->mSLURLCallback = NULL; + LLWorldMapMessage::getInstance()->mSLURLRegionName.clear(); + LLWorldMapMessage::getInstance()->mSLURLRegionHandle = 0; + + callback(handle, LLWorldMapMessage::getInstance()->mSLURL, image_id, LLWorldMapMessage::getInstance()->mSLURLTeleport); + } + } + } + // Tell the UI to update itself + gFloaterWorldMap->updateSims(found_null_sim); +} + +// public static +void LLWorldMapMessage::processMapItemReply(LLMessageSystem* msg, void**) +{ + //LL_INFOS("World Map") << "LLWorldMap::processMapItemReply()" << LL_ENDL; + U32 type; + msg->getU32Fast(_PREHASH_RequestData, _PREHASH_ItemType, type); + + S32 num_blocks = msg->getNumberOfBlocks("Data"); + + for (S32 block=0; block<num_blocks; ++block) + { + U32 X, Y; + std::string name; + S32 extra, extra2; + LLUUID uuid; + msg->getU32Fast(_PREHASH_Data, _PREHASH_X, X, block); + msg->getU32Fast(_PREHASH_Data, _PREHASH_Y, Y, block); + msg->getStringFast(_PREHASH_Data, _PREHASH_Name, name, block); + msg->getUUIDFast(_PREHASH_Data, _PREHASH_ID, uuid, block); + msg->getS32Fast(_PREHASH_Data, _PREHASH_Extra, extra, block); + msg->getS32Fast(_PREHASH_Data, _PREHASH_Extra2, extra2, block); + + LLWorldMap::getInstance()->insertItem(X, Y, name, uuid, type, extra, extra2); + } +} + diff --git a/indra/newview/llworldmapmessage.h b/indra/newview/llworldmapmessage.h new file mode 100644 index 0000000000..2c8fedcb10 --- /dev/null +++ b/indra/newview/llworldmapmessage.h @@ -0,0 +1,83 @@ +/** + * @file llworldmapmessage.h + * @brief Handling of the messages to the DB made by and for the world map. + * + * $LicenseInfo:firstyear=2003&license=viewergpl$ + * + * Copyright (c) 2003-2009, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#ifndef LL_LLWORLDMAPMESSAGE_H +#define LL_LLWORLDMAPMESSAGE_H + +// Handling of messages (send and process) as well as SLURL callback if necessary +class LLMessageSystem; + +class LLWorldMapMessage : public LLSingleton<LLWorldMapMessage> +{ +public: + typedef boost::function<void(U64 region_handle, const std::string& url, const LLUUID& snapshot_id, bool teleport)> + url_callback_t; + + LLWorldMapMessage(); + ~LLWorldMapMessage(); + + // Process incoming answers to map stuff requests + static void processMapBlockReply(LLMessageSystem*, void**); + static void processMapItemReply(LLMessageSystem*, void**); + + // Request data for all regions in a rectangular area. Coordinates in grids (i.e. meters / 256). + void sendMapBlockRequest(U16 min_x, U16 min_y, U16 max_x, U16 max_y, bool return_nonexistent = false); + + // Various methods to request LLSimInfo data to the simulator and asset DB + void sendNamedRegionRequest(std::string region_name); + void sendNamedRegionRequest(std::string region_name, + url_callback_t callback, + const std::string& callback_url, + bool teleport); + void sendHandleRegionRequest(U64 region_handle, + url_callback_t callback, + const std::string& callback_url, + bool teleport); + + // Request item data for regions + // Note: the handle works *only* when requesting agent count (type = MAP_ITEM_AGENT_LOCATIONS). In that case, + // the request will actually be transitting through the spaceserver (all that is done on the sim). + // All other values of type do create a global grid request to the asset DB. So no need to try to get, say, + // the events for one particular region. For such a request, the handle is ignored. + void sendItemRequest(U32 type, U64 handle = 0); + +private: + // Search for region (by name or handle) for SLURL processing and teleport + // None of this relies explicitly on the LLWorldMap instance so better handle it here + std::string mSLURLRegionName; + U64 mSLURLRegionHandle; + std::string mSLURL; + url_callback_t mSLURLCallback; + bool mSLURLTeleport; +}; + +#endif // LL_LLWORLDMAPMESSAGE_H diff --git a/indra/newview/llworldmapview.cpp b/indra/newview/llworldmapview.cpp index 0509898f40..60b1e59645 100644 --- a/indra/newview/llworldmapview.cpp +++ b/indra/newview/llworldmapview.cpp @@ -46,7 +46,6 @@ #include "llagent.h" #include "llcallingcard.h" #include "llviewercontrol.h" -#include "llcylinder.h" #include "llfloatermap.h" #include "llfloaterworldmap.h" #include "llfocusmgr.h" @@ -57,25 +56,29 @@ #include "llviewercamera.h" #include "llviewertexture.h" #include "llviewertexturelist.h" -#include "llviewermenu.h" -#include "llviewerparceloverlay.h" #include "llviewerregion.h" #include "llviewerwindow.h" -#include "llworldmap.h" -#include "lltexturefetch.h" -#include "llappviewer.h" // Only for constants! #include "lltrans.h" #include "llglheaders.h" +// Basically a C++ implementation of the OCEAN_COLOR defined in mapstitcher.py +// Please ensure consistency between those 2 files (TODO: would be better to get that color from an asset source...) +// # Constants +// OCEAN_COLOR = "#1D475F" +const F32 OCEAN_RED = (F32)(0x1D)/255.f; +const F32 OCEAN_GREEN = (F32)(0x47)/255.f; +const F32 OCEAN_BLUE = (F32)(0x5F)/255.f; + const F32 GODLY_TELEPORT_HEIGHT = 200.f; const S32 SCROLL_HINT_WIDTH = 65; const F32 BIG_DOT_RADIUS = 5.f; BOOL LLWorldMapView::sHandledLastClick = FALSE; -LLUIImagePtr LLWorldMapView::sAvatarYouSmallImage = NULL; LLUIImagePtr LLWorldMapView::sAvatarSmallImage = NULL; -LLUIImagePtr LLWorldMapView::sAvatarLargeImage = NULL; +LLUIImagePtr LLWorldMapView::sAvatarYouImage = NULL; +LLUIImagePtr LLWorldMapView::sAvatarYouLargeImage = NULL; +LLUIImagePtr LLWorldMapView::sAvatarLevelImage = NULL; LLUIImagePtr LLWorldMapView::sAvatarAboveImage = NULL; LLUIImagePtr LLWorldMapView::sAvatarBelowImage = NULL; @@ -93,35 +96,34 @@ LLUIImagePtr LLWorldMapView::sClassifiedsImage = NULL; LLUIImagePtr LLWorldMapView::sForSaleImage = NULL; LLUIImagePtr LLWorldMapView::sForSaleAdultImage = NULL; -F32 LLWorldMapView::sThresholdA = 48.f; -F32 LLWorldMapView::sThresholdB = 96.f; F32 LLWorldMapView::sPanX = 0.f; F32 LLWorldMapView::sPanY = 0.f; F32 LLWorldMapView::sTargetPanX = 0.f; F32 LLWorldMapView::sTargetPanY = 0.f; S32 LLWorldMapView::sTrackingArrowX = 0; S32 LLWorldMapView::sTrackingArrowY = 0; -F32 LLWorldMapView::sPixelsPerMeter = 1.f; -F32 CONE_SIZE = 0.6f; +bool LLWorldMapView::sVisibleTilesLoaded = false; +F32 LLWorldMapView::sMapScale = 128.f; std::map<std::string,std::string> LLWorldMapView::sStringsMap; -#define SIM_NULL_MAP_SCALE 1 // width in pixels, where we start drawing "null" sims -#define SIM_MAP_AGENT_SCALE 2 // width in pixels, where we start drawing agents -#define SIM_MAP_SCALE 1 // width in pixels, where we start drawing sim tiles - -// Updates for agent locations. -#define AGENTS_UPDATE_TIME 60.0 // in seconds +// Fetch and draw info thresholds +const F32 DRAW_TEXT_THRESHOLD = 96.f; // Don't draw text under that resolution value (res = width region in meters) +const S32 DRAW_SIMINFO_THRESHOLD = 3; // Max level for which we load or display sim level information (level in LLWorldMipmap sense) +const S32 DRAW_LANDFORSALE_THRESHOLD = 2; // Max level for which we load or display land for sale picture data (level in LLWorldMipmap sense) +// When on, draw an outline for each mipmap tile gotten from S3 +#define DEBUG_DRAW_TILE 0 void LLWorldMapView::initClass() { - sAvatarYouSmallImage = LLUI::getUIImage("map_avatar_you_8.tga"); - sAvatarSmallImage = LLUI::getUIImage("map_avatar_8.tga"); - sAvatarLargeImage = LLUI::getUIImage("map_avatar_16.tga"); - sAvatarAboveImage = LLUI::getUIImage("map_avatar_above_8.tga"); - sAvatarBelowImage = LLUI::getUIImage("map_avatar_below_8.tga"); + sAvatarSmallImage = LLUI::getUIImage("map_avatar_8.tga"); + sAvatarYouImage = LLUI::getUIImage("map_avatar_16.tga"); + sAvatarYouLargeImage = LLUI::getUIImage("map_avatar_you_32.tga"); + sAvatarLevelImage = LLUI::getUIImage("map_avatar_32.tga"); + sAvatarAboveImage = LLUI::getUIImage("map_avatar_above_32.tga"); + sAvatarBelowImage = LLUI::getUIImage("map_avatar_below_32.tga"); sHomeImage = LLUI::getUIImage("map_home.tga"); sTelehubImage = LLUI::getUIImage("map_telehub.tga"); @@ -145,9 +147,10 @@ void LLWorldMapView::initClass() // static void LLWorldMapView::cleanupClass() { - sAvatarYouSmallImage = NULL; sAvatarSmallImage = NULL; - sAvatarLargeImage = NULL; + sAvatarYouImage = NULL; + sAvatarYouLargeImage = NULL; + sAvatarLevelImage = NULL; sAvatarAboveImage = NULL; sAvatarBelowImage = NULL; @@ -167,7 +170,7 @@ void LLWorldMapView::cleanupClass() LLWorldMapView::LLWorldMapView() : LLPanel(), - mBackgroundColor( LLColor4( 4.f/255.f, 4.f/255.f, 75.f/255.f, 1.f ) ), + mBackgroundColor( LLColor4( OCEAN_RED, OCEAN_GREEN, OCEAN_BLUE, 1.f ) ), mItemPicked(FALSE), mPanning( FALSE ), mMouseDownPanX( 0 ), @@ -176,7 +179,8 @@ LLWorldMapView::LLWorldMapView() mMouseDownY( 0 ), mSelectIDStart(0) { - sPixelsPerMeter = gMapScale / REGION_WIDTH_METERS; + //LL_INFOS("World Map") << "Creating the Map -> LLWorldMapView::LLWorldMapView()" << LL_ENDL; + clearLastClick(); } @@ -215,6 +219,7 @@ BOOL LLWorldMapView::postBuild() LLWorldMapView::~LLWorldMapView() { + //LL_INFOS("World Map") << "Destroying the map -> LLWorldMapView::~LLWorldMapView()" << LL_ENDL; cleanupTextures(); } @@ -228,14 +233,14 @@ void LLWorldMapView::cleanupTextures() // static void LLWorldMapView::setScale( F32 scale ) { - if (scale != gMapScale) + if (scale != sMapScale) { - F32 old_scale = gMapScale; + F32 old_scale = sMapScale; - gMapScale = scale; - if (gMapScale == 0.f) + sMapScale = scale; + if (sMapScale <= 0.f) { - gMapScale = 0.1f; + sMapScale = 0.1f; } F32 ratio = (scale / old_scale); @@ -243,8 +248,7 @@ void LLWorldMapView::setScale( F32 scale ) sPanY *= ratio; sTargetPanX = sPanX; sTargetPanY = sPanY; - - sPixelsPerMeter = gMapScale / REGION_WIDTH_METERS; + sVisibleTilesLoaded = false; } } @@ -256,6 +260,7 @@ void LLWorldMapView::translatePan( S32 delta_x, S32 delta_y ) sPanY += delta_y; sTargetPanX = sPanX; sTargetPanY = sPanY; + sVisibleTilesLoaded = false; } @@ -269,18 +274,22 @@ void LLWorldMapView::setPan( S32 x, S32 y, BOOL snap ) sPanX = sTargetPanX; sPanY = sTargetPanY; } + sVisibleTilesLoaded = false; } +bool LLWorldMapView::showRegionInfo() +{ + return (LLWorldMipmap::scaleToLevel(sMapScale) <= DRAW_SIMINFO_THRESHOLD ? true : false); +} /////////////////////////////////////////////////////////////////////////////////// // HELPERS BOOL is_agent_in_region(LLViewerRegion* region, LLSimInfo* info) { - return ((region && info) && (info->mName == region->getName())); + return (region && info && info->isName(region->getName())); } - /////////////////////////////////////////////////////////////////////////////////// void LLWorldMapView::draw() @@ -292,7 +301,7 @@ void LLWorldMapView::draw() F64 current_time = LLTimer::getElapsedSeconds(); mVisibleRegions.clear(); - + // animate pan if necessary sPanX = lerp(sPanX, sTargetPanX, LLCriticalDamp::getInterpolant(0.1f)); sPanY = lerp(sPanY, sTargetPanY, LLCriticalDamp::getInterpolant(0.1f)); @@ -303,10 +312,12 @@ void LLWorldMapView::draw() const F32 half_height = F32(height) / 2.0f; LLVector3d camera_global = gAgent.getCameraPositionGlobal(); + S32 level = LLWorldMipmap::scaleToLevel(sMapScale); + LLLocalClipRect clip(getLocalRect()); { gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - + glMatrixMode(GL_MODELVIEW); // Clear the background alpha to 0 @@ -319,308 +330,59 @@ void LLWorldMapView::draw() } gGL.flush(); + gGL.setAlphaRejectSettings(LLRender::CF_DEFAULT); gGL.setColorMask(true, true); - gGL.setSceneBlendType(LLRender::BT_ALPHA); - - F32 layer_alpha = 1.f; - - // Draw one image per layer - for (U32 layer_idx=0; layer_idx<LLWorldMap::getInstance()->mMapLayers[LLWorldMap::getInstance()->mCurrentMap].size(); ++layer_idx) - { - if (!LLWorldMap::getInstance()->mMapLayers[LLWorldMap::getInstance()->mCurrentMap][layer_idx].LayerDefined) - { - continue; - } - LLWorldMapLayer *layer = &LLWorldMap::getInstance()->mMapLayers[LLWorldMap::getInstance()->mCurrentMap][layer_idx]; - LLViewerFetchedTexture *current_image = layer->LayerImage; - - if (current_image->isMissingAsset()) - { - continue; // better to draw nothing than the missing asset image - } - - LLVector3d origin_global((F64)layer->LayerExtents.mLeft * REGION_WIDTH_METERS, (F64)layer->LayerExtents.mBottom * REGION_WIDTH_METERS, 0.f); - - // Find x and y position relative to camera's center. - LLVector3d rel_region_pos = origin_global - camera_global; - F32 relative_x = (rel_region_pos.mdV[0] / REGION_WIDTH_METERS) * gMapScale; - F32 relative_y = (rel_region_pos.mdV[1] / REGION_WIDTH_METERS) * gMapScale; - - F32 pix_width = gMapScale*(layer->LayerExtents.getWidth() + 1); - F32 pix_height = gMapScale*(layer->LayerExtents.getHeight() + 1); - - // When the view isn't panned, 0,0 = center of rectangle - F32 bottom = sPanY + half_height + relative_y; - F32 left = sPanX + half_width + relative_x; - F32 top = bottom + pix_height; - F32 right = left + pix_width; - F32 pixel_area = pix_width*pix_height; - // discard layers that are outside the rectangle - // and discard small layers - if (top < 0.f || - bottom > height || - right < 0.f || - left > width || - (pixel_area < 4*4)) - { - current_image->setBoostLevel(0); - continue; - } - - current_image->setBoostLevel(LLViewerTexture::BOOST_MAP); - current_image->setKnownDrawSize(llround(pix_width * LLUI::sGLScaleFactor.mV[VX]), llround(pix_height * LLUI::sGLScaleFactor.mV[VY])); - - if (!current_image->hasGLTexture()) - { - continue; // better to draw nothing than the default image - } - -// LLTextureView::addDebugImage(current_image); - - // Draw using the texture. If we don't clamp we get artifact at - // the edge. - gGL.getTexUnit(0)->bind(current_image); - - // Draw map image into RGB - //gGL.blendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - gGL.flush(); - gGL.setColorMask(true, false); - gGL.color4f(1.f, 1.f, 1.f, layer_alpha); - - gGL.begin(LLRender::QUADS); - gGL.texCoord2f(0.0f, 1.0f); - gGL.vertex3f(left, top, -1.0f); - gGL.texCoord2f(0.0f, 0.0f); - gGL.vertex3f(left, bottom, -1.0f); - gGL.texCoord2f(1.0f, 0.0f); - gGL.vertex3f(right, bottom, -1.0f); - gGL.texCoord2f(1.0f, 1.0f); - gGL.vertex3f(right, top, -1.0f); - gGL.end(); - - // draw an alpha of 1 where the sims are visible - gGL.flush(); - gGL.setColorMask(false, true); - gGL.color4f(1.f, 1.f, 1.f, 1.f); - - gGL.begin(LLRender::QUADS); - gGL.texCoord2f(0.0f, 1.0f); - gGL.vertex2f(left, top); - gGL.texCoord2f(0.0f, 0.0f); - gGL.vertex2f(left, bottom); - gGL.texCoord2f(1.0f, 0.0f); - gGL.vertex2f(right, bottom); - gGL.texCoord2f(1.0f, 1.0f); - gGL.vertex2f(right, top); - gGL.end(); - } +#if 1 + // Draw the image tiles + drawMipmap(width, height); gGL.flush(); + gGL.setAlphaRejectSettings(LLRender::CF_DEFAULT); gGL.setColorMask(true, true); - // there used to be an #if 1 here, but it was uncommented; perhaps marking a block of code? - F32 sim_alpha = 1.f; - - // Draw one image per region, centered on the camera position. - const S32 MAX_SIMULTANEOUS_TEX = 100; - const S32 MAX_REQUEST_PER_TICK = 5; - const S32 MIN_REQUEST_PER_TICK = 1; - S32 textures_requested_this_tick = 0; - - for (LLWorldMap::sim_info_map_t::iterator it = LLWorldMap::getInstance()->mSimInfoMap.begin(); - it != LLWorldMap::getInstance()->mSimInfoMap.end(); ++it) + // Draw per sim overlayed information (names, mature, offline...) + for (LLWorldMap::sim_info_map_t::const_iterator it = LLWorldMap::getInstance()->getRegionMap().begin(); + it != LLWorldMap::getInstance()->getRegionMap().end(); ++it) { - U64 handle = (*it).first; - LLSimInfo* info = (*it).second; + U64 handle = it->first; + LLSimInfo* info = it->second; - LLViewerFetchedTexture* simimage = info->mCurrentImage; - LLViewerFetchedTexture* overlayimage = info->mOverlayImage; - - if (gMapScale < SIM_MAP_SCALE) - { - if (simimage != NULL) simimage->setBoostLevel(0); - if (overlayimage != NULL) overlayimage->setBoostLevel(0); - continue; - } - LLVector3d origin_global = from_region_handle(handle); - LLVector3d camera_global = gAgent.getCameraPositionGlobal(); // Find x and y position relative to camera's center. LLVector3d rel_region_pos = origin_global - camera_global; - F32 relative_x = (rel_region_pos.mdV[0] / REGION_WIDTH_METERS) * gMapScale; - F32 relative_y = (rel_region_pos.mdV[1] / REGION_WIDTH_METERS) * gMapScale; + F32 relative_x = (rel_region_pos.mdV[0] / REGION_WIDTH_METERS) * sMapScale; + F32 relative_y = (rel_region_pos.mdV[1] / REGION_WIDTH_METERS) * sMapScale; + // Coordinates of the sim in pixels in the UI panel // When the view isn't panned, 0,0 = center of rectangle - F32 bottom = sPanY + half_height + relative_y; - F32 left = sPanX + half_width + relative_x; - F32 top = bottom + gMapScale ; - F32 right = left + gMapScale ; - - // Switch to world map texture (if available for this region) if either: - // 1. Tiles are zoomed out small enough, or - // 2. Sim's texture has not been loaded yet - F32 map_scale_cutoff = SIM_MAP_SCALE; - if ((info->mRegionFlags & REGION_FLAGS_NULL_LAYER) > 0) - { - map_scale_cutoff = SIM_NULL_MAP_SCALE; - } - - info->mShowAgentLocations = (gMapScale >= SIM_MAP_AGENT_SCALE); - - bool sim_visible = - (gMapScale >= map_scale_cutoff) && - (simimage != NULL) && - (simimage->hasGLTexture()); - - if (sim_visible) - { - // Fade in - if (info->mAlpha < 0.0f) - info->mAlpha = 1.f; // don't fade initially - else - info->mAlpha = lerp(info->mAlpha, 1.f, LLCriticalDamp::getInterpolant(0.15f)); - } - else - { - // Fade out - if (info->mAlpha < 0.0f) - info->mAlpha = 0.f; // don't fade initially - else - info->mAlpha = lerp(info->mAlpha, 0.f, LLCriticalDamp::getInterpolant(0.15f)); - } - - // discard regions that are outside the rectangle - // and discard small regions - if (top < 0.f || - bottom > height || - right < 0.f || - left > width ) + F32 bottom = sPanY + half_height + relative_y; + F32 left = sPanX + half_width + relative_x; + F32 top = bottom + sMapScale ; + F32 right = left + sMapScale ; + + // Discard if region is outside the screen rectangle (not visible on screen) + if ((top < 0.f) || (bottom > height) || + (right < 0.f) || (left > width) ) { - if (simimage != NULL) simimage->setBoostLevel(0); - if (overlayimage != NULL) overlayimage->setBoostLevel(0); + // Drop the "land for sale" fetching priority since it's outside the view rectangle + info->dropImagePriority(); continue; } - if (info->mCurrentImage.isNull()) - { - if ((textures_requested_this_tick < MIN_REQUEST_PER_TICK) || - ((LLAppViewer::getTextureFetch()->getNumRequests() < MAX_SIMULTANEOUS_TEX) && - (textures_requested_this_tick < MAX_REQUEST_PER_TICK))) - { - textures_requested_this_tick++; - info->mCurrentImage = LLViewerTextureManager::getFetchedTexture(info->mMapImageID[LLWorldMap::getInstance()->mCurrentMap], - MIPMAP_TRUE, LLViewerTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE); - info->mCurrentImage->setAddressMode(LLTexUnit::TAM_CLAMP); - simimage = info->mCurrentImage; - gGL.getTexUnit(0)->bind(simimage); - } - } - if (info->mOverlayImage.isNull() && info->mMapImageID[2].notNull()) - { - if ((textures_requested_this_tick < MIN_REQUEST_PER_TICK) || - ((LLAppViewer::getTextureFetch()->getNumRequests() < MAX_SIMULTANEOUS_TEX) && - (textures_requested_this_tick < MAX_REQUEST_PER_TICK))) - { - textures_requested_this_tick++; - info->mOverlayImage = LLViewerTextureManager::getFetchedTexture(info->mMapImageID[2], MIPMAP_TRUE, LLViewerTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE); - info->mOverlayImage->setAddressMode(LLTexUnit::TAM_CLAMP); - overlayimage = info->mOverlayImage; - gGL.getTexUnit(0)->bind(overlayimage); - } - } + // This list is used by other methods to know which regions are indeed displayed on screen mVisibleRegions.push_back(handle); - // See if the agents need updating - if (current_time - info->mAgentsUpdateTime > AGENTS_UPDATE_TIME) - { - LLWorldMap::getInstance()->sendItemRequest(MAP_ITEM_AGENT_LOCATIONS, info->mHandle); - info->mAgentsUpdateTime = current_time; - } - - // Bias the priority escalation for images nearer - LLVector3d center_global = origin_global; - center_global.mdV[VX] += 128.0; - center_global.mdV[VY] += 128.0; - - S32 draw_size = llround(gMapScale); - if (simimage != NULL) - { - simimage->setBoostLevel(LLViewerTexture::BOOST_MAP); - simimage->setKnownDrawSize(llround(draw_size * LLUI::sGLScaleFactor.mV[VX]), llround(draw_size * LLUI::sGLScaleFactor.mV[VY])); - } - - if (overlayimage != NULL) - { - overlayimage->setBoostLevel(LLViewerTexture::BOOST_MAP); - overlayimage->setKnownDrawSize(llround(draw_size * LLUI::sGLScaleFactor.mV[VX]), llround(draw_size * LLUI::sGLScaleFactor.mV[VY])); - } - -// LLTextureView::addDebugImage(simimage); - if (sim_visible && info->mAlpha > 0.001f) + // Update the agent count for that region if we're not too zoomed out already + if (level <= DRAW_SIMINFO_THRESHOLD) { - // Draw using the texture. If we don't clamp we get artifact at - // the edge. - LLGLSUIDefault gls_ui; - if (simimage != NULL) - gGL.getTexUnit(0)->bind(simimage); - - gGL.setSceneBlendType(LLRender::BT_ALPHA); - F32 alpha = sim_alpha * info->mAlpha; - gGL.color4f(1.f, 1.0f, 1.0f, alpha); - - gGL.begin(LLRender::QUADS); - gGL.texCoord2f(0.f, 1.f); - gGL.vertex3f(left, top, 0.f); - gGL.texCoord2f(0.f, 0.f); - gGL.vertex3f(left, bottom, 0.f); - gGL.texCoord2f(1.f, 0.f); - gGL.vertex3f(right, bottom, 0.f); - gGL.texCoord2f(1.f, 1.f); - gGL.vertex3f(right, top, 0.f); - gGL.end(); - - if (gSavedSettings.getBOOL("MapShowLandForSale") && overlayimage && overlayimage->hasGLTexture()) - { - gGL.getTexUnit(0)->bind(overlayimage); - gGL.color4f(1.f, 1.f, 1.f, alpha); - gGL.begin(LLRender::QUADS); - gGL.texCoord2f(0.f, 1.f); - gGL.vertex3f(left, top, -0.5f); - gGL.texCoord2f(0.f, 0.f); - gGL.vertex3f(left, bottom, -0.5f); - gGL.texCoord2f(1.f, 0.f); - gGL.vertex3f(right, bottom, -0.5f); - gGL.texCoord2f(1.f, 1.f); - gGL.vertex3f(right, top, -0.5f); - gGL.end(); - } - - if ((info->mRegionFlags & REGION_FLAGS_NULL_LAYER) == 0) - { - // draw an alpha of 1 where the sims are visible (except NULL sims) - gGL.flush(); - gGL.setSceneBlendType(LLRender::BT_REPLACE); - gGL.setColorMask(false, true); - gGL.color4f(1.f, 1.f, 1.f, 1.f); - - gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - gGL.begin(LLRender::QUADS); - gGL.vertex2f(left, top); - gGL.vertex2f(left, bottom); - gGL.vertex2f(right, bottom); - gGL.vertex2f(right, top); - gGL.end(); - - gGL.flush(); - gGL.setColorMask(true, true); - } + info->updateAgentCount(current_time); } - if (info->mAccess == SIM_ACCESS_DOWN) + if (info->isDown()) { // Draw a transparent red square over down sims gGL.blendFunc(LLRender::BF_DEST_ALPHA, LLRender::BF_SOURCE_ALPHA); @@ -634,18 +396,15 @@ void LLWorldMapView::draw() gGL.vertex2f(right, top); gGL.end(); } - - // As part of the AO project, we no longer want to draw access indicators; - // it's too complicated to get all the rules straight and will only + // As part of the AO project, we no longer want to draw access indicators; + // it's too complicated to get all the rules straight and will only // cause confusion. /********************** - // If this is mature, and you are not, draw a line across it - if (info->mAccess != SIM_ACCESS_DOWN - && info->mAccess > SIM_ACCESS_PG - && gAgent.isTeen()) + else if (!info->isPG() && gAgent.isTeen()) { + // If this is a mature region, and you are not, draw a line across it gGL.blendFunc(LLRender::BF_DEST_ALPHA, LLRender::BF_ZERO); - + gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); gGL.color3f(1.f, 0.f, 0.f); gGL.begin(LLRender::LINES); @@ -656,68 +415,67 @@ void LLWorldMapView::draw() gGL.end(); } **********************/ - - // Draw the region name in the lower left corner - LLFontGL* font = LLFontGL::getFontSansSerifSmall(); - - std::string mesg; - if (gMapScale < sThresholdA) + else if (gSavedSettings.getBOOL("MapShowLandForSale") && (level <= DRAW_LANDFORSALE_THRESHOLD)) { + // Draw the overlay image "Land for Sale / Land for Auction" + LLViewerFetchedTexture* overlayimage = info->getLandForSaleImage(); + if (overlayimage) + { + // Inform the fetch mechanism of the size we need + S32 draw_size = llround(sMapScale); + overlayimage->setKnownDrawSize(llround(draw_size * LLUI::sGLScaleFactor.mV[VX]), llround(draw_size * LLUI::sGLScaleFactor.mV[VY])); + // Draw something whenever we have enough info + if (overlayimage->hasGLTexture()) + { + gGL.blendFunc(LLRender::BF_DEST_ALPHA, LLRender::BF_ZERO); + gGL.getTexUnit(0)->bind(overlayimage); + gGL.color4f(1.f, 1.f, 1.f, 1.f); + gGL.begin(LLRender::QUADS); + gGL.texCoord2f(0.f, 1.f); + gGL.vertex3f(left, top, -0.5f); + gGL.texCoord2f(0.f, 0.f); + gGL.vertex3f(left, bottom, -0.5f); + gGL.texCoord2f(1.f, 0.f); + gGL.vertex3f(right, bottom, -0.5f); + gGL.texCoord2f(1.f, 1.f); + gGL.vertex3f(right, top, -0.5f); + gGL.end(); + } + } } - else if (gMapScale < sThresholdB) + else { - // mesg = llformat( info->mAgents); + // If we're not displaying the "land for sale", drop its fetching priority + info->dropImagePriority(); } - else + + // Draw the region name in the lower left corner + if (sMapScale >= DRAW_TEXT_THRESHOLD) { - //mesg = llformat("%d / %s (%s)", - // info->mAgents, - // info->mName.c_str(), - // LLViewerRegion::accessToShortString(info->mAccess).c_str() ); - if (info->mAccess == SIM_ACCESS_DOWN) + LLFontGL* font = LLFontGL::getFontSansSerifSmall(); + std::string mesg; + if (info->isDown()) { - mesg = llformat( "%s (%s)", info->mName.c_str(), sStringsMap["offline"].c_str()); + mesg = llformat( "%s (%s)", info->getName().c_str(), sStringsMap["offline"].c_str()); } else { - mesg = info->mName; + mesg = info->getName(); } - } - - if (!mesg.empty()) - { - font->renderUTF8( - mesg, 0, - llfloor(left + 3), - llfloor(bottom + 2), - LLColor4::white, - LLFontGL::LEFT, - LLFontGL::BASELINE, - LLFontGL::NORMAL, - LLFontGL::DROP_SHADOW); - - // If map texture is still loading, - // display "Loading" placeholder text. - if ((simimage != NULL) && - simimage->getDiscardLevel() != 1 && - simimage->getDiscardLevel() != 0) + if (!mesg.empty()) { font->renderUTF8( - sStringsMap["loading"], 0, - llfloor(left + 18), - llfloor(top - 25), + mesg, 0, + llfloor(left + 3), llfloor(bottom + 2), LLColor4::white, - LLFontGL::LEFT, - LLFontGL::BASELINE, - LLFontGL::NORMAL, - LLFontGL::DROP_SHADOW); + LLFontGL::LEFT, LLFontGL::BASELINE, LLFontGL::DROP_SHADOW); } } } - // #endif used to be here + #endif - // there used to be an #if 1 here, but it was uncommented; perhaps marking a block of code? + #if 1 // Draw background rectangle LLGLSUIDefault gls_ui; { @@ -727,69 +485,49 @@ void LLWorldMapView::draw() gGL.color4fv( mBackgroundColor.mV ); gl_rect_2d(0, height, width, 0); } - + gGL.setAlphaRejectSettings(LLRender::CF_DEFAULT); gGL.setSceneBlendType(LLRender::BT_ALPHA); - // Infohubs - if (gSavedSettings.getBOOL("MapShowInfohubs")) //(gMapScale >= sThresholdB) + // Draw item infos if we're not zoomed out too much and there's something to draw + if ((level <= DRAW_SIMINFO_THRESHOLD) && (gSavedSettings.getBOOL("MapShowInfohubs") || + gSavedSettings.getBOOL("MapShowTelehubs") || + gSavedSettings.getBOOL("MapShowLandForSale") || + gSavedSettings.getBOOL("MapShowEvents") || + gSavedSettings.getBOOL("ShowMatureEvents") || + gSavedSettings.getBOOL("ShowAdultEvents"))) { - drawGenericItems(LLWorldMap::getInstance()->mInfohubs, sInfohubImage); + drawItems(); } - // Telehubs - if (gSavedSettings.getBOOL("MapShowTelehubs")) //(gMapScale >= sThresholdB) - { - drawGenericItems(LLWorldMap::getInstance()->mTelehubs, sTelehubImage); - } - - // Home Sweet Home + // Draw the Home location (always) LLVector3d home; if (gAgent.getHomePosGlobal(&home)) { drawImage(home, sHomeImage); } - if (gSavedSettings.getBOOL("MapShowLandForSale")) - { - drawGenericItems(LLWorldMap::getInstance()->mLandForSale, sForSaleImage); - // for 1.23, we're showing normal land and adult land in the same UI; you don't - // get a choice about which ones you want. If you're currently asking for adult - // content and land you'll get the adult land. - if (gAgent.canAccessAdult()) - { - drawGenericItems(LLWorldMap::getInstance()->mLandForSaleAdult, sForSaleAdultImage); - } - } - - if (gSavedSettings.getBOOL("MapShowEvents") || - gSavedSettings.getBOOL("ShowMatureEvents") || - gSavedSettings.getBOOL("ShowAdultEvents") ) - { - drawEvents(); - } - - // Now draw your avatar after all that other stuff. + // Draw the current agent after all that other stuff. LLVector3d pos_global = gAgent.getPositionGlobal(); - drawImage(pos_global, sAvatarLargeImage); + drawImage(pos_global, sAvatarYouImage); LLVector3 pos_map = globalPosToView(pos_global); if (!pointInView(llround(pos_map.mV[VX]), llround(pos_map.mV[VY]))) { - drawTracking(pos_global, - lerp(LLColor4::yellow, LLColor4::orange, 0.4f), - TRUE, - "You are here", - "", - llround(LLFontGL::getFontSansSerifSmall()->getLineHeight())); // offset vertically by one line, to avoid overlap with target tracking + drawTracking(pos_global, + lerp(LLColor4::yellow, LLColor4::orange, 0.4f), + TRUE, + "You are here", + "", + llround(LLFontGL::getFontSansSerifSmall()->getLineHeight())); // offset vertically by one line, to avoid overlap with target tracking } - // Show your viewing angle + // Draw the current agent viewing angle drawFrustum(); // Draw icons for the avatars in each region. - // Drawn after your avatar so you can see nearby people. - if (gSavedSettings.getBOOL("MapShowPeople")) + // Drawn this after the current agent avatar so one can see nearby people + if (gSavedSettings.getBOOL("MapShowPeople") && (level <= DRAW_SIMINFO_THRESHOLD)) { drawAgents(); } @@ -799,9 +537,9 @@ void LLWorldMapView::draw() if ( LLTracker::TRACKING_AVATAR == tracking_status ) { drawTracking( LLAvatarTracker::instance().getGlobalPos(), map_track_color, TRUE, LLTracker::getLabel(), "" ); - } - else if ( LLTracker::TRACKING_LANDMARK == tracking_status - || LLTracker::TRACKING_LOCATION == tracking_status ) + } + else if ( LLTracker::TRACKING_LANDMARK == tracking_status + || LLTracker::TRACKING_LOCATION == tracking_status ) { // While fetching landmarks, will have 0,0,0 location for a while, // so don't draw. JC @@ -811,31 +549,33 @@ void LLWorldMapView::draw() drawTracking( pos_global, map_track_color, TRUE, LLTracker::getLabel(), LLTracker::getToolTip() ); } } - else if (LLWorldMap::getInstance()->mIsTrackingUnknownLocation) + else if (LLWorldMap::getInstance()->isTracking()) { - if (LLWorldMap::getInstance()->mInvalidLocation) + if (LLWorldMap::getInstance()->isTrackingInvalidLocation()) { - // We know this location to be invalid + // We know this location to be invalid, draw a blue circle LLColor4 loading_color(0.0, 0.5, 1.0, 1.0); - drawTracking( LLWorldMap::getInstance()->mUnknownLocation, loading_color, TRUE, getString("InvalidLocation"), ""); + drawTracking( LLWorldMap::getInstance()->getTrackedPositionGlobal(), loading_color, TRUE, getString("InvalidLocation"), ""); } else { + // We don't know yet what that location is, draw a throbing blue circle double value = fmod(current_time, 2); - value = 0.5 + 0.5*cos(value * M_PI); + value = 0.5 + 0.5*cos(value * F_PI); LLColor4 loading_color(0.0, F32(value/2), F32(value), 1.0); - drawTracking( LLWorldMap::getInstance()->mUnknownLocation, loading_color, TRUE, getString("Loading"), ""); + drawTracking( LLWorldMap::getInstance()->getTrackedPositionGlobal(), loading_color, TRUE, getString("Loading"), ""); } } - // #endif used to be here - + #endif + // turn off the scissor LLGLDisable no_scissor(GL_SCISSOR_TEST); - + updateDirections(); LLView::draw(); + // Get sim info for all sims in view updateVisibleBlocks(); } // end draw() @@ -846,36 +586,182 @@ void LLWorldMapView::setVisible(BOOL visible) LLPanel::setVisible(visible); if (!visible) { - for (S32 map = 0; map < MAP_SIM_IMAGE_TYPES; map++) + // Drop the download of tiles and images priority to nil if we hide the map + LLWorldMap::getInstance()->dropImagePriorities(); + } +} + +void LLWorldMapView::drawMipmap(S32 width, S32 height) +{ + // Compute the level of the mipmap to use for the current scale level + S32 level = LLWorldMipmap::scaleToLevel(sMapScale); + // Set the tile boost level so that unused tiles get to 0 + LLWorldMap::getInstance()->equalizeBoostLevels(); + + // Render whatever we already have loaded if we haven't the current level + // complete and use it as a background (scaled up or scaled down) + if (!sVisibleTilesLoaded) + { + // Note: the (load = false) parameter avoids missing tiles to be fetched (i.e. we render what we have, no more) + // Check all the lower res levels and render them in reverse order (worse to best) + // We need to traverse all the levels as the user can zoom in very fast + for (S32 l = LLWorldMipmap::MAP_LEVELS; l > level; l--) { - for (U32 layer_idx=0; layer_idx<LLWorldMap::getInstance()->mMapLayers[map].size(); ++layer_idx) - { - if (LLWorldMap::getInstance()->mMapLayers[map][layer_idx].LayerDefined) - { - LLWorldMapLayer *layer = &LLWorldMap::getInstance()->mMapLayers[map][layer_idx]; - layer->LayerImage->setBoostLevel(0); - } - } + drawMipmapLevel(width, height, l, false); } - for (LLWorldMap::sim_info_map_t::iterator it = LLWorldMap::getInstance()->mSimInfoMap.begin(); - it != LLWorldMap::getInstance()->mSimInfoMap.end(); ++it) + // Skip the current level, as we'll do it anyway here under... + + // Just go one level down in res as it can really get too much stuff + // when zooming out and too small to see anyway... + if (level > 1) { - LLSimInfo* info = (*it).second; - if (info->mCurrentImage.notNull()) + drawMipmapLevel(width, height, level - 1, false); + } + } + else + { + //LL_INFOS("World Map") << "Render complete, don't draw background..." << LL_ENDL; + } + + // Render the current level + sVisibleTilesLoaded = drawMipmapLevel(width, height, level); + + return; +} + +// Return true if all the tiles required to render that level have been fetched or are truly missing +bool LLWorldMapView::drawMipmapLevel(S32 width, S32 height, S32 level, bool load) +{ + // Check input level + llassert (level > 0); + if (level <= 0) + return false; + + // Count tiles hit and completed + S32 completed_tiles = 0; + S32 total_tiles = 0; + + // Size in meters (global) of each tile of that level + S32 tile_width = LLWorldMipmap::MAP_TILE_SIZE * (1 << (level - 1)); + // Dimension of the screen in meter at that scale + LLVector3d pos_SW = viewPosToGlobal(0, 0); + LLVector3d pos_NE = viewPosToGlobal(width, height); + // Add external band of tiles on the outskirt so to hit the partially displayed tiles right and top + pos_NE[VX] += tile_width; + pos_NE[VY] += tile_width; + + // Iterate through the tiles on screen: we just need to ask for one tile every tile_width meters + U32 grid_x, grid_y; + for (F64 index_y = pos_SW[VY]; index_y < pos_NE[VY]; index_y += tile_width) + { + for (F64 index_x = pos_SW[VX]; index_x < pos_NE[VX]; index_x += tile_width) + { + // Compute the world coordinates of the current point + LLVector3d pos_global(index_x, index_y, pos_SW[VZ]); + // Convert to the mipmap level coordinates for that point (i.e. which tile to we hit) + LLWorldMipmap::globalToMipmap(pos_global[VX], pos_global[VY], level, &grid_x, &grid_y); + // Get the tile. Note: NULL means that the image does not exist (so it's considered "complete" as far as fetching is concerned) + LLPointer<LLViewerFetchedTexture> simimage = LLWorldMap::getInstance()->getObjectsTile(grid_x, grid_y, level, load); + if (simimage) { - info->mCurrentImage->setBoostLevel(0); + // Checks that the image has a valid texture + if (simimage->hasGLTexture()) + { + // Increment the number of completly fetched tiles + completed_tiles++; + + // Convert those coordinates (SW corner of the mipmap tile) into world (meters) coordinates + pos_global[VX] = grid_x * REGION_WIDTH_METERS; + pos_global[VY] = grid_y * REGION_WIDTH_METERS; + // Now to screen coordinates for SW corner of that tile + LLVector3 pos_screen = globalPosToView (pos_global); + F32 left = pos_screen[VX]; + F32 bottom = pos_screen[VY]; + // Compute the NE corner coordinates of the tile now + pos_global[VX] += tile_width; + pos_global[VY] += tile_width; + pos_screen = globalPosToView (pos_global); + F32 right = pos_screen[VX]; + F32 top = pos_screen[VY]; + + // Draw the tile + LLGLSUIDefault gls_ui; + gGL.getTexUnit(0)->bind(simimage.get()); + simimage->setAddressMode(LLTexUnit::TAM_CLAMP); + + gGL.setSceneBlendType(LLRender::BT_ALPHA); + gGL.color4f(1.f, 1.0f, 1.0f, 1.0f); + + gGL.begin(LLRender::QUADS); + gGL.texCoord2f(0.f, 1.f); + gGL.vertex3f(left, top, 0.f); + gGL.texCoord2f(0.f, 0.f); + gGL.vertex3f(left, bottom, 0.f); + gGL.texCoord2f(1.f, 0.f); + gGL.vertex3f(right, bottom, 0.f); + gGL.texCoord2f(1.f, 1.f); + gGL.vertex3f(right, top, 0.f); + gGL.end(); +#if DEBUG_DRAW_TILE + drawTileOutline(level, top, left, bottom, right); +#endif // DEBUG_DRAW_TILE + } + //else + //{ + // Waiting for a tile -> the level is not complete + // LL_INFOS("World Map") << "Unfetched tile. level = " << level << LL_ENDL; + //} } - if (info->mOverlayImage.notNull()) + else { - info->mOverlayImage->setBoostLevel(0); + // Unexistent tiles are counted as "completed" + completed_tiles++; } + // Increment the number of tiles in that level / screen + total_tiles++; } } + return (completed_tiles == total_tiles); +} + +// Draw lines (rectangle outline and cross) to visualize the position of the tile +// Used for debug only +void LLWorldMapView::drawTileOutline(S32 level, F32 top, F32 left, F32 bottom, F32 right) +{ + gGL.blendFunc(LLRender::BF_DEST_ALPHA, LLRender::BF_ZERO); + + gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); + if (level == 1) + gGL.color3f(1.f, 0.f, 0.f); // red + else if (level == 2) + gGL.color3f(0.f, 1.f, 0.f); // green + else if (level == 3) + gGL.color3f(0.f, 0.f, 1.f); // blue + else if (level == 4) + gGL.color3f(1.f, 1.f, 0.f); // yellow + else if (level == 5) + gGL.color3f(1.f, 0.f, 1.f); // magenta + else if (level == 6) + gGL.color3f(0.f, 1.f, 1.f); // cyan + else if (level == 7) + gGL.color3f(1.f, 1.f, 1.f); // white + else + gGL.color3f(0.f, 0.f, 0.f); // black + gGL.begin(LLRender::LINE_STRIP); + gGL.vertex2f(left, top); + gGL.vertex2f(right, bottom); + gGL.vertex2f(left, bottom); + gGL.vertex2f(right, top); + gGL.vertex2f(left, top); + gGL.vertex2f(left, bottom); + gGL.vertex2f(right, bottom); + gGL.vertex2f(right, top); + gGL.end(); } -void LLWorldMapView::drawGenericItems(const LLWorldMap::item_info_list_t& items, LLUIImagePtr image) +void LLWorldMapView::drawGenericItems(const LLSimInfo::item_info_list_t& items, LLUIImagePtr image) { - LLWorldMap::item_info_list_t::const_iterator e; + LLSimInfo::item_info_list_t::const_iterator e; for (e = items.begin(); e != items.end(); ++e) { drawGenericItem(*e, image); @@ -884,7 +770,7 @@ void LLWorldMapView::drawGenericItems(const LLWorldMap::item_info_list_t& items, void LLWorldMapView::drawGenericItem(const LLItemInfo& item, LLUIImagePtr image) { - drawImage(item.mPosGlobal, image); + drawImage(item.getGlobalPosition(), image); } @@ -907,137 +793,91 @@ void LLWorldMapView::drawImageStack(const LLVector3d& global_pos, LLUIImagePtr i } } - -void LLWorldMapView::drawAgents() +void LLWorldMapView::drawItems() { - static LLUIColor map_avatar_color = LLUIColorTable::instance().getColor("MapAvatarColor", LLColor4::white); - static LLUIColor map_avatar_friend_color = LLUIColorTable::instance().getColor("MapAvatarFriendColor", LLColor4::white); - - F32 agents_scale = (gMapScale * 0.9f) / 256.f; + bool mature_enabled = gAgent.canAccessMature(); + bool adult_enabled = gAgent.canAccessAdult(); + + BOOL show_mature = mature_enabled && gSavedSettings.getBOOL("ShowMatureEvents"); + BOOL show_adult = adult_enabled && gSavedSettings.getBOOL("ShowAdultEvents"); for (handle_list_t::iterator iter = mVisibleRegions.begin(); iter != mVisibleRegions.end(); ++iter) { U64 handle = *iter; - LLSimInfo* siminfo = LLWorldMap::getInstance()->simInfoFromHandle(handle); - if (siminfo && (siminfo->mAccess == SIM_ACCESS_DOWN)) + LLSimInfo* info = LLWorldMap::getInstance()->simInfoFromHandle(handle); + if ((info == NULL) || (info->isDown())) { continue; } - LLWorldMap::agent_list_map_t::iterator counts_iter = LLWorldMap::getInstance()->mAgentLocationsMap.find(handle); - if (siminfo && siminfo->mShowAgentLocations && counts_iter != LLWorldMap::getInstance()->mAgentLocationsMap.end()) + // Infohubs + if (gSavedSettings.getBOOL("MapShowInfohubs")) { - // Show Individual agents (or little stacks where real agents are) - LLWorldMap::item_info_list_t& agentcounts = counts_iter->second; - S32 sim_agent_count = 0; - for (LLWorldMap::item_info_list_t::iterator iter = agentcounts.begin(); - iter != agentcounts.end(); ++iter) - { - const LLItemInfo& info = *iter; - S32 agent_count = info.mExtra; - sim_agent_count += info.mExtra; - // Here's how we'd choose the color if info.mID were available but it's not being sent: - //LLColor4 color = (agent_count == 1 && is_agent_friend(info.mID)) ? map_avatar_friend_color : map_avatar_color; - drawImageStack(info.mPosGlobal, sAvatarSmallImage, agent_count, 3.f, map_avatar_color); - } - LLWorldMap::getInstance()->mNumAgents[handle] = sim_agent_count; // override mNumAgents for this sim + drawGenericItems(info->getInfoHub(), sInfohubImage); } - else + // Telehubs + if (gSavedSettings.getBOOL("MapShowTelehubs")) + { + drawGenericItems(info->getTeleHub(), sTelehubImage); + } + // Land for sale + if (gSavedSettings.getBOOL("MapShowLandForSale")) { - // Show agent 'stack' at center of sim - S32 num_agents = LLWorldMap::getInstance()->mNumAgents[handle]; - if (num_agents > 0) + drawGenericItems(info->getLandForSale(), sForSaleImage); + // for 1.23, we're showing normal land and adult land in the same UI; you don't + // get a choice about which ones you want. If you're currently asking for adult + // content and land you'll get the adult land. + if (gAgent.canAccessAdult()) { - LLVector3d region_center = from_region_handle(handle); - region_center[VX] += REGION_WIDTH_METERS / 2; - region_center[VY] += REGION_WIDTH_METERS / 2; - // Reduce the stack size as you zoom out - always display at lease one agent where there is one or more - S32 agent_count = (S32)(((num_agents-1) * agents_scale + (num_agents-1) * 0.1f)+.1f) + 1; - drawImageStack(region_center, sAvatarSmallImage, agent_count, 3.f, map_avatar_color); + drawGenericItems(info->getLandForSaleAdult(), sForSaleAdultImage); } } + // PG Events + if (gSavedSettings.getBOOL("MapShowEvents")) + { + drawGenericItems(info->getPGEvent(), sEventImage); + } + // Mature Events + if (show_mature) + { + drawGenericItems(info->getMatureEvent(), sEventMatureImage); + } + // Adult Events + if (show_adult) + { + drawGenericItems(info->getAdultEvent(), sEventAdultImage); + } } } - -void LLWorldMapView::drawEvents() +void LLWorldMapView::drawAgents() { - bool mature_enabled = gAgent.canAccessMature(); - bool adult_enabled = gAgent.canAccessAdult(); - - BOOL show_pg = gSavedSettings.getBOOL("MapShowEvents"); - BOOL show_mature = mature_enabled && gSavedSettings.getBOOL("ShowMatureEvents"); - BOOL show_adult = adult_enabled && gSavedSettings.getBOOL("ShowAdultEvents"); + static LLUIColor map_avatar_color = LLUIColorTable::instance().getColor("MapAvatarColor", LLColor4::white); - // First the non-selected events - LLWorldMap::item_info_list_t::const_iterator e; - if (show_pg) + for (handle_list_t::iterator iter = mVisibleRegions.begin(); iter != mVisibleRegions.end(); ++iter) { - for (e = LLWorldMap::getInstance()->mPGEvents.begin(); e != LLWorldMap::getInstance()->mPGEvents.end(); ++e) + U64 handle = *iter; + LLSimInfo* siminfo = LLWorldMap::getInstance()->simInfoFromHandle(handle); + if ((siminfo == NULL) || (siminfo->isDown())) { - if (!e->mSelected) - { - drawGenericItem(*e, sEventImage); - } + continue; } - } - if (show_mature) - { - for (e = LLWorldMap::getInstance()->mMatureEvents.begin(); e != LLWorldMap::getInstance()->mMatureEvents.end(); ++e) - { - if (!e->mSelected) - { - drawGenericItem(*e, sEventMatureImage); - } - } - } - if (show_adult) - { - for (e = LLWorldMap::getInstance()->mAdultEvents.begin(); e != LLWorldMap::getInstance()->mAdultEvents.end(); ++e) - { - if (!e->mSelected) - { - drawGenericItem(*e, sEventAdultImage); - } - } - } - // Then the selected events - if (show_pg) - { - for (e = LLWorldMap::getInstance()->mPGEvents.begin(); e != LLWorldMap::getInstance()->mPGEvents.end(); ++e) + LLSimInfo::item_info_list_t::const_iterator it = siminfo->getAgentLocation().begin(); + while (it != siminfo->getAgentLocation().end()) { - if (e->mSelected) - { - drawGenericItem(*e, sEventImage); - } + // Show Individual agents (or little stacks where real agents are) + + // Here's how we'd choose the color if info.mID were available but it's not being sent: + // LLColor4 color = (agent_count == 1 && is_agent_friend(info.mID)) ? friend_color : avatar_color; + drawImageStack(it->getGlobalPosition(), sAvatarSmallImage, it->getCount(), 3.f, map_avatar_color); + ++it; } } - if (show_mature) - { - for (e = LLWorldMap::getInstance()->mMatureEvents.begin(); e != LLWorldMap::getInstance()->mMatureEvents.end(); ++e) - { - if (e->mSelected) - { - drawGenericItem(*e, sEventMatureImage); - } - } - } - if (show_adult) - { - for (e = LLWorldMap::getInstance()->mAdultEvents.begin(); e != LLWorldMap::getInstance()->mAdultEvents.end(); ++e) - { - if (e->mSelected) - { - drawGenericItem(*e, sEventAdultImage); - } - } - } } - void LLWorldMapView::drawFrustum() { // Draw frustum - F32 meters_to_pixels = gMapScale/ REGION_WIDTH_METERS; + F32 meters_to_pixels = sMapScale/ REGION_WIDTH_METERS; F32 horiz_fov = LLViewerCamera::getInstance()->getView() * LLViewerCamera::getInstance()->getAspect(); F32 far_clip_meters = LLViewerCamera::getInstance()->getFar(); @@ -1078,8 +918,8 @@ LLVector3 LLWorldMapView::globalPosToView( const LLVector3d& global_pos ) LLVector3 pos_local; pos_local.setVec(relative_pos_global); // convert to floats from doubles - pos_local.mV[VX] *= sPixelsPerMeter; - pos_local.mV[VY] *= sPixelsPerMeter; + pos_local.mV[VX] *= sMapScale / REGION_WIDTH_METERS; + pos_local.mV[VY] *= sMapScale / REGION_WIDTH_METERS; // leave Z component in meters @@ -1163,7 +1003,7 @@ LLVector3d LLWorldMapView::viewPosToGlobal( S32 x, S32 y ) LLVector3 pos_local( (F32)x, (F32)y, 0.f ); - pos_local *= ( REGION_WIDTH_METERS / gMapScale ); + pos_local *= ( REGION_WIDTH_METERS / sMapScale ); LLVector3d pos_global; pos_global.setVec( pos_local ); @@ -1184,23 +1024,20 @@ LLVector3d LLWorldMapView::viewPosToGlobal( S32 x, S32 y ) BOOL LLWorldMapView::handleToolTip( S32 x, S32 y, MASK mask ) { LLVector3d pos_global = viewPosToGlobal(x, y); - + U64 handle = to_region_handle(pos_global); std::string tooltip_msg; - LLSimInfo* info = LLWorldMap::getInstance()->simInfoFromPosGlobal(pos_global); + LLSimInfo* info = LLWorldMap::getInstance()->simInfoFromHandle(handle); if (info) { LLViewerRegion *region = gAgent.getRegion(); - std::string message = - llformat("%s (%s)", - info->mName.c_str(), - LLViewerRegion::accessToString(info->mAccess).c_str()); + std::string message = llformat("%s (%s)", info->getName().c_str(), info->getAccessString().c_str()); - if (info->mAccess != SIM_ACCESS_DOWN) + if (!info->isDown()) { - S32 agent_count = LLWorldMap::getInstance()->mNumAgents[info->mHandle]; - if (region && region->getHandle() == info->mHandle) + S32 agent_count = info->getAgentCount(); + if (region && (region->getHandle() == handle)) { ++agent_count; // Bump by 1 if we're here } @@ -1209,6 +1046,8 @@ BOOL LLWorldMapView::handleToolTip( S32 x, S32 y, MASK mask ) // zoomed out, so don't display anything about the count. JC if (agent_count > 0) { + // Merov: i18n horror!!! Even using gettext(), concatenating strings is not localizable. + // The singular/plural switch form here under might make no sense in some languages. Don't do that. message += llformat("\n%d ", agent_count); if (agent_count == 1) @@ -1224,7 +1063,7 @@ BOOL LLWorldMapView::handleToolTip( S32 x, S32 y, MASK mask ) tooltip_msg.assign( message ); // Optionally show region flags - std::string region_flags = LLViewerRegion::regionFlagsToString(info->mRegionFlags); + std::string region_flags = info->getFlagsString(); if (!region_flags.empty()) { @@ -1264,6 +1103,9 @@ static void drawDot(F32 x_pixels, F32 y_pixels, } else { + // Draw V indicator for above or below + // *TODO: Replace this vector drawing with icons + F32 left = x_pixels - dot_radius; F32 right = x_pixels + dot_radius; F32 center = (left + right) * 0.5f; @@ -1272,13 +1114,14 @@ static void drawDot(F32 x_pixels, F32 y_pixels, gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); gGL.color4fv( color.mV ); - LLUI::setLineWidth(1.5f); - F32 h_bar = relative_z > HEIGHT_THRESHOLD ? top : bottom; // horizontal bar Y + LLUI::setLineWidth(3.0f); + F32 point = relative_z > HEIGHT_THRESHOLD ? top : bottom; // Y pos of the point of the V + F32 back = relative_z > HEIGHT_THRESHOLD ? bottom : top; // Y pos of the ends of the V gGL.begin( LLRender::LINES ); - gGL.vertex2f(center, top); - gGL.vertex2f(left, h_bar); - gGL.vertex2f(right, h_bar); - gGL.vertex2f(right, bottom); + gGL.vertex2f(left, back); + gGL.vertex2f(center, point); + gGL.vertex2f(center, point); + gGL.vertex2f(right, back); gGL.end(); LLUI::setLineWidth(1.0f); } @@ -1293,7 +1136,7 @@ void LLWorldMapView::drawAvatar(F32 x_pixels, F32 dot_radius) { const F32 HEIGHT_THRESHOLD = 7.f; - LLUIImagePtr dot_image = sAvatarSmallImage; + LLUIImagePtr dot_image = sAvatarLevelImage; if(relative_z < -HEIGHT_THRESHOLD) { dot_image = sAvatarBelowImage; @@ -1302,10 +1145,12 @@ void LLWorldMapView::drawAvatar(F32 x_pixels, { dot_image = sAvatarAboveImage; } - dot_image->draw( - llround(x_pixels) - dot_image->getWidth()/2, - llround(y_pixels) - dot_image->getHeight()/2, - color); + S32 dot_width = llround(dot_radius * 2.f); + dot_image->draw(llround(x_pixels - dot_radius), + llround(y_pixels - dot_radius), + dot_width, + dot_width, + color); } // Pass relative Z of 0 to draw at same level. @@ -1566,7 +1411,7 @@ void LLWorldMapView::reshape( S32 width, S32 height, BOOL called_from_parent ) bool LLWorldMapView::checkItemHit(S32 x, S32 y, LLItemInfo& item, LLUUID* id, bool track) { - LLVector3 pos_view = globalPosToView(item.mPosGlobal); + LLVector3 pos_view = globalPosToView(item.getGlobalPosition()); S32 item_x = llround(pos_view.mV[VX]); S32 item_y = llround(pos_view.mV[VY]); @@ -1575,12 +1420,12 @@ bool LLWorldMapView::checkItemHit(S32 x, S32 y, LLItemInfo& item, LLUUID* id, bo if (y < item_y - BIG_DOT_RADIUS) return false; if (y > item_y + BIG_DOT_RADIUS) return false; - LLSimInfo* sim_info = LLWorldMap::getInstance()->simInfoFromHandle(item.mRegionHandle); + LLSimInfo* sim_info = LLWorldMap::getInstance()->simInfoFromHandle(item.getRegionHandle()); if (sim_info) { if (track) { - gFloaterWorldMap->trackLocation(item.mPosGlobal); + gFloaterWorldMap->trackLocation(item.getGlobalPosition()); } } @@ -1589,8 +1434,8 @@ bool LLWorldMapView::checkItemHit(S32 x, S32 y, LLItemInfo& item, LLUUID* id, bo gFloaterWorldMap->trackGenericItem(item); } - item.mSelected = TRUE; - *id = item.mID; +// item.setSelected(true); + *id = item.getUUID(); return true; } @@ -1613,108 +1458,116 @@ void LLWorldMapView::handleClick(S32 x, S32 y, MASK mask, *hit_type = 0; // hit nothing - LLWorldMap::getInstance()->mIsTrackingUnknownLocation = FALSE; - LLWorldMap::getInstance()->mIsTrackingDoubleClick = FALSE; - LLWorldMap::getInstance()->mIsTrackingCommit = FALSE; + LLWorldMap::getInstance()->cancelTracking(); - LLWorldMap::item_info_list_t::iterator it; - - // clear old selected stuff - for (it = LLWorldMap::getInstance()->mPGEvents.begin(); it != LLWorldMap::getInstance()->mPGEvents.end(); ++it) - { - (*it).mSelected = FALSE; - } - for (it = LLWorldMap::getInstance()->mMatureEvents.begin(); it != LLWorldMap::getInstance()->mMatureEvents.end(); ++it) + S32 level = LLWorldMipmap::scaleToLevel(sMapScale); + // If the zoom level is not too far out already, test hits + if (level <= DRAW_SIMINFO_THRESHOLD) { - (*it).mSelected = FALSE; - } - for (it = LLWorldMap::getInstance()->mAdultEvents.begin(); it != LLWorldMap::getInstance()->mAdultEvents.end(); ++it) - { - (*it).mSelected = FALSE; - } - for (it = LLWorldMap::getInstance()->mLandForSale.begin(); it != LLWorldMap::getInstance()->mLandForSale.end(); ++it) - { - (*it).mSelected = FALSE; - } - - // Select event you clicked on - if (gSavedSettings.getBOOL("MapShowEvents")) - { - for (it = LLWorldMap::getInstance()->mPGEvents.begin(); it != LLWorldMap::getInstance()->mPGEvents.end(); ++it) - { - LLItemInfo& event = *it; - - if (checkItemHit(x, y, event, id, false)) - { - *hit_type = MAP_ITEM_PG_EVENT; - mItemPicked = TRUE; - gFloaterWorldMap->trackEvent(event); - return; - } - } - } - if (gSavedSettings.getBOOL("ShowMatureEvents")) - { - for (it = LLWorldMap::getInstance()->mMatureEvents.begin(); it != LLWorldMap::getInstance()->mMatureEvents.end(); ++it) - { - LLItemInfo& event = *it; - - if (checkItemHit(x, y, event, id, false)) - { - *hit_type = MAP_ITEM_MATURE_EVENT; - mItemPicked = TRUE; - gFloaterWorldMap->trackEvent(event); - return; - } - } - } - if (gSavedSettings.getBOOL("ShowAdultEvents")) - { - for (it = LLWorldMap::getInstance()->mAdultEvents.begin(); it != LLWorldMap::getInstance()->mAdultEvents.end(); ++it) - { - LLItemInfo& event = *it; - - if (checkItemHit(x, y, event, id, false)) - { - *hit_type = MAP_ITEM_ADULT_EVENT; - mItemPicked = TRUE; - gFloaterWorldMap->trackEvent(event); - return; - } - } - } - - if (gSavedSettings.getBOOL("MapShowLandForSale")) - { - for (it = LLWorldMap::getInstance()->mLandForSale.begin(); it != LLWorldMap::getInstance()->mLandForSale.end(); ++it) - { - LLItemInfo& land = *it; + bool show_mature = gAgent.canAccessMature() && gSavedSettings.getBOOL("ShowMatureEvents"); + bool show_adult = gAgent.canAccessAdult() && gSavedSettings.getBOOL("ShowAdultEvents"); - if (checkItemHit(x, y, land, id, true)) - { - *hit_type = MAP_ITEM_LAND_FOR_SALE; - mItemPicked = TRUE; - return; - } - } - - for (it = LLWorldMap::getInstance()->mLandForSaleAdult.begin(); it != LLWorldMap::getInstance()->mLandForSaleAdult.end(); ++it) + // Test hits if trackable data are displayed, otherwise, we don't even bother + if (gSavedSettings.getBOOL("MapShowEvents") || show_mature || show_adult || gSavedSettings.getBOOL("MapShowLandForSale")) { - LLItemInfo& land = *it; - - if (checkItemHit(x, y, land, id, true)) + // Iterate through the visible regions + for (handle_list_t::iterator iter = mVisibleRegions.begin(); iter != mVisibleRegions.end(); ++iter) { - *hit_type = MAP_ITEM_LAND_FOR_SALE_ADULT; - mItemPicked = TRUE; - return; + U64 handle = *iter; + LLSimInfo* siminfo = LLWorldMap::getInstance()->simInfoFromHandle(handle); + if ((siminfo == NULL) || (siminfo->isDown())) + { + continue; + } + // If on screen check hits with the visible item lists + if (gSavedSettings.getBOOL("MapShowEvents")) + { + LLSimInfo::item_info_list_t::const_iterator it = siminfo->getPGEvent().begin(); + while (it != siminfo->getPGEvent().end()) + { + LLItemInfo event = *it; + if (checkItemHit(x, y, event, id, false)) + { + *hit_type = MAP_ITEM_PG_EVENT; + mItemPicked = TRUE; + gFloaterWorldMap->trackEvent(event); + return; + } + ++it; + } + } + if (show_mature) + { + LLSimInfo::item_info_list_t::const_iterator it = siminfo->getMatureEvent().begin(); + while (it != siminfo->getMatureEvent().end()) + { + LLItemInfo event = *it; + if (checkItemHit(x, y, event, id, false)) + { + *hit_type = MAP_ITEM_MATURE_EVENT; + mItemPicked = TRUE; + gFloaterWorldMap->trackEvent(event); + return; + } + ++it; + } + } + if (show_adult) + { + LLSimInfo::item_info_list_t::const_iterator it = siminfo->getAdultEvent().begin(); + while (it != siminfo->getAdultEvent().end()) + { + LLItemInfo event = *it; + if (checkItemHit(x, y, event, id, false)) + { + *hit_type = MAP_ITEM_ADULT_EVENT; + mItemPicked = TRUE; + gFloaterWorldMap->trackEvent(event); + return; + } + ++it; + } + } + if (gSavedSettings.getBOOL("MapShowLandForSale")) + { + LLSimInfo::item_info_list_t::const_iterator it = siminfo->getLandForSale().begin(); + while (it != siminfo->getLandForSale().end()) + { + LLItemInfo event = *it; + if (checkItemHit(x, y, event, id, true)) + { + *hit_type = MAP_ITEM_LAND_FOR_SALE; + mItemPicked = TRUE; + return; + } + ++it; + } + // for 1.23, we're showing normal land and adult land in the same UI; you don't + // get a choice about which ones you want. If you're currently asking for adult + // content and land you'll get the adult land. + if (gAgent.canAccessAdult()) + { + LLSimInfo::item_info_list_t::const_iterator it = siminfo->getLandForSaleAdult().begin(); + while (it != siminfo->getLandForSaleAdult().end()) + { + LLItemInfo event = *it; + if (checkItemHit(x, y, event, id, true)) + { + *hit_type = MAP_ITEM_LAND_FOR_SALE_ADULT; + mItemPicked = TRUE; + return; + } + ++it; + } + } + } } } } - // If we get here, we haven't clicked on an icon + // If we get here, we haven't clicked on anything gFloaterWorldMap->trackLocation(pos_global); mItemPicked = FALSE; - *id = LLUUID::null; return; } @@ -1775,59 +1628,36 @@ BOOL LLWorldMapView::handleMouseUp( S32 x, S32 y, MASK mask ) return FALSE; } -U32 LLWorldMapView::updateBlock(S32 block_x, S32 block_y) +void LLWorldMapView::updateVisibleBlocks() { - U32 blocks_requested = 0; - S32 offset = block_x | (block_y * MAP_BLOCK_RES); - if (!LLWorldMap::getInstance()->mMapBlockLoaded[LLWorldMap::getInstance()->mCurrentMap][offset]) + if (LLWorldMipmap::scaleToLevel(sMapScale) > DRAW_SIMINFO_THRESHOLD) { -// llinfos << "Loading Block (" << block_x << "," << block_y << ")" << llendl; - LLWorldMap::getInstance()->sendMapBlockRequest(block_x << 3, block_y << 3, (block_x << 3) + 7, (block_y << 3) + 7); - LLWorldMap::getInstance()->mMapBlockLoaded[LLWorldMap::getInstance()->mCurrentMap][offset] = TRUE; - blocks_requested++; + // If we're zoomed out too much, we just don't load all those sim info: too much! + return; } - return blocks_requested; -} -U32 LLWorldMapView::updateVisibleBlocks() -{ - if (gMapScale < SIM_MAP_SCALE) - { - // We don't care what is loaded if we're zoomed out - return 0; - } + // Load the blocks visible in the current World Map view + // Get the World Map view coordinates and boundaries LLVector3d camera_global = gAgent.getCameraPositionGlobal(); - - F32 pixels_per_region = gMapScale; const S32 width = getRect().getWidth(); const S32 height = getRect().getHeight(); - // Convert pan to sim coordinates - S32 world_center_x_lo = S32(((-sPanX - width/2) / pixels_per_region) + (camera_global.mdV[0] / REGION_WIDTH_METERS)); - S32 world_center_x_hi = S32(((-sPanX + width/2) / pixels_per_region) + (camera_global.mdV[0] / REGION_WIDTH_METERS)); - S32 world_center_y_lo = S32(((-sPanY - height/2) / pixels_per_region) + (camera_global.mdV[1] / REGION_WIDTH_METERS)); - S32 world_center_y_hi = S32(((-sPanY + height/2)/ pixels_per_region) + (camera_global.mdV[1] / REGION_WIDTH_METERS)); - - // Find the corresponding 8x8 block - S32 world_block_x_lo = world_center_x_lo >> 3; - S32 world_block_x_hi = world_center_x_hi >> 3; - S32 world_block_y_lo = world_center_y_lo >> 3; - S32 world_block_y_hi = world_center_y_hi >> 3; - - U32 blocks_requested = 0; - const U32 max_blocks_requested = 9; + const F32 half_width = F32(width) / 2.0f; + const F32 half_height = F32(height) / 2.0f; - for (S32 block_x = llmax(world_block_x_lo, 0); block_x <= llmin(world_block_x_hi, MAP_BLOCK_RES-1); ++block_x) - { - for (S32 block_y = llmax(world_block_y_lo, 0); block_y <= llmin(world_block_y_hi, MAP_BLOCK_RES-1); ++block_y) - { - blocks_requested += updateBlock(block_x, block_y); - if (blocks_requested >= max_blocks_requested) - return blocks_requested; - } - } - return blocks_requested; -} + // Compute center into sim grid coordinates + S32 world_center_x = S32((-sPanX / sMapScale) + (camera_global.mdV[0] / REGION_WIDTH_METERS)); + S32 world_center_y = S32((-sPanY / sMapScale) + (camera_global.mdV[1] / REGION_WIDTH_METERS)); + + // Compute the boundaries into sim grid coordinates + S32 world_left = world_center_x - S32(half_width / sMapScale) - 1; + S32 world_right = world_center_x + S32(half_width / sMapScale) + 1; + S32 world_bottom = world_center_y - S32(half_height / sMapScale) - 1; + S32 world_top = world_center_y + S32(half_height / sMapScale) + 1; + + //LL_INFOS("World Map") << "LLWorldMapView::updateVisibleBlocks() : sMapScale = " << sMapScale << ", left = " << world_left << ", right = " << world_right << ", bottom = " << world_bottom << ", top = " << world_top << LL_ENDL; + LLWorldMap::getInstance()->updateRegions(world_left, world_bottom, world_right, world_top); +} BOOL LLWorldMapView::handleHover( S32 x, S32 y, MASK mask ) { @@ -1916,16 +1746,16 @@ BOOL LLWorldMapView::handleDoubleClick( S32 x, S32 y, MASK mask ) } default: { - if (LLWorldMap::getInstance()->mIsTrackingUnknownLocation) + if (LLWorldMap::getInstance()->isTracking()) { - LLWorldMap::getInstance()->mIsTrackingDoubleClick = TRUE; + LLWorldMap::getInstance()->setTrackingDoubleClick(); } else { // Teleport if we got a valid location LLVector3d pos_global = viewPosToGlobal(x,y); LLSimInfo* sim_info = LLWorldMap::getInstance()->simInfoFromPosGlobal(pos_global); - if (sim_info && sim_info->mAccess != SIM_ACCESS_DOWN) + if (sim_info && !sim_info->isDown()) { gAgent.teleportViaLocation( pos_global ); } diff --git a/indra/newview/llworldmapview.h b/indra/newview/llworldmapview.h index 66793f0101..9eecacb2d8 100644 --- a/indra/newview/llworldmapview.h +++ b/indra/newview/llworldmapview.h @@ -30,27 +30,23 @@ * $/LicenseInfo$ */ -// Global map of the world. +// View of the global map of the world + +// The data (model) for the global map (a singleton, unique to the application instance) is +// in LLWorldMap and is typically accessed using LLWorldMap::getInstance() #ifndef LL_LLWORLDMAPVIEW_H #define LL_LLWORLDMAPVIEW_H #include "llpanel.h" -#include "v3math.h" -#include "v3dmath.h" -#include "v4color.h" -#include "llviewertexture.h" -#include "llmapimagetype.h" #include "llworldmap.h" - -class LLItemInfo; +#include "v4color.h" const S32 DEFAULT_TRACKING_ARROW_SIZE = 16; -class LLColor4; -class LLColor4U; -class LLCoordGL; -class LLViewerTexture; +class LLUUID; +class LLVector3d; +class LLVector3; class LLTextBox; @@ -77,22 +73,26 @@ public: bool checkItemHit(S32 x, S32 y, LLItemInfo& item, LLUUID* id, bool track); void handleClick(S32 x, S32 y, MASK mask, S32* hit_type, LLUUID* id); - // Scale and pan are shared across all instances. + // Scale and pan are shared across all instances! (i.e. Terrain and Objects maps are always registered) static void setScale( F32 scale ); static void translatePan( S32 delta_x, S32 delta_y ); static void setPan( S32 x, S32 y, BOOL snap = TRUE ); + // Return true if the current scale level is above the threshold for accessing region info + static bool showRegionInfo(); LLVector3 globalPosToView(const LLVector3d& global_pos); LLVector3d viewPosToGlobal(S32 x,S32 y); virtual void draw(); - void drawGenericItems(const LLWorldMap::item_info_list_t& items, LLUIImagePtr image); + void drawGenericItems(const LLSimInfo::item_info_list_t& items, LLUIImagePtr image); void drawGenericItem(const LLItemInfo& item, LLUIImagePtr image); void drawImage(const LLVector3d& global_pos, LLUIImagePtr image, const LLColor4& color = LLColor4::white); void drawImageStack(const LLVector3d& global_pos, LLUIImagePtr image, U32 count, F32 offset, const LLColor4& color); void drawAgents(); - void drawEvents(); + void drawItems(); void drawFrustum(); + void drawMipmap(S32 width, S32 height); + bool drawMipmapLevel(S32 width, S32 height, S32 level, bool load = true); static void cleanupTextures(); @@ -108,7 +108,7 @@ public: F32 y_pixels, const LLColor4& color, F32 relative_z = 0.f, - F32 dot_radius = 3.f); + F32 dot_radius = 5.f); static void drawTrackingCircle( const LLRect& rect, S32 x, S32 y, const LLColor4& color, @@ -129,9 +129,7 @@ public: static void clearLastClick() { sHandledLastClick = FALSE; } // if the view changes, download additional sim info as needed - // return value is number of blocks newly requested. - U32 updateBlock(S32 block_x, S32 block_y); - U32 updateVisibleBlocks(); + void updateVisibleBlocks(); protected: void setDirectionPos( LLTextBox* text_box, F32 rotation ); @@ -140,11 +138,13 @@ protected: public: LLColor4 mBackgroundColor; - static LLUIImagePtr sAvatarYouSmallImage; static LLUIImagePtr sAvatarSmallImage; - static LLUIImagePtr sAvatarLargeImage; + static LLUIImagePtr sAvatarYouImage; + static LLUIImagePtr sAvatarYouLargeImage; + static LLUIImagePtr sAvatarLevelImage; static LLUIImagePtr sAvatarAboveImage; static LLUIImagePtr sAvatarBelowImage; + static LLUIImagePtr sTelehubImage; static LLUIImagePtr sInfohubImage; static LLUIImagePtr sHomeImage; @@ -157,9 +157,7 @@ public: static LLUIImagePtr sForSaleImage; static LLUIImagePtr sForSaleAdultImage; - static F32 sThresholdA; - static F32 sThresholdB; - static F32 sPixelsPerMeter; // world meters to map pixels + static F32 sMapScale; // scale = size of a region in pixels BOOL mItemPicked; @@ -169,6 +167,7 @@ public: static F32 sTargetPanY; // in pixels static S32 sTrackingArrowX; static S32 sTrackingArrowY; + static bool sVisibleTilesLoaded; // Are we mid-pan from a user drag? BOOL mPanning; @@ -191,10 +190,14 @@ public: static BOOL sHandledLastClick; S32 mSelectIDStart; + // Keep the list of regions that are displayed on screen. Avoids iterating through the whole region map after draw(). typedef std::vector<U64> handle_list_t; handle_list_t mVisibleRegions; // set every frame static std::map<std::string,std::string> sStringsMap; + +private: + void drawTileOutline(S32 level, F32 top, F32 left, F32 bottom, F32 right); }; #endif diff --git a/indra/newview/llworldmipmap.cpp b/indra/newview/llworldmipmap.cpp new file mode 100644 index 0000000000..8d3165b98c --- /dev/null +++ b/indra/newview/llworldmipmap.cpp @@ -0,0 +1,275 @@ +/** + * @file llworldmipmap.cpp + * @brief Data storage for the S3 mipmap of the entire world. + * + * $LicenseInfo:firstyear=2003&license=viewergpl$ + * + * Copyright (c) 2003-2009, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "llworldmipmap.h" + +#include "llviewertexturelist.h" +#include "math.h" // log() + +// Turn this on to output tile stats in the standard output +#define DEBUG_TILES_STAT 0 + +LLWorldMipmap::LLWorldMipmap() : + mCurrentLevel(0) +{ +} + +LLWorldMipmap::~LLWorldMipmap() +{ + reset(); +} + +// Delete all sublevel maps and clean them +void LLWorldMipmap::reset() +{ + for (int level = 0; level < MAP_LEVELS; level++) + { + mWorldObjectsMipMap[level].clear(); + } +} + +// This method should be called before each use of the mipmap (typically, before each draw), so that to let +// the boost level of unused tiles to drop to 0 (BOOST_NONE). +// Tiles that are accessed have had their boost level pushed to BOOST_MAP_VISIBLE so we can identify them. +// The result of this strategy is that if a tile is not used during 2 consecutive loops, its boost level drops to 0. +void LLWorldMipmap::equalizeBoostLevels() +{ +#if DEBUG_TILES_STAT + S32 nb_missing = 0; + S32 nb_tiles = 0; + S32 nb_visible = 0; +#endif // DEBUG_TILES_STAT + // For each level + for (S32 level = 0; level < MAP_LEVELS; level++) + { + sublevel_tiles_t& level_mipmap = mWorldObjectsMipMap[level]; + // For each tile + for (sublevel_tiles_t::iterator iter = level_mipmap.begin(); iter != level_mipmap.end(); iter++) + { + LLPointer<LLViewerFetchedTexture> img = iter->second; + S32 current_boost_level = img->getBoostLevel(); + if (current_boost_level == LLViewerTexture::BOOST_MAP_VISIBLE) + { + // If level was BOOST_MAP_VISIBLE, the tile has been used in the last draw so keep it high + img->setBoostLevel(LLViewerTexture::BOOST_MAP); + } + else + { + // If level was BOOST_MAP only (or anything else...), the tile wasn't used in the last draw + // so we drop its boost level to BOOST_NONE. + img->setBoostLevel(LLViewerTexture::BOOST_NONE); + } +#if DEBUG_TILES_STAT + // Increment some stats if compile option on + nb_tiles++; + if (current_boost_level == LLViewerTexture::BOOST_MAP_VISIBLE) + { + nb_visible++; + } + if (img->isMissingAsset()) + { + nb_missing++; + } +#endif // DEBUG_TILES_STAT + } + } +#if DEBUG_TILES_STAT + LL_INFOS("World Map") << "LLWorldMipmap tile stats : total requested = " << nb_tiles << ", visible = " << nb_visible << ", missing = " << nb_missing << LL_ENDL; +#endif // DEBUG_TILES_STAT +} + +// This method should be used when the mipmap is not actively used for a while, e.g., the map UI is hidden +void LLWorldMipmap::dropBoostLevels() +{ + // For each level + for (S32 level = 0; level < MAP_LEVELS; level++) + { + sublevel_tiles_t& level_mipmap = mWorldObjectsMipMap[level]; + // For each tile + for (sublevel_tiles_t::iterator iter = level_mipmap.begin(); iter != level_mipmap.end(); iter++) + { + LLPointer<LLViewerFetchedTexture> img = iter->second; + img->setBoostLevel(LLViewerTexture::BOOST_NONE); + } + } +} + +LLPointer<LLViewerFetchedTexture> LLWorldMipmap::getObjectsTile(U32 grid_x, U32 grid_y, S32 level, bool load) +{ + // Check the input data + llassert(level <= MAP_LEVELS); + llassert(level >= 1); + + // If the *loading* level changed, cleared the new level from "missed" tiles + // so that we get a chance to reload them + if (load && (level != mCurrentLevel)) + { + cleanMissedTilesFromLevel(level); + mCurrentLevel = level; + } + + // Build the region handle + U64 handle = convertGridToHandle(grid_x, grid_y); + + // Check if the image is around already + sublevel_tiles_t& level_mipmap = mWorldObjectsMipMap[level-1]; + sublevel_tiles_t::iterator found = level_mipmap.find(handle); + + // If not there and load on, go load it + if (found == level_mipmap.end()) + { + if (load) + { + // Load it + LLPointer<LLViewerFetchedTexture> img = loadObjectsTile(grid_x, grid_y, level); + // Insert the image in the map + level_mipmap.insert(sublevel_tiles_t::value_type( handle, img )); + // Find the element again in the map (it's there now...) + found = level_mipmap.find(handle); + } + else + { + // Return with NULL if not found and we're not trying to load + return NULL; + } + } + + // Get the image pointer and check if this asset is missing + LLPointer<LLViewerFetchedTexture> img = found->second; + if (img->isMissingAsset()) + { + // Return NULL if asset missing + return NULL; + } + else + { + // Boost the tile level so to mark it's in use *if* load on + if (load) + { + img->setBoostLevel(LLViewerTexture::BOOST_MAP_VISIBLE); + } + return img; + } +} + +LLPointer<LLViewerFetchedTexture> LLWorldMipmap::loadObjectsTile(U32 grid_x, U32 grid_y, S32 level) +{ + // Get the grid coordinates +// std::string imageurl = llformat("http://map.secondlife.com.s3.amazonaws.com/%d/%05d/%05d/map-%d-%d-%d-objects.jpg", + std::string imageurl = llformat("http://map.secondlife.com.s3.amazonaws.com/map-%d-%d-%d-objects.jpg", + level, grid_x, grid_y, level, grid_x, grid_y); + + // DO NOT COMMIT!! DEBUG ONLY!!! + // Use a local jpeg for every tile to test map speed without S3 access + //imageurl = "file://C:\\Develop\\mapserver-distribute-3\\indra\\build-vc80\\mapserver\\relwithdebinfo\\regions\\00995\\01001\\region-995-1001-prims.jpg"; + // END DEBUG + //LL_INFOS("World Map") << "LLWorldMipmap::loadObjectsTile(), URL = " << imageurl << LL_ENDL; + + LLPointer<LLViewerFetchedTexture> img = LLViewerTextureManager::getFetchedTextureFromUrl(imageurl, TRUE, FALSE, LLViewerTexture::LOD_TEXTURE); + img->setBoostLevel(LLViewerTexture::BOOST_MAP); + + // Return the smart pointer + return img; +} + +// This method is used to clean up a level from tiles marked as "missing". +// The idea is to allow tiles that have been improperly marked missing to be reloaded when retraversing the level again. +// When zooming in and out rapidly, some tiles are never properly loaded and, eventually marked missing. +// This creates "blue" areas in a subresolution that never got a chance to reload if we don't clean up the level. +void LLWorldMipmap::cleanMissedTilesFromLevel(S32 level) +{ + // Check the input data + llassert(level <= MAP_LEVELS); + llassert(level >= 0); + + // This happens when the object is first initialized + if (level == 0) + { + return; + } + + // Iterate through the subresolution level and suppress the tiles that are marked as missing + // Note: erasing in a map while iterating through it is bug prone. Using a postfix increment is mandatory here. + sublevel_tiles_t& level_mipmap = mWorldObjectsMipMap[level-1]; + sublevel_tiles_t::iterator it = level_mipmap.begin(); + while (it != level_mipmap.end()) + { + LLPointer<LLViewerFetchedTexture> img = it->second; + if (img->isMissingAsset()) + { + level_mipmap.erase(it++); + } + else + { + ++it; + } + } + return; +} + +// static methods +// Compute the level in the world mipmap (between 1 and MAP_LEVELS, as in the URL) given the scale (size of a sim in screen pixels) +S32 LLWorldMipmap::scaleToLevel(F32 scale) +{ + // If scale really small, picks up the higest level there is (lowest resolution) + if (scale <= F32_MIN) + return MAP_LEVELS; + // Compute the power of two resolution level knowing the base level + S32 level = llfloor((log(REGION_WIDTH_METERS/scale)/log(2.0f)) + 1.0f); + // Check bounds and return the value + if (level > MAP_LEVELS) + return MAP_LEVELS; + else if (level < 1) + return 1; + else + return level; +} + +// Convert world coordinates to mipmap grid coordinates at a given level (between 1 and MAP_LEVELS) +void LLWorldMipmap::globalToMipmap(F64 global_x, F64 global_y, S32 level, U32* grid_x, U32* grid_y) +{ + // Check the input data + llassert(level <= MAP_LEVELS); + llassert(level >= 1); + + // Convert world coordinates into grid coordinates + *grid_x = lltrunc(global_x/REGION_WIDTH_METERS); + *grid_y = lltrunc(global_y/REGION_WIDTH_METERS); + // Compute the valid grid coordinates at that level of the mipmap + S32 regions_in_tile = 1 << (level - 1); + *grid_x = *grid_x - (*grid_x % regions_in_tile); + *grid_y = *grid_y - (*grid_y % regions_in_tile); +} + + diff --git a/indra/newview/llworldmipmap.h b/indra/newview/llworldmipmap.h new file mode 100644 index 0000000000..ecf1003468 --- /dev/null +++ b/indra/newview/llworldmipmap.h @@ -0,0 +1,100 @@ +/** + * @file llworldmipmap.h + * @brief Data storage for the S3 mipmap of the entire world. + * + * $LicenseInfo:firstyear=2003&license=viewergpl$ + * + * Copyright (c) 2003-2009, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#ifndef LL_LLWORLDMIPMAP_H +#define LL_LLWORLDMIPMAP_H + +#include <map> + +#include "llmemory.h" // LLPointer +#include "indra_constants.h" // REGION_WIDTH_UNITS +#include "llregionhandle.h" // to_region_handle() + +class LLViewerFetchedTexture; + +// LLWorldMipmap : Mipmap handling of all the tiles used to render the world at any resolution. +// This class provides a clean structured access to the hierarchy of tiles stored in the +// Amazon S3 repository and abstracts its directory/file structure. +// The interface of this class though still assumes that the caller knows the general level/tiles +// structure (at least, that it exists...) but doesn't requite the caller to know the details of it. +// IOW, you need to know that rendering levels exists as well as grid coordinates for regions, +// but you can ignore where those tiles are located, how to get them, etc... +// The class API gives you back LLPointer<LLViewerFetchedTexture> per tile. + +// See llworldmipmapview.cpp for the implementation of a class who knows how to render an LLWorldMipmap. + +// Implementation notes: +// - On the S3 servers, the tiles are rendered in 2 flavors: Objects and Terrain. +// - For the moment, LLWorldMipmap implements access only to the Objects tiles. +class LLWorldMipmap +{ +public: + // Parameters of the mipmap + static const S32 MAP_LEVELS = 8; // Number of subresolution levels computed by the mapserver + static const S32 MAP_TILE_SIZE = 256; // Width in pixels of the tiles computed by the mapserver + + LLWorldMipmap(); + ~LLWorldMipmap(); + + // Clear up the maps and release all image handles + void reset(); + // Manage the boost levels between loops (typically draw() loops) + void equalizeBoostLevels(); + // Drop the boost levels to none (used when hiding the map) + void dropBoostLevels(); + // Get the tile smart pointer, does the loading if necessary + LLPointer<LLViewerFetchedTexture> getObjectsTile(U32 grid_x, U32 grid_y, S32 level, bool load = true); + + // Helper functions: those are here as they depend solely on the topology of the mipmap though they don't access it + // Convert sim scale (given in sim width in display pixels) into a mipmap level + static S32 scaleToLevel(F32 scale); + // Convert world coordinates to mipmap grid coordinates at a given level + static void globalToMipmap(F64 global_x, F64 global_y, S32 level, U32* grid_x, U32* grid_y); + +private: + // Get a handle (key) from grid coordinates + U64 convertGridToHandle(U32 grid_x, U32 grid_y) { return to_region_handle(grid_x * REGION_WIDTH_UNITS, grid_y * REGION_WIDTH_UNITS); } + // Load the relevant tile from S3 + LLPointer<LLViewerFetchedTexture> loadObjectsTile(U32 grid_x, U32 grid_y, S32 level); + // Clear a level from its "missing" tiles + void cleanMissedTilesFromLevel(S32 level); + + // The mipmap is organized by resolution level (MAP_LEVELS of them). Each resolution level is an std::map + // using a region_handle as a key and storing a smart pointer to the image as a value. + typedef std::map<U64, LLPointer<LLViewerFetchedTexture> > sublevel_tiles_t; + sublevel_tiles_t mWorldObjectsMipMap[MAP_LEVELS]; +// sublevel_tiles_t mWorldTerrainMipMap[MAP_LEVELS]; + + S32 mCurrentLevel; // The level last accessed by a getObjectsTile() +}; + +#endif // LL_LLWORLDMIPMAP_H diff --git a/indra/newview/pipeline.cpp b/indra/newview/pipeline.cpp index 3390463aa6..2daaf77772 100644 --- a/indra/newview/pipeline.cpp +++ b/indra/newview/pipeline.cpp @@ -1464,6 +1464,7 @@ F32 LLPipeline::calcPixelArea(LLVector3 center, LLVector3 size, LLCamera &camera F32 dist = lookAt.length(); //ramp down distance for nearby objects + //shrink dist by dist/16. if (dist < 16.f) { dist /= 16.f; @@ -3002,15 +3003,6 @@ void LLPipeline::renderGeom(LLCamera& camera, BOOL forceVBOUpdate) LLAppViewer::instance()->pingMainloopTimeout("Pipeline:ForceVBO"); - //by bao - //fake vertex buffer updating - //to guaranttee at least updating one VBO buffer every frame - //to walk around the bug caused by ATI card --> DEV-3855 - // - if(forceVBOUpdate) - gSky.mVOSkyp->updateDummyVertexBuffer() ; - - // Initialize lots of GL state to "safe" values glMatrixMode(GL_TEXTURE); glLoadIdentity(); diff --git a/indra/newview/res/viewerRes.rc b/indra/newview/res/viewerRes.rc index 433070ce34..38291e45c9 100644 --- a/indra/newview/res/viewerRes.rc +++ b/indra/newview/res/viewerRes.rc @@ -134,8 +134,8 @@ TOOLMEDIAOPEN CURSOR "toolmediaopen.cur" // VS_VERSION_INFO VERSIONINFO - FILEVERSION 2,0,0,3256 - PRODUCTVERSION 2,0,0,3256 + FILEVERSION 2,0,0,200030 + PRODUCTVERSION 2,0,0,200030 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -152,12 +152,12 @@ BEGIN BEGIN VALUE "CompanyName", "Linden Lab" VALUE "FileDescription", "Second Life" - VALUE "FileVersion", "2.0.0.3256" + VALUE "FileVersion", "2.0.0.200030" VALUE "InternalName", "Second Life" VALUE "LegalCopyright", "Copyright © 2001-2008, Linden Research, Inc." VALUE "OriginalFilename", "SecondLife.exe" VALUE "ProductName", "Second Life" - VALUE "ProductVersion", "2.0.0.3256" + VALUE "ProductVersion", "2.0.0.200030" END END BLOCK "VarFileInfo" diff --git a/indra/newview/skins/default/textures/Blank.png b/indra/newview/skins/default/textures/Blank.png Binary files differnew file mode 100644 index 0000000000..f38e9f9100 --- /dev/null +++ b/indra/newview/skins/default/textures/Blank.png diff --git a/indra/newview/skins/default/textures/bottomtray/Cam_Preset_Back_Off.png b/indra/newview/skins/default/textures/bottomtray/Cam_Preset_Back_Off.png Binary files differnew file mode 100644 index 0000000000..3cfe2e850e --- /dev/null +++ b/indra/newview/skins/default/textures/bottomtray/Cam_Preset_Back_Off.png diff --git a/indra/newview/skins/default/textures/bottomtray/Cam_Preset_Back_On.png b/indra/newview/skins/default/textures/bottomtray/Cam_Preset_Back_On.png Binary files differnew file mode 100644 index 0000000000..bb5d85e410 --- /dev/null +++ b/indra/newview/skins/default/textures/bottomtray/Cam_Preset_Back_On.png diff --git a/indra/newview/skins/default/textures/bottomtray/Cam_Preset_Eye_Off.png b/indra/newview/skins/default/textures/bottomtray/Cam_Preset_Eye_Off.png Binary files differnew file mode 100644 index 0000000000..2b50986780 --- /dev/null +++ b/indra/newview/skins/default/textures/bottomtray/Cam_Preset_Eye_Off.png diff --git a/indra/newview/skins/default/textures/bottomtray/Cam_Preset_Front_Off.png b/indra/newview/skins/default/textures/bottomtray/Cam_Preset_Front_Off.png Binary files differnew file mode 100644 index 0000000000..9876aa456c --- /dev/null +++ b/indra/newview/skins/default/textures/bottomtray/Cam_Preset_Front_Off.png diff --git a/indra/newview/skins/default/textures/bottomtray/Cam_Preset_Front_On.png b/indra/newview/skins/default/textures/bottomtray/Cam_Preset_Front_On.png Binary files differnew file mode 100644 index 0000000000..f481fed88c --- /dev/null +++ b/indra/newview/skins/default/textures/bottomtray/Cam_Preset_Front_On.png diff --git a/indra/newview/skins/default/textures/bottomtray/Cam_Preset_Side_Off.png b/indra/newview/skins/default/textures/bottomtray/Cam_Preset_Side_Off.png Binary files differnew file mode 100644 index 0000000000..d58b4ff990 --- /dev/null +++ b/indra/newview/skins/default/textures/bottomtray/Cam_Preset_Side_Off.png diff --git a/indra/newview/skins/default/textures/bottomtray/Cam_Preset_Side_On.png b/indra/newview/skins/default/textures/bottomtray/Cam_Preset_Side_On.png Binary files differnew file mode 100644 index 0000000000..6e73898992 --- /dev/null +++ b/indra/newview/skins/default/textures/bottomtray/Cam_Preset_Side_On.png diff --git a/indra/newview/skins/default/textures/default_profile_picture.j2c b/indra/newview/skins/default/textures/default_profile_picture.j2c Binary files differindex c53a22e816..f21742cf09 100644 --- a/indra/newview/skins/default/textures/default_profile_picture.j2c +++ b/indra/newview/skins/default/textures/default_profile_picture.j2c diff --git a/indra/newview/skins/default/textures/icons/Inv_Alpha.png b/indra/newview/skins/default/textures/icons/Inv_Alpha.png Binary files differindex e8d246c6fa..b65dc1929d 100644 --- a/indra/newview/skins/default/textures/icons/Inv_Alpha.png +++ b/indra/newview/skins/default/textures/icons/Inv_Alpha.png diff --git a/indra/newview/skins/default/textures/icons/Inv_Tattoo.png b/indra/newview/skins/default/textures/icons/Inv_Tattoo.png Binary files differindex e8d246c6fa..a632197eb5 100644 --- a/indra/newview/skins/default/textures/icons/Inv_Tattoo.png +++ b/indra/newview/skins/default/textures/icons/Inv_Tattoo.png diff --git a/indra/newview/skins/default/textures/map_avatar_32.tga b/indra/newview/skins/default/textures/map_avatar_32.tga Binary files differnew file mode 100644 index 0000000000..aebeab4093 --- /dev/null +++ b/indra/newview/skins/default/textures/map_avatar_32.tga diff --git a/indra/newview/skins/default/textures/map_avatar_above_32.tga b/indra/newview/skins/default/textures/map_avatar_above_32.tga Binary files differnew file mode 100644 index 0000000000..65bd0561a7 --- /dev/null +++ b/indra/newview/skins/default/textures/map_avatar_above_32.tga diff --git a/indra/newview/skins/default/textures/map_avatar_below_32.tga b/indra/newview/skins/default/textures/map_avatar_below_32.tga Binary files differnew file mode 100644 index 0000000000..496c44b369 --- /dev/null +++ b/indra/newview/skins/default/textures/map_avatar_below_32.tga diff --git a/indra/newview/skins/default/textures/map_avatar_you_32.tga b/indra/newview/skins/default/textures/map_avatar_you_32.tga Binary files differnew file mode 100644 index 0000000000..782207efd6 --- /dev/null +++ b/indra/newview/skins/default/textures/map_avatar_you_32.tga diff --git a/indra/newview/skins/default/textures/textures.xml b/indra/newview/skins/default/textures/textures.xml index cf7ab46dd5..fba8e0b06c 100644 --- a/indra/newview/skins/default/textures/textures.xml +++ b/indra/newview/skins/default/textures/textures.xml @@ -27,6 +27,8 @@ <texture name="BackArrow_Off" file_name="icons/BackArrow_Off.png" preload="false" /> <texture name="BackArrow_Press" file_name="icons/BackArrow_Press.png" preload="false" /> + <texture name="Blank" file_name="Blank.png" preload="false" /> + <texture name="BottomTray_BG" file_name="bottomtray/BottomTray_BG.png" preload="false" /> <texture name="BuyArrow_Off" file_name="navbar/BuyArrow_Off.png" preload="true" scale.left="1" scale.top="1" scale.right="0" scale.bottom="0" /> @@ -49,6 +51,16 @@ <texture name="Cam_Pan_Off" file_name="bottomtray/Cam_Pan_Off.png" preload="false" /> <texture name="Cam_Pan_Over" file_name="bottomtray/CCam_Pan_Over.png" preload="false" /> <texture name="Cam_Pan_Press" file_name="bottomtray/Cam_Pan_Press.png" preload="false" /> + + <texture name="Cam_Preset_Back_Off" file_name="bottomtray/Cam_Preset_Back_Off.png" preload="false" /> + <texture name="Cam_Preset_Back_On" file_name="bottomtray/Cam_Preset_Back_On.png" preload="false" /> + <texture name="Cam_Preset_Eye_Off" file_name="bottomtray/Cam_Preset_Eye_Off.png" preload="false" /> + <texture name="Cam_Preset_Eye_On" file_name="bottomtray/Cam_Preset_Eye_On.png" preload="false" /> + <texture name="Cam_Preset_Front_Off" file_name="bottomtray/Cam_Preset_Front_Off.png" preload="false" /> + <texture name="Cam_Preset_Front_On" file_name="bottomtray/Cam_Preset_Front_On.png" preload="false" /> + <texture name="Cam_Preset_Side_Off" file_name="bottomtray/Cam_Preset_Side_Off.png" preload="false" /> + <texture name="Cam_Preset_Side_On" file_name="bottomtray/Cam_Preset_Side_On.png" preload="false" /> + <texture name="Cam_Rotate_In" file_name="bottomtray/Cam_Rotate_In.png" preload="false" /> <texture name="Cam_Rotate_Out" file_name="bottomtray/Cam_Rotate_Out.png" preload="false" /> <texture name="Cam_Tracking_In" file_name="bottomtray/Cam_Tracking_In.png" preload="false" /> @@ -497,9 +509,6 @@ <!--WARNING OLD ART *do not use*--> - <texture name="Banner_ForSale" file_name="Banner_ForSale.png" preload="false" /> - <texture name="Banner_YouAreHere" file_name="Banner_YouAreHere.png" preload="false" /> - <texture name="btn_chatbar.tga" scale.left="20" scale.top="24" scale.right="44" scale.bottom="0" /> <texture name="btn_chatbar_selected.tga" scale.left="20" scale.top="24" scale.right="44" scale.bottom="0" /> diff --git a/indra/newview/skins/default/xui/en/floater_about.xml b/indra/newview/skins/default/xui/en/floater_about.xml index c74b1929b4..2ff99dcf5a 100644 --- a/indra/newview/skins/default/xui/en/floater_about.xml +++ b/indra/newview/skins/default/xui/en/floater_about.xml @@ -69,7 +69,7 @@ Packets Lost: [PACKETS_LOST,number,0]/[PACKETS_IN,number,0] ([PACKETS_PCT,number tab_position="top"> <panel border="true" - label="Support" + label="Info" help_topic="about_support_tab" name="support_panel"> <text_editor diff --git a/indra/newview/skins/default/xui/en/floater_buy_land.xml b/indra/newview/skins/default/xui/en/floater_buy_land.xml index 0f710a8047..6e0c3dfe54 100644 --- a/indra/newview/skins/default/xui/en/floater_buy_land.xml +++ b/indra/newview/skins/default/xui/en/floater_buy_land.xml @@ -115,7 +115,7 @@ Try selecting a smaller area. </floater.string> <floater.string name="buy_for_US"> - Buy L$ [AMOUNT] for approx. US$ [AMOUNT2], + Buy L$ [AMOUNT] for approx. [LOCAL_AMOUNT], </floater.string> <floater.string name="parcel_meters"> @@ -172,10 +172,6 @@ supports [AMOUNT2] objects name="no_parcel_selected"> (no parcel selected) </floater.string> - <floater.string - name="buy_currency"> - Buy L$ [LINDENS] for approx. US$ [USD] - </floater.string> <text type="string" length="1" @@ -667,7 +663,7 @@ This parcel is 512 m² of land. name="currency_est" top="409" width="178"> - for approx. US$ [AMOUNT2] + for approx. [LOCAL_AMOUNT] </text> <text type="string" diff --git a/indra/newview/skins/default/xui/en/floater_camera.xml b/indra/newview/skins/default/xui/en/floater_camera.xml index a569b62e96..5c09bc3a02 100644 --- a/indra/newview/skins/default/xui/en/floater_camera.xml +++ b/indra/newview/skins/default/xui/en/floater_camera.xml @@ -34,7 +34,7 @@ mouse_opaque="false" name="controls" width="148"> - <joystick_track + <joystick_track follows="top|left" height="78" image_selected="Cam_Tracking_In" @@ -50,7 +50,7 @@ visible="false" width="78" /> <!--TODO: replace with slider, + - images --> - <joystick_zoom + <joystick_zoom follows="top|left" height="78" image_unselected="ScrollThumb_Vert" @@ -80,66 +80,65 @@ tool_tip="Orbit camera around focus" top="22" width="78" /> - <panel + <panel height="78" layout="topleft" left="36" name="camera_presets" - top="30" + top="20" visible="false" width="78"> <button - height="30" - image_selected="CameraPreset_Rear" - image_unselected="CameraPreset_Rear" + height="40" + image_selected="Cam_Preset_Back_On" + image_unselected="Cam_Preset_Back_Off" layout="topleft" - left="5" + left="0" name="rear_view" tool_tip="Rear View" top="2" - width="30"> + width="40"> <click_callback function="CameraPresets.ChangeView" parameter="rear_view" /> </button> <button - height="30" - image_selected="CameraPreset_3_4" - image_unselected="CameraPreset_3_4" + height="40" + image_selected="Cam_Preset_Side_On" + image_unselected="Cam_Preset_Side_Off" layout="topleft" left_pad="5" name="group_view" tool_tip="Group View" top="2" - width="30"> + width="40"> <click_callback function="CameraPresets.ChangeView" parameter="group_view" /> </button> <button - height="30" - image_selected="CameraPreset_Front" - image_unselected="CameraPreset_Front" + height="40" + image_selected="Cam_Preset_Front_On" + image_unselected="Cam_Preset_Front_Off" layout="topleft" - left="5" + left="0" name="front_view" tool_tip="Front View" top_pad="5" - width="30"> + width="40"> <click_callback function="CameraPresets.ChangeView" parameter="front_view" /> </button> <button - height="30" - image_selected="CameraPreset_Mouselook" - image_unselected="CameraPreset_Mouselook" + height="40" + image_selected="Cam_Preset_Eye_Off" + image_unselected="Cam_Preset_Eye_Off" layout="topleft" left_pad="5" name="mouselook_view" tool_tip="Mouselook View" - top_pad="-30" - width="30"> + width="40"> <click_callback function="CameraPresets.ChangeView" parameter="mouselook_view" /> @@ -165,7 +164,6 @@ name="orbit_btn" tab_stop="false" tool_tip="Orbit camera" - value="true" width="25"> </button> <button diff --git a/indra/newview/skins/default/xui/en/floater_media_settings.xml b/indra/newview/skins/default/xui/en/floater_media_settings.xml index 6b884d46a7..68dd2001af 100644 --- a/indra/newview/skins/default/xui/en/floater_media_settings.xml +++ b/indra/newview/skins/default/xui/en/floater_media_settings.xml @@ -15,7 +15,7 @@ min_height="430" min_width="620" mouse_opaque="true" - name="Medis Settings" + name="Media Settings" help_topic = "media_settings" title="MEDIA SETTINGS"> <button diff --git a/indra/newview/skins/default/xui/en/floater_pay.xml b/indra/newview/skins/default/xui/en/floater_pay.xml index b4becfa022..509cffe490 100644 --- a/indra/newview/skins/default/xui/en/floater_pay.xml +++ b/indra/newview/skins/default/xui/en/floater_pay.xml @@ -2,22 +2,29 @@ <floater legacy_header_height="18" can_minimize="false" - height="185" + height="200" layout="topleft" name="Give Money" help_topic="give_money" save_rect="true" width="225"> + <string + name="payee_group"> + Pay Group + </string> + <string + name="payee_resident"> + Pay Resident + </string> <text type="string" length="1" follows="left|top" - font="SansSerifBold" height="18" layout="topleft" left="12" name="payee_label" - top="7" + top="22" width="75"> Pay: </text> @@ -45,7 +52,6 @@ </text> <button height="23" - font="SansSerifSmall" label="L$1" label_selected="L$1" layout="topleft" @@ -55,8 +61,7 @@ width="80" /> <button height="23" - label="L$1" - font="SansSerif" + label="L$5" label_selected="L$5" layout="topleft" left_pad="15" @@ -65,7 +70,6 @@ <button height="23" label="L$10" - font="SansSerifHuge" label_selected="L$10" layout="topleft" left="25" diff --git a/indra/newview/skins/default/xui/en/floater_test_textbox.xml b/indra/newview/skins/default/xui/en/floater_test_textbox.xml index 9e2e9e74e3..8dba05f1ee 100644 --- a/indra/newview/skins/default/xui/en/floater_test_textbox.xml +++ b/indra/newview/skins/default/xui/en/floater_test_textbox.xml @@ -2,11 +2,11 @@ <floater legacy_header_height="18" can_resize="true" - height="400" + height="450" layout="topleft" name="floater_test_textbox" help_topic="floater_test_textbox" - width="400"> + width="600"> <text type="string" length="1" @@ -48,61 +48,80 @@ First line of multiple lines Second line of multiple lines </text> + <text + top_pad="10" + left="10" + right="-10" + follows="top|left" + font.name="SansSerifSmall" + name="test_text10" + tool_tip="text"> + SansSerifSmall. РуÑÑкий ä¸æ–‡ (简体) The quick brown fox jumped over the lazy dog. + </text> + <text + top_pad="10" + left="10" + right="-10" + follows="top|left" + font.name="SansSerif" + name="test_text11" + tool_tip="text"> + SansSerif. РуÑÑкий ä¸æ–‡ (简体) The quick brown fox jumped over the lazy dog. + </text> + <text + top_pad="10" + left="10" + right="-10" + follows="top|left" + font.name="SansSerifLarge" + name="test_text12" + tool_tip="text"> + SansSerifLarge. РуÑÑкий ä¸æ–‡ (简体) The quick brown fox jumped over the lazy dog. + </text> + <text + top_pad="10" + left="10" + right="-10" + follows="top|left" + font.name="SansSerifHuge" + name="test_text13" + tool_tip="text"> + SansSerifHuge. РуÑÑкий ä¸æ–‡ (简体) The quick brown fox jumped over the lazy dog. + </text> <text type="string" length="1" font="SansSerif" - height="10" - layout="topleft" - left_delta="0" - top_pad="30" - width="300"> - font SansSerif - </text> - <text - type="string" - length="1" - follows="bottom|right" - height="10" - layout="topleft" - left_delta="0" - top_pad="10" - width="300"> - follows bottom right - </text> - <text - type="string" - length="1" - font="SansSerifSmall" font.style="BOLD" height="10" layout="topleft" left_delta="0" - top_pad="10" + top_pad="40" width="300"> - font style BOLD + SansSerif BOLD </text> <text type="string" length="1" - font="SansSerifSmall" + font="SansSerif" font.style="BOLD|UNDERLINE" height="10" layout="topleft" left_delta="0" top_pad="10" width="300"> - font style BOLD UNDERLINE + SansSerif BOLD UNDERLINE </text> <text type="string" length="1" + font="SansSerif" height="10" layout="topleft" left_delta="0" top_pad="10" width="300"> - font style UNDERLINE + SansSerif UNDERLINE </text> <text type="string" diff --git a/indra/newview/skins/default/xui/en/floater_test_widgets.xml b/indra/newview/skins/default/xui/en/floater_test_widgets.xml index 718d724aa6..44dcb07526 100644 --- a/indra/newview/skins/default/xui/en/floater_test_widgets.xml +++ b/indra/newview/skins/default/xui/en/floater_test_widgets.xml @@ -338,13 +338,13 @@ line to actually fit height="400" halign="center" layout="topleft" - left="575" + left="525" name="group_tab_container" tab_position="top" tab_height="20" tool_tip="tab container" top="80" - width="250"> + width="300"> <!-- "panel" is a container for widgets. It is automatically resized to fit the parent tab_container. --> <panel @@ -380,6 +380,46 @@ line to actually fit tool_tip="Color Swatch: Click to open Color Picker" top_delta="0" width="80" /> + <text + top_pad="10" + left="10" + width="250" + follows="top|left" + font.name="SansSerifSmall" + name="test_text10" + tool_tip="text"> + SansSerifSmall. РуÑÑкий ä¸æ–‡ (简体) + </text> + <text + top_pad="10" + left="10" + width="250" + follows="top|left" + font.name="SansSerif" + name="test_text11" + tool_tip="text"> + SansSerif. РуÑÑкий ä¸æ–‡ (简体) + </text> + <text + top_pad="10" + left="10" + width="250" + follows="top|left" + font.name="SansSerifLarge" + name="test_text12" + tool_tip="text"> + SansSerifLarge. РуÑÑкий ä¸æ–‡ (简体) + </text> + <text + top_pad="10" + left="10" + width="250" + follows="top|left" + font.name="SansSerifHuge" + name="test_text13" + tool_tip="text"> + SansSerifHuge. РуÑÑкий ä¸æ–‡ (简体) + </text> </panel> <!-- panels can also refer to other floaters or panels --> <panel diff --git a/indra/newview/skins/default/xui/en/floater_world_map.xml b/indra/newview/skins/default/xui/en/floater_world_map.xml index f5ae611f93..387ffbd5b3 100644 --- a/indra/newview/skins/default/xui/en/floater_world_map.xml +++ b/indra/newview/skins/default/xui/en/floater_world_map.xml @@ -15,38 +15,15 @@ single_instance="true" title="WORLD MAP" width="800"> - <tab_container + <panel + filename="panel_world_map.xml" follows="left|top|right|bottom" height="565" layout="topleft" left="15" - name="maptab" - tab_position="top" + name="objects_mapview" top="25" - width="542"> - <panel - filename="panel_world_map.xml" - follows="left|top|right|bottom" - height="550" - label="Objects" - layout="topleft" - left="1" - help_topic="worldmap_objects_tab" - name="objects_mapview" - top="19" - width="540" /> - <panel - filename="panel_world_map.xml" - follows="left|top|right|bottom" - height="550" - label="Terrain" - layout="topleft" - left_delta="0" - help_topic="worldmap_terrain_tab" - name="terrain_mapview" - top_delta="3" - width="540" /> - </tab_container> + width="542" /> <icon follows="top|right" height="16" @@ -210,7 +187,7 @@ label="Telehub" layout="topleft" left_pad="4" - name="telehubchk" + name="telehub_chk" top_delta="0" width="110" /> <icon diff --git a/indra/newview/skins/default/xui/en/fonts.xml b/indra/newview/skins/default/xui/en/fonts.xml index 65dfb13f4a..f3da62a896 100644 --- a/indra/newview/skins/default/xui/en/fonts.xml +++ b/indra/newview/skins/default/xui/en/fonts.xml @@ -150,11 +150,9 @@ comment="Size of large font (points, or 1/72 of an inch)" size="10.0" /> - <!-- Changed Medium size to address menu font size not matching spec. - May want to revert/update after menu font configuration has been enabled. --> <font_size name="Medium" comment="Size of medium font (points, or 1/72 of an inch)" - size="8.0" + size="9.0" /> <font_size name="Small" comment="Size of small font (points, or 1/72 of an inch)" diff --git a/indra/newview/skins/default/xui/en/menu_inspect_avatar_gear.xml b/indra/newview/skins/default/xui/en/menu_inspect_avatar_gear.xml index 8ee67b9a02..590621062c 100644 --- a/indra/newview/skins/default/xui/en/menu_inspect_avatar_gear.xml +++ b/indra/newview/skins/default/xui/en/menu_inspect_avatar_gear.xml @@ -91,6 +91,8 @@ name="zoom_in">
<menu_item_call.on_click
function="InspectAvatar.ZoomIn"/>
+ <menu_item_call.on_visible
+ function="InspectAvatar.VisibleZoomIn"/>
</menu_item_call>
<menu_item_call
label="Pay"
diff --git a/indra/newview/skins/default/xui/en/menu_inspect_object_gear.xml b/indra/newview/skins/default/xui/en/menu_inspect_object_gear.xml index 93c53981f3..04a247fd54 100644 --- a/indra/newview/skins/default/xui/en/menu_inspect_object_gear.xml +++ b/indra/newview/skins/default/xui/en/menu_inspect_object_gear.xml @@ -73,7 +73,7 @@ <menu_item_call label="Edit" layout="topleft" - name="report"> + name="edit"> <menu_item_call.on_click function="Object.Edit" /> <menu_item_call.on_enable diff --git a/indra/newview/skins/default/xui/en/menu_inventory.xml b/indra/newview/skins/default/xui/en/menu_inventory.xml index 62940b87dc..8b6ab4e4d8 100644 --- a/indra/newview/skins/default/xui/en/menu_inventory.xml +++ b/indra/newview/skins/default/xui/en/menu_inventory.xml @@ -553,6 +553,16 @@ <menu_item_separator layout="topleft" /> <menu_item_call + label="Save As" + layout="topleft" + name="Save As"> + <menu_item_call.on_click + function="Inventory.DoToSelected" + parameter="save_as" /> + </menu_item_call> + <menu_item_separator + layout="topleft" /> + <menu_item_call label="Detach From Yourself" layout="topleft" name="Detach From Yourself"> diff --git a/indra/newview/skins/default/xui/en/menu_inventory_add.xml b/indra/newview/skins/default/xui/en/menu_inventory_add.xml new file mode 100644 index 0000000000..b07a8bb512 --- /dev/null +++ b/indra/newview/skins/default/xui/en/menu_inventory_add.xml @@ -0,0 +1,229 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<menu + layout="topleft" + left="0" + mouse_opaque="false" + name="menu_inventory_add" + visible="false"> + <menu + create_jump_keys="true" + label="Upload" + layout="topleft" + name="upload" + tear_off="true"> + <menu_item_call + label="Image (L$[COST])..." + layout="topleft" + name="Upload Image" + shortcut="control|U"> + <menu_item_call.on_click + function="File.UploadImage" + parameter="" /> + <menu_item_call.on_enable + function="File.EnableUpload" /> + </menu_item_call> + <menu_item_call + label="Sound (L$[COST])..." + layout="topleft" + name="Upload Sound"> + <menu_item_call.on_click + function="File.UploadSound" + parameter="" /> + <menu_item_call.on_enable + function="File.EnableUpload" /> + </menu_item_call> + <menu_item_call + label="Animation (L$[COST])..." + layout="topleft" + name="Upload Animation"> + <menu_item_call.on_click + function="File.UploadAnim" + parameter="" /> + <menu_item_call.on_enable + function="File.EnableUpload" /> + </menu_item_call> + <menu_item_call + label="Bulk (L$[COST] per file)..." + layout="topleft" + name="Bulk Upload"> + <menu_item_call.on_click + function="File.UploadBulk" + parameter="" /> + </menu_item_call> + <menu_item_separator + layout="topleft" /> + </menu> + + <menu_item_call + label="New Folder" + layout="topleft" + name="New Folder"> + <menu_item_call.on_click + function="Inventory.DoCreate" + parameter="category" /> + </menu_item_call> + <menu_item_call + label="New Script" + layout="topleft" + name="New Script"> + <menu_item_call.on_click + function="Inventory.DoCreate" + parameter="lsl" /> + </menu_item_call> + <menu_item_call + label="New Note" + layout="topleft" + name="New Note"> + <menu_item_call.on_click + function="Inventory.DoCreate" + parameter="notecard" /> + </menu_item_call> + <menu_item_call + label="New Gesture" + layout="topleft" + name="New Gesture"> + <menu_item_call.on_click + function="Inventory.DoCreate" + parameter="gesture" /> + </menu_item_call> + <menu + height="175" + label="New Clothes" + layout="topleft" + left_delta="0" + mouse_opaque="false" + name="New Clothes" + top_pad="514" + width="125"> + <menu_item_call + label="New Shirt" + layout="topleft" + name="New Shirt"> + <menu_item_call.on_click + function="Inventory.DoCreate" + parameter="shirt" /> + </menu_item_call> + <menu_item_call + label="New Pants" + layout="topleft" + name="New Pants"> + <menu_item_call.on_click + function="Inventory.DoCreate" + parameter="pants" /> + </menu_item_call> + <menu_item_call + label="New Shoes" + layout="topleft" + name="New Shoes"> + <menu_item_call.on_click + function="Inventory.DoCreate" + parameter="shoes" /> + </menu_item_call> + <menu_item_call + label="New Socks" + layout="topleft" + name="New Socks"> + <menu_item_call.on_click + function="Inventory.DoCreate" + parameter="socks" /> + </menu_item_call> + <menu_item_call + label="New Jacket" + layout="topleft" + name="New Jacket"> + <menu_item_call.on_click + function="Inventory.DoCreate" + parameter="jacket" /> + </menu_item_call> + <menu_item_call + label="New Skirt" + layout="topleft" + name="New Skirt"> + <menu_item_call.on_click + function="Inventory.DoCreate" + parameter="skirt" /> + </menu_item_call> + <menu_item_call + label="New Gloves" + layout="topleft" + name="New Gloves"> + <menu_item_call.on_click + function="Inventory.DoCreate" + parameter="gloves" /> + </menu_item_call> + <menu_item_call + label="New Undershirt" + layout="topleft" + name="New Undershirt"> + <menu_item_call.on_click + function="Inventory.DoCreate" + parameter="undershirt" /> + </menu_item_call> + <menu_item_call + label="New Underpants" + layout="topleft" + name="New Underpants"> + <menu_item_call.on_click + function="Inventory.DoCreate" + parameter="underpants" /> + </menu_item_call> + <menu_item_call + label="New Alpha" + layout="topleft" + name="New Alpha"> + <menu_item_call.on_click + function="Inventory.DoCreate" + parameter="alpha" /> + </menu_item_call> + <menu_item_call + label="New Tattoo" + layout="topleft" + name="New Tattoo"> + <menu_item_call.on_click + function="Inventory.DoCreate" + parameter="tattoo" /> + </menu_item_call> + </menu> + <menu + height="85" + label="New Body Parts" + layout="topleft" + left_delta="0" + mouse_opaque="false" + name="New Body Parts" + top_pad="514" + width="118"> + <menu_item_call + label="New Shape" + layout="topleft" + name="New Shape"> + <menu_item_call.on_click + function="Inventory.DoCreate" + parameter="shape" /> + </menu_item_call> + <menu_item_call + label="New Skin" + layout="topleft" + name="New Skin"> + <menu_item_call.on_click + function="Inventory.DoCreate" + parameter="skin" /> + </menu_item_call> + <menu_item_call + label="New Hair" + layout="topleft" + name="New Hair"> + <menu_item_call.on_click + function="Inventory.DoCreate" + parameter="hair" /> + </menu_item_call> + <menu_item_call + label="New Eyes" + layout="topleft" + name="New Eyes"> + <menu_item_call.on_click + function="Inventory.DoCreate" + parameter="eyes" /> + </menu_item_call> + </menu> +</menu>
\ No newline at end of file diff --git a/indra/newview/skins/default/xui/en/menu_inventory_gear_default.xml b/indra/newview/skins/default/xui/en/menu_inventory_gear_default.xml new file mode 100644 index 0000000000..435a3e6d34 --- /dev/null +++ b/indra/newview/skins/default/xui/en/menu_inventory_gear_default.xml @@ -0,0 +1,90 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<menu + bottom="806" + layout="topleft" + left="0" + mouse_opaque="false" + name="menu_gear_default" + visible="false"> + <menu_item_call + label="New Inventory Window" + layout="topleft" + name="new_window"> + <on_click + function="Inventory.GearDefault.Custom.Action" + parameter="new_window" /> + </menu_item_call> + <menu_item_separator + layout="topleft" /> + <menu_item_call + label="Sort by Name" + layout="topleft" + name="sort_by_name"> + <on_click + function="Inventory.GearDefault.Custom.Action" + parameter="sort_by_name" /> + </menu_item_call> + <menu_item_call + label="Sort by Most Recent" + layout="topleft" + name="sort_by_recent"> + <on_click + function="Inventory.GearDefault.Custom.Action" + parameter="sort_by_recent" /> + </menu_item_call> + <menu_item_separator + layout="topleft" /> + <menu_item_call + label="Show Filters" + layout="topleft" + name="show_filters"> + <on_click + function="Inventory.GearDefault.Custom.Action" + parameter="show_filters" /> + </menu_item_call> + <menu_item_call + label="Reset Filters" + layout="topleft" + name="reset_filters"> + <on_click + function="Inventory.GearDefault.Custom.Action" + parameter="reset_filters" /> + </menu_item_call> + <menu_item_call + label="Close All Folders" + layout="topleft" + name="close_folders"> + <on_click + function="Inventory.GearDefault.Custom.Action" + parameter="close_folders" /> + </menu_item_call> + <menu_item_separator + layout="topleft" /> + <menu_item_call + label="Empty Trash" + layout="topleft" + name="empty_trash"> + <on_click + function="Inventory.GearDefault.Custom.Action" + parameter="empty_trash" /> + </menu_item_call> + <menu_item_call + label="Empty Lost and Found" + layout="topleft" + name="empty_lostnfound"> + <on_click + function="Inventory.GearDefault.Custom.Action" + parameter="empty_lostnfound" /> + </menu_item_call> + <menu_item_call + label="Save Texture As" + layout="topleft" + name="Save Texture As"> + <on_click + function="Inventory.GearDefault.Custom.Action" + parameter="save_texture" /> + <on_enable + function="Inventory.GearDefault.Enable" + parameter="save_texture" /> + </menu_item_call> +</menu> diff --git a/indra/newview/skins/default/xui/en/menu_login.xml b/indra/newview/skins/default/xui/en/menu_login.xml index dfa5adb743..5eb0560962 100644 --- a/indra/newview/skins/default/xui/en/menu_login.xml +++ b/indra/newview/skins/default/xui/en/menu_login.xml @@ -180,23 +180,6 @@ </menu_item_call> <menu_item_separator /> <menu_item_call - label="Widget Test" - name="Widget Test" - shortcut="control|shift|T"> - <menu_item_call.on_click - function="ShowFloater" - parameter="test_widgets" /> - </menu_item_call> - <menu_item_call - label="Inspectors Test" - name="inspectors_test" - shortcut="control|shift|I" - > - <menu_item_call.on_click - function="ShowFloater" - parameter="test_inspectors" /> - </menu_item_call> - <menu_item_call label="Show Side Tray" name="Show Side Tray"> <menu_item_call.on_click diff --git a/indra/newview/skins/default/xui/en/menu_viewer.xml b/indra/newview/skins/default/xui/en/menu_viewer.xml index ebdfb01787..66c9060b06 100644 --- a/indra/newview/skins/default/xui/en/menu_viewer.xml +++ b/indra/newview/skins/default/xui/en/menu_viewer.xml @@ -98,6 +98,24 @@ function="World.SetBusy"/> </menu_item_call> </menu> + <menu_item_call + label="Request Admin Status" + layout="topleft" + name="Request Admin Options" + shortcut="control|alt|G" + visible="false"> + <menu_item_call.on_click + function="Advanced.RequestAdminStatus" /> + </menu_item_call> + <menu_item_call + label="Leave Admin Status" + layout="topleft" + name="Leave Admin Options" + shortcut="control|alt|shift|G" + visible="false"> + <menu_item_call.on_click + function="Advanced.LeaveAdminStatus" /> + </menu_item_call> <menu_item_separator layout="topleft" /> <menu_item_call @@ -1915,6 +1933,42 @@ parameter="debug" /> </menu_item_check> <menu_item_check + label="Notifications Console" + layout="topleft" + name="Notifications" + shortcut="control|shift|5"> + <menu_item_check.on_check + function="Advanced.CheckConsole" + parameter="notifications" /> + <menu_item_check.on_click + function="Floater.Show" + parameter="notifications_console" /> + </menu_item_check> + <menu_item_check + label="Texture Size Console" + layout="topleft" + name="Texture Size" + shortcut="control|shift|6"> + <menu_item_check.on_check + function="Advanced.CheckConsole" + parameter="texture size" /> + <menu_item_check.on_click + function="Advanced.ToggleConsole" + parameter="texture size" /> + </menu_item_check> + <menu_item_check + label="Texture Category Console" + layout="topleft" + name="Texture Category" + shortcut="control|shift|7"> + <menu_item_check.on_check + function="Advanced.CheckConsole" + parameter="texture category" /> + <menu_item_check.on_click + function="Advanced.ToggleConsole" + parameter="texture category" /> + </menu_item_check> + <menu_item_check label="Fast Timers" layout="topleft" name="Fast Timers" @@ -2471,19 +2525,43 @@ label="Disable Textures" name="Disable Textures"> <menu_item_check.on_check - function="Advanced.CheckDisableTextures" - parameter="DisableTextures" /> + function="CheckControl" + parameter="TextureDisable" /> <menu_item_check.on_click - function="Advanced.ToggleDisableTextures" /> + function="ToggleControl" + parameter="TextureDisable" /> + </menu_item_check> + <menu_item_check + label="Full Res Textures" + layout="topleft" + name="Rull Res Textures"> + <menu_item_check.on_check + function="CheckControl" + parameter="TextureLoadFullRes" /> + <menu_item_check.on_click + function="ToggleControl" + parameter="TextureLoadFullRes" /> + </menu_item_check> + <menu_item_check + label="Audit Textures" + layout="topleft" + name="Audit Textures"> + <menu_item_check.on_check + function="CheckControl" + parameter="AuditTexture" /> + <menu_item_check.on_click + function="ToggleControl" + parameter="AuditTexture" /> </menu_item_check> <menu_item_check label="Texture Atlas" name="Texture Atlas"> <menu_item_check.on_check - function="Advanced.CheckTextureAtlas" + function="CheckControl" parameter="TextureAtlas" /> <menu_item_check.on_click - function="Advanced.ToggleTextureAtlas" /> + function="ToggleControl" + parameter="TextureAtlas" /> </menu_item_check> <menu_item_check label="Render Attached Lights" @@ -2506,8 +2584,8 @@ parameter="RenderAttachedParticles" /> </menu_item_check> <menu_item_check - label="Hover Highlight Objects" - name="Hover Highlight Objects"> + label="Hover Glow Objects" + name="Hover Glow Objects"> <menu_item_check.on_check function="CheckControl" parameter="RenderHighlightEnable" /> diff --git a/indra/newview/skins/default/xui/en/notifications.xml b/indra/newview/skins/default/xui/en/notifications.xml index f26a4d09d4..989dc88851 100644 --- a/indra/newview/skins/default/xui/en/notifications.xml +++ b/indra/newview/skins/default/xui/en/notifications.xml @@ -5669,7 +5669,7 @@ An error has occurred while trying to connect to voice chat for [VOICE_CHANNEL_N name="ServerVersionChanged" priority="high" type="notifytip"> -You just entered a region using a different server version, which may affect performance. Click to see the release notes. +You just entered a region using a different server version, which may affect performance. [[URL] View the release notes.] </notification> <notification diff --git a/indra/newview/skins/default/xui/en/panel_main_inventory.xml b/indra/newview/skins/default/xui/en/panel_main_inventory.xml index 9a3fdcc327..fcee0ef953 100644 --- a/indra/newview/skins/default/xui/en/panel_main_inventory.xml +++ b/indra/newview/skins/default/xui/en/panel_main_inventory.xml @@ -1,415 +1,467 @@ -<?xml version="1.0" encoding="utf-8" standalone="yes" ?> -<panel - background_visible="true" - follows="all" - height="400" - label="Things" - layout="topleft" - min_height="350" - min_width="240" - name="inventory panel" - width="330"> - <panel.string - name="Title"> - Things - </panel.string> - <filter_editor - text_pad_left="12" - follows="left|top|right" - font="SanSerif" - height="20" - label="Filter" - layout="topleft" - left="15" - name="inventory search editor" - top="34" - width="300" /> - <tab_container - follows="left|top|right|bottom" - height="300" - layout="topleft" - left_delta="-4" - name="inventory filter tabs" - tab_position="top" - top_pad="4" - width="305"> - <inventory_panel - follows="left|top|right|bottom" - height="295" - label="All Items" - layout="topleft" - left="1" - name="All Items" - top="16" - width="290" /> - <inventory_panel - follows="left|top|right|bottom" - height="295" - label="Recent Items" - layout="topleft" - left_delta="0" - name="Recent Items" - top_delta="0" - width="290" /> - </tab_container> - <menu_bar - bg_visible="false" - follows="left|top|right" - height="18" - layout="topleft" - left_delta="0" - mouse_opaque="false" - name="Inventory Menu" - top_delta="-45" - width="290"> - <menu - height="101" - label="File" - layout="topleft" - left="0" - mouse_opaque="false" - name="File" - tear_off="true" - top="-117" - width="128"> - <menu_item_call - label="Open" - layout="topleft" - name="Open"> - <menu_item_call.on_click - function="Inventory.DoToSelected" - parameter="open" /> - </menu_item_call> - <menu - create_jump_keys="true" - label="Upload" - layout="topleft" - name="upload" - tear_off="true"> - <menu_item_call - label="Image (L$[COST])..." - layout="topleft" - name="Upload Image" - shortcut="control|U"> - <menu_item_call.on_click - function="File.UploadImage" - parameter="" /> - <menu_item_call.on_enable - function="File.EnableUpload" /> - </menu_item_call> - <menu_item_call - label="Sound (L$[COST])..." - layout="topleft" - name="Upload Sound"> - <menu_item_call.on_click - function="File.UploadSound" - parameter="" /> - <menu_item_call.on_enable - function="File.EnableUpload" /> - </menu_item_call> - <menu_item_call - label="Animation (L$[COST])..." - layout="topleft" - name="Upload Animation"> - <menu_item_call.on_click - function="File.UploadAnim" - parameter="" /> - <menu_item_call.on_enable - function="File.EnableUpload" /> - </menu_item_call> - <menu_item_call - label="Bulk (L$[COST] per file)..." - layout="topleft" - name="Bulk Upload"> - <menu_item_call.on_click - function="File.UploadBulk" - parameter="" /> - </menu_item_call> - <menu_item_separator - layout="topleft" /> - </menu> - <menu_item_separator - layout="topleft" /> - <menu_item_call - label="New Window" - layout="topleft" - name="New Window"> - <menu_item_call.on_click - function="Inventory.NewWindow" /> - </menu_item_call> - <menu_item_separator - layout="topleft" - name="separator2" /> - <menu_item_call - label="Show Filters" - layout="topleft" - name="Show Filters"> - <menu_item_call.on_click - function="Inventory.ShowFilters" /> - </menu_item_call> - <menu_item_call - label="Reset Filters" - layout="topleft" - name="Reset Current"> - <menu_item_call.on_click - function="Inventory.ResetFilter" /> - </menu_item_call> - <menu_item_call - label="Close All Folders" - layout="topleft" - name="Close All Folders"> - <menu_item_call.on_click - function="Inventory.CloseAllFolders" /> - </menu_item_call> - <menu_item_separator - layout="topleft" - name="separator3" /> - <menu_item_call - label="Empty Trash" - layout="topleft" - name="Empty Trash"> - <menu_item_call.on_click - function="Inventory.EmptyTrash" /> - </menu_item_call> - <menu_item_call - label="Empty Lost And Found" - layout="topleft" - name="Empty Lost And Found"> - <menu_item_call.on_click - function="Inventory.EmptyLostAndFound" /> - </menu_item_call> - </menu> - <menu - height="121" - label="Create" - layout="topleft" - left="0" - mouse_opaque="false" - name="Create" - tear_off="true" - top="-201" - width="121"> - <menu_item_call - label="New Folder" - layout="topleft" - name="New Folder"> - <menu_item_call.on_click - function="Inventory.DoCreate" - parameter="category" /> - </menu_item_call> - <menu_item_call - label="New Script" - layout="topleft" - name="New Script"> - <menu_item_call.on_click - function="Inventory.DoCreate" - parameter="lsl" /> - </menu_item_call> - <menu_item_call - label="New Note" - layout="topleft" - name="New Note"> - <menu_item_call.on_click - function="Inventory.DoCreate" - parameter="notecard" /> - </menu_item_call> - <menu_item_call - label="New Gesture" - layout="topleft" - name="New Gesture"> - <menu_item_call.on_click - function="Inventory.DoCreate" - parameter="gesture" /> - </menu_item_call> - <menu - height="175" - label="New Clothes" - layout="topleft" - left_delta="0" - mouse_opaque="false" - name="New Clothes" - top_pad="514" - width="125"> - <menu_item_call - label="New Shirt" - layout="topleft" - name="New Shirt"> - <menu_item_call.on_click - function="Inventory.DoCreate" - parameter="shirt" /> - </menu_item_call> - <menu_item_call - label="New Pants" - layout="topleft" - name="New Pants"> - <menu_item_call.on_click - function="Inventory.DoCreate" - parameter="pants" /> - </menu_item_call> - <menu_item_call - label="New Shoes" - layout="topleft" - name="New Shoes"> - <menu_item_call.on_click - function="Inventory.DoCreate" - parameter="shoes" /> - </menu_item_call> - <menu_item_call - label="New Socks" - layout="topleft" - name="New Socks"> - <menu_item_call.on_click - function="Inventory.DoCreate" - parameter="socks" /> - </menu_item_call> - <menu_item_call - label="New Jacket" - layout="topleft" - name="New Jacket"> - <menu_item_call.on_click - function="Inventory.DoCreate" - parameter="jacket" /> - </menu_item_call> - <menu_item_call - label="New Skirt" - layout="topleft" - name="New Skirt"> - <menu_item_call.on_click - function="Inventory.DoCreate" - parameter="skirt" /> - </menu_item_call> - <menu_item_call - label="New Gloves" - layout="topleft" - name="New Gloves"> - <menu_item_call.on_click - function="Inventory.DoCreate" - parameter="gloves" /> - </menu_item_call> - <menu_item_call - label="New Undershirt" - layout="topleft" - name="New Undershirt"> - <menu_item_call.on_click - function="Inventory.DoCreate" - parameter="undershirt" /> - </menu_item_call> - <menu_item_call - label="New Underpants" - layout="topleft" - name="New Underpants"> - <menu_item_call.on_click - function="Inventory.DoCreate" - parameter="underpants" /> - </menu_item_call> - <menu_item_call - label="New Alpha" - layout="topleft" - name="New Alpha"> - <menu_item_call.on_click - function="Inventory.DoCreate" - parameter="alpha" /> - </menu_item_call> - <menu_item_call - label="New Tattoo" - layout="topleft" - name="New Tattoo"> - <menu_item_call.on_click - function="Inventory.DoCreate" - parameter="tattoo" /> - </menu_item_call> - </menu> - <menu - height="85" - label="New Body Parts" - layout="topleft" - left_delta="0" - mouse_opaque="false" - name="New Body Parts" - top_pad="514" - width="118"> - <menu_item_call - label="New Shape" - layout="topleft" - name="New Shape"> - <menu_item_call.on_click - function="Inventory.DoCreate" - parameter="shape" /> - </menu_item_call> - <menu_item_call - label="New Skin" - layout="topleft" - name="New Skin"> - <menu_item_call.on_click - function="Inventory.DoCreate" - parameter="skin" /> - </menu_item_call> - <menu_item_call - label="New Hair" - layout="topleft" - name="New Hair"> - <menu_item_call.on_click - function="Inventory.DoCreate" - parameter="hair" /> - </menu_item_call> - <menu_item_call - label="New Eyes" - layout="topleft" - name="New Eyes"> - <menu_item_call.on_click - function="Inventory.DoCreate" - parameter="eyes" /> - </menu_item_call> - </menu> - </menu> - <menu - height="49" - label="Sort" - layout="topleft" - left="0" - mouse_opaque="false" - name="Sort" - tear_off="true" - top="-113" - width="118"> - <menu_item_check - control_name="Inventory.SortByName" - label="By Name" - layout="topleft" - name="By Name"> - <menu_item_check.on_click - function="Inventory.SetSortBy" - parameter="name" /> - </menu_item_check> - <menu_item_check - control_name="Inventory.SortByDate" - label="By Date" - layout="topleft" - name="By Date"> - <menu_item_check.on_click - function="Inventory.SetSortBy" - parameter="date" /> - </menu_item_check> - <menu_item_separator - layout="topleft" /> - <menu_item_check - control_name="Inventory.FoldersAlwaysByName" - label="Folders Always By Name" - layout="topleft" - name="Folders Always By Name"> - <menu_item_check.on_click - function="Inventory.SetSortBy" - parameter="foldersalwaysbyname" /> - </menu_item_check> - <menu_item_check - control_name="Inventory.SystemFoldersToTop" - label="System Folders To Top" - layout="topleft" - name="System Folders To Top"> - <menu_item_check.on_click - function="Inventory.SetSortBy" - parameter="systemfolderstotop" /> - </menu_item_check> - </menu> - </menu_bar> -</panel> +<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
+<panel
+ background_visible="true"
+ follows="all"
+ height="400"
+ label="Things"
+ layout="topleft"
+ min_height="350"
+ min_width="240"
+ name="main inventory panel"
+ width="330">
+ <panel.string
+ name="Title">
+ Things
+ </panel.string>
+ <filter_editor
+ text_pad_left="12"
+ follows="left|top|right"
+ font="SanSerif"
+ height="20"
+ label="Filter"
+ layout="topleft"
+ left="15"
+ name="inventory search editor"
+ top="34"
+ width="300" />
+ <tab_container
+ follows="left|top|right|bottom"
+ height="300"
+ layout="topleft"
+ left_delta="-4"
+ name="inventory filter tabs"
+ tab_position="top"
+ top_pad="4"
+ width="305">
+ <inventory_panel
+ follows="left|top|right|bottom"
+ height="295"
+ label="All Items"
+ layout="topleft"
+ left="1"
+ name="All Items"
+ top="16"
+ width="290" />
+ <inventory_panel
+ follows="left|top|right|bottom"
+ height="295"
+ label="Recent Items"
+ layout="topleft"
+ left_delta="0"
+ name="Recent Items"
+ top_delta="0"
+ width="290" />
+ </tab_container>
+
+ <panel
+ background_visible="true"
+ bevel_style="none"
+ bottom="0"
+ follows="left|right|bottom"
+ height="30"
+ layout="bottomleft"
+ left="0"
+ visible="true"
+ name="bottom_panel"
+ width="330">
+ <button
+ follows="bottom|left"
+ tool_tip="Show additional options"
+ height="18"
+ image_disabled="OptionsMenu_Disabled"
+ image_selected="OptionsMenu_Press"
+ image_unselected="OptionsMenu_Off"
+ layout="topleft"
+ left="10"
+ name="options_gear_btn"
+ picture_style="true"
+ top="6"
+ width="18" />
+ <button
+ follows="bottom|left"
+ height="18"
+ image_selected="AddItem_Press"
+ image_unselected="AddItem_Off"
+ image_disabled="AddItem_Disabled"
+ layout="topleft"
+ left_pad="5"
+ name="add_btn"
+ picture_style="true"
+ tool_tip="Add new item"
+ width="18" />
+ <dnd_button
+ follows="bottom|right"
+ height="18"
+ image_selected="TrashItem_Press"
+ image_unselected="TrashItem_Off"
+ layout="topleft"
+ right="-5"
+ name="trash_btn"
+ picture_style="true"
+ tool_tip="Remove selected item"
+ top="6"
+ width="18" />
+ </panel>
+
+ <menu_bar
+ bg_visible="false"
+ follows="left|top|right"
+ height="18"
+ layout="topleft"
+ left_delta="0"
+ mouse_opaque="false"
+ name="Inventory Menu"
+ top="15"
+ visible="true"
+ width="290">
+ <menu
+ height="101"
+ label="File"
+ layout="topleft"
+ left="0"
+ mouse_opaque="false"
+ name="File"
+ tear_off="true"
+ top="-117"
+ width="128">
+ <menu_item_call
+ label="Open"
+ layout="topleft"
+ name="Open">
+ <menu_item_call.on_click
+ function="Inventory.DoToSelected"
+ parameter="open" />
+ </menu_item_call>
+ <menu
+ create_jump_keys="true"
+ label="Upload"
+ layout="topleft"
+ name="upload"
+ tear_off="true">
+ <menu_item_call
+ label="Image (L$[COST])..."
+ layout="topleft"
+ name="Upload Image"
+ shortcut="control|U">
+ <menu_item_call.on_click
+ function="File.UploadImage"
+ parameter="" />
+ <menu_item_call.on_enable
+ function="File.EnableUpload" />
+ </menu_item_call>
+ <menu_item_call
+ label="Sound (L$[COST])..."
+ layout="topleft"
+ name="Upload Sound">
+ <menu_item_call.on_click
+ function="File.UploadSound"
+ parameter="" />
+ <menu_item_call.on_enable
+ function="File.EnableUpload" />
+ </menu_item_call>
+ <menu_item_call
+ label="Animation (L$[COST])..."
+ layout="topleft"
+ name="Upload Animation">
+ <menu_item_call.on_click
+ function="File.UploadAnim"
+ parameter="" />
+ <menu_item_call.on_enable
+ function="File.EnableUpload" />
+ </menu_item_call>
+ <menu_item_call
+ label="Bulk (L$[COST] per file)..."
+ layout="topleft"
+ name="Bulk Upload">
+ <menu_item_call.on_click
+ function="File.UploadBulk"
+ parameter="" />
+ </menu_item_call>
+ <menu_item_separator
+ layout="topleft" />
+ </menu>
+ <menu_item_separator
+ layout="topleft" />
+ <menu_item_call
+ label="New Window"
+ layout="topleft"
+ name="New Window">
+ <menu_item_call.on_click
+ function="Inventory.NewWindow" />
+ </menu_item_call>
+ <menu_item_separator
+ layout="topleft"
+ name="separator2" />
+ <menu_item_call
+ label="Show Filters"
+ layout="topleft"
+ name="Show Filters">
+ <menu_item_call.on_click
+ function="Inventory.ShowFilters" />
+ </menu_item_call>
+ <menu_item_call
+ label="Reset Filters"
+ layout="topleft"
+ name="Reset Current">
+ <menu_item_call.on_click
+ function="Inventory.ResetFilter" />
+ </menu_item_call>
+ <menu_item_call
+ label="Close All Folders"
+ layout="topleft"
+ name="Close All Folders">
+ <menu_item_call.on_click
+ function="Inventory.CloseAllFolders" />
+ </menu_item_call>
+ <menu_item_separator
+ layout="topleft"
+ name="separator3" />
+ <menu_item_call
+ label="Empty Trash"
+ layout="topleft"
+ name="Empty Trash">
+ <menu_item_call.on_click
+ function="Inventory.EmptyTrash" />
+ </menu_item_call>
+ <menu_item_call
+ label="Empty Lost And Found"
+ layout="topleft"
+ name="Empty Lost And Found">
+ <menu_item_call.on_click
+ function="Inventory.EmptyLostAndFound" />
+ </menu_item_call>
+ </menu>
+ <menu
+ height="121"
+ label="Create"
+ layout="topleft"
+ left="0"
+ mouse_opaque="false"
+ name="Create"
+ tear_off="true"
+ top="-201"
+ width="121">
+ <menu_item_call
+ label="New Folder"
+ layout="topleft"
+ name="New Folder">
+ <menu_item_call.on_click
+ function="Inventory.DoCreate"
+ parameter="category" />
+ </menu_item_call>
+ <menu_item_call
+ label="New Script"
+ layout="topleft"
+ name="New Script">
+ <menu_item_call.on_click
+ function="Inventory.DoCreate"
+ parameter="lsl" />
+ </menu_item_call>
+ <menu_item_call
+ label="New Note"
+ layout="topleft"
+ name="New Note">
+ <menu_item_call.on_click
+ function="Inventory.DoCreate"
+ parameter="notecard" />
+ </menu_item_call>
+ <menu_item_call
+ label="New Gesture"
+ layout="topleft"
+ name="New Gesture">
+ <menu_item_call.on_click
+ function="Inventory.DoCreate"
+ parameter="gesture" />
+ </menu_item_call>
+ <menu
+ height="175"
+ label="New Clothes"
+ layout="topleft"
+ left_delta="0"
+ mouse_opaque="false"
+ name="New Clothes"
+ top_pad="514"
+ width="125">
+ <menu_item_call
+ label="New Shirt"
+ layout="topleft"
+ name="New Shirt">
+ <menu_item_call.on_click
+ function="Inventory.DoCreate"
+ parameter="shirt" />
+ </menu_item_call>
+ <menu_item_call
+ label="New Pants"
+ layout="topleft"
+ name="New Pants">
+ <menu_item_call.on_click
+ function="Inventory.DoCreate"
+ parameter="pants" />
+ </menu_item_call>
+ <menu_item_call
+ label="New Shoes"
+ layout="topleft"
+ name="New Shoes">
+ <menu_item_call.on_click
+ function="Inventory.DoCreate"
+ parameter="shoes" />
+ </menu_item_call>
+ <menu_item_call
+ label="New Socks"
+ layout="topleft"
+ name="New Socks">
+ <menu_item_call.on_click
+ function="Inventory.DoCreate"
+ parameter="socks" />
+ </menu_item_call>
+ <menu_item_call
+ label="New Jacket"
+ layout="topleft"
+ name="New Jacket">
+ <menu_item_call.on_click
+ function="Inventory.DoCreate"
+ parameter="jacket" />
+ </menu_item_call>
+ <menu_item_call
+ label="New Skirt"
+ layout="topleft"
+ name="New Skirt">
+ <menu_item_call.on_click
+ function="Inventory.DoCreate"
+ parameter="skirt" />
+ </menu_item_call>
+ <menu_item_call
+ label="New Gloves"
+ layout="topleft"
+ name="New Gloves">
+ <menu_item_call.on_click
+ function="Inventory.DoCreate"
+ parameter="gloves" />
+ </menu_item_call>
+ <menu_item_call
+ label="New Undershirt"
+ layout="topleft"
+ name="New Undershirt">
+ <menu_item_call.on_click
+ function="Inventory.DoCreate"
+ parameter="undershirt" />
+ </menu_item_call>
+ <menu_item_call
+ label="New Underpants"
+ layout="topleft"
+ name="New Underpants">
+ <menu_item_call.on_click
+ function="Inventory.DoCreate"
+ parameter="underpants" />
+ </menu_item_call>
+ <menu_item_call
+ label="New Alpha"
+ layout="topleft"
+ name="New Alpha">
+ <menu_item_call.on_click
+ function="Inventory.DoCreate"
+ parameter="alpha" />
+ </menu_item_call>
+ <menu_item_call
+ label="New Tattoo"
+ layout="topleft"
+ name="New Tattoo">
+ <menu_item_call.on_click
+ function="Inventory.DoCreate"
+ parameter="tattoo" />
+ </menu_item_call>
+ </menu>
+ <menu
+ height="85"
+ label="New Body Parts"
+ layout="topleft"
+ left_delta="0"
+ mouse_opaque="false"
+ name="New Body Parts"
+ top_pad="514"
+ width="118">
+ <menu_item_call
+ label="New Shape"
+ layout="topleft"
+ name="New Shape">
+ <menu_item_call.on_click
+ function="Inventory.DoCreate"
+ parameter="shape" />
+ </menu_item_call>
+ <menu_item_call
+ label="New Skin"
+ layout="topleft"
+ name="New Skin">
+ <menu_item_call.on_click
+ function="Inventory.DoCreate"
+ parameter="skin" />
+ </menu_item_call>
+ <menu_item_call
+ label="New Hair"
+ layout="topleft"
+ name="New Hair">
+ <menu_item_call.on_click
+ function="Inventory.DoCreate"
+ parameter="hair" />
+ </menu_item_call>
+ <menu_item_call
+ label="New Eyes"
+ layout="topleft"
+ name="New Eyes">
+ <menu_item_call.on_click
+ function="Inventory.DoCreate"
+ parameter="eyes" />
+ </menu_item_call>
+ </menu>
+ </menu>
+ <menu
+ height="49"
+ label="Sort"
+ layout="topleft"
+ left="0"
+ mouse_opaque="false"
+ name="Sort"
+ tear_off="true"
+ top="-113"
+ width="118">
+ <menu_item_check
+ control_name="Inventory.SortByName"
+ label="By Name"
+ layout="topleft"
+ name="By Name">
+ <menu_item_check.on_click
+ function="Inventory.SetSortBy"
+ parameter="name" />
+ </menu_item_check>
+ <menu_item_check
+ control_name="Inventory.SortByDate"
+ label="By Date"
+ layout="topleft"
+ name="By Date">
+ <menu_item_check.on_click
+ function="Inventory.SetSortBy"
+ parameter="date" />
+ </menu_item_check>
+ <menu_item_separator
+ layout="topleft" />
+ <menu_item_check
+ control_name="Inventory.FoldersAlwaysByName"
+ label="Folders Always By Name"
+ layout="topleft"
+ name="Folders Always By Name">
+ <menu_item_check.on_click
+ function="Inventory.SetSortBy"
+ parameter="foldersalwaysbyname" />
+ </menu_item_check>
+ <menu_item_check
+ control_name="Inventory.SystemFoldersToTop"
+ label="System Folders To Top"
+ layout="topleft"
+ name="System Folders To Top">
+ <menu_item_check.on_click
+ function="Inventory.SetSortBy"
+ parameter="systemfolderstotop" />
+ </menu_item_check>
+ </menu>
+ </menu_bar>
+</panel>
diff --git a/indra/newview/skins/default/xui/en/panel_navigation_bar.xml b/indra/newview/skins/default/xui/en/panel_navigation_bar.xml index 44fd44cde5..a90337d31a 100644 --- a/indra/newview/skins/default/xui/en/panel_navigation_bar.xml +++ b/indra/newview/skins/default/xui/en/panel_navigation_bar.xml @@ -7,7 +7,7 @@ height="65" layout="topleft" name="navigation_bar" - chrome="true" + chrome="true" width="600"> <icon follows="all" @@ -145,7 +145,8 @@ top_delta="0" width="200" > <combo_editor - label="Search" /> + label="Search [SECOND_LIFE]" + name="search_combo_editor"/> </search_combo_box> </panel> diff --git a/indra/newview/skins/default/xui/en/panel_preferences_advanced.xml b/indra/newview/skins/default/xui/en/panel_preferences_advanced.xml index 613decad8d..06f0710406 100644 --- a/indra/newview/skins/default/xui/en/panel_preferences_advanced.xml +++ b/indra/newview/skins/default/xui/en/panel_preferences_advanced.xml @@ -272,7 +272,7 @@ Avatars: width="315" /> <radio_item height="16" - label="In window" + label="In a window" layout="topleft" left_delta="175" name="1" @@ -284,13 +284,13 @@ Avatars: enabled_control="EnableVoiceChat" control_name="PushToTalkToggle" height="20" - label="Use Push-to-talk in toggle mode" + label="Toggle mode for microphone when I press the Speak trigger key:" layout="topleft" left="30" name="push_to_talk_toggle_check" width="237" top_pad="-25" - tool_tip="When in toggle mode, press and release the push-to-talk trigger to switch your microphone on and off. When not in toggle mode, the microphone is active only when the trigger is held down."/> + tool_tip="When in toggle mode, press and release the trigger key ONCE to switch your microphone on or off. When not in toggle mode, the microphone broadcasts your voice only while the trigger is being held down."/> <line_editor follows="top|left" control_name="PushToTalkButton" @@ -300,7 +300,7 @@ Avatars: left_delta="50" max_length="254" name="modifier_combo" - label="Push-to-talk trigger" + label="Push-to-Speak trigger" top_pad="0" width="280" /> <button diff --git a/indra/newview/skins/default/xui/en/panel_preferences_privacy.xml b/indra/newview/skins/default/xui/en/panel_preferences_privacy.xml index ce7939c00f..acf4601bfe 100644 --- a/indra/newview/skins/default/xui/en/panel_preferences_privacy.xml +++ b/indra/newview/skins/default/xui/en/panel_preferences_privacy.xml @@ -94,6 +94,7 @@ layout="topleft" left="30" mouse_opaque="false" + name="Logs:" top_pad="10" width="350"> Logs: @@ -184,6 +185,7 @@ layout="topleft" left_delta="0" mouse_opaque="false" + name="log_path_desc" top_pad="1" width="128" text_color="LtGray_50"> diff --git a/indra/newview/skins/default/xui/en/panel_preferences_sound.xml b/indra/newview/skins/default/xui/en/panel_preferences_sound.xml index c3e0cb352b..d454b9e5c8 100644 --- a/indra/newview/skins/default/xui/en/panel_preferences_sound.xml +++ b/indra/newview/skins/default/xui/en/panel_preferences_sound.xml @@ -336,7 +336,7 @@ follows="left|bottom" height="19" is_toggle="true" - label="Input / Output Devices" + label="Input/Output Devices" layout="topleft" left="165" top_pad="12" @@ -504,17 +504,4 @@ top_pad="0" width="200" /> </panel> - <!-- Until new panel is hooked up to code, we need to be able to get to - the old window to change input devices. James --> - <button - follows="left|bottom" - label="Old" - name="legacy_device_window_btn" - height="16" - left="20" - top="-270" - width="40" - commit_callback.function="Floater.Show" - commit_callback.parameter="pref_voicedevicesettings" - /> </panel> diff --git a/indra/newview/skins/default/xui/en/panel_prim_media_controls.xml b/indra/newview/skins/default/xui/en/panel_prim_media_controls.xml index fc5ccdb63e..3bdd7114ee 100644 --- a/indra/newview/skins/default/xui/en/panel_prim_media_controls.xml +++ b/indra/newview/skins/default/xui/en/panel_prim_media_controls.xml @@ -494,13 +494,32 @@ disabled image_selected="media_btn_optimalzoom.png" image_unselected="media_btn_optimalzoom.png" layout="topleft" - tool_tip="Zoom" + tool_tip="Zoom into media" min_width="22" width="22"> <button.commit_callback function="MediaCtrl.Zoom" /> </button> </layout_panel> + <layout_panel + name="close" + auto_resize="false" + user_resize="false" + layout="topleft" + min_width="21" + width="21" > + <button + height="22" + image_selected="media_btn_done.png" + image_unselected="media_btn_done.png" + layout="topleft" + tool_tip ="Zoom Back" + top_delta="-4" + width="21" > + <button.commit_callback + function="MediaCtrl.Close" /> + </button> + </layout_panel> <!-- <panel height="22" @@ -554,25 +573,6 @@ disabled </panel> --> <layout_panel - name="close" - auto_resize="false" - user_resize="false" - layout="topleft" - min_width="21" - width="21" > - <button - height="22" - image_selected="media_btn_done.png" - image_unselected="media_btn_done.png" - layout="topleft" - tool_tip ="Close media control" - top_delta="-4" - width="21" > - <button.commit_callback - function="MediaCtrl.Close" /> - </button> - </layout_panel> - <layout_panel width="0" layout="topleft" user_resize="false" /> diff --git a/indra/newview/skins/default/xui/en/panel_profile.xml b/indra/newview/skins/default/xui/en/panel_profile.xml index 23d55b33e2..a0055383b1 100644 --- a/indra/newview/skins/default/xui/en/panel_profile.xml +++ b/indra/newview/skins/default/xui/en/panel_profile.xml @@ -58,7 +58,7 @@ default_image_name="None" enabled="false" follows="top|left" - height="117" + height="102" layout="topleft" left="0" name="2nd_life_pic" @@ -102,7 +102,7 @@ default_image_name="None" enabled="false" follows="top|left" - height="117" + height="102" layout="topleft" left="0" name="real_world_pic" diff --git a/indra/newview/skins/default/xui/en/panel_side_tray.xml b/indra/newview/skins/default/xui/en/panel_side_tray.xml index 3582de1c71..a9874f4553 100644 --- a/indra/newview/skins/default/xui/en/panel_side_tray.xml +++ b/indra/newview/skins/default/xui/en/panel_side_tray.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="utf-8" standalone="yes" ?> -<!-- Side tray cannot show background because it is always +<!-- Side tray cannot show background because it is always partially on screen to hold tab buttons. --> <side_tray name="sidebar" @@ -10,7 +10,7 @@ > <!-- Individual tabs must show background to have seemless appearance up to tray panel header word like "Home". - Embedded panels are inset by a pixel and so their + Embedded panels are inset by a pixel and so their backgrounds will not block the world fully. --> <sidetray_tab name="sidebar_home" @@ -70,7 +70,7 @@ </panel_container> </sidetray_tab> - + <sidetray_tab name="sidebar_places" help_topic="sidebar_places" @@ -133,8 +133,8 @@ help_topic="sidebar_inventory" tab_title="Inventory" description="Browse your inventory." - image="TabIcon_Inventory_Off" - image_selected="TabIcon_Inventory_Selected" + image="TabIcon_Things_Off" + image_selected="TabIcon_Things_Selected" mouse_opaque="false" background_visible="true" > diff --git a/indra/newview/skins/default/xui/en/sidepanel_inventory.xml b/indra/newview/skins/default/xui/en/sidepanel_inventory.xml index d0c3cdfafc..cbcc3f10ad 100644 --- a/indra/newview/skins/default/xui/en/sidepanel_inventory.xml +++ b/indra/newview/skins/default/xui/en/sidepanel_inventory.xml @@ -1,122 +1,127 @@ -<?xml version="1.0" encoding="utf-8" standalone="yes" ?> -<panel - background_visible="true" - follows="all" - height="400" - label="Things" - layout="topleft" - min_height="350" - min_width="240" - name="objects panel" - width="333"> - <tab_container - follows="all" - height="390" - layout="topleft" - left="9" - name="Inventory Tabs" - tab_position="top" - top="0" - width="313" - tab_height="0" - visible="true"> - <panel - class="panel_main_inventory" - filename="panel_main_inventory.xml" - follows="all" - layout="topleft" - left="0" - name="panel_main_inventory" - top="15" - label="" - height="330" - width="467"> - <panel - height="25" - layout="bottomright" - left="0" - help_topic="objects_button_tab" - name="button_panel" - bottom="0" - width="313"> - <button - enabled="true" - follows="bottom|left" - font="SansSerifSmallBold" - height="25" - label="Info" - layout="topleft" - left="0" - name="info_btn" - top="0" - width="60" /> - <button - enabled="true" - follows="bottom|left" - font="SansSerifSmallBold" - height="25" - label="Share" - layout="topleft" - left_pad="5" - name="share_btn" - top="0" - width="60" /> - <button - enabled="false" - follows="bottom|left" - font="SansSerifSmallBold" - height="25" - label="Wear" - layout="topleft" - left="130" - name="wear_btn" - top="0" - width="60" /> - <button - enabled="false" - follows="bottom|left" - font="SansSerifSmallBold" - height="25" - label="Play" - layout="topleft" - name="play_btn" - left="130" - top="0" - width="50" /> - <button - enabled="false" - follows="bottom|left" - font="SansSerifSmallBold" - height="25" - label="Teleport" - layout="topleft" - left="130" - name="teleport_btn" - top="0" - width="77" /> - <button - follows="bottom|right" - font="SansSerifSmallBold" - height="25" - label="v" - layout="topleft" - name="overflow_btn" - right="-10" - top="0" - width="30" /> - </panel> - </panel> - </tab_container> - - <panel - class="sidepanel_object_info" - filename="sidepanel_object_info.xml" - follows="all" - height="360" - layout="topleft" - left="0" - help_topic="objects_info_tab" - name="sidepanel_object_info" - top="30" - visible="false" /> -</panel> +<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
+<panel
+ background_visible="true"
+ follows="all"
+ height="570"
+ label="Things"
+ layout="topleft"
+ min_height="350"
+ min_width="240"
+ name="objects panel"
+ width="333">
+ <panel
+ follows="all"
+ layout="topleft"
+ left="0"
+ name="sidepanel__inventory_panel"
+ top="0"
+ label=""
+ height="570"
+ visible="true"
+ width="330">
+ <panel
+ class="panel_main_inventory"
+ filename="panel_main_inventory.xml"
+ follows="all"
+ layout="topleft"
+ left="0"
+ name="panel_main_inventory"
+ top="0"
+ label=""
+ height="500"
+ width="330" />
+ <panel
+ height="25"
+ layout="bottomright"
+ help_topic="item_button_tab"
+ name="button_panel"
+ left="5"
+ bottom="5"
+ width="313">
+ <button
+ enabled="true"
+ follows="bottom|left"
+ font="SansSerifSmallBold"
+ height="25"
+ label="Info"
+ layout="topleft"
+ left="0"
+ name="info_btn"
+ top="0"
+ width="60" />
+ <button
+ enabled="true"
+ follows="bottom|left"
+ font="SansSerifSmallBold"
+ height="25"
+ label="Share"
+ layout="topleft"
+ left_pad="5"
+ name="share_btn"
+ top="0"
+ width="60" />
+ <button
+ enabled="false"
+ follows="bottom|left"
+ font="SansSerifSmallBold"
+ height="25"
+ label="Wear"
+ layout="topleft"
+ left="130"
+ name="wear_btn"
+ top="0"
+ width="60" />
+ <button
+ enabled="false"
+ follows="bottom|left"
+ font="SansSerifSmallBold"
+ height="25"
+ label="Play"
+ layout="topleft"
+ name="play_btn"
+ left="130"
+ top="0"
+ width="50" />
+ <button
+ enabled="false"
+ follows="bottom|left"
+ font="SansSerifSmallBold"
+ height="25"
+ label="Teleport"
+ layout="topleft"
+ left="130"
+ name="teleport_btn"
+ top="0"
+ width="77" />
+ </panel>
+ </panel>
+
+ <panel
+ follows="all"
+ layout="topleft"
+ left="0"
+ class="sidepanel_item_info"
+ filename="sidepanel_item_info.xml"
+ name="sidepanel__item_panel"
+ top="0"
+ label=""
+ height="570"
+ visible="false"
+ width="330">
+ </panel>
+
+ <panel
+ follows="all"
+ layout="topleft"
+ left="0"
+ class="sidepanel_task_info"
+ filename="sidepanel_task_info.xml"
+ name="sidepanel__task_panel"
+ top="0"
+ label=""
+ height="570"
+ visible="false"
+ width="330">
+ </panel>
+
+</panel>
diff --git a/indra/newview/skins/default/xui/en/sidepanel_item_info.xml b/indra/newview/skins/default/xui/en/sidepanel_item_info.xml new file mode 100644 index 0000000000..39cd75074e --- /dev/null +++ b/indra/newview/skins/default/xui/en/sidepanel_item_info.xml @@ -0,0 +1,511 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
+<panel
+ auto_tile="true"
+ height="570"
+ layout="topleft"
+ name="item properties"
+ help_topic="item_properties"
+ save_rect="true"
+ title="Inventory Item Properties"
+ width="333">
+ <panel.string
+ name="unknown">
+ (unknown)
+ </panel.string>
+ <panel.string
+ name="public">
+ (public)
+ </panel.string>
+ <panel.string
+ name="you_can">
+ You can:
+ </panel.string>
+ <panel.string
+ name="owner_can">
+ Owner can:
+ </panel.string>
+ <panel.string
+ name="acquiredDate">
+ [wkday,datetime,local] [mth,datetime,local] [day,datetime,local] [hour,datetime,local]:[min,datetime,local]:[second,datetime,local] [year,datetime,local]
+ </panel.string>
+ <icon
+ follows="top|right"
+ height="18"
+ image_name="Lock"
+ layout="topleft"
+ right="-50"
+ mouse_opaque="true"
+ name="IconLocked"
+ top="4"
+ width="18" />
+ <button
+ follows="top|right"
+ height="25"
+ image_overlay="BackArrow_Off"
+ layout="topleft"
+ name="back_btn"
+ picture_style="true"
+ right="-5"
+ tab_stop="false"
+ top="0"
+ width="25" />
+ <panel
+ follows="all"
+ height="500"
+ label=""
+ layout="topleft"
+ left="5"
+ help_topic=""
+ top="30"
+ border="1"
+ width="313">
+ <text
+ type="string"
+ length="1"
+ follows="left|top"
+ height="10"
+ layout="topleft"
+ left="5"
+ name="LabelItemNameTitle"
+ top="5"
+ width="78">
+ Name:
+ </text>
+ <line_editor
+ border_style="line"
+ border_thickness="1"
+ follows="left|top|right"
+ height="16"
+ layout="topleft"
+ left_delta="78"
+ max_length="63"
+ name="LabelItemName"
+ top_delta="0"
+ width="225" />
+ <text
+ type="string"
+ length="1"
+ follows="left|top"
+ height="10"
+ layout="topleft"
+ left="5"
+ name="LabelItemDescTitle"
+ top_delta="20"
+ width="78">
+ Description:
+ </text>
+ <line_editor
+ border_style="line"
+ border_thickness="1"
+ follows="left|top|right"
+ height="16"
+ layout="topleft"
+ left_delta="78"
+ max_length="127"
+ name="LabelItemDesc"
+ top_delta="0"
+ width="225" />
+ <text
+ type="string"
+ length="1"
+ follows="left|top"
+ height="16"
+ layout="topleft"
+ left="10"
+ name="LabelCreatorTitle"
+ top="65"
+ width="78">
+ Creator:
+ </text>
+ <text
+ type="string"
+ length="1"
+ follows="left|top"
+ height="16"
+ layout="topleft"
+ left_delta="78"
+ name="LabelCreatorName"
+ top_delta="0"
+ width="200">
+ Nicole Linden
+ </text>
+ <button
+ follows="top|right"
+ font="SansSerifSmall"
+ height="16"
+ label="Profile..."
+ layout="topleft"
+ left_delta="144"
+ name="BtnCreator"
+ top_delta="0"
+ width="78" />
+ <text
+ type="string"
+ length="1"
+ follows="left|top"
+ height="16"
+ layout="topleft"
+ left="10"
+ name="LabelOwnerTitle"
+ top="85"
+ width="78">
+ Owner:
+ </text>
+ <text
+ type="string"
+ length="1"
+ follows="left|top"
+ height="16"
+ layout="topleft"
+ left_delta="78"
+ name="LabelOwnerName"
+ top_delta="0"
+ width="200">
+ Thrax Linden
+ </text>
+ <button
+ follows="top|right"
+ font="SansSerifSmall"
+ height="16"
+ label="Profile..."
+ layout="topleft"
+ left_delta="144"
+ name="BtnOwner"
+ top_delta="0"
+ width="78" />
+ <text
+ type="string"
+ length="1"
+ follows="left|top"
+ height="16"
+ layout="topleft"
+ left="10"
+ name="LabelAcquiredTitle"
+ top="105"
+ width="78">
+ Acquired:
+ </text>
+ <text
+ type="string"
+ length="1"
+ follows="left|top"
+ height="16"
+ layout="topleft"
+ left_delta="78"
+ name="LabelAcquiredDate"
+ top_delta="0"
+ width="252">
+ Wed May 24 12:50:46 2006
+ </text>
+ <text
+ type="string"
+ length="1"
+ follows="left|top"
+ height="10"
+ layout="topleft"
+ left="10"
+ name="OwnerLabel"
+ top="125"
+ width="78">
+ You:
+ </text>
+ <check_box
+ height="16"
+ label="Edit"
+ layout="topleft"
+ left_pad="5"
+ name="CheckOwnerModify"
+ top_delta="0"
+ width="78" />
+ <check_box
+ height="16"
+ label="Copy"
+ layout="topleft"
+ left_delta="0"
+ name="CheckOwnerCopy"
+ top_pad="5"
+ width="88" />
+ <check_box
+ height="16"
+ label="Resell"
+ layout="topleft"
+ left_delta="0"
+ name="CheckOwnerTransfer"
+ top_pad="5"
+ width="106" />
+ <text
+ type="string"
+ length="1"
+ follows="left|top"
+ height="10"
+ layout="topleft"
+ left="10"
+ name="AnyoneLabel"
+ top_pad="5"
+ width="78">
+ Anyone:
+ </text>
+ <check_box
+ height="16"
+ label="Copy"
+ layout="topleft"
+ left_pad="5"
+ name="CheckEveryoneCopy"
+ top_delta="0"
+ width="130" />
+ <text
+ type="string"
+ length="1"
+ follows="left|top"
+ height="10"
+ layout="topleft"
+ left="10"
+ name="GroupLabel"
+ top_pad="5"
+ width="78">
+ Group:
+ </text>
+ <check_box
+ height="16"
+ label="Share"
+ layout="topleft"
+ left_pad="5"
+ name="CheckShareWithGroup"
+ top_delta="5"
+ width="106" />
+ <text
+ type="string"
+ length="1"
+ follows="left|top"
+ height="10"
+ layout="topleft"
+ left="10"
+ name="NextOwnerLabel"
+ top_pad="5"
+ width="78">
+ Next owner:
+ </text>
+ <check_box
+ height="16"
+ label="Edit"
+ layout="topleft"
+ left_pad="5"
+ name="CheckNextOwnerModify"
+ top_delta="0"
+ width="78" />
+ <check_box
+ height="16"
+ label="Copy"
+ layout="topleft"
+ left_delta="0"
+ name="CheckNextOwnerCopy"
+ top_pad="5"
+ width="88" />
+ <check_box
+ height="16"
+ label="Resell"
+ layout="topleft"
+ left_delta="0"
+ name="CheckNextOwnerTransfer"
+ top_pad="5"
+ width="106" />
+ <check_box
+ height="16"
+ label="For Sale"
+ layout="topleft"
+ left="10"
+ name="CheckPurchase"
+ top_pad="5"
+ width="78" />
+ <combo_box
+ height="19"
+ left_pad="5"
+ layout="topleft"
+ follows="left|top"
+ name="combobox sale copy"
+ width="90">
+ <combo_box.item
+ label="Copy"
+ name="Copy"
+ value="Copy" />
+ <combo_box.item
+ label="Original"
+ name="Original"
+ value="Original" />
+ </combo_box>
+ <spinner
+ follows="left|top"
+ decimal_digits="0"
+ increment="1"
+ control_name="Edit Cost"
+ name="Edit Cost"
+ label="Price: L$"
+ label_width="60"
+ left="10"
+ width="180"
+ min_val="1"
+ height="19"
+ max_val="999999999"
+ top_pad="5"/>
+ <!--line_editor
+ border_style="line"
+ border_thickness="1"
+ follows="left|top|right"
+ height="16"
+ layout="topleft"
+ left_pad="5"
+ max_length="25"
+ name="EditPrice"
+ top_delta="0"
+ width="242" /-->
+ <!--text
+ type="string"
+ length="1"
+ follows="left|top"
+ height="10"
+ layout="topleft"
+ left="10"
+ name="BaseMaskDebug"
+ top="155"
+ width="330">
+ B:
+ </text>
+ <text
+ type="string"
+ length="1"
+ follows="left|top"
+ height="10"
+ layout="topleft"
+ left_delta="60"
+ name="OwnerMaskDebug"
+ top_delta="0"
+ width="270">
+ O:
+ </text>
+ <text
+ type="string"
+ length="1"
+ follows="left|top"
+ height="10"
+ layout="topleft"
+ left_delta="60"
+ name="GroupMaskDebug"
+ top_delta="0"
+ width="210">
+ G:
+ </text>
+ <text
+ type="string"
+ length="1"
+ follows="left|top"
+ height="10"
+ layout="topleft"
+ left_delta="60"
+ name="EveryoneMaskDebug"
+ top_delta="0"
+ width="150">
+ E:
+ </text>
+ <text
+ type="string"
+ length="1"
+ follows="left|top"
+ height="10"
+ layout="topleft"
+ left_delta="60"
+ name="NextMaskDebug"
+ top_delta="0"
+ width="90">
+ N:
+ </text-->
+ <!--text
+ type="string"
+ length="1"
+ follows="left|top"
+ height="10"
+ layout="topleft"
+ left="10"
+ name="SaleLabel"
+ top_pad="5"
+ width="330">
+ Mark Item:
+ </text-->
+ <!--radio_group
+ draw_border="false"
+ follows="left|top|right"
+ height="16"
+ layout="topleft"
+ left_delta="78"
+ name="RadioSaleType"
+ top_delta="0"
+ width="252">
+ <radio_item
+ height="16"
+ label="Original"
+ layout="topleft"
+ left="0"
+ name="radio"
+ top="0"
+ width="70" />
+ <radio_item
+ height="16"
+ label="Copy"
+ layout="topleft"
+ left_delta="60"
+ name="radio2"
+ top_delta="0"
+ width="70" />
+ </radio_group-->
+ <!--text
+ type="string"
+ length="1"
+ follows="left|top"
+ height="16"
+ layout="topleft"
+ left="10"
+ name="TextPrice"
+ top_pad="5"
+ width="78">
+ Price: L$
+ </text-->
+ </panel>
+ <panel
+ height="25"
+ layout="bottomright"
+ help_topic="button_tab"
+ name="button_panel"
+ left="5"
+ bottom="5"
+ width="313">
+ <button
+ follows="bottom|left"
+ font="SansSerifSmallBold"
+ height="25"
+ label="Edit"
+ layout="topleft"
+ left="0"
+ name="edit_btn"
+ top="0"
+ width="50" />
+ <button
+ follows="bottom|right"
+ font="SansSerifSmallBold"
+ height="25"
+ label="Cancel"
+ layout="topleft"
+ name="cancel_btn"
+ right="-1"
+ top="0"
+ width="70" />
+ <button
+ follows="bottom|right"
+ font="SansSerifSmallBold"
+ height="25"
+ label="Save"
+ layout="topleft"
+ name="save_btn"
+ left_pad="-135"
+ top="0"
+ width="60" />
+ </panel>
+</panel>
diff --git a/indra/newview/skins/default/xui/en/widgets/menu_item.xml b/indra/newview/skins/default/xui/en/widgets/menu_item.xml new file mode 100644 index 0000000000..c98e9cb6b8 --- /dev/null +++ b/indra/newview/skins/default/xui/en/widgets/menu_item.xml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<!-- Use this for the top-level menu styling --> +<menu_item + font="SansSerifSmall" + > +</menu_item> diff --git a/indra/newview/tests/lltextureinfo_test.cpp b/indra/newview/tests/lltextureinfo_test.cpp new file mode 100644 index 0000000000..8dfba46262 --- /dev/null +++ b/indra/newview/tests/lltextureinfo_test.cpp @@ -0,0 +1,284 @@ +/** + * @file llwtextureinfo_test.cpp + * @author Si & Gabriel + * @date 2009-03-30 + * + * $LicenseInfo:firstyear=2006&license=viewergpl$ + * + * Copyright (c) 2006-2009, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +// Precompiled header: almost always required for newview cpp files +#include "../llviewerprecompiledheaders.h" +// Class to test +#include "../lltextureinfo.h" +// Dependencies +#include "../lltextureinfodetails.cpp" + +// Tut header +#include "../test/lltut.h" + +// ------------------------------------------------------------------------------------------- +// Stubbing: Declarations required to link and run the class being tested +// Notes: +// * Add here stubbed implementation of the few classes and methods used in the class to be tested +// * Add as little as possible (let the link errors guide you) +// * Do not make any assumption as to how those classes or methods work (i.e. don't copy/paste code) +// * A simulator for a class can be implemented here. Please comment and document thoroughly. + + +// End Stubbing +// ------------------------------------------------------------------------------------------- + +// ------------------------------------------------------------------------------------------- +// TUT +// ------------------------------------------------------------------------------------------- + +namespace tut +{ + // Test wrapper declarations + struct textureinfo_test + { + // Constructor and destructor of the test wrapper + textureinfo_test() + { + } + ~textureinfo_test() + { + } + }; + + // Tut templating thingamagic: test group, object and test instance + typedef test_group<textureinfo_test> textureinfo_t; + typedef textureinfo_t::object textureinfo_object_t; + tut::textureinfo_t tut_textureinfo("textureinfo"); + + + // --------------------------------------------------------------------------------------- + // Test functions + // Notes: + // * Test as many as you possibly can without requiring a full blown simulation of everything + // * The tests are executed in sequence so the test instance state may change between calls + // * Remember that you cannot test private methods with tut + // --------------------------------------------------------------------------------------- + + // --------------------------------------------------------------------------------------- + // Test the LLTextureInfo + // --------------------------------------------------------------------------------------- + + + // Test instantiation + template<> template<> + void textureinfo_object_t::test<1>() + { + LLTextureInfo tex_info; + tex_info.setUpLogging(true, true); + ensure("have we crashed?", true); + } + + // Check lltextureinfo does not contain UUIDs we haven't added + template<> template<> + void textureinfo_object_t::test<2>() + { + LLTextureInfo tex_info; + tex_info.setUpLogging(true, true); + + LLUUID nonExistant("3a0efa3b-84dc-4e17-9b8c-79ea028850c1"); + ensure(!tex_info.has(nonExistant)); + } + + // Check we can add a request time for a texture + template<> template<> + void textureinfo_object_t::test<3>() + { + LLTextureInfo tex_info; + tex_info.setUpLogging(true, true); + + LLUUID id("10e65d70-46fd-429f-841a-bf698e9424d3"); + tex_info.setRequestStartTime(id, 200); + + ensure_equals(tex_info.getRequestStartTime(id), 200); + } + + // Check time for non-existant texture + template<> template<> + void textureinfo_object_t::test<4>() + { + LLTextureInfo tex_info; + tex_info.setUpLogging(true, true); + + LLUUID nonExistant("3a0efa3b-84dc-4e17-9b8c-79ea028850c1"); + ensure_equals(tex_info.getRequestStartTime(nonExistant), 0); + } + + // Check download complete time for non existant texture + template<> template<> + void textureinfo_object_t::test<5>() + { + LLTextureInfo tex_info; + tex_info.setUpLogging(true, true); + + LLUUID nonExistant("3a0efa3b-84dc-4e17-9b8c-79ea028850c1"); + ensure_equals(tex_info.getRequestCompleteTime(nonExistant), 0); + } + + // requested size is passed in correctly + template<> template<> + void textureinfo_object_t::test<6>() + { + LLTextureInfo tex_info; + tex_info.setUpLogging(true, true); + + LLUUID id("10e65d70-46fd-429f-841a-bf698e9424d3"); + tex_info.setRequestSize(id, 600); + + ensure_equals(tex_info.getRequestSize(id), 600); + } + + // transport type is recorded correctly (http) + template<> template<> + void textureinfo_object_t::test<7>() + { + LLTextureInfo tex_info; + tex_info.setUpLogging(true, true); + + LLUUID id("10e65d70-46fd-429f-841a-bf698e9424d3"); + tex_info.setRequestType(id, LLTextureInfoDetails::REQUEST_TYPE_HTTP); + + ensure_equals(tex_info.getRequestType(id), LLTextureInfoDetails::REQUEST_TYPE_HTTP); + } + + // transport type is recorded correctly (udp) + template<> template<> + void textureinfo_object_t::test<8>() + { + LLTextureInfo tex_info; + tex_info.setUpLogging(true, true); + + LLUUID id("10e65d70-46fd-429f-841a-bf698e9424d3"); + tex_info.setRequestType(id, LLTextureInfoDetails::REQUEST_TYPE_UDP); + + ensure_equals(tex_info.getRequestType(id), LLTextureInfoDetails::REQUEST_TYPE_UDP); + } + + // request offset is recorded correctly + template<> template<> + void textureinfo_object_t::test<9>() + { + LLTextureInfo tex_info; + tex_info.setUpLogging(true, true); + + LLUUID id("10e65d70-46fd-429f-841a-bf698e9424d3"); + tex_info.setRequestOffset(id, 1234); + + ensure_equals(tex_info.getRequestOffset(id), 1234); + } + + // ask for averages gives us correct figure + template<> template<> + void textureinfo_object_t::test<10>() + { + LLTextureInfo tex_info; + tex_info.setUpLogging(true, true); + + S32 requestStartTimeOne = 200; + S32 requestEndTimeOne = 400; + S32 requestSizeOne = 1024; + S32 requestSizeOneBits = requestSizeOne * 8; + LLUUID id1("10e65d70-46fd-429f-841a-bf698e9424d3"); + tex_info.setRequestStartTime(id1, requestStartTimeOne); + tex_info.setRequestSize(id1, requestSizeOne); + tex_info.setRequestType(id1, LLTextureInfoDetails::REQUEST_TYPE_HTTP); + tex_info.setRequestCompleteTimeAndLog(id1, requestEndTimeOne); + + U32 requestStartTimeTwo = 100; + U32 requestEndTimeTwo = 500; + U32 requestSizeTwo = 2048; + S32 requestSizeTwoBits = requestSizeTwo * 8; + LLUUID id2("10e65d70-46fd-429f-841a-bf698e9424d4"); + tex_info.setRequestStartTime(id2, requestStartTimeTwo); + tex_info.setRequestSize(id2, requestSizeTwo); + tex_info.setRequestType(id2, LLTextureInfoDetails::REQUEST_TYPE_HTTP); + tex_info.setRequestCompleteTimeAndLog(id2, requestEndTimeTwo); + + S32 averageBitRate = ((requestSizeOneBits/(requestEndTimeOne - requestStartTimeOne)) + + (requestSizeTwoBits/(requestEndTimeTwo - requestStartTimeTwo))) / 2; + + S32 totalBytes = requestSizeOne + requestSizeTwo; + + LLSD results = tex_info.getAverages(); + ensure_equals("is average bits per second correct", results["bits_per_second"].asInteger(), averageBitRate); + ensure_equals("is total bytes is correct", results["bytes_downloaded"].asInteger(), totalBytes); + ensure_equals("is transport correct", results["transport"].asString(), std::string("HTTP")); + } + + // make sure averages cleared when reset is called + template<> template<> + void textureinfo_object_t::test<11>() + { + LLTextureInfo tex_info; + tex_info.setUpLogging(true, true); + + S32 requestStartTimeOne = 200; + S32 requestEndTimeOne = 400; + S32 requestSizeOne = 1024; + LLUUID id1("10e65d70-46fd-429f-841a-bf698e9424d3"); + tex_info.setRequestStartTime(id1, requestStartTimeOne); + tex_info.setRequestSize(id1, requestSizeOne); + tex_info.setRequestType(id1, LLTextureInfoDetails::REQUEST_TYPE_HTTP); + tex_info.setRequestCompleteTimeAndLog(id1, requestEndTimeOne); + + tex_info.getAverages(); + tex_info.reset(); + LLSD results = tex_info.getAverages(); + ensure_equals("is average bits per second correct", results["bits_per_second"].asInteger(), 0); + ensure_equals("is total bytes is correct", results["bytes_downloaded"].asInteger(), 0); + ensure_equals("is transport correct", results["transport"].asString(), std::string("NONE")); + } + + // make sure map item removed when expired + template<> template<> + void textureinfo_object_t::test<12>() + { + LLTextureInfo tex_info; + tex_info.setUpLogging(true, true); + + S32 requestStartTimeOne = 200; + S32 requestEndTimeOne = 400; + S32 requestSizeOne = 1024; + LLUUID id1("10e65d70-46fd-429f-841a-bf698e9424d3"); + tex_info.setRequestStartTime(id1, requestStartTimeOne); + tex_info.setRequestSize(id1, requestSizeOne); + tex_info.setRequestType(id1, LLTextureInfoDetails::REQUEST_TYPE_HTTP); + + ensure_equals("map item created", tex_info.getTextureInfoMapSize(), 1); + + tex_info.setRequestCompleteTimeAndLog(id1, requestEndTimeOne); + + ensure_equals("map item removed when consumed", tex_info.getTextureInfoMapSize(), 0); + } +} + diff --git a/indra/newview/tests/lltextureinfodetails_test.cpp b/indra/newview/tests/lltextureinfodetails_test.cpp new file mode 100644 index 0000000000..aa2697fb8e --- /dev/null +++ b/indra/newview/tests/lltextureinfodetails_test.cpp @@ -0,0 +1,98 @@ +/** + * @file llwtextureinfodetails_test.cpp + * @author Si & Gabriel + * @date 2009-03-30 + * + * $LicenseInfo:firstyear=2006&license=viewergpl$ + * + * Copyright (c) 2006-2009, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +// Precompiled header: almost always required for newview cpp files +#include "../llviewerprecompiledheaders.h" +// Class to test +#include "../lltextureinfodetails.h" +// Dependencies + +// Tut header +#include "../test/lltut.h" + +// ------------------------------------------------------------------------------------------- +// Stubbing: Declarations required to link and run the class being tested +// Notes: +// * Add here stubbed implementation of the few classes and methods used in the class to be tested +// * Add as little as possible (let the link errors guide you) +// * Do not make any assumption as to how those classes or methods work (i.e. don't copy/paste code) +// * A simulator for a class can be implemented here. Please comment and document thoroughly. + + +// End Stubbing +// ------------------------------------------------------------------------------------------- + +// ------------------------------------------------------------------------------------------- +// TUT +// ------------------------------------------------------------------------------------------- + +namespace tut +{ + // Test wrapper declarations + struct textureinfodetails_test + { + // Constructor and destructor of the test wrapper + textureinfodetails_test() + { + } + ~textureinfodetails_test() + { + } + }; + + // Tut templating thingamagic: test group, object and test instance + typedef test_group<textureinfodetails_test> textureinfodetails_t; + typedef textureinfodetails_t::object textureinfodetails_object_t; + tut::textureinfodetails_t tut_textureinfodetails("textureinfodetails"); + + + // --------------------------------------------------------------------------------------- + // Test functions + // Notes: + // * Test as many as you possibly can without requiring a full blown simulation of everything + // * The tests are executed in sequence so the test instance state may change between calls + // * Remember that you cannot test private methods with tut + // --------------------------------------------------------------------------------------- + + // --------------------------------------------------------------------------------------- + // Test the LLTextureInfo + // --------------------------------------------------------------------------------------- + + + // Test instantiation + template<> template<> + void textureinfodetails_object_t::test<1>() + { + ensure("have we crashed?", true); + } +} diff --git a/indra/newview/tests/lltexturestatsuploader_test.cpp b/indra/newview/tests/lltexturestatsuploader_test.cpp new file mode 100644 index 0000000000..77a3e2c3d8 --- /dev/null +++ b/indra/newview/tests/lltexturestatsuploader_test.cpp @@ -0,0 +1,156 @@ +/** + * @file lltexturestatsuploader_test.cpp + * @author Si + * @date 2009-05-27 + * + * $LicenseInfo:firstyear=2006&license=viewergpl$ + * + * Copyright (c) 2006-2009, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +// Precompiled header: almost always required for newview cpp files +#include "../llviewerprecompiledheaders.h" +// Class to test +#include "../lltexturestatsuploader.h" +// Dependencies + +// Tut header +#include "../test/lltut.h" + +// ------------------------------------------------------------------------------------------- +// Stubbing: Declarations required to link and run the class being tested +// Notes: +// * Add here stubbed implementation of the few classes and methods used in the class to be tested +// * Add as little as possible (let the link errors guide you) +// * Do not make any assumption as to how those classes or methods work (i.e. don't copy/paste code) +// * A simulator for a class can be implemented here. Please comment and document thoroughly. + +#include "boost/intrusive_ptr.hpp" +void boost::intrusive_ptr_add_ref(LLCurl::Responder*){} +void boost::intrusive_ptr_release(LLCurl::Responder* p){} +const F32 HTTP_REQUEST_EXPIRY_SECS = 0.0f; + +static std::string most_recent_url; +static LLSD most_recent_body; + +void LLHTTPClient::post( + const std::string& url, + const LLSD& body, + ResponderPtr, + const LLSD& headers, + const F32 timeout) +{ + // set some sensor code + most_recent_url = url; + most_recent_body = body; + return; +} + +// End Stubbing +// ------------------------------------------------------------------------------------------- + +// ------------------------------------------------------------------------------------------- +// TUT +// ------------------------------------------------------------------------------------------- + +namespace tut +{ + // Test wrapper declarations + struct texturestatsuploader_test + { + // Constructor and destructor of the test wrapper + texturestatsuploader_test() + { + most_recent_url = "some sort of default text that should never match anything the tests are expecting!"; + LLSD blank_llsd; + most_recent_body = blank_llsd; + } + ~texturestatsuploader_test() + { + } + }; + + // Tut templating thingamagic: test group, object and test instance + typedef test_group<texturestatsuploader_test> texturestatsuploader_t; + typedef texturestatsuploader_t::object texturestatsuploader_object_t; + tut::texturestatsuploader_t tut_texturestatsuploader("texturestatsuploader"); + + + // --------------------------------------------------------------------------------------- + // Test functions + // Notes: + // * Test as many as you possibly can without requiring a full blown simulation of everything + // * The tests are executed in sequence so the test instance state may change between calls + // * Remember that you cannot test private methods with tut + // --------------------------------------------------------------------------------------- + + // --------------------------------------------------------------------------------------- + // Test the LLTextureInfo + // --------------------------------------------------------------------------------------- + + + // Test instantiation + template<> template<> + void texturestatsuploader_object_t::test<1>() + { + LLTextureStatsUploader tsu; + llinfos << &tsu << llendl; + ensure("have we crashed?", true); + } + + // does it call out to the provided url if we ask it to? + template<> template<> + void texturestatsuploader_object_t::test<2>() + { + LLTextureStatsUploader tsu; + std::string url = "http://blahblahblah"; + LLSD texture_stats; + tsu.uploadStatsToSimulator(url, texture_stats); + ensure_equals("did the right url get called?", most_recent_url, url); + ensure_equals("did the right body get sent?", most_recent_body, texture_stats); + } + + // does it not call out to the provided url if we send it an ungranted cap? + template<> template<> + void texturestatsuploader_object_t::test<3>() + { + LLTextureStatsUploader tsu; + + // this url left intentionally blank to mirror + // not getting a cap in the caller. + std::string url_for_ungranted_cap = ""; + + LLSD texture_stats; + std::string most_recent_url_before_test = most_recent_url; + tsu.uploadStatsToSimulator(url_for_ungranted_cap, texture_stats); + + ensure_equals("hopefully no url got called!", most_recent_url, most_recent_url_before_test); + } + + // does it call out if the data is empty? + // should it even do that? +} + diff --git a/indra/newview/tests/llworldmap_test.cpp b/indra/newview/tests/llworldmap_test.cpp new file mode 100644 index 0000000000..56cf86f6df --- /dev/null +++ b/indra/newview/tests/llworldmap_test.cpp @@ -0,0 +1,523 @@ +/** + * @file llworldmap_test.cpp + * @author Merov Linden + * @date 2009-03-09 + * + * $LicenseInfo:firstyear=2006&license=viewergpl$ + * + * Copyright (c) 2006-2009, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +// Precompiled header: almost always required for newview cpp files +#include "../llviewerprecompiledheaders.h" +// Class to test +#include "../llworldmap.h" +// Dependencies +#include "../llviewerimagelist.h" +#include "../llworldmapmessage.h" +// Tut header +#include "../test/lltut.h" + +// ------------------------------------------------------------------------------------------- +// Stubbing: Declarations required to link and run the class being tested +// Notes: +// * Add here stubbed implementation of the few classes and methods used in the class to be tested +// * Add as little as possible (let the link errors guide you) +// * Do not make any assumption as to how those classes or methods work (i.e. don't copy/paste code) +// * A simulator for a class can be implemented here. Please comment and document thoroughly. + +// Stub image calls +LLViewerImageList::LLViewerImageList() { } +LLViewerImageList::~LLViewerImageList() { } +LLViewerImageList gImageList; +LLViewerImage* LLViewerImageList::getImage(const LLUUID &image_id, + BOOL usemipmaps, + BOOL level_immediate, + LLGLint internal_format, + LLGLenum primary_format, + LLHost request_from_host) +{ return NULL; } +void LLViewerImage::setBoostLevel(S32 level) { } +void LLImageGL::setAddressMode(LLTexUnit::eTextureAddressMode mode) { } + +// Stub related map calls +LLWorldMapMessage::LLWorldMapMessage() { } +LLWorldMapMessage::~LLWorldMapMessage() { } +void LLWorldMapMessage::sendItemRequest(U32 type, U64 handle) { } +void LLWorldMapMessage::sendMapBlockRequest(U16 min_x, U16 min_y, U16 max_x, U16 max_y, bool return_nonexistent) { } +LLWorldMipmap::LLWorldMipmap() { } +LLWorldMipmap::~LLWorldMipmap() { } +void LLWorldMipmap::reset() { } +void LLWorldMipmap::dropBoostLevels() { } +void LLWorldMipmap::equalizeBoostLevels() { } +LLPointer<LLViewerImage> LLWorldMipmap::getObjectsTile(U32 grid_x, U32 grid_y, S32 level, bool load) +{ return NULL; } + +// Stub other stuff +BOOL gPacificDaylightTime; + +// End Stubbing +// ------------------------------------------------------------------------------------------- + +// ------------------------------------------------------------------------------------------- +// TUT +// ------------------------------------------------------------------------------------------- + +const F32 X_WORLD_TEST = 1000.0f * REGION_WIDTH_METERS; +const F32 Y_WORLD_TEST = 2000.0f * REGION_WIDTH_METERS; +const F32 Z_WORLD_TEST = 240.0f; +const std::string ITEM_NAME_TEST = "Item Foo"; +const std::string TOOLTIP_TEST = "Tooltip Foo"; + +const std::string SIM_NAME_TEST = "Sim Foo"; + +namespace tut +{ + // Test wrapper declarations + struct iteminfo_test + { + // Instance to be tested + LLItemInfo* mItem; + + // Constructor and destructor of the test wrapper + iteminfo_test() + { + LLUUID id; + mItem = new LLItemInfo(X_WORLD_TEST, Y_WORLD_TEST, ITEM_NAME_TEST, id); + } + ~iteminfo_test() + { + delete mItem; + } + }; + + struct siminfo_test + { + // Instance to be tested + LLSimInfo* mSim; + + // Constructor and destructor of the test wrapper + siminfo_test() + { + U64 handle = to_region_handle_global(X_WORLD_TEST, Y_WORLD_TEST); + mSim = new LLSimInfo(handle); + } + ~siminfo_test() + { + delete mSim; + } + }; + + struct worldmap_test + { + // Instance to be tested + LLWorldMap* mWorld; + + // Constructor and destructor of the test wrapper + worldmap_test() + { + mWorld = LLWorldMap::getInstance(); + } + ~worldmap_test() + { + mWorld = NULL; + } + }; + + // Tut templating thingamagic: test group, object and test instance + typedef test_group<iteminfo_test> iteminfo_t; + typedef iteminfo_t::object iteminfo_object_t; + tut::iteminfo_t tut_iteminfo("iteminfo"); + + typedef test_group<siminfo_test> siminfo_t; + typedef siminfo_t::object siminfo_object_t; + tut::siminfo_t tut_siminfo("siminfo"); + + typedef test_group<worldmap_test> worldmap_t; + typedef worldmap_t::object worldmap_object_t; + tut::worldmap_t tut_worldmap("worldmap"); + + // --------------------------------------------------------------------------------------- + // Test functions + // Notes: + // * Test as many as you possibly can without requiring a full blown simulation of everything + // * The tests are executed in sequence so the test instance state may change between calls + // * Remember that you cannot test private methods with tut + // --------------------------------------------------------------------------------------- + + // --------------------------------------------------------------------------------------- + // Test the LLItemInfo interface + // --------------------------------------------------------------------------------------- + template<> template<> + void iteminfo_object_t::test<1>() + { + // Test 1 : setCount() / getCount() + mItem->setCount(10); + ensure("LLItemInfo::setCount() test failed", mItem->getCount() == 10); + // Test 2 : setTooltip() / getToolTip() + std::string tooltip = TOOLTIP_TEST; + mItem->setTooltip(tooltip); + ensure("LLItemInfo::setTooltip() test failed", mItem->getToolTip() == TOOLTIP_TEST); + // Test 3 : setElevation() / getGlobalPosition() + mItem->setElevation(Z_WORLD_TEST); + LLVector3d pos = mItem->getGlobalPosition(); + LLVector3d ref(X_WORLD_TEST, Y_WORLD_TEST, Z_WORLD_TEST); + ensure("LLItemInfo::getGlobalPosition() test failed", pos == ref); + // Test 4 : getName() + std::string name = mItem->getName(); + ensure("LLItemInfo::getName() test failed", name == ITEM_NAME_TEST); + // Test 5 : isName() + ensure("LLItemInfo::isName() test failed", mItem->isName(name)); + // Test 6 : getUUID() + LLUUID id; + ensure("LLItemInfo::getUUID() test failed", mItem->getUUID() == id); + // Test 7 : getRegionHandle() + U64 handle = to_region_handle_global(X_WORLD_TEST, Y_WORLD_TEST); + ensure("LLItemInfo::getRegionHandle() test failed", mItem->getRegionHandle() == handle); + } + // --------------------------------------------------------------------------------------- + // Test the LLSimInfo interface + // --------------------------------------------------------------------------------------- + // Test Setters and Accessors methods + template<> template<> + void siminfo_object_t::test<1>() + { + // Test 1 : setName() / getName() + std::string name = SIM_NAME_TEST; + mSim->setName(name); + ensure("LLSimInfo::setName() test failed", mSim->getName() == SIM_NAME_TEST); + // Test 2 : isName() + ensure("LLSimInfo::isName() test failed", mSim->isName(name)); + // Test 3 : getGlobalPos() + LLVector3 local; + LLVector3d ref(X_WORLD_TEST, Y_WORLD_TEST, 0.0f); + LLVector3d pos = mSim->getGlobalPos(local); + ensure("LLSimInfo::getGlobalPos() test failed", pos == ref); + // Test 4 : getGlobalOrigin() + pos = mSim->getGlobalOrigin(); + ensure("LLSimInfo::getGlobalOrigin() test failed", pos == ref); + // Test 5 : clearImage() + try { + mSim->clearImage(); + } catch (...) { + fail("LLSimInfo::clearImage() test failed"); + } + // Test 6 : dropImagePriority() + try { + mSim->dropImagePriority(); + } catch (...) { + fail("LLSimInfo::dropImagePriority() test failed"); + } + // Test 7 : updateAgentCount() + try { + mSim->updateAgentCount(0.0f); + } catch (...) { + fail("LLSimInfo::updateAgentCount() test failed"); + } + // Test 8 : getAgentCount() + S32 agents = mSim->getAgentCount(); + ensure("LLSimInfo::getAgentCount() test failed", agents == 0); + // Test 9 : setLandForSaleImage() / getLandForSaleImage() + LLUUID id; + mSim->setLandForSaleImage(id); + LLPointer<LLViewerImage> image = mSim->getLandForSaleImage(); + ensure("LLSimInfo::getLandForSaleImage() test failed", image.isNull()); + // Test 10 : isPG() + mSim->setAccess(SIM_ACCESS_PG); + ensure("LLSimInfo::isPG() test failed", mSim->isPG()); + // Test 11 : isDown() + mSim->setAccess(SIM_ACCESS_DOWN); + ensure("LLSimInfo::isDown() test failed", mSim->isDown()); + // Test 12 : Access strings can't be accessed from unit test... + //ensure("LLSimInfo::getAccessString() test failed", mSim->getAccessString() == "Offline"); + // Test 13 : Region strings can't be accessed from unit test... + //mSim->setRegionFlags(REGION_FLAGS_SANDBOX); + //ensure("LLSimInfo::setRegionFlags() test failed", mSim->getFlagsString() == "Sandbox"); + } + // Test management of LLInfoItem lists + template<> template<> + void siminfo_object_t::test<2>() + { + // Test 14 : clearItems() + try { + mSim->clearItems(); + } catch (...) { + fail("LLSimInfo::clearItems() at init test failed"); + } + + // Test 15 : Verify that all the lists are empty + LLSimInfo::item_info_list_t list; + list = mSim->getTeleHub(); + ensure("LLSimInfo::getTeleHub() empty at init test failed", list.empty()); + list = mSim->getInfoHub(); + ensure("LLSimInfo::getInfoHub() empty at init test failed", list.empty()); + list = mSim->getPGEvent(); + ensure("LLSimInfo::getPGEvent() empty at init test failed", list.empty()); + list = mSim->getMatureEvent(); + ensure("LLSimInfo::getMatureEvent() empty at init test failed", list.empty()); + list = mSim->getLandForSale(); + ensure("LLSimInfo::getLandForSale() empty at init test failed", list.empty()); + list = mSim->getAgentLocation(); + ensure("LLSimInfo::getAgentLocation() empty at init test failed", list.empty()); + + // Create an item to be inserted + LLUUID id; + LLItemInfo item(X_WORLD_TEST, Y_WORLD_TEST, ITEM_NAME_TEST, id); + + // Insert the item in each list + mSim->insertTeleHub(item); + mSim->insertInfoHub(item); + mSim->insertPGEvent(item); + mSim->insertMatureEvent(item); + mSim->insertLandForSale(item); + mSim->insertAgentLocation(item); + + // Test 16 : Verify that the lists contain 1 item each + list = mSim->getTeleHub(); + ensure("LLSimInfo::insertTeleHub() test failed", list.size() == 1); + list = mSim->getInfoHub(); + ensure("LLSimInfo::insertInfoHub() test failed", list.size() == 1); + list = mSim->getPGEvent(); + ensure("LLSimInfo::insertPGEvent() test failed", list.size() == 1); + list = mSim->getMatureEvent(); + ensure("LLSimInfo::insertMatureEvent() test failed", list.size() == 1); + list = mSim->getLandForSale(); + ensure("LLSimInfo::insertLandForSale() test failed", list.size() == 1); + list = mSim->getAgentLocation(); + ensure("LLSimInfo::insertAgentLocation() test failed", list.size() == 1); + + // Test 17 : clearItems() + try { + mSim->clearItems(); + } catch (...) { + fail("LLSimInfo::clearItems() at end test failed"); + } + + // Test 18 : Verify that all the lists are empty again... *except* agent which is persisted!! (on purpose) + list = mSim->getTeleHub(); + ensure("LLSimInfo::getTeleHub() empty after clear test failed", list.empty()); + list = mSim->getInfoHub(); + ensure("LLSimInfo::getInfoHub() empty after clear test failed", list.empty()); + list = mSim->getPGEvent(); + ensure("LLSimInfo::getPGEvent() empty after clear test failed", list.empty()); + list = mSim->getMatureEvent(); + ensure("LLSimInfo::getMatureEvent() empty after clear test failed", list.empty()); + list = mSim->getLandForSale(); + ensure("LLSimInfo::getLandForSale() empty after clear test failed", list.empty()); + list = mSim->getAgentLocation(); + ensure("LLSimInfo::getAgentLocation() empty after clear test failed", list.size() == 1); + } + + // --------------------------------------------------------------------------------------- + // Test the LLWorldMap interface + // --------------------------------------------------------------------------------------- + // Test Setters and Accessors methods + template<> template<> + void worldmap_object_t::test<1>() + { + // Test 1 : reset() + try { + mWorld->reset(); + } catch (...) { + fail("LLWorldMap::reset() at init test failed"); + } + // Test 2 : clearImageRefs() + try { + mWorld->clearImageRefs(); + } catch (...) { + fail("LLWorldMap::clearImageRefs() test failed"); + } + // Test 3 : dropImagePriorities() + try { + mWorld->dropImagePriorities(); + } catch (...) { + fail("LLWorldMap::dropImagePriorities() test failed"); + } + // Test 4 : reloadItems() + try { + mWorld->reloadItems(true); + } catch (...) { + fail("LLWorldMap::reloadItems() test failed"); + } + // Test 5 : updateRegions() + try { + mWorld->updateRegions(1000, 1000, 1004, 1004); + } catch (...) { + fail("LLWorldMap::updateRegions() test failed"); + } + // Test 6 : equalizeBoostLevels() + try { + mWorld->equalizeBoostLevels(); + } catch (...) { + fail("LLWorldMap::equalizeBoostLevels() test failed"); + } + // Test 7 : getObjectsTile() + try { + LLPointer<LLViewerImage> image = mWorld->getObjectsTile((U32)(X_WORLD_TEST/REGION_WIDTH_METERS), (U32)(Y_WORLD_TEST/REGION_WIDTH_METERS), 1); + ensure("LLWorldMap::getObjectsTile() failed", image.isNull()); + } catch (...) { + fail("LLWorldMap::getObjectsTile() test failed with exception"); + } + } + // Test management of LLSimInfo lists + template<> template<> + void worldmap_object_t::test<2>() + { + // Test 8 : reset() + try { + mWorld->reset(); + } catch (...) { + fail("LLWorldMap::reset() at init test failed"); + } + + // Test 9 : Verify that all the region list is empty + LLWorldMap::sim_info_map_t list; + list = mWorld->getRegionMap(); + ensure("LLWorldMap::getRegionMap() empty at init test failed", list.empty()); + + // Test 10 : Insert a region + bool success; + LLUUID id; + std::string name_sim = SIM_NAME_TEST; + success = mWorld->insertRegion( U32(X_WORLD_TEST), + U32(Y_WORLD_TEST), + name_sim, + id, + SIM_ACCESS_PG, + REGION_FLAGS_SANDBOX); + list = mWorld->getRegionMap(); + ensure("LLWorldMap::insertRegion() failed", success && (list.size() == 1)); + + // Test 11 : Insert an item in the same region -> number of regions doesn't increase + std::string name_item = ITEM_NAME_TEST; + success = mWorld->insertItem( U32(X_WORLD_TEST + REGION_WIDTH_METERS/2), + U32(Y_WORLD_TEST + REGION_WIDTH_METERS/2), + name_item, + id, + MAP_ITEM_LAND_FOR_SALE, + 0, 0); + list = mWorld->getRegionMap(); + ensure("LLWorldMap::insertItem() in existing region failed", success && (list.size() == 1)); + + // Test 12 : Insert an item in another region -> number of regions increases + success = mWorld->insertItem( U32(X_WORLD_TEST + REGION_WIDTH_METERS*2), + U32(Y_WORLD_TEST + REGION_WIDTH_METERS*2), + name_item, + id, + MAP_ITEM_LAND_FOR_SALE, + 0, 0); + list = mWorld->getRegionMap(); + ensure("LLWorldMap::insertItem() in unexisting region failed", success && (list.size() == 2)); + + // Test 13 : simInfoFromPosGlobal() in region + LLVector3d pos1( X_WORLD_TEST + REGION_WIDTH_METERS*2 + REGION_WIDTH_METERS/2, + Y_WORLD_TEST + REGION_WIDTH_METERS*2 + REGION_WIDTH_METERS/2, + 0.0f); + LLSimInfo* sim; + sim = mWorld->simInfoFromPosGlobal(pos1); + ensure("LLWorldMap::simInfoFromPosGlobal() test on existing region failed", sim != NULL); + + // Test 14 : simInfoFromPosGlobal() outside region + LLVector3d pos2( X_WORLD_TEST + REGION_WIDTH_METERS*4 + REGION_WIDTH_METERS/2, + Y_WORLD_TEST + REGION_WIDTH_METERS*4 + REGION_WIDTH_METERS/2, + 0.0f); + sim = mWorld->simInfoFromPosGlobal(pos2); + ensure("LLWorldMap::simInfoFromPosGlobal() test outside region failed", sim == NULL); + + // Test 15 : simInfoFromName() + sim = mWorld->simInfoFromName(name_sim); + ensure("LLWorldMap::simInfoFromName() test on existing region failed", sim != NULL); + + // Test 16 : simInfoFromHandle() + U64 handle = to_region_handle_global(X_WORLD_TEST, Y_WORLD_TEST); + sim = mWorld->simInfoFromHandle(handle); + ensure("LLWorldMap::simInfoFromHandle() test on existing region failed", sim != NULL); + + // Test 17 : simNameFromPosGlobal() + LLVector3d pos3( X_WORLD_TEST + REGION_WIDTH_METERS/2, + Y_WORLD_TEST + REGION_WIDTH_METERS/2, + 0.0f); + success = mWorld->simNameFromPosGlobal(pos3, name_sim); + ensure("LLWorldMap::simNameFromPosGlobal() test on existing region failed", success && (name_sim == SIM_NAME_TEST)); + + // Test 18 : reset() + try { + mWorld->reset(); + } catch (...) { + fail("LLWorldMap::reset() at end test failed"); + } + + // Test 19 : Verify that all the region list is empty + list = mWorld->getRegionMap(); + ensure("LLWorldMap::getRegionMap() empty at end test failed", list.empty()); + } + // Test tracking + template<> template<> + void worldmap_object_t::test<3>() + { + // Point to track + LLVector3d pos( X_WORLD_TEST + REGION_WIDTH_METERS/2, Y_WORLD_TEST + REGION_WIDTH_METERS/2, Z_WORLD_TEST); + + // Test 20 : no tracking + mWorld->cancelTracking(); + ensure("LLWorldMap::cancelTracking() at begin test failed", mWorld->isTracking() == false); + + // Test 21 : set tracking + mWorld->setTracking(pos); + ensure("LLWorldMap::setTracking() failed", mWorld->isTracking() && !mWorld->isTrackingValidLocation()); + + // Test 22 : set click and commit flags + mWorld->setTrackingDoubleClick(); + ensure("LLWorldMap::setTrackingDoubleClick() failed", mWorld->isTrackingDoubleClick()); + mWorld->setTrackingCommit(); + ensure("LLWorldMap::setTrackingCommit() failed", mWorld->isTrackingCommit()); + + // Test 23 : in rectangle test + bool inRect = mWorld->isTrackingInRectangle( X_WORLD_TEST, Y_WORLD_TEST, + X_WORLD_TEST + REGION_WIDTH_METERS, + Y_WORLD_TEST + REGION_WIDTH_METERS); + ensure("LLWorldMap::isTrackingInRectangle() in rectangle failed", inRect); + inRect = mWorld->isTrackingInRectangle( X_WORLD_TEST + REGION_WIDTH_METERS, + Y_WORLD_TEST + REGION_WIDTH_METERS, + X_WORLD_TEST + 2 * REGION_WIDTH_METERS, + Y_WORLD_TEST + 2 * REGION_WIDTH_METERS); + ensure("LLWorldMap::isTrackingInRectangle() outside rectangle failed", !inRect); + + // Test 24 : set tracking to valid and invalid + mWorld->setTrackingValid(); + ensure("LLWorldMap::setTrackingValid() failed", mWorld->isTrackingValidLocation() && !mWorld->isTrackingInvalidLocation()); + mWorld->setTrackingInvalid(); + ensure("LLWorldMap::setTrackingInvalid() failed", !mWorld->isTrackingValidLocation() && mWorld->isTrackingInvalidLocation()); + + // Test 25 : getTrackedPositionGlobal() + LLVector3d res = mWorld->getTrackedPositionGlobal(); + ensure("LLWorldMap::getTrackedPositionGlobal() failed", res == pos); + + // Test 26 : reset tracking + mWorld->cancelTracking(); + ensure("LLWorldMap::cancelTracking() at end test failed", mWorld->isTracking() == false); + } +} diff --git a/indra/newview/tests/llworldmipmap_test.cpp b/indra/newview/tests/llworldmipmap_test.cpp new file mode 100644 index 0000000000..9938175c55 --- /dev/null +++ b/indra/newview/tests/llworldmipmap_test.cpp @@ -0,0 +1,176 @@ +/** + * @file llworldmipmap_test.cpp + * @author Merov Linden + * @date 2009-02-03 + * + * $LicenseInfo:firstyear=2006&license=viewergpl$ + * + * Copyright (c) 2006-2009, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +// Precompiled header: almost always required for newview cpp files +#include "../llviewerprecompiledheaders.h" +// Class to test +#include "../llworldmipmap.h" +// Dependencies +#include "../llviewerimagelist.h" +// Tut header +#include "../test/lltut.h" + +// ------------------------------------------------------------------------------------------- +// Stubbing: Declarations required to link and run the class being tested +// Notes: +// * Add here stubbed implementation of the few classes and methods used in the class to be tested +// * Add as little as possible (let the link errors guide you) +// * Do not make any assumption as to how those classes or methods work (i.e. don't copy/paste code) +// * A simulator for a class can be implemented here. Please comment and document thoroughly. + +LLViewerImageList::LLViewerImageList() { } +LLViewerImageList::~LLViewerImageList() { } + +LLViewerImageList gImageList; + +LLViewerImage* LLViewerImageList::getImageFromUrl(const std::string& url, + BOOL usemipmaps, + BOOL level_immediate, + LLGLint internal_format, + LLGLenum primary_format, + const LLUUID& force_id) +{ return NULL; } +void LLViewerImage::setBoostLevel(S32 level) { } + +// End Stubbing +// ------------------------------------------------------------------------------------------- + +// ------------------------------------------------------------------------------------------- +// TUT +// ------------------------------------------------------------------------------------------- +namespace tut +{ + // Test wrapper declaration + struct worldmipmap_test + { + // Derived test class + class LLTestWorldMipmap : public LLWorldMipmap + { + // Put here stubbs of virtual methods we shouldn't call all the way down + }; + // Instance to be tested + LLTestWorldMipmap* mMap; + + // Constructor and destructor of the test wrapper + worldmipmap_test() + { + mMap = new LLTestWorldMipmap; + } + ~worldmipmap_test() + { + delete mMap; + } + }; + + // Tut templating thingamagic: test group, object and test instance + typedef test_group<worldmipmap_test> worldmipmap_t; + typedef worldmipmap_t::object worldmipmap_object_t; + tut::worldmipmap_t tut_worldmipmap("worldmipmap"); + + // --------------------------------------------------------------------------------------- + // Test functions + // Notes: + // * Test as many as you possibly can without requiring a full blown simulation of everything + // * The tests are executed in sequence so the test instance state may change between calls + // * Remember that you cannot test private methods with tut + // --------------------------------------------------------------------------------------- + // Test static methods + // Test 1 : scaleToLevel() + template<> template<> + void worldmipmap_object_t::test<1>() + { + S32 level = mMap->scaleToLevel(0.0); + ensure("scaleToLevel() test 1 failed", level == LLWorldMipmap::MAP_LEVELS); + level = mMap->scaleToLevel(LLWorldMipmap::MAP_TILE_SIZE); + ensure("scaleToLevel() test 2 failed", level == 1); + level = mMap->scaleToLevel(10 * LLWorldMipmap::MAP_TILE_SIZE); + ensure("scaleToLevel() test 3 failed", level == 1); + } + // Test 2 : globalToMipmap() + template<> template<> + void worldmipmap_object_t::test<2>() + { + U32 grid_x, grid_y; + mMap->globalToMipmap(1000.f*REGION_WIDTH_METERS, 1000.f*REGION_WIDTH_METERS, 1, &grid_x, &grid_y); + ensure("globalToMipmap() test 1 failed", (grid_x == 1000) && (grid_y == 1000)); + mMap->globalToMipmap(0.0, 0.0, LLWorldMipmap::MAP_LEVELS, &grid_x, &grid_y); + ensure("globalToMipmap() test 2 failed", (grid_x == 0) && (grid_y == 0)); + } + // Test 3 : getObjectsTile() + template<> template<> + void worldmipmap_object_t::test<3>() + { + // Depends on some inline methods in LLViewerImage... Thinking about how to make this work + // LLPointer<LLViewerImage> img = mMap->getObjectsTile(0, 0, 1); + // ensure("getObjectsTile() test failed", img.isNull()); + } + // Test 4 : equalizeBoostLevels() + template<> template<> + void worldmipmap_object_t::test<4>() + { + try + { + mMap->equalizeBoostLevels(); + } + catch (...) + { + fail("equalizeBoostLevels() test failed"); + } + } + // Test 5 : dropBoostLevels() + template<> template<> + void worldmipmap_object_t::test<5>() + { + try + { + mMap->dropBoostLevels(); + } + catch (...) + { + fail("dropBoostLevels() test failed"); + } + } + // Test 6 : reset() + template<> template<> + void worldmipmap_object_t::test<6>() + { + try + { + mMap->reset(); + } + catch (...) + { + fail("reset() test failed"); + } + } +} |